@anonympins/fingerprint 0.7.3 → 0.7.5
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.
- package/CHANGELOG.md +41 -0
- package/README.md +1 -1
- package/package.json +109 -107
- package/public/fingerprint-anti-bot.zip +0 -0
- package/src/js/dynamic-wasm.js +218 -218
- package/src/js/fingerprint.client.js +329 -88
- package/src/js/fingerprint.client.obfuscated.js +1 -0
- package/src/js/fingerprint.js +486 -231
- package/src/js/fingerprint.worker.js +81 -0
- package/src/js/library.js +6 -2
- package/src/js/pow.solver.js +2 -3
- package/src/js/problem-manager.js +113 -113
- package/src/js/tests/dynamic-wasm.test.js +77 -77
- package/src/js/tests/fingerprint.client.init.test.js +108 -0
- package/src/js/tests/fingerprint.test.js +153 -3
- package/src/js/tests/threatIntel.test.js +40 -1
- package/src/php/Challenge/ChallengeUtils.php +25 -16
- package/src/php/Config/SecurityProfiles.php +5 -0
- package/src/php/FingerprintEngine.php +186 -37
- package/src/php/Tests/ChallengeUtilsTest.php +469 -453
- package/src/php/Tests/RequestUtilsTest.php +1 -1
- package/src/php/Tests/ThreatIntelTest.php +34 -0
- package/src/php/Utils/RequestUtils.php +257 -127
- package/src/php/WordPress/fingerprint-anti-bot.php +1 -1
- package/src/php/WordPress/languages/fingerprint-wordpress-de_DE.mo +0 -0
- package/src/php/WordPress/languages/fingerprint-wordpress-de_DE.po +1 -1
- package/src/php/WordPress/languages/fingerprint-wordpress-fr_FR.mo +0 -0
- package/src/php/WordPress/languages/fingerprint-wordpress-fr_FR.po +1 -1
- package/src/php/WordPress/readme.txt +1 -1
|
@@ -233,7 +233,7 @@ class RequestUtilsTest extends TestCase
|
|
|
233
233
|
'headers' => ['x-behavior-metrics' => json_encode($metricsNoActivity)]
|
|
234
234
|
]);
|
|
235
235
|
$scoreNoActivity = RequestUtils::getBehaviorScore($contextNoActivity);
|
|
236
|
-
$this->assertEquals(
|
|
236
|
+
$this->assertEquals(5.0, $scoreNoActivity['behaviorScore']);
|
|
237
237
|
}
|
|
238
238
|
|
|
239
239
|
public function testGetBehaviorScoreWithBotLikeKeystrokeDynamics(): void
|
|
@@ -61,4 +61,38 @@ class ThreatIntelTest extends TestCase
|
|
|
61
61
|
$this->assertGreaterThan(75.0, $score);
|
|
62
62
|
$this->assertLessThanOrEqual(95.0, $score);
|
|
63
63
|
}
|
|
64
|
+
|
|
65
|
+
public function testDifferentialPrivacyDecoyInjectionAndTimestampNoise(): void
|
|
66
|
+
{
|
|
67
|
+
$config = [
|
|
68
|
+
'federatedPeers' => ['https://peer1.example.com'],
|
|
69
|
+
'federationSecret' => 'test-secret-key-32-chars-long!!',
|
|
70
|
+
'differentialPrivacy' => [
|
|
71
|
+
'enabled' => true,
|
|
72
|
+
'epsilon' => 1.0,
|
|
73
|
+
'dummyRate' => 1.0 // Forcer l'injection d'un leurre pour le test
|
|
74
|
+
]
|
|
75
|
+
];
|
|
76
|
+
|
|
77
|
+
$postedCalls = [];
|
|
78
|
+
$engineSub = new class($config, $postedCalls) extends \Anonympins\Fingerprint\FingerprintEngine {
|
|
79
|
+
public array $calls = [];
|
|
80
|
+
public function __construct(array $cfg, array &$callsRef) {
|
|
81
|
+
$this->calls = &$callsRef;
|
|
82
|
+
parent::__construct($cfg);
|
|
83
|
+
}
|
|
84
|
+
protected function asyncPost(string $url, array $params): void {
|
|
85
|
+
$this->calls[] = ['url' => $url, 'params' => $params];
|
|
86
|
+
}
|
|
87
|
+
};
|
|
88
|
+
|
|
89
|
+
$refSub = new \ReflectionClass($engineSub);
|
|
90
|
+
$broadcastMethod = $refSub->getMethod('broadcastBannedZkp');
|
|
91
|
+
$broadcastMethod->setAccessible(true);
|
|
92
|
+
|
|
93
|
+
$realZkpY = 'realzkpykey12345';
|
|
94
|
+
$broadcastMethod->invoke($engineSub, $realZkpY);
|
|
95
|
+
|
|
96
|
+
$this->assertCount(2, $postedCalls, 'Doit diffuser à la fois la clé réelle et la clé leurre');
|
|
97
|
+
}
|
|
64
98
|
}
|
|
@@ -116,6 +116,54 @@ class RequestUtils
|
|
|
116
116
|
return (string)$srv;
|
|
117
117
|
}
|
|
118
118
|
|
|
119
|
+
/**
|
|
120
|
+
* Analyzes TCP MTU and fragmentation flags to detect network tunnels (VPN/Proxy).
|
|
121
|
+
* @param RequestContext $context The request context.
|
|
122
|
+
* @return array{'mtuAnomalyScore': float}
|
|
123
|
+
*/
|
|
124
|
+
public static function getMtuAnomalyScore(RequestContext $context): array
|
|
125
|
+
{
|
|
126
|
+
$mtuHeader = $context->getHeader('x-tcp-mtu-info');
|
|
127
|
+
if (empty($mtuHeader) || !is_string($mtuHeader)) {
|
|
128
|
+
return ['mtuAnomalyScore' => 0.0];
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
$parts = explode(':', $mtuHeader);
|
|
132
|
+
if (count($parts) < 2) {
|
|
133
|
+
return ['mtuAnomalyScore' => 0.0];
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
$mtu = filter_var($parts[0], FILTER_VALIDATE_INT);
|
|
137
|
+
$df = filter_var($parts[1], FILTER_VALIDATE_INT);
|
|
138
|
+
|
|
139
|
+
if ($mtu === false || $df === false) {
|
|
140
|
+
return ['mtuAnomalyScore' => 0.0];
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
$score = 0.0;
|
|
144
|
+
|
|
145
|
+
// 1. Pénalité modérée pour les MTU typiques des VPNs/tunnels.
|
|
146
|
+
if ($mtu > 1200 && $mtu <= 1420) {
|
|
147
|
+
$score += 35.0;
|
|
148
|
+
} elseif ($mtu > 1420 && $mtu < 1492) {
|
|
149
|
+
$score += 20.0;
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
// 2. OS vs. Network Stack Inconsistency
|
|
153
|
+
$ua = $context->getHeader('user-agent') ?? '';
|
|
154
|
+
$uaParts = self::parseUserAgent($ua);
|
|
155
|
+
$os = $uaParts['os'] ?? null;
|
|
156
|
+
|
|
157
|
+
// Pénalités additionnelles en cas d'incohérence OS vs signature réseau.
|
|
158
|
+
if ($os) {
|
|
159
|
+
if (str_starts_with($os, 'Windows') && $mtu < 1492) $score += 20.0;
|
|
160
|
+
if ((str_starts_with($os, 'Android') || str_starts_with($os, 'iOS')) && $mtu < 1480) $score += 15.0;
|
|
161
|
+
if ($df === 0 && (str_starts_with($os, 'Windows') || str_starts_with($os, 'Mac') || str_starts_with($os, 'Linux'))) $score += 40.0;
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
return ['mtuAnomalyScore' => min(100.0, $score)];
|
|
165
|
+
}
|
|
166
|
+
|
|
119
167
|
/**
|
|
120
168
|
* Calcule un score d'incohérence entre la signature TLS (JA3) et le User-Agent.
|
|
121
169
|
* @return array{'tlsSpoofingScore': float}
|
|
@@ -392,125 +440,145 @@ class RequestUtils
|
|
|
392
440
|
|
|
393
441
|
/**
|
|
394
442
|
* Calcule un score basé sur les métriques comportementales envoyées par le client.
|
|
395
|
-
* @
|
|
443
|
+
* @param RequestContext $context Le contexte de la requête.
|
|
444
|
+
* @return array{behaviorScore: float}
|
|
396
445
|
*/
|
|
397
446
|
public static function getBehaviorScore(RequestContext $context): array
|
|
398
447
|
{
|
|
399
|
-
$
|
|
400
|
-
if (
|
|
448
|
+
$header = $context->getHeader('x-behavior-metrics');
|
|
449
|
+
if (empty($header)) {
|
|
401
450
|
return ['behaviorScore' => 0.0];
|
|
402
451
|
}
|
|
403
452
|
|
|
404
|
-
$
|
|
405
|
-
|
|
406
|
-
return ['behaviorScore' => 10.0]; // En-tête malformé
|
|
453
|
+
if ($header === '1' || $header === 1) {
|
|
454
|
+
return ['behaviorScore' => 0.0];
|
|
407
455
|
}
|
|
408
|
-
|
|
409
|
-
if (!empty($metrics['honeypotInteraction'])) {
|
|
456
|
+
if ($header === '0' || $header === 0) {
|
|
410
457
|
return ['behaviorScore' => 100.0];
|
|
411
458
|
}
|
|
412
459
|
|
|
413
|
-
$
|
|
414
|
-
|
|
415
|
-
$score
|
|
460
|
+
if (is_numeric($header)) {
|
|
461
|
+
$score = max(0.0, min(100.0, (float)$header));
|
|
462
|
+
return ['behaviorScore' => $score];
|
|
416
463
|
}
|
|
417
464
|
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
$score
|
|
465
|
+
if (is_string($header)) {
|
|
466
|
+
$trimmed = trim($header);
|
|
467
|
+
if (is_numeric($trimmed)) {
|
|
468
|
+
if ($trimmed === '1') {
|
|
469
|
+
return ['behaviorScore' => 0.0];
|
|
470
|
+
}
|
|
471
|
+
if ($trimmed === '0') {
|
|
472
|
+
return ['behaviorScore' => 100.0];
|
|
473
|
+
}
|
|
474
|
+
$score = max(0.0, min(100.0, (float)$trimmed));
|
|
475
|
+
return ['behaviorScore' => $score];
|
|
429
476
|
}
|
|
430
477
|
}
|
|
431
478
|
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
if ($mouseAnalysis['straightness'] > 0.95) $score += 30;
|
|
436
|
-
if ($mouseAnalysis['pauses'] === 0 && count($mouseAnalysis['segments']) > 20) $score += 15;
|
|
479
|
+
$metrics = json_decode($header, true);
|
|
480
|
+
if (json_last_error() !== JSON_ERROR_NONE) {
|
|
481
|
+
return ['behaviorScore' => 10.0];
|
|
437
482
|
}
|
|
438
483
|
|
|
439
|
-
if (
|
|
440
|
-
|
|
484
|
+
if ($metrics === 1) {
|
|
485
|
+
return ['behaviorScore' => 0.0];
|
|
486
|
+
}
|
|
487
|
+
if ($metrics === 0) {
|
|
488
|
+
return ['behaviorScore' => 100.0];
|
|
489
|
+
}
|
|
441
490
|
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
491
|
+
if (is_numeric($metrics)) {
|
|
492
|
+
$score = max(0.0, min(100.0, (float)$metrics));
|
|
493
|
+
return ['behaviorScore' => $score];
|
|
494
|
+
}
|
|
445
495
|
|
|
446
|
-
if (
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
$stdDevDwell = sqrt($varDwell);
|
|
496
|
+
if (!is_array($metrics)) {
|
|
497
|
+
return ['behaviorScore' => 0.0];
|
|
498
|
+
}
|
|
450
499
|
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
}
|
|
454
|
-
if ($meanDwell < 15.0) {
|
|
455
|
-
$score += 25.0;
|
|
456
|
-
}
|
|
500
|
+
if ($metrics['honeypotInteraction'] ?? false) {
|
|
501
|
+
return ['behaviorScore' => 100.0];
|
|
457
502
|
}
|
|
458
503
|
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
$
|
|
462
|
-
|
|
463
|
-
$stdDevFlight = sqrt($varFlight);
|
|
504
|
+
$score = 0.0;
|
|
505
|
+
if ($metrics['prototypeTampered'] ?? false) {
|
|
506
|
+
$score += 80.0;
|
|
507
|
+
}
|
|
464
508
|
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
509
|
+
$mouseHistory = $metrics['mouseMovementsHistory'] ?? [];
|
|
510
|
+
$touchHistory = $metrics['touchMovementsHistory'] ?? [];
|
|
511
|
+
|
|
512
|
+
$mouseAnalysis = RequestUtils::analyzeMouseMovements($mouseHistory);
|
|
513
|
+
$touchAnalysis = RequestUtils::analyzeTouchMovements($touchHistory);
|
|
514
|
+
|
|
515
|
+
$mouseAvgSpeed = (float)($mouseAnalysis['avgSpeed'] ?? 0.0);
|
|
516
|
+
$touchAvgSpeed = (float)($touchAnalysis['avgSpeed'] ?? 0.0);
|
|
517
|
+
$keystrokeLatency = (float)($metrics['keystrokeLatency'] ?? 0.0);
|
|
518
|
+
|
|
519
|
+
if (isset($metrics['historyLength'])) {
|
|
520
|
+
$historyLength = (int)$metrics['historyLength'];
|
|
521
|
+
if ($historyLength === 1) $score += 15;
|
|
522
|
+
elseif ($historyLength >= 5) $score -= 20;
|
|
523
|
+
elseif ($historyLength >= 2) $score -= 10;
|
|
524
|
+
} else {
|
|
525
|
+
if ($mouseAvgSpeed === 0.0 && $touchAvgSpeed === 0.0 && $keystrokeLatency === 0.0) {
|
|
526
|
+
$noInteractionPenalty = 5.0;
|
|
527
|
+
if ($metrics['rendering']['offscreenAnom'] ?? false) {
|
|
528
|
+
$noInteractionPenalty += 40.0;
|
|
529
|
+
}
|
|
530
|
+
$score += $noInteractionPenalty;
|
|
474
531
|
}
|
|
475
532
|
}
|
|
476
533
|
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
if ($
|
|
481
|
-
|
|
482
|
-
}
|
|
534
|
+
if ($mouseAvgSpeed > 0) {
|
|
535
|
+
if ($mouseAvgSpeed > 3.0) $score += 25;
|
|
536
|
+
if (($mouseAnalysis['avgAcceleration'] ?? 0.0) > 0.5) $score += 20;
|
|
537
|
+
if (($mouseAnalysis['straightness'] ?? 1.0) > 0.95) $score += 30;
|
|
538
|
+
if (($mouseAnalysis['pauses'] ?? 0) === 0 && count($mouseAnalysis['segments'] ?? []) > 20) $score += 15;
|
|
483
539
|
}
|
|
484
|
-
// Analyse comportementale des événements tactiles (Touch Move)
|
|
485
|
-
$touchHistory = $metrics['touchMovementsHistory'] ?? null;
|
|
486
|
-
if (!empty($touchHistory)) {
|
|
487
|
-
if ($touch['avgSpeed'] > 0) {
|
|
488
|
-
if ($touch['avgSpeed'] > 5) $score += 30;
|
|
489
|
-
if ($touch['avgAcceleration'] > 0.8) $score += 20;
|
|
490
|
-
if ($touch['straightness'] > 0.98) $score += 35;
|
|
491
|
-
if ($touch['pauses'] === 0 && count($touch['segments']) > 25) $score += 15;
|
|
492
540
|
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
541
|
+
if ($touchAvgSpeed > 0 || !empty($touchHistory)) {
|
|
542
|
+
if (count($touchHistory) >= 3) {
|
|
543
|
+
if (($touchAnalysis['pressureVariance'] ?? 1.0) < 0.0001) {
|
|
544
|
+
$score += 35.0;
|
|
545
|
+
}
|
|
546
|
+
if (($touchAnalysis['radiusVariance'] ?? 1.0) < 0.0001) {
|
|
547
|
+
$score += 25.0;
|
|
496
548
|
}
|
|
497
|
-
if ($
|
|
498
|
-
$score +=
|
|
549
|
+
if (($touchAnalysis['straightness'] ?? 0.0) > 0.98) {
|
|
550
|
+
$score += 20.0;
|
|
499
551
|
}
|
|
500
552
|
}
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
553
|
+
}
|
|
554
|
+
|
|
555
|
+
$dwellTimes = $metrics['keystrokeDwellTimes'] ?? [];
|
|
556
|
+
if (is_array($dwellTimes) && count($dwellTimes) >= 3) {
|
|
557
|
+
$dwellNumeric = array_filter($dwellTimes, 'is_numeric');
|
|
558
|
+
if (count($dwellNumeric) >= 3) {
|
|
559
|
+
$meanDwell = array_sum($dwellNumeric) / count($dwellNumeric);
|
|
560
|
+
$dwellVar = array_reduce($dwellNumeric, fn($carry, $val) => $carry + pow((float)$val - $meanDwell, 2), 0.0) / count($dwellNumeric);
|
|
561
|
+
if ($dwellVar < 2.0) {
|
|
562
|
+
$score += 40.0;
|
|
505
563
|
}
|
|
506
564
|
}
|
|
565
|
+
}
|
|
507
566
|
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
$
|
|
511
|
-
|
|
512
|
-
if ((
|
|
513
|
-
$
|
|
567
|
+
$flightTimes = $metrics['keystrokeFlightTimes'] ?? [];
|
|
568
|
+
if (is_array($flightTimes) && count($flightTimes) >= 3) {
|
|
569
|
+
$extractedFlightTimes = [];
|
|
570
|
+
foreach ($flightTimes as $ft) {
|
|
571
|
+
if (is_array($ft) && isset($ft['time']) && is_numeric($ft['time'])) {
|
|
572
|
+
$extractedFlightTimes[] = (float)$ft['time'];
|
|
573
|
+
} elseif (is_numeric($ft)) {
|
|
574
|
+
$extractedFlightTimes[] = (float)$ft;
|
|
575
|
+
}
|
|
576
|
+
}
|
|
577
|
+
if (count($extractedFlightTimes) >= 3) {
|
|
578
|
+
$meanFlight = array_sum($extractedFlightTimes) / count($extractedFlightTimes);
|
|
579
|
+
$flightVar = array_reduce($extractedFlightTimes, fn($carry, $val) => $carry + pow($val - $meanFlight, 2), 0.0) / count($extractedFlightTimes);
|
|
580
|
+
if ($flightVar < 2.0) {
|
|
581
|
+
$score += 40.0;
|
|
514
582
|
}
|
|
515
583
|
}
|
|
516
584
|
}
|
|
@@ -518,6 +586,7 @@ class RequestUtils
|
|
|
518
586
|
return ['behaviorScore' => min(100.0, $score)];
|
|
519
587
|
}
|
|
520
588
|
|
|
589
|
+
|
|
521
590
|
/**
|
|
522
591
|
* Calcule un score basé sur la régularité d'affichage (V-Sync) et l'utilisation suspecte d'OffscreenCanvas.
|
|
523
592
|
* @return array{'renderingAnomalyScore': float}
|
|
@@ -2008,6 +2077,21 @@ class RequestUtils
|
|
|
2008
2077
|
return ['tcpAnomalyScore' => $tcpAnomalyScore];
|
|
2009
2078
|
}
|
|
2010
2079
|
|
|
2080
|
+
/**
|
|
2081
|
+
* Helper to find the first key of a value from a list of needles in an array.
|
|
2082
|
+
* @param array<int, string> $haystack
|
|
2083
|
+
* @param array<int, string> $needles
|
|
2084
|
+
* @return int|null
|
|
2085
|
+
*/
|
|
2086
|
+
private static function array_find_key(array $haystack, array $needles): ?int
|
|
2087
|
+
{
|
|
2088
|
+
foreach ($needles as $needle) {
|
|
2089
|
+
$key = array_search($needle, $haystack, true);
|
|
2090
|
+
if ($key !== false) return (int)$key;
|
|
2091
|
+
}
|
|
2092
|
+
return null;
|
|
2093
|
+
}
|
|
2094
|
+
|
|
2011
2095
|
/**
|
|
2012
2096
|
* Détecte les anomalies de protocole (HTTP/2 et QUIC/HTTP3) par rapport au User-Agent.
|
|
2013
2097
|
* @param RequestContext $context
|
|
@@ -2022,51 +2106,95 @@ class RequestUtils
|
|
|
2022
2106
|
$uaParts = self::parseUserAgent($ua);
|
|
2023
2107
|
$browser = $uaParts['browser'] ?? null;
|
|
2024
2108
|
|
|
2025
|
-
|
|
2026
|
-
|
|
2027
|
-
|
|
2028
|
-
$
|
|
2029
|
-
|
|
2030
|
-
$
|
|
2031
|
-
|
|
2032
|
-
|
|
2033
|
-
|
|
2034
|
-
|
|
2035
|
-
|
|
2036
|
-
|
|
2037
|
-
|
|
2038
|
-
if ($
|
|
2039
|
-
|
|
2040
|
-
|
|
2041
|
-
|
|
2042
|
-
|
|
2109
|
+
if ($browser) {
|
|
2110
|
+
// HTTP/2 Anomaly logic
|
|
2111
|
+
$h2Fp = $context->getHeader('x-http2-fingerprint') ?? $context->http2Fingerprint ?? null;
|
|
2112
|
+
if ($h2Fp && is_string($h2Fp)) {
|
|
2113
|
+
$parts = explode('|', $h2Fp);
|
|
2114
|
+
if (count($parts) >= 3) {
|
|
2115
|
+
$connWindow = (int)$parts[1];
|
|
2116
|
+
$streamPriority = $parts[2] ?? '';
|
|
2117
|
+
$headerOrder = $parts[3] ?? '';
|
|
2118
|
+
$isChromium = str_starts_with($browser, 'Chrome') || str_starts_with($browser, 'Edge');
|
|
2119
|
+
$isFirefox = str_starts_with($browser, 'Firefox');
|
|
2120
|
+
$isSafari = str_starts_with($browser, 'Safari');
|
|
2121
|
+
|
|
2122
|
+
if ($isChromium) {
|
|
2123
|
+
if ($headerOrder && $headerOrder !== 'm,a,s,p') $http2Anomaly += 60.0;
|
|
2124
|
+
if ($connWindow === 65535 || $connWindow === 65536) $http2Anomaly += 40.0;
|
|
2125
|
+
if ($streamPriority === '0' || $streamPriority === '') $http2Anomaly += 50.0;
|
|
2126
|
+
} elseif ($isFirefox) {
|
|
2127
|
+
if ($headerOrder && $headerOrder !== 'm,s,p,a') $http2Anomaly += 60.0;
|
|
2128
|
+
} elseif ($isSafari) {
|
|
2129
|
+
if ($headerOrder && $headerOrder !== 'm,s,p,a') $http2Anomaly += 60.0;
|
|
2130
|
+
}
|
|
2043
2131
|
}
|
|
2044
2132
|
}
|
|
2045
|
-
}
|
|
2046
2133
|
|
|
2047
|
-
|
|
2048
|
-
|
|
2049
|
-
|
|
2050
|
-
|
|
2051
|
-
|
|
2052
|
-
|
|
2053
|
-
|
|
2054
|
-
|
|
2055
|
-
|
|
2056
|
-
|
|
2134
|
+
// QUIC Anomaly logic
|
|
2135
|
+
$quicFp = $context->getHeader('x-quic-fp') ?? $context->quicFingerprint ?? null;
|
|
2136
|
+
if ($quicFp && is_string($quicFp)) {
|
|
2137
|
+
$parts = explode(';', $quicFp);
|
|
2138
|
+
if (count($parts) >= 2) {
|
|
2139
|
+
$params = [];
|
|
2140
|
+
foreach (explode(',', $parts[1]) as $p) {
|
|
2141
|
+
$kv = explode('=', $p, 2);
|
|
2142
|
+
if (count($kv) === 2) {
|
|
2143
|
+
$params[$kv[0]] = $kv[1];
|
|
2144
|
+
}
|
|
2145
|
+
}
|
|
2146
|
+
$priorityOrder = $parts[2] ?? '';
|
|
2147
|
+
$frameOrderRaw = $parts[3] ?? $context->getHeader('x-quic-frame-order') ?? '';
|
|
2148
|
+
$frameOrder = array_values(array_filter(array_map('trim', explode(',', strtolower($frameOrderRaw)))));
|
|
2149
|
+
|
|
2150
|
+
$isChromium = str_starts_with($browser, 'Chrome') || str_starts_with($browser, 'Edge');
|
|
2151
|
+
$isFirefox = str_starts_with($browser, 'Firefox');
|
|
2152
|
+
$isSafari = str_starts_with($browser, 'Safari');
|
|
2153
|
+
|
|
2154
|
+
$maxData = (int)($params['1'] ?? $params['0x01'] ?? 0);
|
|
2155
|
+
$maxStreams = (int)($params['4'] ?? $params['8'] ?? $params['0x08'] ?? 0);
|
|
2156
|
+
$bidiLocal = (int)($params['5'] ?? $params['0x05'] ?? 0);
|
|
2157
|
+
$bidiRemote = (int)($params['6'] ?? $params['0x06'] ?? 0);
|
|
2158
|
+
|
|
2159
|
+
if ($isChromium) {
|
|
2160
|
+
if ($maxData > 0 && $maxData < 1048576) $quicAnomaly += 40.0;
|
|
2161
|
+
if ($maxStreams > 0 && $maxStreams !== 100) $quicAnomaly += 30.0;
|
|
2162
|
+
if (!empty($priorityOrder) && !str_contains($priorityOrder, 'u=')) $quicAnomaly += 30.0;
|
|
2163
|
+
if ($bidiLocal > 0 && ($bidiLocal < 524288 || $bidiLocal === 262144)) $quicAnomaly += 40.0;
|
|
2164
|
+
if ($bidiRemote > 0 && ($bidiRemote < 524288 || $bidiRemote === 262144)) $quicAnomaly += 30.0;
|
|
2165
|
+
|
|
2166
|
+
if (count($frameOrder) >= 2) {
|
|
2167
|
+
$sIdx = self::array_find_key($frameOrder, ['s', 'settings', '4']);
|
|
2168
|
+
$mIdx = self::array_find_key($frameOrder, ['m', 'max_streams', '18']);
|
|
2169
|
+
$pIdx = self::array_find_key($frameOrder, ['p', 'priority', 'priority_update', '15']);
|
|
2170
|
+
|
|
2171
|
+
if ($sIdx !== null && $sIdx !== 0) $quicAnomaly += 50.0;
|
|
2172
|
+
if ($mIdx !== null && $sIdx !== null && $mIdx < $sIdx) $quicAnomaly += 60.0;
|
|
2173
|
+
if ($pIdx !== null && $sIdx !== null && $pIdx < $sIdx) $quicAnomaly += 60.0;
|
|
2174
|
+
}
|
|
2175
|
+
} elseif ($isFirefox) {
|
|
2176
|
+
if ($maxData > 0 && $maxData > 5000000) $quicAnomaly += 40.0;
|
|
2177
|
+
if ($maxStreams === 100) $quicAnomaly += 50.0;
|
|
2178
|
+
if ($bidiLocal === 6291456) $quicAnomaly += 50.0;
|
|
2179
|
+
|
|
2180
|
+
if (count($frameOrder) >= 2) {
|
|
2181
|
+
$sIdx = self::array_find_key($frameOrder, ['s', 'settings', '4']);
|
|
2182
|
+
if ($sIdx !== null && $sIdx !== 0) $quicAnomaly += 50.0;
|
|
2183
|
+
}
|
|
2184
|
+
} elseif ($isSafari) {
|
|
2185
|
+
if ($maxStreams === 100 && $maxData === 1572864 && str_contains($priorityOrder, 'u=2,i')) {
|
|
2186
|
+
$quicAnomaly += 60.0;
|
|
2187
|
+
}
|
|
2188
|
+
if ($bidiLocal === 6291456) $quicAnomaly += 50.0;
|
|
2189
|
+
|
|
2190
|
+
if (count($frameOrder) >= 2) {
|
|
2191
|
+
$sIdx = self::array_find_key($frameOrder, ['s', 'settings', '4']);
|
|
2192
|
+
$mIdx = self::array_find_key($frameOrder, ['m', 'max_streams']);
|
|
2193
|
+
if ($mIdx !== null && ($mIdx === 0 || ($sIdx !== null && $mIdx < $sIdx))) {
|
|
2194
|
+
$quicAnomaly += 50.0;
|
|
2195
|
+
}
|
|
2196
|
+
}
|
|
2057
2197
|
}
|
|
2058
|
-
}
|
|
2059
|
-
$priorityOrder = $parts[2] ?? '';
|
|
2060
|
-
|
|
2061
|
-
if (str_starts_with($browser, 'Chrome') || str_starts_with($browser, 'Edge')) {
|
|
2062
|
-
$maxData = isset($params['1']) ? (int)$params['1'] : 0;
|
|
2063
|
-
$maxStreams = isset($params['4']) ? (int)$params['4'] : 0;
|
|
2064
|
-
if ($maxData > 0 && $maxData < 1048576) $quicAnomaly += 40.0;
|
|
2065
|
-
if ($maxStreams > 0 && $maxStreams !== 100) $quicAnomaly += 30.0;
|
|
2066
|
-
if (!empty($priorityOrder) && !str_contains($priorityOrder, 'u=')) $quicAnomaly += 30.0;
|
|
2067
|
-
} elseif (str_starts_with($browser, 'Firefox')) {
|
|
2068
|
-
$maxData = isset($params['1']) ? (int)$params['1'] : 0;
|
|
2069
|
-
if ($maxData > 0 && $maxData > 5000000) $quicAnomaly += 40.0;
|
|
2070
2198
|
}
|
|
2071
2199
|
}
|
|
2072
2200
|
}
|
|
@@ -2076,7 +2204,9 @@ class RequestUtils
|
|
|
2076
2204
|
0.0,
|
|
2077
2205
|
min(100.0, $http2Anomaly),
|
|
2078
2206
|
min(100.0, $quicAnomaly)
|
|
2079
|
-
)
|
|
2207
|
+
),
|
|
2208
|
+
'http2AnomalyScore' => min(100.0, $http2Anomaly),
|
|
2209
|
+
'quicAnomalyScore' => min(100.0, $quicAnomaly)
|
|
2080
2210
|
];
|
|
2081
2211
|
}
|
|
2082
2212
|
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
* Plugin Name: Fingerprint Anti-Bot & Proof-of-Work
|
|
4
4
|
* Plugin URI: https://github.com/anonympins/fingerprint
|
|
5
5
|
* Description: High-performance client-side anti-bot protection and Proof-of-Work challenge verification for WordPress.
|
|
6
|
-
* Version: 0.7.
|
|
6
|
+
* Version: 0.7.5
|
|
7
7
|
* Author: anonympins
|
|
8
8
|
* Requires at least: 5.9
|
|
9
9
|
* Requires PHP: 8.0
|
|
Binary file
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
msgid ""
|
|
2
2
|
msgstr ""
|
|
3
|
-
"Project-Id-Version: Fingerprint Anti-Bot 0.7.
|
|
3
|
+
"Project-Id-Version: Fingerprint Anti-Bot 0.7.5\n"
|
|
4
4
|
"Report-Msgid-Bugs-To: https://github.com/anonympins/fingerprint/issues\n"
|
|
5
5
|
"POT-Creation-Date: 2026-09-24 12:00+0000\n"
|
|
6
6
|
"PO-Revision-Date: 2026-09-24 12:00+0000\n"
|
|
Binary file
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
msgid ""
|
|
2
2
|
msgstr ""
|
|
3
|
-
"Project-Id-Version: Fingerprint Anti-Bot 0.7.
|
|
3
|
+
"Project-Id-Version: Fingerprint Anti-Bot 0.7.5\n"
|
|
4
4
|
"Report-Msgid-Bugs-To: https://github.com/anonympins/fingerprint/issues\n"
|
|
5
5
|
"POT-Creation-Date: 2026-09-24 12:00+0000\n"
|
|
6
6
|
"PO-Revision-Date: 2026-09-24 12:00+0000\n"
|