@anonympins/fingerprint 0.7.4 → 0.8.0
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 +59 -0
- package/README.md +1 -1
- package/package.json +1 -1
- package/public/anonympins-bot-mitigation-pow.zip +0 -0
- package/src/js/dynamic-wasm.js +218 -218
- package/src/js/fingerprint.client.js +1784 -1738
- package/src/js/fingerprint.client.obfuscated.js +1 -1
- package/src/js/fingerprint.js +489 -99
- package/src/js/fingerprint.utils.js +252 -2
- package/src/js/pow.worker.js +2 -2
- package/src/js/tests/cross_parity.test.js +3 -3
- package/src/js/tests/dns-circuit-breaker.test.js +6 -6
- package/src/js/tests/dynamic-wasm.test.js +77 -77
- package/src/js/tests/fingerprint.builder.test.js +76 -78
- package/src/js/tests/fingerprint.client.init.test.js +78 -0
- package/src/js/tests/fingerprint.client.test.js +16 -17
- package/src/js/tests/fingerprint.test.js +37 -12
- package/src/js/tests/ja3AnomalyDetector.test.js +135 -136
- package/src/js/tests/library.test.js +94 -95
- package/src/js/tests/obfuscation.test.js +8 -8
- package/src/js/tests/quicFingerprint.test.js +47 -0
- package/src/js/tests/tcpFingerprint.test.js +4 -4
- package/src/js/tests/threatIntel.test.js +40 -1
- package/src/js/upow-model-task.js +155 -112
- package/src/php/Challenge/ChallengeUtils.php +392 -86
- package/src/php/Config/SecurityProfiles.php +79 -61
- package/src/php/DirectFingerprint.php +55 -8
- package/src/php/FingerprintBuilder.php +13 -13
- package/src/php/FingerprintClient.php +6 -6
- package/src/php/FingerprintEngine.php +2122 -1690
- package/src/php/Ja3AnomalyDetector.php +22 -22
- package/src/php/Optimization/FunctionRegistry.php +4 -4
- package/src/php/Optimization/Optimization.php +6 -6
- package/src/php/RequestContext.php +11 -11
- package/src/php/Store/InMemoryStore.php +96 -66
- package/src/php/Store/MongoDbStore.php +2 -2
- package/src/php/Store/RedisStore.php +1 -1
- package/src/php/Store/StoreManager.php +48 -35
- package/src/php/Tests/ChallengeUtilsTest.php +31 -0
- package/src/php/Tests/DnsCircuitBreakerTest.php +105 -105
- package/src/php/Tests/FingerprintClientTest.php +70 -70
- package/src/php/Tests/PowTest.php +37 -39
- package/src/php/Tests/QuicFingerprintTest.php +115 -53
- package/src/php/Tests/ThreatIntelTest.php +34 -0
- package/src/php/Utils/BigInt.php +202 -202
- package/src/php/Utils/BlockList.php +13 -13
- package/src/php/Utils/Logger.php +19 -1
- package/src/php/Utils/MaliciousPatterns.php +6 -6
- package/src/php/Utils/MetricsManager.php +209 -209
- package/src/php/Utils/RequestUtils.php +362 -1
- package/src/php/Utils/TLSClientHelloParser.php +385 -369
- package/src/php/WordPress/WpDbStore.php +7 -7
- package/src/php/WordPress/anonympins-bot-mitigation-pow.php +1121 -0
- package/src/php/WordPress/fingerprint-anti-bot.php +0 -559
- package/src/php/WordPress/languages/anonympins-bot-mitigation-pow-fr_FR.mo +0 -0
- package/src/php/WordPress/languages/anonympins-bot-mitigation-pow-fr_FR.po +232 -0
- package/src/php/WordPress/languages/fingerprint-wordpress-de_DE.mo +0 -0
- package/src/php/WordPress/languages/fingerprint-wordpress-de_DE.po +297 -261
- package/src/php/WordPress/languages/fingerprint-wordpress-fr_FR.mo +0 -0
- package/src/php/WordPress/languages/fingerprint-wordpress-fr_FR.po +348 -267
- package/src/php/WordPress/package.php +80 -101
- package/src/php/WordPress/readme.txt +21 -5
- package/src/php/bin/auto-tune.php +1 -1
- package/public/fingerprint-anti-bot.zip +0 -0
|
@@ -3,8 +3,8 @@
|
|
|
3
3
|
declare(strict_types=1);
|
|
4
4
|
|
|
5
5
|
/**
|
|
6
|
-
*
|
|
7
|
-
*
|
|
6
|
+
* Automated packaging script for the WordPress plugin.
|
|
7
|
+
* Copies PHP engine classes, JS assets, and creates an installation-ready ZIP archive.
|
|
8
8
|
*
|
|
9
9
|
* Usage:
|
|
10
10
|
* php src/php/WordPress/package.php
|
|
@@ -20,13 +20,13 @@ $phpSrcDir = $rootDir . '/src/php';
|
|
|
20
20
|
$jsSrcDir = $rootDir . '/src/js';
|
|
21
21
|
$wpDir = $phpSrcDir . '/WordPress';
|
|
22
22
|
$distDir = $rootDir . '/public';
|
|
23
|
-
$pluginSlug = '
|
|
23
|
+
$pluginSlug = 'anonympins-bot-mitigation-pow';
|
|
24
24
|
$buildDir = $distDir . '/' . $pluginSlug;
|
|
25
25
|
$zipFile = $distDir . '/' . $pluginSlug . '.zip';
|
|
26
26
|
|
|
27
|
-
echo "==>
|
|
27
|
+
echo "==> Packaging WordPress plugin...\n";
|
|
28
28
|
|
|
29
|
-
// 1.
|
|
29
|
+
// 1. Clean previous build directory
|
|
30
30
|
if (is_dir($buildDir)) {
|
|
31
31
|
$iterator = new RecursiveIteratorIterator(
|
|
32
32
|
new RecursiveDirectoryIterator($buildDir, FilesystemIterator::SKIP_DOTS),
|
|
@@ -45,114 +45,93 @@ if (file_exists($zipFile)) {
|
|
|
45
45
|
@mkdir($buildDir . '/assets', 0777, true);
|
|
46
46
|
@mkdir($buildDir . '/languages', 0777, true);
|
|
47
47
|
|
|
48
|
+
// 2. Copy main WordPress plugin files
|
|
49
|
+
copy($wpDir . '/anonympins-bot-mitigation-pow.php', $buildDir . '/anonympins-bot-mitigation-pow.php');
|
|
50
|
+
copy($wpDir . '/WpDbStore.php', $buildDir . '/WpDbStore.php');
|
|
51
|
+
if (file_exists($wpDir . '/readme.txt')) {
|
|
52
|
+
copy($wpDir . '/readme.txt', $buildDir . '/readme.txt');
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
// 2.b Compile and copy language files (PO -> MO)
|
|
56
|
+
$languagesDir = $wpDir . '/languages';
|
|
57
|
+
if (is_dir($languagesDir)) {
|
|
58
|
+
$poFiles = glob($languagesDir . '/*.po') ?: [];
|
|
59
|
+
foreach ($poFiles as $poFile) {
|
|
60
|
+
$filename = basename($poFile);
|
|
61
|
+
copy($poFile, $buildDir . '/languages/' . $filename);
|
|
62
|
+
|
|
63
|
+
$moFile = preg_replace('/\.po$/', '.mo', $poFile);
|
|
64
|
+
// Compile PO to binary MO if missing or outdated
|
|
65
|
+
if (!file_exists($moFile) || filemtime($poFile) > filemtime($moFile)) {
|
|
66
|
+
compilePoToMo($poFile, $moFile);
|
|
67
|
+
}
|
|
68
|
+
if (file_exists($moFile)) {
|
|
69
|
+
copy($moFile, $buildDir . '/languages/' . basename($moFile));
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
|
|
48
74
|
/**
|
|
49
|
-
*
|
|
75
|
+
* Minimal native PHP PO -> binary gettext MO compiler
|
|
50
76
|
*/
|
|
51
|
-
function compilePoToMo(string $
|
|
52
|
-
|
|
53
|
-
$
|
|
54
|
-
if ($content
|
|
55
|
-
|
|
77
|
+
function compilePoToMo(string $poPath, string $moPath): void {
|
|
78
|
+
$content = (string)file_get_contents($poPath);
|
|
79
|
+
$pattern = '/msgid\s+("(?:[^"\\\\]|\\\\.)*")\s+msgstr\s+("(?:[^"\\\\]|\\\\.)*")/s';
|
|
80
|
+
if (!preg_match_all($pattern, $content, $matches, PREG_SET_ORDER)) {
|
|
81
|
+
return;
|
|
82
|
+
}
|
|
56
83
|
$entries = [];
|
|
57
|
-
$
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
foreach ($lines as $line) {
|
|
63
|
-
$line = trim($line);
|
|
64
|
-
if ($line === '' || str_starts_with($line, '#')) continue;
|
|
65
|
-
if (preg_match('/^msgid\s+"(.*)"$/', $line, $m)) {
|
|
66
|
-
if ($currentMsgId !== null && $currentMsgStr !== null) {
|
|
67
|
-
$entries[$currentMsgId] = $currentMsgStr;
|
|
68
|
-
}
|
|
69
|
-
$currentMsgId = stripcslashes($m[1]);
|
|
70
|
-
$currentMsgStr = null;
|
|
71
|
-
$state = 'msgid';
|
|
72
|
-
} elseif (preg_match('/^msgstr\s+"(.*)"$/', $line, $m)) {
|
|
73
|
-
$currentMsgStr = stripcslashes($m[1]);
|
|
74
|
-
$state = 'msgstr';
|
|
75
|
-
} elseif (preg_match('/^"(.*)"$/', $line, $m)) {
|
|
76
|
-
if ($state === 'msgid') {
|
|
77
|
-
$currentMsgId .= stripcslashes($m[1]);
|
|
78
|
-
} elseif ($state === 'msgstr') {
|
|
79
|
-
$currentMsgStr .= stripcslashes($m[1]);
|
|
80
|
-
}
|
|
84
|
+
foreach ($matches as $m) {
|
|
85
|
+
$orig = stripcslashes(substr($m[1], 1, -1));
|
|
86
|
+
$trans = stripcslashes(substr($m[2], 1, -1));
|
|
87
|
+
if ($orig !== '' && $trans !== '') {
|
|
88
|
+
$entries[$orig] = $trans;
|
|
81
89
|
}
|
|
82
90
|
}
|
|
83
|
-
if ($currentMsgId !== null && $currentMsgStr !== null) {
|
|
84
|
-
$entries[$currentMsgId] = $currentMsgStr;
|
|
85
|
-
}
|
|
86
|
-
|
|
87
91
|
ksort($entries);
|
|
88
92
|
$count = count($entries);
|
|
89
|
-
$
|
|
90
|
-
$
|
|
91
|
-
$
|
|
92
|
-
$
|
|
93
|
-
|
|
94
|
-
$
|
|
95
|
-
$tablesSize = $count * 8 * 2;
|
|
96
|
-
$curOffset = $headerSize + $tablesSize;
|
|
97
|
-
|
|
93
|
+
$originalsTable = '';
|
|
94
|
+
$translationsTable = '';
|
|
95
|
+
$origOffsets = [];
|
|
96
|
+
$transOffsets = [];
|
|
97
|
+
$headerLength = 28 + ($count * 8) * 2;
|
|
98
|
+
$currentOffset = $headerLength;
|
|
98
99
|
foreach ($entries as $orig => $trans) {
|
|
99
|
-
$
|
|
100
|
-
$
|
|
101
|
-
$originals .= $orig . "\0";
|
|
100
|
+
$origOffsets[] = ['len' => strlen($orig), 'off' => $currentOffset];
|
|
101
|
+
$currentOffset += strlen($orig) + 1;
|
|
102
102
|
}
|
|
103
|
-
|
|
104
|
-
$transOffset = $curOffset + strlen($originals);
|
|
105
103
|
foreach ($entries as $orig => $trans) {
|
|
106
|
-
$
|
|
107
|
-
$
|
|
108
|
-
$translations .= $trans . "\0";
|
|
109
|
-
}
|
|
110
|
-
|
|
111
|
-
$mo = pack('V*',
|
|
112
|
-
0x950412de, // Magic Number
|
|
113
|
-
0, // Revision
|
|
114
|
-
$count, // Number of strings
|
|
115
|
-
28, // Offset of table with original string lengths and offsets
|
|
116
|
-
28 + ($count * 8), // Offset of table with translation string lengths and offsets
|
|
117
|
-
0, 0 // Hash table size and offset
|
|
118
|
-
);
|
|
119
|
-
|
|
120
|
-
foreach ($origTable as $t) {
|
|
121
|
-
$mo .= pack('VV', $t['len'], $t['off']);
|
|
104
|
+
$transOffsets[] = ['len' => strlen($trans), 'off' => $currentOffset];
|
|
105
|
+
$currentOffset += strlen($trans) + 1;
|
|
122
106
|
}
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
}
|
|
126
|
-
$
|
|
127
|
-
|
|
107
|
+
$binary = pack('V7', 0x950412de, 0, $count, 28, 28 + ($count * 8), 0, 0);
|
|
108
|
+
foreach ($origOffsets as $o) { $binary .= pack('V2', $o['len'], $o['off']); }
|
|
109
|
+
foreach ($transOffsets as $t) { $binary .= pack('V2', $t['len'], $t['off']); }
|
|
110
|
+
foreach ($entries as $orig => $trans) { $binary .= $orig . "\0"; }
|
|
111
|
+
foreach ($entries as $orig => $trans) { $binary .= $trans . "\0"; }
|
|
112
|
+
file_put_contents($moPath, $binary);
|
|
128
113
|
}
|
|
129
114
|
|
|
130
|
-
//
|
|
131
|
-
copy($wpDir . '/fingerprint-anti-bot.php', $buildDir . '/fingerprint-anti-bot.php');
|
|
132
|
-
copy($wpDir . '/WpDbStore.php', $buildDir . '/WpDbStore.php');
|
|
133
|
-
if (file_exists($wpDir . '/readme.txt')) {
|
|
134
|
-
copy($wpDir . '/readme.txt', $buildDir . '/readme.txt');
|
|
135
|
-
}
|
|
136
|
-
|
|
137
|
-
// 3. Copie des assets clients nécessaires (solveur PoW)
|
|
115
|
+
// 3. Copy required client assets (PoW solver and client bundle without external CDN)
|
|
138
116
|
$solverSource = $jsSrcDir . '/pow.solver.inline.js';
|
|
139
117
|
if (file_exists($solverSource)) {
|
|
140
|
-
|
|
118
|
+
$solverContent = (string)file_get_contents($solverSource);
|
|
119
|
+
// Neutralize remote CDN scripts prohibited by WordPress.org guidelines
|
|
120
|
+
$solverContent = str_replace(
|
|
121
|
+
["'https://cdn.jsdelivr.net/npm/onnxruntime-web/dist/ort.min.js'", "'https://cdn.jsdelivr.net/npm/@tensorflow/tfjs/dist/tf.min.js'"],
|
|
122
|
+
["''", "''"],
|
|
123
|
+
$solverContent
|
|
124
|
+
);
|
|
125
|
+
file_put_contents($buildDir . '/assets/pow.solver.inline.js', $solverContent);
|
|
141
126
|
}
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
if (
|
|
146
|
-
|
|
147
|
-
$baseName = basename($poFile);
|
|
148
|
-
copy($poFile, $buildDir . '/languages/' . $baseName);
|
|
149
|
-
$moTarget = $buildDir . '/languages/' . preg_replace('/\.po$/', '.mo', $baseName);
|
|
150
|
-
compilePoToMo($poFile, $moTarget);
|
|
151
|
-
compilePoToMo($poFile, $langSourceDir . '/' . preg_replace('/\.po$/', '.mo', $baseName));
|
|
152
|
-
}
|
|
127
|
+
$clientSource = file_exists($jsSrcDir . '/fingerprint.client.obfuscated.js')
|
|
128
|
+
? $jsSrcDir . '/fingerprint.client.obfuscated.js'
|
|
129
|
+
: $jsSrcDir . '/fingerprint.client.js';
|
|
130
|
+
if (file_exists($clientSource)) {
|
|
131
|
+
copy($clientSource, $buildDir . '/assets/fingerprint.client.js');
|
|
153
132
|
}
|
|
154
133
|
|
|
155
|
-
//
|
|
134
|
+
// 4. Recursively copy PHP library (src/php -> build/src), excluding dev folders
|
|
156
135
|
$excludeDirs = ['WordPress', 'Tests', 'bin'];
|
|
157
136
|
$phpIterator = new RecursiveIteratorIterator(
|
|
158
137
|
new RecursiveDirectoryIterator($phpSrcDir, FilesystemIterator::SKIP_DOTS),
|
|
@@ -177,9 +156,9 @@ foreach ($phpIterator as $item) {
|
|
|
177
156
|
}
|
|
178
157
|
}
|
|
179
158
|
|
|
180
|
-
echo "==>
|
|
159
|
+
echo "==> Files copied to {$buildDir}\n";
|
|
181
160
|
|
|
182
|
-
// 5.
|
|
161
|
+
// 5. Create ZIP archive
|
|
183
162
|
$zip = new ZipArchive();
|
|
184
163
|
if ($zip->open($zipFile, ZipArchive::CREATE | ZipArchive::OVERWRITE) !== true) {
|
|
185
164
|
fwrite(STDERR, "Erreur : Impossible de créer le fichier {$zipFile}\n");
|
|
@@ -194,7 +173,7 @@ $distIterator = new RecursiveIteratorIterator(
|
|
|
194
173
|
foreach ($distIterator as $file) {
|
|
195
174
|
if (!$file->isDir()) {
|
|
196
175
|
$filePath = $file->getPathname();
|
|
197
|
-
//
|
|
176
|
+
// Preserve plugin root directory prefix in archive: anonympins-bot-mitigation-pow/...
|
|
198
177
|
$relativePath = $pluginSlug . '/' . substr($filePath, strlen($buildDir) + 1);
|
|
199
178
|
$zip->addFile($filePath, str_replace('\\', '/', $relativePath));
|
|
200
179
|
}
|
|
@@ -216,6 +195,6 @@ function removeDirectory(string $dir): void
|
|
|
216
195
|
$zip->close();
|
|
217
196
|
removeDirectory($buildDir);
|
|
218
197
|
$sizeKb = round(filesize($zipFile) / 1024, 2);
|
|
219
|
-
echo "==>
|
|
220
|
-
echo "
|
|
221
|
-
echo "
|
|
198
|
+
echo "==> ZIP archive generated successfully:\n";
|
|
199
|
+
echo " File: {$zipFile} ({$sizeKb} KB)\n";
|
|
200
|
+
echo " Ready to be installed via wp-admin or deployed to production.\n";
|
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
===
|
|
1
|
+
=== Anonympins Bot Mitigation with Proof-of-Work ===
|
|
2
2
|
Contributors: anonympins
|
|
3
3
|
Tags: bot protection, security, proof of work, firewall, anti scraping
|
|
4
4
|
Requires at least: 5.9
|
|
5
5
|
Tested up to: 7.1
|
|
6
6
|
Requires PHP: 8.0
|
|
7
|
-
Stable tag: 0.
|
|
7
|
+
Stable tag: 0.8.0
|
|
8
8
|
License: MIT
|
|
9
9
|
License URI: https://opensource.org/licenses/MIT
|
|
10
10
|
|
|
@@ -12,20 +12,22 @@ High-performance client-side anti-bot protection and Proof-of-Work challenge ver
|
|
|
12
12
|
|
|
13
13
|
== Description ==
|
|
14
14
|
|
|
15
|
-
|
|
15
|
+
Anonympins Bot Mitigation with Proof-of-Work is a high-performance, privacy-friendly bot mitigation engine for WordPress. It combines behavioral telemetry, TLS/HTTP2/QUIC transport inspection (JA3, JA4), passive TCP/IP stack analysis, and asynchronous Proof-of-Work (PoW) verification.
|
|
16
16
|
|
|
17
17
|
= Features =
|
|
18
18
|
* Transparent protection against scrapers, credential stuffers, and inventory scalpers.
|
|
19
19
|
* Multi-layered behavioral analysis (mouse, keystrokes, touch dynamics).
|
|
20
20
|
* Cryptographic Proof-of-Work challenges (CPU and Memory bound) without third-party CAPTCHA cookies or trackers.
|
|
21
21
|
* Prometheus-compatible metrics endpoint for monitoring and observability.
|
|
22
|
+
* Dedicated Sandbox / Dry-Run mode with IP targeting for testing without blocking legitimate traffic.
|
|
22
23
|
* Tailored security profiles for Frontend, Admin Area (wp-login), and REST API endpoints.
|
|
24
|
+
* Real-time suspicion vector inspection via Server-Sent Events (SSE), REST telemetry, and DOM CustomEvents.
|
|
23
25
|
|
|
24
26
|
== Installation ==
|
|
25
27
|
|
|
26
|
-
1. Upload the plugin files to the `/wp-content/plugins/
|
|
28
|
+
1. Upload the plugin files to the `/wp-content/plugins/anonympins-bot-mitigation-pow` directory, or install the plugin through the WordPress plugins screen directly.
|
|
27
29
|
2. Activate the plugin through the 'Plugins' screen in WordPress.
|
|
28
|
-
3. Navigate to **Settings ->
|
|
30
|
+
3. Navigate to **Settings -> Anonympins Bot Mitigation** to review security profiles, threshold adjustments, and Prometheus metrics.
|
|
29
31
|
|
|
30
32
|
== Frequently Asked Questions ==
|
|
31
33
|
|
|
@@ -34,3 +36,17 @@ No. All evaluations, challenges, and validations occur directly on your WordPres
|
|
|
34
36
|
|
|
35
37
|
= Is HTTPS required? =
|
|
36
38
|
HTTPS is strongly recommended. Under unencrypted HTTP, modern browsers disable the Web Cryptography API (`crypto.subtle`), which reduces challenge verification performance.
|
|
39
|
+
|
|
40
|
+
= How can I monitor incoming request suspicion scores in real time? =
|
|
41
|
+
Under **Settings -> Anonympins Bot Mitigation -> Sandbox / Test Mode**, the plugin provides real-time observability interfaces for all challenged visitors across the website:
|
|
42
|
+
|
|
43
|
+
1. **Server-Sent Events (SSE)**:
|
|
44
|
+
`GET /wp-json/fingerprint/v1/sandbox/sse`
|
|
45
|
+
Streams timestamped telemetry objects containing `id`, `timestamp`, `ip`, `uri`, `action`, `suspicionScore`, and `suspicionVector` for every challenged visitor.
|
|
46
|
+
|
|
47
|
+
2. **REST Telemetry Polling**:
|
|
48
|
+
`GET /wp-json/fingerprint/v1/sandbox/telemetry`
|
|
49
|
+
Returns the latest list and evaluated challenge payloads across all visitors in JSON format.
|
|
50
|
+
|
|
51
|
+
3. **Client-side JavaScript event**:
|
|
52
|
+
Every page emits a DOM `CustomEvent('fingerprint:suspicion')` and sets `window.__FINGERPRINT_VECTOR__` when Sandbox mode is active or when an administrator is authenticated.
|
|
@@ -32,7 +32,7 @@ use Anonympins\Fingerprint\Optimization\Optimization;
|
|
|
32
32
|
use Anonympins\Fingerprint\Store\StoreManager;
|
|
33
33
|
use Anonympins\Fingerprint\Utils\RequestUtils;
|
|
34
34
|
|
|
35
|
-
// 1.
|
|
35
|
+
// 1. Retrieve CLI arguments
|
|
36
36
|
$configPath = $argv[1] ?? null;
|
|
37
37
|
if (!$configPath) {
|
|
38
38
|
echo "Usage: php auto-tune.php [path_to_security-config.json]\n";
|
|
Binary file
|