opnsense-plugins/www/nginx/src/opnsense/scripts/nginx/tls_ua_fingerprint.php

124 lines
4.0 KiB
PHP
Executable File

#!/usr/local/bin/php
<?php
/*
* Copyright (C) 2018 Fabian Franz
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
* INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
* AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
* AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
* OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
$tls_logfile = '/var/log/nginx/tls_handshake.log.work';
$database_name = '/var/log/nginx/handshakes.json';
function parse_line($line)
{
$tmp = explode('"', trim($line));
return array(
'ua' => $tmp[1],
'ciphers' => $tmp[3],
'curves' => $tmp[5] == '-' ? '' : $tmp[5],
'count' => 1
);
}
function filter_ua($key)
{
return $key != 'ua';
}
if (!file_exists($tls_logfile)) {
echo "logfile $tls_logfile does not exist\n";
exit(0);
}
$fingerprints_old = @json_decode(@file_get_contents($database_name), true);
if (!is_array($fingerprints_old)) {
$fingerprints_old = array();
}
// parse all finger prints and count them
$fingerprints = array();
$handle = @fopen($tls_logfile, 'r');
if ($handle) {
while (($buffer = fgets($handle)) !== false) {
$md5line = md5($buffer);
if (array_key_exists($md5line, $fingerprints)) {
$fingerprints[$md5line]['count']++;
} else {
$parsed_line = parse_line($buffer);
if ($parsed_line['ciphers'] != '-') {
$fingerprints[$md5line] = $parsed_line;
}
}
}
fclose($handle);
}
unset($handle);
// group by user agent
$fingerprints2 = array();
foreach ($fingerprints as $fingerprint) {
$user_agent = $fingerprint['ua'];
if (isset($fingerprints2[$user_agent]) && !is_array($fingerprints2[$user_agent])) {
$fingerprints2[$user_agent] = array();
}
$hashkey = md5($fingerprint['ciphers'] . $fingerprint['curves']);
$fingerprints2[$user_agent][$hashkey] = array_filter($fingerprint, 'filter_ua', ARRAY_FILTER_USE_KEY);
}
// free some memory so we have more to use
unset($fingerprints);
unset($hashkey);
unset($md5line);
// remove the hash key
$fingerprints = array();
foreach ($fingerprints2 as $ua => $fingerprint_data) {
$fingerprints[$ua] = array_values($fingerprint_data);
}
unset($fingerprints2);
foreach ($fingerprints_old as $ua_old => $fingerprint_data_old) {
if (isset($fingerprints[$ua_old])) {
foreach ($fingerprint_data_old as &$fpdo) {
$changed = false;
foreach ($fingerprints[$ua_old] as &$fingerprint_new) {
if ($fpdo['ciphers'] == $fingerprint_new['ciphers'] && $fpdo['curves'] == $fingerprint_new['curves']) {
$changed = true;
$fingerprint_new['count'] = $fingerprint_new['count'] + $fpdo['count'];
break;
}
}
if (!$changed) {
$fingerprints[$ua_old][] = $fpdo;
}
}
} else {
// the new log does not have it, apply the old one
$fingerprints[$ua_old] = $fingerprint_data_old;
}
}
file_put_contents($database_name, json_encode($fingerprints));
unlink($tls_logfile);