@anonympins/fingerprint 0.7.2 → 0.7.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 (41) hide show
  1. package/CHANGELOG.md +36 -9
  2. package/README.md +1 -1
  3. package/package.json +109 -107
  4. package/public/fingerprint-anti-bot.zip +0 -0
  5. package/src/js/fingerprint.client.js +252 -45
  6. package/src/js/fingerprint.client.obfuscated.js +1 -1
  7. package/src/js/fingerprint.js +265 -185
  8. package/src/js/fingerprint.utils.js +32 -5
  9. package/src/js/fingerprint.worker.js +81 -0
  10. package/src/js/library.js +6 -2
  11. package/src/js/pow.solver.js +2 -3
  12. package/src/js/problem-manager.js +113 -113
  13. package/src/js/tests/fingerprint.client.init.test.js +30 -0
  14. package/src/js/tests/fingerprint.test.js +136 -4
  15. package/src/js/tests/threatIntel.test.js +76 -0
  16. package/src/php/AutoTuner.php +36 -18
  17. package/src/php/Challenge/ChallengeUtils.php +121 -240
  18. package/src/php/DirectFingerprint.php +16 -4
  19. package/src/php/FingerprintClient.php +17 -19
  20. package/src/php/FingerprintEngine.php +77 -39
  21. package/src/php/Ja3AnomalyDetector.php +20 -12
  22. package/src/php/Optimization/Optimization.php +5 -1
  23. package/src/php/Optimization/OptimizationOperators.php +5 -1
  24. package/src/php/Optimization/ProblemInitializers.php +16 -4
  25. package/src/php/ProblemManager.php +20 -14
  26. package/src/php/Tests/ChallengeUtilsTest.php +469 -453
  27. package/src/php/Tests/FingerprintEngineTest.php +6 -1
  28. package/src/php/Tests/RequestUtilsTest.php +1 -1
  29. package/src/php/Tests/ThreatIntelTest.php +64 -0
  30. package/src/php/Utils/Env.php +51 -49
  31. package/src/php/Utils/MaliciousPatterns.php +74 -74
  32. package/src/php/Utils/RequestUtils.php +231 -132
  33. package/src/php/WordPress/WpDbStore.php +4 -0
  34. package/src/php/WordPress/{fingerprint-wordpress.php → fingerprint-anti-bot.php} +131 -126
  35. package/src/php/WordPress/languages/fingerprint-wordpress-de_DE.mo +0 -0
  36. package/src/php/WordPress/languages/fingerprint-wordpress-de_DE.po +1 -1
  37. package/src/php/WordPress/languages/fingerprint-wordpress-fr_FR.mo +0 -0
  38. package/src/php/WordPress/languages/fingerprint-wordpress-fr_FR.po +1 -1
  39. package/src/php/WordPress/package.php +6 -3
  40. package/src/php/WordPress/readme.txt +36 -0
  41. package/public/fingerprint-wordpress.zip +0 -0
@@ -304,6 +304,9 @@ class FingerprintEngineTest extends TestCase
304
304
  }
305
305
 
306
306
  $configDir = dirname(__FILE__, 3) . '/config'; // Match engine's behavior
307
+ if (!is_dir($configDir)) {
308
+ mkdir($configDir, 0777, true);
309
+ }
307
310
  $keyPath = $configDir . '/ed25519_key.json';
308
311
 
309
312
  Env::clear('ED25519_PRIVATE_KEY');
@@ -328,7 +331,9 @@ class FingerprintEngineTest extends TestCase
328
331
  $this->assertEquals(Env::get('ED25519_PRIVATE_KEY'), $stored['privateKey']);
329
332
  $this->assertEquals(Env::get('ED25519_PUBLIC_KEY'), $stored['publicKey']);
330
333
 
331
- unlink($keyPath);
334
+ if (file_exists($keyPath)) {
335
+ unlink($keyPath);
336
+ }
332
337
  }
333
338
 
334
339
  public function testEd25519KeyLoadingFromDisk(): void
@@ -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(40.0, $scoreNoActivity['behaviorScore']);
236
+ $this->assertEquals(5.0, $scoreNoActivity['behaviorScore']);
237
237
  }
238
238
 
239
239
  public function testGetBehaviorScoreWithBotLikeKeystrokeDynamics(): void
@@ -0,0 +1,64 @@
1
+ <?php
2
+
3
+ declare(strict_types=1);
4
+
5
+ namespace Anonympins\Fingerprint\Tests;
6
+
7
+ use Anonympins\Fingerprint\Ja3AnomalyDetector;
8
+ use Anonympins\Fingerprint\RequestContext;
9
+ use Anonympins\Fingerprint\Store\InMemoryStore;
10
+ use Anonympins\Fingerprint\Store\StoreManager;
11
+ use Anonympins\Fingerprint\Utils\RequestUtils;
12
+ use PHPUnit\Framework\TestCase;
13
+
14
+ class ThreatIntelTest extends TestCase
15
+ {
16
+ protected function setUp(): void
17
+ {
18
+ parent::setUp();
19
+ StoreManager::configureStore(new InMemoryStore());
20
+ }
21
+
22
+ public function testRttProxyScoreReturnsZeroForCoherentTurnaround(): void
23
+ {
24
+ $reqTime = 1700000000000;
25
+ $context = new RequestContext(
26
+ '127.0.0.1', '/',
27
+ [
28
+ 'x-tcp-rtt' => '15',
29
+ 'x-behavior-metrics' => json_encode(['clientTimestamp' => $reqTime - 30])
30
+ ],
31
+ [], null, [], '1.1', $reqTime
32
+ );
33
+
34
+ $score = Ja3AnomalyDetector::getRttProxyScore($context);
35
+ $this->assertEquals(0.0, $score);
36
+ }
37
+
38
+ public function testRttProxyScoreIgnoresSlowConnectionWithoutTcpRtt(): void
39
+ {
40
+ $reqTime = 1700000000000;
41
+ $context = new RequestContext(
42
+ '127.0.0.1', '/',
43
+ ['x-behavior-metrics' => json_encode(['clientTimestamp' => $reqTime - 450])],
44
+ [], null, [], '1.1', $reqTime
45
+ );
46
+
47
+ $score = Ja3AnomalyDetector::getRttProxyScore($context);
48
+ $this->assertEquals(0.0, $score, 'A slow connection without TCP RTT context should not trigger a false positive.');
49
+ }
50
+
51
+ public function testRttProxyScoreContinuouslyPenalizesResidentialTunneling(): void
52
+ {
53
+ $reqTime = 1700000000000;
54
+ $context = new RequestContext(
55
+ '127.0.0.1', '/',
56
+ ['x-tcp-rtt' => '10', 'x-behavior-metrics' => json_encode(['clientTimestamp' => $reqTime - 300])],
57
+ [], null, [], '1.1', $reqTime
58
+ );
59
+
60
+ $score = Ja3AnomalyDetector::getRttProxyScore($context);
61
+ $this->assertGreaterThan(75.0, $score);
62
+ $this->assertLessThanOrEqual(95.0, $score);
63
+ }
64
+ }
@@ -1,50 +1,52 @@
1
- <?php
2
-
3
- declare(strict_types=1);
4
-
5
- namespace Anonympins\Fingerprint\Utils;
6
-
7
- class Env
8
- {
9
- /** @var array<string, mixed> */
10
- private static array $localCache = [];
11
-
12
- /**
13
- * @param string $key
14
- * @param mixed $default
15
- * @return mixed
16
- */
17
- public static function get(string $key, $default = null)
18
- {
19
- if (array_key_exists($key, self::$localCache)) {
20
- return self::$localCache[$key];
21
- }
22
- if (isset($_ENV[$key]) && $_ENV[$key] !== '') {
23
- return $_ENV[$key];
24
- }
25
- if (isset($_SERVER[$key]) && $_SERVER[$key] !== '') {
26
- return $_SERVER[$key];
27
- }
28
- $val = getenv($key);
29
- if ($val !== false && $val !== '') {
30
- return $val;
31
- }
32
- return $default;
33
- }
34
-
35
- public static function set(string $key, $value): void
36
- {
37
- self::$localCache[$key] = $value;
38
- $_ENV[$key] = $value;
39
- $_SERVER[$key] = $value;
40
- @putenv("{$key}={$value}");
41
- }
42
-
43
- public static function clear(string $key): void
44
- {
45
- unset(self::$localCache[$key]);
46
- unset($_ENV[$key]);
47
- unset($_SERVER[$key]);
48
- @putenv($key);
49
- }
1
+ <?php
2
+
3
+ declare(strict_types=1);
4
+
5
+ namespace Anonympins\Fingerprint\Utils;
6
+
7
+ class Env
8
+ {
9
+ /** @var array<string, mixed> */
10
+ private static array $localCache = [];
11
+
12
+ /**
13
+ * @param string $key
14
+ * @param mixed $default
15
+ * @return mixed
16
+ */
17
+ public static function get(string $key, $default = null)
18
+ {
19
+ if (array_key_exists($key, self::$localCache)) {
20
+ return self::$localCache[$key];
21
+ }
22
+ // phpcs:disable WordPress.Security.ValidatedSanitizedInput.InputNotSanitized,WordPress.Security.ValidatedSanitizedInput.MissingUnslash -- Environment and server variables may contain raw cryptographic keys.
23
+ if (isset($_ENV[$key]) && $_ENV[$key] !== '') {
24
+ return $_ENV[$key];
25
+ }
26
+ if (isset($_SERVER[$key]) && $_SERVER[$key] !== '') {
27
+ return $_SERVER[$key];
28
+ }
29
+ // phpcs:enable WordPress.Security.ValidatedSanitizedInput.InputNotSanitized,WordPress.Security.ValidatedSanitizedInput.MissingUnslash
30
+ $val = getenv($key);
31
+ if ($val !== false && $val !== '') {
32
+ return $val;
33
+ }
34
+ return $default;
35
+ }
36
+
37
+ public static function set(string $key, $value): void
38
+ {
39
+ self::$localCache[$key] = $value;
40
+ $_ENV[$key] = $value;
41
+ $_SERVER[$key] = $value;
42
+ @putenv("{$key}={$value}");
43
+ }
44
+
45
+ public static function clear(string $key): void
46
+ {
47
+ unset(self::$localCache[$key]);
48
+ unset($_ENV[$key]);
49
+ unset($_SERVER[$key]);
50
+ @putenv($key);
51
+ }
50
52
  }
@@ -1,75 +1,75 @@
1
- <?php
2
-
3
- declare(strict_types=1);
4
-
5
- namespace Anonympins\Fingerprint\Utils;
6
-
7
- /**
8
- * Fournit des fonctions pour détecter les patterns d'injection malveillants.
9
- */
10
- class MaliciousPatterns
11
- {
12
- /**
13
- * @var array<string, string> Map des patterns malveillants regroupés par type.
14
- */
15
- private const INJECTION_PATTERNS = [
16
- // SQL/NoSQL injections, including time-based attacks
17
- 'sql' => '/(\$ne|\' *OR *\'1\'=\'1|[\'";]\s*--|; ?(DROP|TRUNCATE|DELETE)|UNION SELECT|(?:SLEEP|BENCHMARK)\s*\(|WAITFOR DELAY)/i',
18
- // Log4Shell (JNDI injection)
19
- 'log4shell' => '/\$\{jndi:(ldap|rmi|dns):/i',
20
- // Server-Side Template Injection (SSTI) for engines like Jinja2, Twig, etc.
21
- 'ssti' => '/\{\{.*\}\}|\{%.*%\}/',
22
- // XML External Entity (XXE) injection
23
- 'xxe' => '/<!ENTITY\s+.*SYSTEM/i',
24
- // Path Traversal
25
- 'traversal' => '/(\.\.\/|\.\.)/',
26
- // Remote Command Execution (RCE)
27
- // The original regex had an issue with `|cmd` being interpreted as a modifier in some PCRE versions.
28
- // Using a non-capturing group (?:...) makes it more robust and fixes the compilation error.
29
- 'rce' => '/`.*`|(?:^|[\n;&|]\s*)(?:ping|ls|whoami|cat|rm|ncat|nc|bash|sh|powershell|cmd)\b/i',
30
- // Server-Side Request Forgery (SSRF)
31
- 'ssrf' => '/(?:https?:\/\/)?(?:127\.\d+\.\d+\.\d+|169\.254\.169\.254|10\.\d+\.\d+\.\d+|172\.(?:1[6-9]|2\d|3[01])\.\d+\.\d+|192\.168\.\d+\.\d+|localhost|0\.0\.0\.0|\[[0:]+1\])\b/i',
32
- // Carriage Return Line Feed (CRLF) Injection
33
- 'crlf' => '/[\r\n]|%0[ad]/i',
34
- // Cross-Site Scripting (XSS)
35
- 'xss' => '/(<script|javascript:|on\w+\s*=|alert\s*\(|confirm\s*\(|prompt\s*\(|<img\s+src[^>]+onerror|<iframe)/i',
36
- // Open Redirect
37
- 'openRedirect' => '/^(https?:)?\/\/(?![^\/]*?(localhost|127\.0\.0\.1))[^\s\/]+/i',
38
- // Local/Remote File Inclusion (LFI/RFI)
39
- 'lfi' => '/(?:etc\/passwd|win\.ini|boot\.ini|php:\/\/filter|data:\/\/|zip:\/\/)/i',
40
- // Shellshock (CVE-2014-6271)
41
- 'shellshock' => '/\(\)\s*\{\s*:\s*;\s*\}\s*/i',
42
- // NoSQL Injection (MongoDB query operators)
43
- 'nosql' => '/\$(?:eq|ne|gt|gte|lt|lte|in|nin|and|or|nor|not|expr|jsonSchema|mod|regex|text|where|elemMatch)/i',
44
- ];
45
-
46
- /**
47
- * Vérifie si une chaîne de caractères contient des patterns d'injection connus.
48
- * @param string $str La chaîne à vérifier.
49
- * @param array<string> $typesToDetect Les types d'injections à détecter (par défaut, tous).
50
- * @return bool True si un pattern malveillant est détecté, false sinon.
51
- */
52
- public static function isMalicious(string $str, array $typesToDetect = []): bool
53
- {
54
- if (empty($typesToDetect)) {
55
- $typesToDetect = array_filter(array_keys(self::INJECTION_PATTERNS), function ($key) {
56
- return $key !== 'openRedirect';
57
- });
58
- }
59
-
60
- foreach ($typesToDetect as $type) {
61
- if (isset(self::INJECTION_PATTERNS[$type])) {
62
- try {
63
- if (preg_match(self::INJECTION_PATTERNS[$type], $str)) {
64
- return true;
65
- }
66
- }catch (\Exception $e){
67
- error_log($type);
68
- error_log($e->getMessage());
69
- }
70
- }
71
- }
72
-
73
- return false;
74
- }
1
+ <?php
2
+
3
+ declare(strict_types=1);
4
+
5
+ namespace Anonympins\Fingerprint\Utils;
6
+
7
+ /**
8
+ * Fournit des fonctions pour détecter les patterns d'injection malveillants.
9
+ */
10
+ class MaliciousPatterns
11
+ {
12
+ /**
13
+ * @var array<string, string> Map des patterns malveillants regroupés par type.
14
+ */
15
+ private const INJECTION_PATTERNS = [
16
+ // SQL/NoSQL injections, including time-based attacks
17
+ 'sql' => '/(\$ne|\' *OR *\'1\'=\'1|[\'";]\s*--|; ?(DROP|TRUNCATE|DELETE)|UNION SELECT|(?:SLEEP|BENCHMARK)\s*\(|WAITFOR DELAY)/i',
18
+ // Log4Shell (JNDI injection)
19
+ 'log4shell' => '/\$\{jndi:(ldap|rmi|dns):/i',
20
+ // Server-Side Template Injection (SSTI) for engines like Jinja2, Twig, etc.
21
+ 'ssti' => '/\{\{.*\}\}|\{%.*%\}/',
22
+ // XML External Entity (XXE) injection
23
+ 'xxe' => '/<!ENTITY\s+.*SYSTEM/i',
24
+ // Path Traversal
25
+ 'traversal' => '/(\.\.\/|\.\.)/',
26
+ // Remote Command Execution (RCE)
27
+ // The original regex had an issue with `|cmd` being interpreted as a modifier in some PCRE versions.
28
+ // Using a non-capturing group (?:...) makes it more robust and fixes the compilation error.
29
+ 'rce' => '/`.*`|(?:^|[\n;&|]\s*)(?:ping|ls|whoami|cat|rm|ncat|nc|bash|sh|powershell|cmd)\b/i',
30
+ // Server-Side Request Forgery (SSRF)
31
+ 'ssrf' => '/(?:https?:\/\/)?(?:127\.\d+\.\d+\.\d+|169\.254\.169\.254|10\.\d+\.\d+\.\d+|172\.(?:1[6-9]|2\d|3[01])\.\d+\.\d+|192\.168\.\d+\.\d+|localhost|0\.0\.0\.0|\[[0:]+1\])\b/i',
32
+ // Carriage Return Line Feed (CRLF) Injection
33
+ 'crlf' => '/[\r\n]|%0[ad]/i',
34
+ // Cross-Site Scripting (XSS)
35
+ 'xss' => '/(<script|javascript:|on\w+\s*=|alert\s*\(|confirm\s*\(|prompt\s*\(|<img\s+src[^>]+onerror|<iframe)/i',
36
+ // Open Redirect
37
+ 'openRedirect' => '/^(https?:)?\/\/(?![^\/]*?(localhost|127\.0\.0\.1))[^\s\/]+/i',
38
+ // Local/Remote File Inclusion (LFI/RFI)
39
+ 'lfi' => '/(?:etc\/passwd|win\.ini|boot\.ini|php:\/\/filter|data:\/\/|zip:\/\/)/i',
40
+ // Shellshock (CVE-2014-6271)
41
+ 'shellshock' => '/\(\)\s*\{\s*:\s*;\s*\}\s*/i',
42
+ // NoSQL Injection (MongoDB query operators)
43
+ 'nosql' => '/\$(?:eq|ne|gt|gte|lt|lte|in|nin|and|or|nor|not|expr|jsonSchema|mod|regex|text|where|elemMatch)/i',
44
+ ];
45
+
46
+ /**
47
+ * Vérifie si une chaîne de caractères contient des patterns d'injection connus.
48
+ * @param string $str La chaîne à vérifier.
49
+ * @param array<string> $typesToDetect Les types d'injections à détecter (par défaut, tous).
50
+ * @return bool True si un pattern malveillant est détecté, false sinon.
51
+ */
52
+ public static function isMalicious(string $str, array $typesToDetect = []): bool
53
+ {
54
+ if (empty($typesToDetect)) {
55
+ $typesToDetect = array_filter(array_keys(self::INJECTION_PATTERNS), function ($key) {
56
+ return $key !== 'openRedirect';
57
+ });
58
+ }
59
+
60
+ foreach ($typesToDetect as $type) {
61
+ if (isset(self::INJECTION_PATTERNS[$type])) {
62
+ try {
63
+ if (preg_match(self::INJECTION_PATTERNS[$type], $str)) {
64
+ return true;
65
+ }
66
+ } catch (\Exception $e) {
67
+ // phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_error_log -- WAF regex validation diagnostic
68
+ error_log("Pattern inspection failed for {$type}: " . $e->getMessage());
69
+ }
70
+ }
71
+ }
72
+
73
+ return false;
74
+ }
75
75
  }