@anonympins/fingerprint 0.3.8 → 0.4.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (58) hide show
  1. package/CHANGELOG.md +94 -0
  2. package/README.md +60 -53
  3. package/composer.json +38 -38
  4. package/index.js +4 -4
  5. package/package.json +103 -103
  6. package/phpunit.xml +20 -20
  7. package/public/fp.js +1 -1
  8. package/public/fp.wasm +0 -0
  9. package/src/js/build-client.js +1 -1
  10. package/src/js/fingerprint.client.js +2 -0
  11. package/src/js/fingerprint.js +4429 -4381
  12. package/src/js/mongodb-store.js +79 -79
  13. package/src/js/pow.solver.inline.js +31 -0
  14. package/src/js/pow.solver.js +31 -0
  15. package/src/js/tests/fingerprint.builder.test.js +79 -0
  16. package/src/js/tests/fingerprint.client.init.test.js +120 -0
  17. package/src/js/tests/fingerprint.client.test.js +105 -0
  18. package/src/js/tests/fingerprint.engine.test.js +371 -0
  19. package/src/js/tests/fingerprint.isMalicious.test.js +117 -0
  20. package/src/js/tests/fingerprint.test.js +2319 -0
  21. package/src/js/tests/ip-reputation.test.js +132 -0
  22. package/src/js/tests/ja3AnomalyDetector.test.js +135 -0
  23. package/src/js/tests/library.test.js +96 -0
  24. package/src/js/tests/metrics.test.js +104 -0
  25. package/src/js/tests/pow.solver.test.js +198 -0
  26. package/src/js/tests/problem-manager.test.js +323 -0
  27. package/src/js/tests/stores.test.js +118 -0
  28. package/src/php/Challenge/ChallengeUtils.php +361 -361
  29. package/src/php/Config/SecurityProfiles.php +271 -266
  30. package/src/php/FingerprintBuilder.php +185 -185
  31. package/src/php/FingerprintClient.php +131 -131
  32. package/src/php/FingerprintEngine.php +1006 -1006
  33. package/src/php/Ja3AnomalyDetector.php +227 -227
  34. package/src/php/Optimization/FunctionRegistry.php +62 -62
  35. package/src/php/Optimization/Optimization.php +255 -255
  36. package/src/php/Optimization/OptimizationOperators.php +304 -304
  37. package/src/php/Store/InMemoryStore.php +66 -66
  38. package/src/php/Store/MongoDbStore.php +104 -104
  39. package/src/php/Store/RedisStore.php +53 -53
  40. package/src/php/Tests/ChallengeUtilsTest.php +81 -81
  41. package/src/php/Tests/FingerprintBuilderTest.php +57 -57
  42. package/src/php/Tests/FingerprintClientTest.php +71 -0
  43. package/src/php/Tests/FingerprintEngineTest.php +299 -299
  44. package/src/php/Tests/IpReputationTest.php +156 -156
  45. package/src/php/Tests/Ja3AnomalyDetectorTest.php +179 -179
  46. package/src/php/Tests/MetricsTest.php +45 -45
  47. package/src/php/Tests/PowTest.php +39 -39
  48. package/src/php/Tests/ProblemManagerTest.php +296 -296
  49. package/src/php/Tests/RequestUtilsTest.php +253 -145
  50. package/src/php/Tests/TLSClientHelloParserTest.php +118 -0
  51. package/src/php/Tests/problems.config.json +8 -8
  52. package/src/php/Utils/BigInt.php +144 -144
  53. package/src/php/Utils/Logger.php +29 -29
  54. package/src/php/Utils/MaliciousPatterns.php +58 -58
  55. package/src/php/Utils/MetricsManager.php +166 -166
  56. package/src/php/Utils/RequestUtils.php +1169 -1169
  57. package/src/php/Utils/TLSClientHelloParser.php +117 -0
  58. package/src/php/bin/auto-tune.php +117 -117
@@ -0,0 +1,117 @@
1
+ <?php
2
+
3
+ declare(strict_types=1);
4
+
5
+ namespace Anonympins\Fingerprint\Utils;
6
+
7
+ /**
8
+ * Décode nativement un paquet TLS Client Hello binaire pour calculer l'empreinte JA3/JA4.
9
+ */
10
+ class TLSClientHelloParser
11
+ {
12
+ private const GREASE_VALUES = [
13
+ 2570, 6682, 10794, 14906, 19018, 23130, 27242, 31354,
14
+ 35466, 39578, 43690, 47802, 51914, 55926, 60038, 64150
15
+ ];
16
+
17
+ /**
18
+ * Parse le Client Hello brut et retourne l'empreinte JA3 et une approximation JA4.
19
+ *
20
+ * @param string $binary Le premier paquet TCP reçu sur la socket.
21
+ * @return array{ja3_string: string, ja3_hash: string, ja4_raw: string}|null
22
+ */
23
+ public static function parse(string $binary): ?array
24
+ {
25
+ $len = strlen($binary);
26
+ if ($len < 43) {
27
+ return null; // Paquet trop court
28
+ }
29
+
30
+ // 1. Vérification du Record Layer Type (0x16 = Handshake)
31
+ if (ord($binary[0]) !== 0x16) {
32
+ return null;
33
+ }
34
+
35
+ // 2. Vérification du Handshake Type (0x01 = Client Hello)
36
+ if (ord($binary[5]) !== 0x01) {
37
+ return null;
38
+ }
39
+
40
+ $offset = 43; // Sauter l'en-tête, la version et le Random Client (32 octets)
41
+ if ($len < $offset + 1) return null;
42
+
43
+ // 3. Lecture du Session ID
44
+ $sessionLen = ord($binary[$offset]);
45
+ $offset += 1 + $sessionLen;
46
+ if ($len < $offset + 2) return null;
47
+
48
+ // 4. Lecture des Cipher Suites
49
+ $ciphersLen = unpack('n', substr($binary, $offset, 2))[1];
50
+ $offset += 2;
51
+ if ($len < $offset + $ciphersLen + 1) return null;
52
+
53
+ $ciphers = [];
54
+ for ($i = 0; $i < $ciphersLen; $i += 2) {
55
+ $ciphers[] = unpack('n', substr($binary, $offset + $i, 2))[1];
56
+ }
57
+ $offset += $ciphersLen;
58
+
59
+ // 5. Lecture des Compression Methods
60
+ $compressionLen = ord($binary[$offset]);
61
+ $offset += 1 + $compressionLen;
62
+ if ($len < $offset + 2) return null;
63
+
64
+ // 6. Lecture des Extensions
65
+ $extensionsLen = unpack('n', substr($binary, $offset, 2))[1];
66
+ $offset += 2;
67
+
68
+ $extensions = [];
69
+ $curves = [];
70
+ $points = [];
71
+
72
+ $extLimit = $offset + $extensionsLen;
73
+ while ($offset < $extLimit && $offset + 4 <= $len) {
74
+ $extType = unpack('n', substr($binary, $offset, 2))[1];
75
+ $extLen = unpack('n', substr($binary, $offset + 2, 2))[1];
76
+ $offset += 4;
77
+
78
+ if ($offset + $extLen > $len) break;
79
+
80
+ $extensions[] = $extType;
81
+
82
+ if ($extType === 10) { // Extension Supported Groups (Elliptic Curves)
83
+ if ($extLen >= 2) {
84
+ $curvesLen = unpack('n', substr($binary, $offset, 2))[1];
85
+ for ($j = 2; $j < $curvesLen + 2; $j += 2) {
86
+ $curves[] = unpack('n', substr($binary, $offset + $j, 2))[1];
87
+ }
88
+ }
89
+ } elseif ($extType === 11) { // Extension EC Point Formats
90
+ if ($extLen >= 1) {
91
+ $pointsLen = ord($binary[$offset]);
92
+ for ($j = 1; $j < $pointsLen + 1; $j++) {
93
+ $points[] = ord($binary[$offset + $j]);
94
+ }
95
+ }
96
+ }
97
+ $offset += $extLen;
98
+ }
99
+
100
+ // Nettoyage des valeurs GREASE (RFC 8701) pour la conformité JA3
101
+ $filterGrease = fn(array $arr) => array_values(array_filter($arr, fn($v) => !in_array($v, self::GREASE_VALUES, true)));
102
+
103
+ $sslVersion = unpack('n', substr($binary, 9, 2))[1];
104
+ $ja3String = implode(',', [
105
+ $sslVersion,
106
+ implode('-', $filterGrease($ciphers)),
107
+ implode('-', $filterGrease($extensions)),
108
+ implode('-', $filterGrease($curves)),
109
+ implode('-', $filterGrease($points))
110
+ ]);
111
+
112
+ return [
113
+ 'ja3_string' => $ja3String,
114
+ 'ja3_hash' => md5($ja3String)
115
+ ];
116
+ }
117
+ }
@@ -1,118 +1,118 @@
1
- <?php
2
-
3
- declare(strict_types=1);
4
-
5
- /**
6
- * CLI Script d'auto-tuning périodique des configurations de sécurité.
7
- * Ce script lit la configuration courante, récupère les logs, les assainit,
8
- * exécute l'optimiseur génétique, et écrase le fichier JSON d'origine.
9
- */
10
-
11
- $autoloaderPaths = [
12
- __DIR__ . '/../../vendor/autoload.php',
13
- __DIR__ . '/../vendor/autoload.php',
14
- __DIR__ . '/../../../autoload.php',
15
- ];
16
-
17
- $autoloaded = false;
18
- foreach ($autoloaderPaths as $path) {
19
- if (file_exists($path)) {
20
- require_once $path;
21
- $autoloaded = true;
22
- break;
23
- }
24
- }
25
-
26
- if (!$autoloaded) {
27
- fwrite(STDERR, "Erreur : Impossible de charger l'autoloader. Exécutez 'composer install'.\n");
28
- exit(1);
29
- }
30
-
31
- use Anonympins\Fingerprint\Optimization\Optimization;
32
- use Anonympins\Fingerprint\Store\StoreManager;
33
- use Anonympins\Fingerprint\Utils\RequestUtils;
34
-
35
- // 1. Récupération des arguments CLI
36
- $configPath = $argv[1] ?? null;
37
- if (!$configPath) {
38
- echo "Usage: php auto-tune.php [chemin_vers_security-config.json]\n";
39
- exit(1);
40
- }
41
-
42
- if (!file_exists($configPath)) {
43
- fwrite(STDERR, "Erreur : Fichier de configuration introuvable : {$configPath}\n");
44
- exit(1);
45
- }
46
-
47
- $config = json_decode(file_get_contents($configPath), true);
48
- if (json_last_error() !== JSON_ERROR_NONE) {
49
- fwrite(STDERR, "Erreur : Fichier de configuration JSON invalide.\n");
50
- exit(1);
51
- }
52
-
53
- // 2. Connexion au store pour récupérer les logs accumulés
54
- $store = StoreManager::getStore();
55
- if (!$store) {
56
- fwrite(STDERR, "Erreur : Aucun store de persistance actif.\n");
57
- exit(1);
58
- }
59
-
60
- $rawTrafficLogs = $store->get('traffic_logs') ?? [];
61
- if (empty($rawTrafficLogs)) {
62
- echo "[AutoTuning] Aucun log de trafic disponible pour l'optimisation.\n";
63
- exit(0);
64
- }
65
-
66
- // 3. Nettoyage des données pour prévenir l'empoisonnement (Sybil attacks)
67
- $sanitizedLogs = RequestUtils::sanitizeTrafficData($rawTrafficLogs);
68
-
69
- $minDataPoints = $config['autotuning']['minDataPoints'] ?? 200;
70
- if (count($sanitizedLogs) < $minDataPoints) {
71
- echo "[AutoTuning] Reporté : Pas assez de données assainies (" . count($sanitizedLogs) . "/{$minDataPoints}).\n";
72
- exit(0);
73
- }
74
-
75
- echo "[AutoTuning] Lancement de l'optimisation sur " . count($sanitizedLogs) . " données de trafic...\n";
76
-
77
- // 4. Résolution du Front de Pareto
78
- $paretoFront = Optimization::solveFullSecurityTuning(['trafficData' => $sanitizedLogs]);
79
- if (empty($paretoFront)) {
80
- fwrite(STDERR, "[AutoTuning] L'optimisation n'a retourné aucun résultat.\n");
81
- exit(1);
82
- }
83
-
84
- // 5. Sélection de la solution la plus équilibrée
85
- $bestSolution = $paretoFront[0];
86
- $minDistance = sqrt(pow($bestSolution['objectives'][0], 2) + pow($bestSolution['objectives'][1], 2));
87
- foreach ($paretoFront as $candidate) {
88
- $distance = sqrt(pow($candidate['objectives'][0], 2) + pow($candidate['objectives'][1], 2));
89
- if ($distance < $minDistance) {
90
- $minDistance = $distance;
91
- $bestSolution = $candidate;
92
- }
93
- }
94
-
95
- $newConfig = $bestSolution['solution'];
96
- $maxChangeVelocity = 0.15; // Inertie de 15% maximum par cycle
97
-
98
- $applyInertialUpdate = function (array &$current, array $target) use ($maxChangeVelocity) {
99
- $sumCurrent = array_sum($current);
100
- if ($sumCurrent === 0) return;
101
- $sumTarget = 0;
102
- foreach ($current as $k => $v) {
103
- if (isset($target[$k])) $sumTarget += $target[$k];
104
- }
105
- $ratio = ($sumTarget - $sumCurrent) / $sumCurrent;
106
- $factor = 1 + max(-$maxChangeVelocity, min($maxChangeVelocity, $ratio));
107
- foreach ($current as $k => &$v) {
108
- if (isset($target[$k])) $v *= $factor;
109
- }
110
- };
111
-
112
- $applyInertialUpdate($config['thresholds'], $newConfig['thresholds']);
113
- $applyInertialUpdate($config['weights'], $newConfig['weights']);
114
- $applyInertialUpdate($config['patterns'], $newConfig['patterns']);
115
-
116
- // 6. Écrasement propre du fichier de configuration original
117
- file_put_contents($configPath, json_encode($config, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES));
1
+ <?php
2
+
3
+ declare(strict_types=1);
4
+
5
+ /**
6
+ * CLI Script d'auto-tuning périodique des configurations de sécurité.
7
+ * Ce script lit la configuration courante, récupère les logs, les assainit,
8
+ * exécute l'optimiseur génétique, et écrase le fichier JSON d'origine.
9
+ */
10
+
11
+ $autoloaderPaths = [
12
+ __DIR__ . '/../../vendor/autoload.php',
13
+ __DIR__ . '/../vendor/autoload.php',
14
+ __DIR__ . '/../../../autoload.php',
15
+ ];
16
+
17
+ $autoloaded = false;
18
+ foreach ($autoloaderPaths as $path) {
19
+ if (file_exists($path)) {
20
+ require_once $path;
21
+ $autoloaded = true;
22
+ break;
23
+ }
24
+ }
25
+
26
+ if (!$autoloaded) {
27
+ fwrite(STDERR, "Erreur : Impossible de charger l'autoloader. Exécutez 'composer install'.\n");
28
+ exit(1);
29
+ }
30
+
31
+ use Anonympins\Fingerprint\Optimization\Optimization;
32
+ use Anonympins\Fingerprint\Store\StoreManager;
33
+ use Anonympins\Fingerprint\Utils\RequestUtils;
34
+
35
+ // 1. Récupération des arguments CLI
36
+ $configPath = $argv[1] ?? null;
37
+ if (!$configPath) {
38
+ echo "Usage: php auto-tune.php [chemin_vers_security-config.json]\n";
39
+ exit(1);
40
+ }
41
+
42
+ if (!file_exists($configPath)) {
43
+ fwrite(STDERR, "Erreur : Fichier de configuration introuvable : {$configPath}\n");
44
+ exit(1);
45
+ }
46
+
47
+ $config = json_decode(file_get_contents($configPath), true);
48
+ if (json_last_error() !== JSON_ERROR_NONE) {
49
+ fwrite(STDERR, "Erreur : Fichier de configuration JSON invalide.\n");
50
+ exit(1);
51
+ }
52
+
53
+ // 2. Connexion au store pour récupérer les logs accumulés
54
+ $store = StoreManager::getStore();
55
+ if (!$store) {
56
+ fwrite(STDERR, "Erreur : Aucun store de persistance actif.\n");
57
+ exit(1);
58
+ }
59
+
60
+ $rawTrafficLogs = $store->get('traffic_logs') ?? [];
61
+ if (empty($rawTrafficLogs)) {
62
+ echo "[AutoTuning] Aucun log de trafic disponible pour l'optimisation.\n";
63
+ exit(0);
64
+ }
65
+
66
+ // 3. Nettoyage des données pour prévenir l'empoisonnement (Sybil attacks)
67
+ $sanitizedLogs = RequestUtils::sanitizeTrafficData($rawTrafficLogs);
68
+
69
+ $minDataPoints = $config['autotuning']['minDataPoints'] ?? 200;
70
+ if (count($sanitizedLogs) < $minDataPoints) {
71
+ echo "[AutoTuning] Reporté : Pas assez de données assainies (" . count($sanitizedLogs) . "/{$minDataPoints}).\n";
72
+ exit(0);
73
+ }
74
+
75
+ echo "[AutoTuning] Lancement de l'optimisation sur " . count($sanitizedLogs) . " données de trafic...\n";
76
+
77
+ // 4. Résolution du Front de Pareto
78
+ $paretoFront = Optimization::solveFullSecurityTuning(['trafficData' => $sanitizedLogs]);
79
+ if (empty($paretoFront)) {
80
+ fwrite(STDERR, "[AutoTuning] L'optimisation n'a retourné aucun résultat.\n");
81
+ exit(1);
82
+ }
83
+
84
+ // 5. Sélection de la solution la plus équilibrée
85
+ $bestSolution = $paretoFront[0];
86
+ $minDistance = sqrt(pow($bestSolution['objectives'][0], 2) + pow($bestSolution['objectives'][1], 2));
87
+ foreach ($paretoFront as $candidate) {
88
+ $distance = sqrt(pow($candidate['objectives'][0], 2) + pow($candidate['objectives'][1], 2));
89
+ if ($distance < $minDistance) {
90
+ $minDistance = $distance;
91
+ $bestSolution = $candidate;
92
+ }
93
+ }
94
+
95
+ $newConfig = $bestSolution['solution'];
96
+ $maxChangeVelocity = 0.15; // Inertie de 15% maximum par cycle
97
+
98
+ $applyInertialUpdate = function (array &$current, array $target) use ($maxChangeVelocity) {
99
+ $sumCurrent = array_sum($current);
100
+ if ($sumCurrent === 0) return;
101
+ $sumTarget = 0;
102
+ foreach ($current as $k => $v) {
103
+ if (isset($target[$k])) $sumTarget += $target[$k];
104
+ }
105
+ $ratio = ($sumTarget - $sumCurrent) / $sumCurrent;
106
+ $factor = 1 + max(-$maxChangeVelocity, min($maxChangeVelocity, $ratio));
107
+ foreach ($current as $k => &$v) {
108
+ if (isset($target[$k])) $v *= $factor;
109
+ }
110
+ };
111
+
112
+ $applyInertialUpdate($config['thresholds'], $newConfig['thresholds']);
113
+ $applyInertialUpdate($config['weights'], $newConfig['weights']);
114
+ $applyInertialUpdate($config['patterns'], $newConfig['patterns']);
115
+
116
+ // 6. Écrasement propre du fichier de configuration original
117
+ file_put_contents($configPath, json_encode($config, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES));
118
118
  echo "[AutoTuning] Succès : Fichier {$configPath} mis à jour avec les paramètres optimisés.\n";