diff --git a/www/nginx/src/opnsense/scripts/nginx/ngx_functions.js b/www/nginx/src/opnsense/scripts/nginx/ngx_functions.js new file mode 100644 index 000000000..2775845de --- /dev/null +++ b/www/nginx/src/opnsense/scripts/nginx/ngx_functions.js @@ -0,0 +1,60 @@ +var fs = require('fs'); +var tls_fingerprints = JSON.parse(fs.readFileSync('/usr/local/etc/nginx/tls_fingerprints.json')); + +function customArrayIndexOf(haystack, needle) { + var element, element_id; + for (element_id in haystack) { + element = haystack[element_id]; + if (element == needle) return element_id; + } + return -1; +} + +function check_cipher_array(r, browser_ciphers, fingerprint_ciphers, result) { + if (result.status == 'Intercepted') { + return; + } + if (browser_ciphers.length > fingerprint_ciphers.length) { + // the proxy supports more cipers than the browser -> intercepted + result.status = "Intercepted"; + return; + } + var browser_cipher; + var browser_cipher_index; + var last_index = -1; + var current_index; + for (browser_cipher_index in browser_ciphers) { + browser_cipher = browser_ciphers[browser_cipher_index]; + current_index = customArrayIndexOf(fingerprint_ciphers, browser_cipher); + if (current_index === -1 || current_index <= last_index) { + // a cipher has been found, which is not supported by the browser + // such a connection is definitly intercepted + result.status = "Intercepted"; + //result.status = JSON.stringify(fingerprint_ciphers[0].toBytes().toString('hex')); + return; + } + last_index = current_index; + } + if (result.status == 'Unknown') { + result.status = browser_ciphers.length === fingerprint_ciphers.length ? 'Original' : 'Hardened' + } +} + +function check_intercept(r) { + var tls_result = {'status': 'Unknown'}; + if (r.headersIn['User-Agent'] && r.variables.ssl_ciphers != '') { + var ua = r.headersIn['User-Agent']; + if (ua in tls_fingerprints) { + var fp = tls_fingerprints[ua]; + var browser_ciphers = r.variables.ssl_ciphers.split(':'); + check_cipher_array(r, browser_ciphers, fp.ciphers, tls_result); + if (r.variables.ssl_curves != '') + { + var browser_curves = r.variables.ssl_curves.split(':'); + check_cipher_array(r, browser_curves, fp.curves, tls_result); + } + } + } + return tls_result.status; +} + diff --git a/www/nginx/src/opnsense/service/templates/OPNsense/Nginx/http.conf b/www/nginx/src/opnsense/service/templates/OPNsense/Nginx/http.conf index c677ef93e..53d1f0dee 100644 --- a/www/nginx/src/opnsense/service/templates/OPNsense/Nginx/http.conf +++ b/www/nginx/src/opnsense/service/templates/OPNsense/Nginx/http.conf @@ -11,6 +11,9 @@ log_format anonymized ':: - $remote_user [$time_local] "$request" ' '"$http_user_agent" "$http_x_forwarded_for"'; #tcp_nopush on; +# https intercept detection +js_include /usr/local/opnsense/scripts/nginx/ngx_functions.js; +js_set $tls_intercepted check_intercept; # 200M should be big enough for file servers etc. client_max_body_size 200M; diff --git a/www/nginx/src/opnsense/service/templates/OPNsense/Nginx/location.conf b/www/nginx/src/opnsense/service/templates/OPNsense/Nginx/location.conf index d690e4028..366492fdc 100644 --- a/www/nginx/src/opnsense/service/templates/OPNsense/Nginx/location.conf +++ b/www/nginx/src/opnsense/service/templates/OPNsense/Nginx/location.conf @@ -84,6 +84,7 @@ location {{ location.matchtype }} {{ location.urlpattern }} { fastcgi_param TLS-Cipher $ssl_cipher; fastcgi_param TLS-Protocol $ssl_protocol; fastcgi_param TLS-SNI-Host $ssl_server_name; + fastcgi_param TLS-Client-Intercepted $tls_intercepted; fastcgi_intercept_errors off; {% if location.upstream is not defined %} fastcgi_pass unix:/var/run/php-www.socket; @@ -135,6 +136,7 @@ location {{ location.matchtype }} {{ location.urlpattern }} { proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto $scheme; + proxy_set_header X-TLS-Client-Intercepted $tls_intercepted; proxy_ignore_client_abort {% if location.proxy_ignore_client_abort == '1' %}on{% else %}off{% endif %}; proxy_request_buffering {% if location.proxy_request_buffering == '1' %}on{% else %}off{% endif %}; proxy_buffering {% if location.proxy_buffering == '1' %}on{% else %}off{% endif %}; diff --git a/www/nginx/src/opnsense/service/templates/OPNsense/Nginx/nginx.conf b/www/nginx/src/opnsense/service/templates/OPNsense/Nginx/nginx.conf index 596bd3cfb..c9592a5d1 100644 --- a/www/nginx/src/opnsense/service/templates/OPNsense/Nginx/nginx.conf +++ b/www/nginx/src/opnsense/service/templates/OPNsense/Nginx/nginx.conf @@ -4,6 +4,7 @@ load_module /usr/local/libexec/nginx/ngx_http_naxsi_module.so; load_module /usr/local/libexec/nginx/ngx_mail_module.so; load_module /usr/local/libexec/nginx/ngx_http_brotli_filter_module.so; load_module /usr/local/libexec/nginx/ngx_http_brotli_static_module.so; +load_module /usr/local/libexec/nginx/ngx_http_js_module.so; user www staff; worker_processes 1;