@anonympins/fingerprint 0.5.2 → 0.5.4

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 (35) hide show
  1. package/CHANGELOG.md +18 -0
  2. package/package.json +1 -1
  3. package/src/js/fingerprint.client.js +1271 -1191
  4. package/src/js/fingerprint.js +202 -108
  5. package/src/js/fingerprint.utils.js +213 -213
  6. package/src/js/library.js +24 -8
  7. package/src/js/mongodb-store.js +165 -79
  8. package/src/js/pow.solver.inline.js +500 -14
  9. package/src/js/pow.solver.js +323 -14
  10. package/src/js/problem-manager.js +1 -1
  11. package/src/js/redis-store.js +114 -42
  12. package/src/js/tests/dns-circuit-breaker.test.js +55 -0
  13. package/src/js/tests/fingerprint.client.init.test.js +143 -143
  14. package/src/js/tests/fingerprint.client.test.js +128 -128
  15. package/src/js/tests/fingerprint.test.js +32 -2
  16. package/src/js/tests/ip-reputation.test.js +1 -1
  17. package/src/js/tests/pow.solver.test.js +5 -2
  18. package/src/js/tests/usefulWorkSolver.test.js +162 -0
  19. package/src/php/Challenge/ChallengeUtils.php +21 -8
  20. package/src/php/FingerprintClient.php +22 -5
  21. package/src/php/FingerprintEngine.php +232 -56
  22. package/src/php/Optimization/Optimization.php +23 -16
  23. package/src/php/Optimization/OptimizationOperators.php +27 -0
  24. package/src/php/ProblemManager.php +1 -1
  25. package/src/php/Store/MongoDbStore.php +184 -104
  26. package/src/php/Store/RedisStore.php +91 -11
  27. package/src/php/Tests/ChallengeUtilsTest.php +285 -285
  28. package/src/php/Tests/DnsCircuitBreakerTest.php +106 -0
  29. package/src/php/Tests/IpReputationTest.php +1 -1
  30. package/src/php/Tests/PowTest.php +1 -1
  31. package/src/php/Tests/RequestUtilsTest.php +29 -0
  32. package/src/php/Tests/TLSClientHelloParserTest.php +177 -117
  33. package/src/php/Utils/BigInt.php +202 -202
  34. package/src/php/Utils/RequestUtils.php +74 -21
  35. package/src/php/Utils/TLSClientHelloParser.php +212 -116
@@ -1,203 +1,203 @@
1
- <?php
2
-
3
- declare(strict_types=1);
4
-
5
- namespace Anonympins\Fingerprint\Utils;
6
-
7
- class BigInt
8
- {
9
- private static ?bool $useGmp = null;
10
- /** @var \GMP|string */
11
- private $value;
12
-
13
- private static function useGmp(): bool
14
- {
15
- if (self::$useGmp === null) {
16
- self::$useGmp = extension_loaded('gmp');
17
- }
18
- return self::$useGmp;
19
- }
20
-
21
- /**
22
- * @param \GMP|string|int $number Le nombre initial.
23
- */
24
- public function __construct($number)
25
- {
26
- if (self::useGmp()) {
27
- if ($number instanceof \GMP) {
28
- $this->value = $number;
29
- } else {
30
- $str = (string)$number;
31
- if (($dot = strpos($str, '.')) !== false) {
32
- $str = substr($str, 0, $dot);
33
- }
34
- $this->value = gmp_init($str);
35
- }
36
- } else {
37
- if ($number instanceof BigInt) {
38
- $this->value = $number->value;
39
- } else {
40
- $str = (string)$number;
41
- if (($dot = strpos($str, '.')) !== false) {
42
- $str = substr($str, 0, $dot);
43
- }
44
- $this->value = $str;
45
- }
46
- }
47
- }
48
-
49
- /**
50
- * Crée une instance à partir d'une chaîne hexadécimale.
51
- */
52
- public static function fromHex(string $hex): BigInt
53
- {
54
- $hex = trim(strtolower($hex));
55
- if (substr($hex, 0, 2) === '0x') {
56
- $hex = substr($hex, 2);
57
- }
58
- if (self::useGmp()) {
59
- return new self(gmp_init($hex, 16));
60
- } else {
61
- $dec = '0';
62
- $len = strlen($hex);
63
- for ($i = 0; $i < $len; $i++) {
64
- $dec = bcadd(bcmul($dec, '16', 0), (string)hexdec($hex[$i]), 0);
65
- }
66
- return new self($dec);
67
- }
68
- }
69
-
70
- /**
71
- * Compare cette instance avec une autre.
72
- * @return int < 0 si this < other, 0 si this == other, > 0 si this > other.
73
- */
74
- public function compareTo(BigInt $other): int
75
- {
76
- if (self::useGmp()) {
77
- return gmp_cmp($this->value, $other->value);
78
- } else {
79
- return bccomp($this->value, $other->value, 0);
80
- }
81
- }
82
-
83
- /**
84
- * Effectue un décalage de bits vers la gauche (<<).
85
- */
86
- public function shiftLeft(int $bits): BigInt
87
- {
88
- if (self::useGmp()) {
89
- return new self(gmp_mul($this->value, gmp_pow("2", $bits)));
90
- } else {
91
- return new self(bcmul($this->value, bcpow("2", (string)$bits, 0), 0));
92
- }
93
- }
94
-
95
- /**
96
- * Effectue un décalage de bits vers la droite (>>).
97
- */
98
- public function shiftRight(int $bits): BigInt
99
- {
100
- if (self::useGmp()) {
101
- return new self(gmp_div_q($this->value, gmp_pow("2", $bits)));
102
- } else {
103
- return new self(bcdiv($this->value, bcpow("2", (string)$bits, 0), 0));
104
- }
105
- }
106
-
107
- /**
108
- * Retourne la représentation en chaîne de caractères.
109
- */
110
- public function __toString(): string
111
- {
112
- if (self::useGmp()) {
113
- return gmp_strval($this->value);
114
- } else {
115
- return $this->value;
116
- }
117
- }
118
-
119
- /**
120
- * Retourne la représentation hexadécimale.
121
- */
122
- public function toHex(): string
123
- {
124
- if (self::useGmp()) {
125
- return gmp_strval($this->value, 16);
126
- } else {
127
- $hex = '';
128
- $dec = $this->value;
129
- while (bccomp($dec, '0', 0) > 0) {
130
- $rem = bcmod($dec, '16', 0);
131
- $hex = dechex((int)$rem) . $hex;
132
- $dec = bcdiv($dec, '16', 0);
133
- }
134
- return $hex ?: '0';
135
- }
136
- }
137
-
138
- /**
139
- * Crée une instance à partir d'une puissance de 2.
140
- */
141
- public static function pow(int $base, int $exp): BigInt
142
- {
143
- if (self::useGmp()) {
144
- return new self(gmp_pow((string)$base, $exp));
145
- } else {
146
- return new self(bcpow((string)$base, (string)$exp, 0));
147
- }
148
- }
149
-
150
- /**
151
- * Soustrait un autre BigInt.
152
- */
153
- public function sub(BigInt $other): BigInt
154
- {
155
- if (self::useGmp()) {
156
- return new self(gmp_sub($this->value, $other->value));
157
- } else {
158
- return new self(bcsub($this->value, $other->value, 0));
159
- }
160
- }
161
-
162
- /**
163
- * Computes ($this ^ $exponent) % $modulus
164
- */
165
- public function modPow(BigInt $exponent, BigInt $modulus): BigInt
166
- {
167
- if (self::useGmp()) {
168
- return new self(gmp_powm($this->value, $exponent->value, $modulus->value));
169
- } else {
170
- return new self(bcpowmod((string)$this->value, (string)$exponent->value, (string)$modulus->value, 0));
171
- }
172
- }
173
-
174
- /**
175
- * Computes $this % $modulus
176
- */
177
- public function mod(BigInt $modulus): BigInt
178
- {
179
- if (self::useGmp()) {
180
- return new self(gmp_mod($this->value, $modulus->value));
181
- } else {
182
- return new self(bcmod((string)$this->value, (string)$modulus->value, 0));
183
- }
184
- }
185
-
186
- public function add(BigInt $other): BigInt
187
- {
188
- if (self::useGmp()) {
189
- return new self(gmp_add($this->value, $other->value));
190
- } else {
191
- return new self(bcadd((string)$this->value, (string)$other->value, 0));
192
- }
193
- }
194
-
195
- public function mul(BigInt $other): BigInt
196
- {
197
- if (self::useGmp()) {
198
- return new self(gmp_mul($this->value, $other->value));
199
- } else {
200
- return new self(bcmul((string)$this->value, (string)$other->value, 0));
201
- }
202
- }
1
+ <?php
2
+
3
+ declare(strict_types=1);
4
+
5
+ namespace Anonympins\Fingerprint\Utils;
6
+
7
+ class BigInt
8
+ {
9
+ private static ?bool $useGmp = null;
10
+ /** @var \GMP|string */
11
+ private $value;
12
+
13
+ private static function useGmp(): bool
14
+ {
15
+ if (self::$useGmp === null) {
16
+ self::$useGmp = extension_loaded('gmp');
17
+ }
18
+ return self::$useGmp;
19
+ }
20
+
21
+ /**
22
+ * @param \GMP|string|int $number Le nombre initial.
23
+ */
24
+ public function __construct($number)
25
+ {
26
+ if (self::useGmp()) {
27
+ if ($number instanceof \GMP) {
28
+ $this->value = $number;
29
+ } else {
30
+ $str = (string)$number;
31
+ if (($dot = strpos($str, '.')) !== false) {
32
+ $str = substr($str, 0, $dot);
33
+ }
34
+ $this->value = gmp_init($str);
35
+ }
36
+ } else {
37
+ if ($number instanceof BigInt) {
38
+ $this->value = $number->value;
39
+ } else {
40
+ $str = (string)$number;
41
+ if (($dot = strpos($str, '.')) !== false) {
42
+ $str = substr($str, 0, $dot);
43
+ }
44
+ $this->value = $str;
45
+ }
46
+ }
47
+ }
48
+
49
+ /**
50
+ * Crée une instance à partir d'une chaîne hexadécimale.
51
+ */
52
+ public static function fromHex(string $hex): BigInt
53
+ {
54
+ $hex = trim(strtolower($hex));
55
+ if (substr($hex, 0, 2) === '0x') {
56
+ $hex = substr($hex, 2);
57
+ }
58
+ if (self::useGmp()) {
59
+ return new self(gmp_init($hex, 16));
60
+ } else {
61
+ $dec = '0';
62
+ $len = strlen($hex);
63
+ for ($i = 0; $i < $len; $i++) {
64
+ $dec = bcadd(bcmul($dec, '16', 0), (string)hexdec($hex[$i]), 0);
65
+ }
66
+ return new self($dec);
67
+ }
68
+ }
69
+
70
+ /**
71
+ * Compare cette instance avec une autre.
72
+ * @return int < 0 si this < other, 0 si this == other, > 0 si this > other.
73
+ */
74
+ public function compareTo(BigInt $other): int
75
+ {
76
+ if (self::useGmp()) {
77
+ return gmp_cmp($this->value, $other->value);
78
+ } else {
79
+ return bccomp($this->value, $other->value, 0);
80
+ }
81
+ }
82
+
83
+ /**
84
+ * Effectue un décalage de bits vers la gauche (<<).
85
+ */
86
+ public function shiftLeft(int $bits): BigInt
87
+ {
88
+ if (self::useGmp()) {
89
+ return new self(gmp_mul($this->value, gmp_pow("2", $bits)));
90
+ } else {
91
+ return new self(bcmul($this->value, bcpow("2", (string)$bits, 0), 0));
92
+ }
93
+ }
94
+
95
+ /**
96
+ * Effectue un décalage de bits vers la droite (>>).
97
+ */
98
+ public function shiftRight(int $bits): BigInt
99
+ {
100
+ if (self::useGmp()) {
101
+ return new self(gmp_div_q($this->value, gmp_pow("2", $bits)));
102
+ } else {
103
+ return new self(bcdiv($this->value, bcpow("2", (string)$bits, 0), 0));
104
+ }
105
+ }
106
+
107
+ /**
108
+ * Retourne la représentation en chaîne de caractères.
109
+ */
110
+ public function __toString(): string
111
+ {
112
+ if (self::useGmp()) {
113
+ return gmp_strval($this->value);
114
+ } else {
115
+ return $this->value;
116
+ }
117
+ }
118
+
119
+ /**
120
+ * Retourne la représentation hexadécimale.
121
+ */
122
+ public function toHex(): string
123
+ {
124
+ if (self::useGmp()) {
125
+ return gmp_strval($this->value, 16);
126
+ } else {
127
+ $hex = '';
128
+ $dec = $this->value;
129
+ while (bccomp($dec, '0', 0) > 0) {
130
+ $rem = bcmod($dec, '16', 0);
131
+ $hex = dechex((int)$rem) . $hex;
132
+ $dec = bcdiv($dec, '16', 0);
133
+ }
134
+ return $hex ?: '0';
135
+ }
136
+ }
137
+
138
+ /**
139
+ * Crée une instance à partir d'une puissance de 2.
140
+ */
141
+ public static function pow(int $base, int $exp): BigInt
142
+ {
143
+ if (self::useGmp()) {
144
+ return new self(gmp_pow((string)$base, $exp));
145
+ } else {
146
+ return new self(bcpow((string)$base, (string)$exp, 0));
147
+ }
148
+ }
149
+
150
+ /**
151
+ * Soustrait un autre BigInt.
152
+ */
153
+ public function sub(BigInt $other): BigInt
154
+ {
155
+ if (self::useGmp()) {
156
+ return new self(gmp_sub($this->value, $other->value));
157
+ } else {
158
+ return new self(bcsub($this->value, $other->value, 0));
159
+ }
160
+ }
161
+
162
+ /**
163
+ * Computes ($this ^ $exponent) % $modulus
164
+ */
165
+ public function modPow(BigInt $exponent, BigInt $modulus): BigInt
166
+ {
167
+ if (self::useGmp()) {
168
+ return new self(gmp_powm($this->value, $exponent->value, $modulus->value));
169
+ } else {
170
+ return new self(bcpowmod((string)$this->value, (string)$exponent->value, (string)$modulus->value, 0));
171
+ }
172
+ }
173
+
174
+ /**
175
+ * Computes $this % $modulus
176
+ */
177
+ public function mod(BigInt $modulus): BigInt
178
+ {
179
+ if (self::useGmp()) {
180
+ return new self(gmp_mod($this->value, $modulus->value));
181
+ } else {
182
+ return new self(bcmod((string)$this->value, (string)$modulus->value, 0));
183
+ }
184
+ }
185
+
186
+ public function add(BigInt $other): BigInt
187
+ {
188
+ if (self::useGmp()) {
189
+ return new self(gmp_add($this->value, $other->value));
190
+ } else {
191
+ return new self(bcadd((string)$this->value, (string)$other->value, 0));
192
+ }
193
+ }
194
+
195
+ public function mul(BigInt $other): BigInt
196
+ {
197
+ if (self::useGmp()) {
198
+ return new self(gmp_mul($this->value, $other->value));
199
+ } else {
200
+ return new self(bcmul((string)$this->value, (string)$other->value, 0));
201
+ }
202
+ }
203
203
  }
@@ -1175,14 +1175,39 @@ class RequestUtils
1175
1175
  $networkBinary = $ipBinary & $mask;
1176
1176
  return inet_ntop($networkBinary) . '/' . $ipv4Prefix;
1177
1177
  } elseif (filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_IPV6)) {
1178
- $ipBinary = inet_pton($ip);
1179
- if ($ipBinary === false) return null;
1180
-
1181
- $mask = self::generateMask($ipv6Prefix, 16);
1182
- if ($mask === null) return null;
1183
-
1184
- $networkBinary = $ipBinary & $mask;
1185
- return inet_ntop($networkBinary) . '/' . $ipv6Prefix; // FIX: Use the provided ipv6Prefix
1178
+ $normalized = trim(strtolower($ip));
1179
+ if (str_contains($normalized, '::')) {
1180
+ $parts = explode('::', $normalized, 2);
1181
+ $left = $parts[0] !== '' ? explode(':', $parts[0]) : [];
1182
+ $right = $parts[1] !== '' ? explode(':', $parts[1]) : [];
1183
+ $missing = 8 - (count($left) + count($right));
1184
+ $middle = array_fill(0, $missing, '0000');
1185
+ $groups = array_merge($left, $middle, $right);
1186
+ } else {
1187
+ $groups = explode(':', $normalized);
1188
+ if (count($groups) !== 8) {
1189
+ return null;
1190
+ }
1191
+ }
1192
+ foreach ($groups as $i => &$g) {
1193
+ $val = hexdec($g);
1194
+ $g = str_pad(dechex($val), 4, '0', STR_PAD_LEFT);
1195
+ }
1196
+ unset($g);
1197
+ for ($i = 0; $i < 8; $i++) {
1198
+ $startBit = $i * 16;
1199
+ if ($ipv6Prefix >= ($i + 1) * 16) {
1200
+ continue;
1201
+ } elseif ($ipv6Prefix <= $startBit) {
1202
+ $groups[$i] = '0000';
1203
+ } else {
1204
+ $bitsToKeep = $ipv6Prefix - $startBit;
1205
+ $val = hexdec($groups[$i]);
1206
+ $mask = (0xffff << (16 - $bitsToKeep)) & 0xffff;
1207
+ $groups[$i] = str_pad(dechex($val & $mask), 4, '0', STR_PAD_LEFT);
1208
+ }
1209
+ }
1210
+ return implode(':', $groups) . '/' . $ipv6Prefix;
1186
1211
  }
1187
1212
  return null;
1188
1213
  }
@@ -1382,29 +1407,62 @@ class RequestUtils
1382
1407
  return [];
1383
1408
  }
1384
1409
 
1385
- $tempSanitized = [];
1410
+ $suspiciousLogs = [];
1411
+ $passedLogs = [];
1386
1412
  $deviceCounts = [];
1387
1413
  $ipCounts = [];
1388
1414
  $subnetCounts = [];
1415
+ $hardwareClusterCounts = [];
1416
+ $hwClusterCache = [];
1389
1417
 
1390
1418
  $totalCount = count($trafficData);
1391
1419
  $maxLogsPerDevice = max(3, (int)floor($totalCount * 0.02)); // Max 2% contribution per device
1392
1420
  $maxLogsPerIp = max(3, (int)floor($totalCount * 0.02)); // Max 2% par adresse IP individuelle
1393
1421
  $maxLogsPerSubnet = max(5, (int)floor($totalCount * 0.05)); // Max 5% par bloc réseau (anti-proxy-rotation)
1422
+ $maxLogsPerHardwareCluster = max(3, (int)floor($totalCount * 0.02)); // Max 2% par cluster matériel stable
1423
+
1424
+ $getHardwareCluster = function (array $log) use (&$hwClusterCache): string {
1425
+ $fp = $log['deviceHash'] ?? $log['fingerprint'] ?? $log['deviceFingerprint'] ?? '';
1426
+ if (!empty($fp) && is_string($fp)) {
1427
+ if (isset($hwClusterCache[$fp])) {
1428
+ return $hwClusterCache[$fp];
1429
+ }
1430
+ $parts = explode('|', $fp);
1431
+ $hwComponents = [];
1432
+ foreach ($parts as $part) {
1433
+ $pair = explode(':', $part, 2);
1434
+ if (count($pair) === 2 && in_array($pair[0], ['gpu', 'cvs', 'hw'], true)) {
1435
+ $hwComponents[] = $part;
1436
+ }
1437
+ }
1438
+ if (!empty($hwComponents)) {
1439
+ sort($hwComponents);
1440
+ $result = implode('|', $hwComponents);
1441
+ } else {
1442
+ $result = $log['deviceId'] ?? 'anonymous-cluster';
1443
+ }
1444
+ $hwClusterCache[$fp] = $result;
1445
+ return $result;
1446
+ }
1447
+ return $log['deviceId'] ?? 'anonymous-cluster';
1448
+ };
1394
1449
 
1395
1450
  foreach ($trafficData as $log) {
1396
1451
  $devId = $log['deviceId'] ?? 'anonymous';
1397
1452
  $ip = $log['clientIp'] ?? $log['ip'] ?? 'unknown';
1398
1453
  $subnet = self::getIpSubnet($ip) ?? 'unknown-subnet';
1454
+ $hwCluster = $getHardwareCluster($log);
1399
1455
 
1400
1456
  $currentDeviceCount = $deviceCounts[$devId] ?? 0;
1401
1457
  $currentIpCount = $ipCounts[$ip] ?? 0;
1402
1458
  $currentSubnetCount = $subnetCounts[$subnet] ?? 0;
1459
+ $currentHwClusterCount = $hardwareClusterCounts[$hwCluster] ?? 0;
1403
1460
 
1404
1461
  if (
1405
1462
  $currentDeviceCount < $maxLogsPerDevice &&
1406
1463
  ($ip === 'unknown' || $currentIpCount < $maxLogsPerIp) &&
1407
- ($subnet === 'unknown-subnet' || $currentSubnetCount < $maxLogsPerSubnet)
1464
+ ($subnet === 'unknown-subnet' || $currentSubnetCount < $maxLogsPerSubnet) &&
1465
+ $currentHwClusterCount < $maxLogsPerHardwareCluster
1408
1466
  ) {
1409
1467
  $deviceCounts[$devId] = $currentDeviceCount + 1;
1410
1468
  if ($ip !== 'unknown') {
@@ -1413,17 +1471,12 @@ class RequestUtils
1413
1471
  if ($subnet !== 'unknown-subnet') {
1414
1472
  $subnetCounts[$subnet] = $currentSubnetCount + 1;
1415
1473
  }
1416
- $tempSanitized[] = $log;
1417
- }
1418
- }
1419
-
1420
- $passedLogs = [];
1421
- $suspiciousLogs = [];
1422
- foreach ($tempSanitized as $log) {
1423
- if (($log['type'] ?? '') === 'request_passed') {
1424
- $passedLogs[] = $log;
1425
- } else {
1426
- $suspiciousLogs[] = $log;
1474
+ $hardwareClusterCounts[$hwCluster] = $currentHwClusterCount + 1;
1475
+ if (($log['type'] ?? '') === 'request_passed') {
1476
+ $passedLogs[] = $log;
1477
+ } else {
1478
+ $suspiciousLogs[] = $log;
1479
+ }
1427
1480
  }
1428
1481
  }
1429
1482