WIP: www/nginx: add njs script to detect MitM attacks on TLS connections (#1070)

* www/nginx: add njs script to detect MitM attacks on TLS connections
* www/nginx: prepare config for njs / tls intercept detection
This commit is contained in:
Fabian Franz BSc
2018-12-16 20:57:57 +01:00
committed by GitHub
parent 2d80e4492c
commit fe92693622
4 changed files with 66 additions and 0 deletions
@@ -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;
}
@@ -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;
@@ -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 %};
@@ -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;