@anonympins/fingerprint 0.7.5 → 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 +32 -0
- package/README.md +1 -1
- package/package.json +1 -1
- package/public/anonympins-bot-mitigation-pow.zip +0 -0
- package/src/js/fingerprint.client.js +1784 -1772
- package/src/js/fingerprint.client.obfuscated.js +1 -1
- package/src/js/fingerprint.js +198 -28
- 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/fingerprint.builder.test.js +76 -78
- package/src/js/tests/fingerprint.client.test.js +16 -17
- package/src/js/tests/fingerprint.test.js +19 -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/upow-model-task.js +155 -112
- package/src/php/Challenge/ChallengeUtils.php +382 -86
- package/src/php/Config/SecurityProfiles.php +75 -62
- 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 +2120 -1837
- 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 +2 -2
- 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 +327 -14
- 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
|
@@ -5,8 +5,8 @@ declare(strict_types=1);
|
|
|
5
5
|
namespace Anonympins\Fingerprint\Config;
|
|
6
6
|
|
|
7
7
|
/**
|
|
8
|
-
*
|
|
9
|
-
*
|
|
8
|
+
* Defines predefined security profiles for the Fingerprint library.
|
|
9
|
+
* These profiles contain suspicion score weights and trigger thresholds.
|
|
10
10
|
*/
|
|
11
11
|
class SecurityProfiles
|
|
12
12
|
{
|
|
@@ -33,19 +33,21 @@ class SecurityProfiles
|
|
|
33
33
|
'crossLayerInconsistencyScore' => 0.4,
|
|
34
34
|
'timeInconsistencyScore' => 0.9,
|
|
35
35
|
'tlsSpoofingScore' => 0.8,
|
|
36
|
-
'botScore' => 1.0,
|
|
37
|
-
'cookieDroppingScore' => 0.
|
|
38
|
-
'threatIntelScore' => 0.4,
|
|
39
|
-
'clientHintsInconsistencyScore' => 0.7,
|
|
40
|
-
'clickVarianceScore' => 0.6,
|
|
41
|
-
'subnetScore' => 0.5,
|
|
42
|
-
'
|
|
36
|
+
'botScore' => 1.0,
|
|
37
|
+
'cookieDroppingScore' => 0.4,
|
|
38
|
+
'threatIntelScore' => 0.4,
|
|
39
|
+
'clientHintsInconsistencyScore' => 0.7,
|
|
40
|
+
'clickVarianceScore' => 0.6,
|
|
41
|
+
'subnetScore' => 0.5,
|
|
42
|
+
'ipReputationScore' => 0.5,
|
|
43
|
+
'botnetClusterScore' => 0.6,
|
|
43
44
|
'tcpAnomalyScore' => 0.8,
|
|
44
|
-
'protocolAnomalyScore' => 0.8,
|
|
45
|
-
'
|
|
46
|
-
'
|
|
47
|
-
'
|
|
45
|
+
'protocolAnomalyScore' => 0.8,
|
|
46
|
+
'http2AnomalyScore' => 0.8,
|
|
47
|
+
'quicAnomalyScore' => 0.8,
|
|
48
|
+
'renderingAnomalyScore' => 0.8,
|
|
48
49
|
'virtualizationScore' => 0.8,
|
|
50
|
+
'mtuAnomalyScore' => 0.9,
|
|
49
51
|
],
|
|
50
52
|
'thresholds' => ['low' => 20, 'medium' => 45, 'high' => 75, 'block' => 95],
|
|
51
53
|
'patterns' => [
|
|
@@ -64,7 +66,7 @@ class SecurityProfiles
|
|
|
64
66
|
'minDifficultyBits' => 8,
|
|
65
67
|
'maxDifficultyBits' => 22,
|
|
66
68
|
],
|
|
67
|
-
'filterWhitelist' => 85.0, //
|
|
69
|
+
'filterWhitelist' => 85.0, // Moderate inspection strategy for allowlisted IPs/paths
|
|
68
70
|
'wasm' => true,
|
|
69
71
|
],
|
|
70
72
|
|
|
@@ -89,17 +91,20 @@ class SecurityProfiles
|
|
|
89
91
|
'timeInconsistencyScore' => 1.0,
|
|
90
92
|
'tlsSpoofingScore' => 1.0,
|
|
91
93
|
'botScore' => 1.0,
|
|
92
|
-
'cookieDroppingScore' =>
|
|
93
|
-
'threatIntelScore' => 0.7,
|
|
94
|
-
'clientHintsInconsistencyScore' => 0.9,
|
|
95
|
-
'clickVarianceScore' => 0.7,
|
|
96
|
-
'subnetScore' => 0.7,
|
|
97
|
-
'
|
|
98
|
-
'
|
|
99
|
-
'
|
|
100
|
-
'
|
|
94
|
+
'cookieDroppingScore' => 0.6,
|
|
95
|
+
'threatIntelScore' => 0.7,
|
|
96
|
+
'clientHintsInconsistencyScore' => 0.9,
|
|
97
|
+
'clickVarianceScore' => 0.7,
|
|
98
|
+
'subnetScore' => 0.7,
|
|
99
|
+
'ipReputationScore' => 0.7,
|
|
100
|
+
'botnetClusterScore' => 0.8,
|
|
101
|
+
'tcpAnomalyScore' => 1.0,
|
|
102
|
+
'protocolAnomalyScore' => 1.0,
|
|
103
|
+
'http2AnomalyScore' => 1.0,
|
|
104
|
+
'quicAnomalyScore' => 1.0,
|
|
105
|
+
'renderingAnomalyScore' => 1.0,
|
|
106
|
+
'virtualizationScore' => 1.0,
|
|
101
107
|
'mtuAnomalyScore' => 0.9,
|
|
102
|
-
|
|
103
108
|
],
|
|
104
109
|
'thresholds' => ['low' => 10, 'medium' => 35, 'high' => 65, 'block' => 90],
|
|
105
110
|
'patterns' => [
|
|
@@ -120,7 +125,7 @@ class SecurityProfiles
|
|
|
120
125
|
],
|
|
121
126
|
'challengeNewDevices' => true, // Challenge all new devices
|
|
122
127
|
'wasm' => true,
|
|
123
|
-
'filterWhitelist' => true, //
|
|
128
|
+
'filterWhitelist' => true, // Any definite attack behavior immediately bypasses the allowlist
|
|
124
129
|
],
|
|
125
130
|
|
|
126
131
|
/**
|
|
@@ -143,16 +148,20 @@ class SecurityProfiles
|
|
|
143
148
|
'timeInconsistencyScore' => 0.8,
|
|
144
149
|
'tlsSpoofingScore' => 0.7,
|
|
145
150
|
'botScore' => 0.5,
|
|
146
|
-
'cookieDroppingScore' => 0.
|
|
151
|
+
'cookieDroppingScore' => 0.5, // Important for API clients that need to maintain state
|
|
147
152
|
'threatIntelScore' => 0.5, // APIs are often targeted by malicious IPs
|
|
148
153
|
'clientHintsInconsistencyScore' => 0.6, // Relevant signal for APIs
|
|
149
154
|
'clickVarianceScore' => 0.3, // Low weight as not applicable to APIs
|
|
150
|
-
'subnetScore' => 0.8, //
|
|
151
|
-
'
|
|
152
|
-
'
|
|
153
|
-
'
|
|
155
|
+
'subnetScore' => 0.8, // Highly important for APIs to detect botnets
|
|
156
|
+
'ipReputationScore' => 0.6,
|
|
157
|
+
'botnetClusterScore' => 0.7,
|
|
158
|
+
'tcpAnomalyScore' => 0.8,
|
|
159
|
+
'protocolAnomalyScore' => 0.8,
|
|
160
|
+
'http2AnomalyScore' => 0.8,
|
|
161
|
+
'quicAnomalyScore' => 0.8,
|
|
162
|
+
'renderingAnomalyScore' => 0.2,
|
|
163
|
+
'virtualizationScore' => 0.5,
|
|
154
164
|
'mtuAnomalyScore' => 0.9,
|
|
155
|
-
|
|
156
165
|
],
|
|
157
166
|
'thresholds' => ['low' => 25, 'medium' => 50, 'high' => 80, 'block' => 95],
|
|
158
167
|
'patterns' => [
|
|
@@ -173,7 +182,7 @@ class SecurityProfiles
|
|
|
173
182
|
],
|
|
174
183
|
// This would be a callable in PHP, but for now, we represent its intent.
|
|
175
184
|
'isApiRequest' => 'req.path.startsWith("/api/") || req.headers.accept?.includes("application/json")',
|
|
176
|
-
'filterWhitelist' => 75.0, //
|
|
185
|
+
'filterWhitelist' => 75.0, // Low threshold to defend against stolen legitimate API keys/tokens
|
|
177
186
|
'wasm' => true,
|
|
178
187
|
],
|
|
179
188
|
|
|
@@ -196,20 +205,22 @@ class SecurityProfiles
|
|
|
196
205
|
'honeypotScore' => 1.0, // Crucial for comment spam
|
|
197
206
|
'crossLayerInconsistencyScore' => 0.4,
|
|
198
207
|
'timeInconsistencyScore' => 0.8,
|
|
199
|
-
'tlsSpoofingScore' => 0.6,
|
|
208
|
+
'tlsSpoofingScore' => 0.6,
|
|
200
209
|
'botScore' => 0.8,
|
|
201
|
-
'cookieDroppingScore' => 0.
|
|
202
|
-
'threatIntelScore' => 0.3,
|
|
210
|
+
'cookieDroppingScore' => 0.3,
|
|
211
|
+
'threatIntelScore' => 0.3,
|
|
203
212
|
'clientHintsInconsistencyScore' => 0.5,
|
|
204
|
-
'clickVarianceScore' => 0.5,
|
|
205
|
-
'subnetScore' => 0.4,
|
|
206
|
-
'ipReputationScore' => 0.3,
|
|
207
|
-
'botnetClusterScore' => 0.5,
|
|
208
|
-
'tcpAnomalyScore' => 0.5,
|
|
209
|
-
'protocolAnomalyScore' => 0.5,
|
|
210
|
-
'
|
|
213
|
+
'clickVarianceScore' => 0.5,
|
|
214
|
+
'subnetScore' => 0.4,
|
|
215
|
+
'ipReputationScore' => 0.3,
|
|
216
|
+
'botnetClusterScore' => 0.5,
|
|
217
|
+
'tcpAnomalyScore' => 0.5,
|
|
218
|
+
'protocolAnomalyScore' => 0.5,
|
|
219
|
+
'http2AnomalyScore' => 0.5,
|
|
220
|
+
'quicAnomalyScore' => 0.5,
|
|
221
|
+
'renderingAnomalyScore' => 0.5,
|
|
222
|
+
'virtualizationScore' => 0.6,
|
|
211
223
|
'mtuAnomalyScore' => 0.9,
|
|
212
|
-
|
|
213
224
|
],
|
|
214
225
|
'thresholds' => ['low' => 25, 'medium' => 55, 'high' => 80, 'block' => 95],
|
|
215
226
|
'patterns' => [
|
|
@@ -228,7 +239,7 @@ class SecurityProfiles
|
|
|
228
239
|
'minDifficultyBits' => 8,
|
|
229
240
|
'maxDifficultyBits' => 20,
|
|
230
241
|
],
|
|
231
|
-
'filterWhitelist' => 90.0, //
|
|
242
|
+
'filterWhitelist' => 90.0, // Highly lenient, only inspects if the score is near blocking
|
|
232
243
|
'wasm' => true,
|
|
233
244
|
],
|
|
234
245
|
|
|
@@ -245,26 +256,28 @@ class SecurityProfiles
|
|
|
245
256
|
'historyScore' => 0.4,
|
|
246
257
|
'rotationScore' => 0.6,
|
|
247
258
|
'headerAnomalyScore' => 0.2,
|
|
259
|
+
'requestPatternScore' => 0.9,
|
|
248
260
|
'inconsistencyScore' => 1.0, // Crucial for preventing account takeover
|
|
249
261
|
'behaviorScore' => 0.8, // Important for checkout/login forms
|
|
250
262
|
'honeypotScore' => 1.0,
|
|
251
263
|
'crossLayerInconsistencyScore' => 0.7,
|
|
252
|
-
// NOUVEAU: Ajout des scores manquants pour une configuration complète
|
|
253
|
-
'requestPatternScore' => 0.9, // Poids unifié pour les patterns, remplace les scores scindés
|
|
254
264
|
'timeInconsistencyScore' => 0.9,
|
|
255
265
|
'tlsSpoofingScore' => 0.9,
|
|
256
266
|
'botScore' => 1.0,
|
|
257
|
-
'cookieDroppingScore' =>
|
|
267
|
+
'cookieDroppingScore' => 0.6, // Crucial for e-commerce bot detection
|
|
258
268
|
'threatIntelScore' => 0.8, // Very important for e-commerce (scalping proxies)
|
|
259
269
|
'clientHintsInconsistencyScore' => 0.9, // Very important for e-commerce
|
|
260
270
|
'clickVarianceScore' => 0.8, // Very high weight for click variance
|
|
261
|
-
'subnetScore' => 0.9, // Crucial
|
|
262
|
-
'
|
|
263
|
-
'
|
|
264
|
-
'
|
|
265
|
-
'
|
|
271
|
+
'subnetScore' => 0.9, // Crucial against distributed scalping attacks
|
|
272
|
+
'ipReputationScore' => 0.8,
|
|
273
|
+
'botnetClusterScore' => 0.9,
|
|
274
|
+
'tcpAnomalyScore' => 0.9,
|
|
275
|
+
'protocolAnomalyScore' => 0.9,
|
|
276
|
+
'http2AnomalyScore' => 0.9,
|
|
277
|
+
'quicAnomalyScore' => 0.9,
|
|
278
|
+
'renderingAnomalyScore' => 0.9,
|
|
279
|
+
'virtualizationScore' => 0.9,
|
|
266
280
|
'mtuAnomalyScore' => 0.9,
|
|
267
|
-
|
|
268
281
|
],
|
|
269
282
|
'thresholds' => ['low' => 15, 'medium' => 40, 'high' => 70, 'block' => 90],
|
|
270
283
|
'patterns' => [
|
|
@@ -287,16 +300,16 @@ class SecurityProfiles
|
|
|
287
300
|
// This would be a callable in PHP, but for now, we represent its intent.
|
|
288
301
|
'isApiRequest' => 'req.path.startsWith("/api/cart") || req.path.startsWith("/api/stock") || req.path.startsWith("/api/checkout")',
|
|
289
302
|
'wasm' => true,
|
|
290
|
-
'filterWhitelist' => true, //
|
|
303
|
+
'filterWhitelist' => true, // Zero tolerance for scraping / distributed scalping
|
|
291
304
|
],
|
|
292
305
|
];
|
|
293
306
|
|
|
294
307
|
/**
|
|
295
|
-
*
|
|
308
|
+
* Creates a security configuration based on a named profile, with optional overrides.
|
|
296
309
|
*
|
|
297
|
-
* @param string $profileName
|
|
298
|
-
* @param array<string, mixed> $overrides
|
|
299
|
-
* @return array<string, mixed>
|
|
310
|
+
* @param string $profileName The profile name to use ('balanced', 'strict', 'api', etc.).
|
|
311
|
+
* @param array<string, mixed> $overrides An array to deep-merge with the profile, allowing customization.
|
|
312
|
+
* @return array<string, mixed> The final security configuration object.
|
|
300
313
|
*/
|
|
301
314
|
public static function createSecurityProfile(string $profileName = 'balanced', array $overrides = []): array
|
|
302
315
|
{
|
|
@@ -305,11 +318,11 @@ class SecurityProfiles
|
|
|
305
318
|
}
|
|
306
319
|
|
|
307
320
|
/**
|
|
308
|
-
*
|
|
321
|
+
* Deep-merges two arrays. Properties of the `$source` array overwrite those of `$target`.
|
|
309
322
|
*
|
|
310
|
-
* @param array<string, mixed> $target
|
|
311
|
-
* @param array<string, mixed> $source
|
|
312
|
-
* @return array<string, mixed>
|
|
323
|
+
* @param array<string, mixed> $target The target array.
|
|
324
|
+
* @param array<string, mixed> $source The source array.
|
|
325
|
+
* @return array<string, mixed> The merged array.
|
|
313
326
|
*/
|
|
314
327
|
public static function deepMerge(array $target, array $source): array
|
|
315
328
|
{
|
|
@@ -25,15 +25,10 @@ class DirectFingerprint
|
|
|
25
25
|
}
|
|
26
26
|
|
|
27
27
|
/**
|
|
28
|
-
*
|
|
29
|
-
* Analyzes the incoming request and issues challenges or block responses, exiting the script if necessary.
|
|
30
|
-
* If the request is allowed, returns the fingerprint data.
|
|
31
|
-
*
|
|
32
|
-
* @return array{score: float, vector: array}|null Fingerprint data if allowed, null otherwise.
|
|
28
|
+
* Builds request context from PHP superglobals.
|
|
33
29
|
*/
|
|
34
|
-
public function
|
|
30
|
+
public function buildRequestContext(): RequestContext
|
|
35
31
|
{
|
|
36
|
-
// 1. Build request context from PHP superglobals
|
|
37
32
|
// phpcs:ignore WordPress.Security.NonceVerification.Missing,WordPress.Security.NonceVerification.Recommended -- Direct WAF inspection firewall before WP core logic
|
|
38
33
|
$body = $_POST ?: json_decode(file_get_contents('php://input'), true);
|
|
39
34
|
$headers = function_exists('getallheaders') ? getallheaders() : [];
|
|
@@ -44,7 +39,7 @@ class DirectFingerprint
|
|
|
44
39
|
$path = function_exists('wp_parse_url') ? (string)wp_parse_url($rawUri, PHP_URL_PATH) : (string)parse_url($rawUri, PHP_URL_PATH);
|
|
45
40
|
$serverProtocol = isset($_SERVER['SERVER_PROTOCOL']) ? sanitize_text_field(wp_unslash($_SERVER['SERVER_PROTOCOL'])) : '1.1';
|
|
46
41
|
|
|
47
|
-
|
|
42
|
+
return new RequestContext(
|
|
48
43
|
$remoteAddr,
|
|
49
44
|
!empty($path) ? $path : '/',
|
|
50
45
|
$headers,
|
|
@@ -54,10 +49,62 @@ class DirectFingerprint
|
|
|
54
49
|
$_COOKIE,
|
|
55
50
|
$serverProtocol
|
|
56
51
|
);
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* Inspects the incoming request without terminating execution or issuing redirects/exits.
|
|
56
|
+
*
|
|
57
|
+
* @param RequestContext|null $context
|
|
58
|
+
* @return array
|
|
59
|
+
*/
|
|
60
|
+
public function inspect(?RequestContext $context = null): array
|
|
61
|
+
{
|
|
62
|
+
$context = $context ?? $this->buildRequestContext();
|
|
63
|
+
$decision = $this->engine->processRequest($context);
|
|
64
|
+
|
|
65
|
+
return [
|
|
66
|
+
'action' => $decision['action'] ?? 'next',
|
|
67
|
+
'intendedAction' => $decision['intendedAction'] ?? null,
|
|
68
|
+
'score' => (float)($decision['score'] ?? 0.0),
|
|
69
|
+
'suspicionScore' => (float)($decision['score'] ?? 0.0),
|
|
70
|
+
'vector' => $decision['vector'] ?? [],
|
|
71
|
+
'status' => $decision['status'] ?? 200,
|
|
72
|
+
'body' => $decision['body'] ?? null,
|
|
73
|
+
'context' => $context,
|
|
74
|
+
];
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* Alias for inspect().
|
|
79
|
+
*
|
|
80
|
+
* @param RequestContext|null $context
|
|
81
|
+
* @return array
|
|
82
|
+
*/
|
|
83
|
+
public function evaluate(?RequestContext $context = null): array
|
|
84
|
+
{
|
|
85
|
+
return $this->inspect($context);
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
/**
|
|
89
|
+
* Protects the current entry point.
|
|
90
|
+
* Analyzes the incoming request and issues challenges or block responses, exiting the script if necessary.
|
|
91
|
+
* If the request is allowed, returns the fingerprint data.
|
|
92
|
+
*
|
|
93
|
+
* @return array{score: float, vector: array}|null Fingerprint data if allowed, null otherwise.
|
|
94
|
+
*/
|
|
95
|
+
public function protect(): ?array
|
|
96
|
+
{
|
|
97
|
+
// 1. Build request context from PHP superglobals
|
|
98
|
+
$context = $this->buildRequestContext();
|
|
57
99
|
|
|
58
100
|
// 2. Process request with engine
|
|
59
101
|
$decision = $this->engine->processRequest($context);
|
|
60
102
|
|
|
103
|
+
// 3. Callback hook before acting on decision (allows logging, telemetry, SSE recording)
|
|
104
|
+
if (isset($this->securityConfig['onDecision']) && is_callable($this->securityConfig['onDecision'])) {
|
|
105
|
+
call_user_func($this->securityConfig['onDecision'], $decision, $context);
|
|
106
|
+
}
|
|
107
|
+
|
|
61
108
|
// 3. Act on decision
|
|
62
109
|
if (isset($context->newCookieForResponse)) {
|
|
63
110
|
$cookie = $context->newCookieForResponse;
|
|
@@ -5,7 +5,7 @@ declare(strict_types=1);
|
|
|
5
5
|
namespace Anonympins\Fingerprint;
|
|
6
6
|
|
|
7
7
|
/**
|
|
8
|
-
*
|
|
8
|
+
* Builds a composite fingerprint (Multi-Hash).
|
|
9
9
|
* Output format: "grp1:hash1|grp2:hash2|grp3:hash3"
|
|
10
10
|
*/
|
|
11
11
|
class FingerprintBuilder
|
|
@@ -17,7 +17,7 @@ class FingerprintBuilder
|
|
|
17
17
|
|
|
18
18
|
/**
|
|
19
19
|
* Adds a component to the fingerprint.
|
|
20
|
-
*
|
|
20
|
+
*
|
|
21
21
|
* @param string $group Group name (e.g. 'hw', 'screen', 'geo').
|
|
22
22
|
* @param string|int|bool|null $value Raw value to hash.
|
|
23
23
|
* @return self
|
|
@@ -27,14 +27,14 @@ class FingerprintBuilder
|
|
|
27
27
|
if ($value === null || $value === '') {
|
|
28
28
|
return $this;
|
|
29
29
|
}
|
|
30
|
-
// Hash the value individually
|
|
30
|
+
// Hash the value individually to anonymize and normalize its length.
|
|
31
31
|
$this->components[$group] = self::cyrb53((string)$value);
|
|
32
32
|
return $this;
|
|
33
33
|
}
|
|
34
34
|
|
|
35
35
|
/**
|
|
36
36
|
* Adds a raw component without hashing it.
|
|
37
|
-
* Useful for metrics that need to be read as-is on the server.
|
|
37
|
+
* Useful for metrics that need to be read as-is on the server side.
|
|
38
38
|
*
|
|
39
39
|
* @param string $group Group name.
|
|
40
40
|
* @param string|int|null $value Raw value.
|
|
@@ -51,13 +51,13 @@ class FingerprintBuilder
|
|
|
51
51
|
|
|
52
52
|
/**
|
|
53
53
|
* Generates the final fingerprint string.
|
|
54
|
-
* Components are sorted by key to
|
|
54
|
+
* Components are sorted by key to ensure deterministic ordering.
|
|
55
55
|
*
|
|
56
56
|
* @return string
|
|
57
57
|
*/
|
|
58
58
|
public function __toString(): string
|
|
59
59
|
{
|
|
60
|
-
// Sort array by key
|
|
60
|
+
// Sort array by key.
|
|
61
61
|
ksort($this->components);
|
|
62
62
|
|
|
63
63
|
$parts = [];
|
|
@@ -70,7 +70,7 @@ class FingerprintBuilder
|
|
|
70
70
|
|
|
71
71
|
/**
|
|
72
72
|
* Compares two fingerprints and returns a similarity score (0 to 1).
|
|
73
|
-
* Uses weights to emphasize strong invariants (Canvas, GPU, JA3).
|
|
73
|
+
* Uses weights to emphasize strong invariants (e.g., Canvas, GPU, JA3).
|
|
74
74
|
*
|
|
75
75
|
* @param string|null $fpString1 Fingerprint A.
|
|
76
76
|
* @param string|null $fpString2 Fingerprint B.
|
|
@@ -137,7 +137,7 @@ class FingerprintBuilder
|
|
|
137
137
|
|
|
138
138
|
/**
|
|
139
139
|
* cyrb53 hashing algorithm (fast and low collision rate).
|
|
140
|
-
*
|
|
140
|
+
* This is a direct port of the JavaScript version.
|
|
141
141
|
*
|
|
142
142
|
* @param string $str String to hash.
|
|
143
143
|
* @param int $seed Optional seed.
|
|
@@ -157,8 +157,8 @@ class FingerprintBuilder
|
|
|
157
157
|
$h1 = self::imul($h1 ^ ($h1 >> 16), 2246822507) ^ self::imul($h2 ^ ($h2 >> 13), 3266489909);
|
|
158
158
|
$h2 = self::imul($h2 ^ ($h2 >> 16), 2246822507) ^ self::imul($h1 ^ ($h1 >> 13), 3266489909);
|
|
159
159
|
|
|
160
|
-
// On 64-bit platforms, PHP handles 64-bit signed
|
|
161
|
-
// Use native shifts instead of bcmath for
|
|
160
|
+
// On 64-bit platforms, PHP handles 64-bit signed integers natively.
|
|
161
|
+
// Use native shifts instead of bcmath for improved performance.
|
|
162
162
|
if (PHP_INT_SIZE === 8) {
|
|
163
163
|
$h1_u = $h1 & 0xffffffff;
|
|
164
164
|
$h2_u = $h2 & 0xffffffff;
|
|
@@ -166,7 +166,7 @@ class FingerprintBuilder
|
|
|
166
166
|
return (string)$val_h2;
|
|
167
167
|
}
|
|
168
168
|
|
|
169
|
-
// Fallback to bcmath on 32-bit platforms
|
|
169
|
+
// Fallback to bcmath on 32-bit platforms.
|
|
170
170
|
$val_h2 = bcadd(bcmul((string)(2097151 & $h2), '4294967296'), (string)($h1 >= 0 ? $h1 : $h1 + 4294967296));
|
|
171
171
|
return $val_h2;
|
|
172
172
|
}
|
|
@@ -180,8 +180,8 @@ class FingerprintBuilder
|
|
|
180
180
|
*/
|
|
181
181
|
private static function imul(int $a, int $b): int
|
|
182
182
|
{
|
|
183
|
-
//
|
|
184
|
-
// This
|
|
183
|
+
// Emulates JavaScript's Math.imul for signed 32-bit integer multiplication.
|
|
184
|
+
// This implementation correctly handles overflows on 64-bit systems.
|
|
185
185
|
$ah = ($a >> 16) & 0xffff;
|
|
186
186
|
$al = $a & 0xffff;
|
|
187
187
|
$bh = ($b >> 16) & 0xffff;
|
|
@@ -6,8 +6,8 @@ namespace Anonympins\Fingerprint;
|
|
|
6
6
|
use Anonympins\Fingerprint\Config\SecurityProfiles;
|
|
7
7
|
|
|
8
8
|
/**
|
|
9
|
-
*
|
|
10
|
-
*
|
|
9
|
+
* PHP wrapper for the client-side fingerprinting library.
|
|
10
|
+
*
|
|
11
11
|
* Handles script injection and generation of polymorphic honeypot fields
|
|
12
12
|
* inside HTML forms.
|
|
13
13
|
*/
|
|
@@ -64,7 +64,7 @@ class FingerprintClient
|
|
|
64
64
|
|
|
65
65
|
/**
|
|
66
66
|
* Generates a hidden honeypot form field.
|
|
67
|
-
* Traps automated bots while remaining invisible to human users.
|
|
67
|
+
* Traps automated bots while remaining invisible to human users and screen readers.
|
|
68
68
|
*
|
|
69
69
|
* @param string $fieldName Field name (must match client honeypot registration).
|
|
70
70
|
* @return string Generated HTML markup.
|
|
@@ -76,7 +76,7 @@ class FingerprintClient
|
|
|
76
76
|
$this->clientConfig['honeypots'][] = $fieldName;
|
|
77
77
|
}
|
|
78
78
|
|
|
79
|
-
// Polymorphic CSS styling to robustly obscure
|
|
79
|
+
// Polymorphic CSS styling to robustly obscure honeypot fields
|
|
80
80
|
$styleOptions = [
|
|
81
81
|
'position:absolute; left:-9999px; top:-9999px; transform:scale(0); opacity:0; pointer-events:none;',
|
|
82
82
|
'position:fixed; left:-8888px; top:-8888px; width:0; height:0; overflow:hidden; opacity:0; pointer-events:none;',
|
|
@@ -112,7 +112,7 @@ class FingerprintClient
|
|
|
112
112
|
$configJson = json_encode($this->clientConfig);
|
|
113
113
|
$nonceAttr = $this->nonce ? ' nonce="' . $this->nonce . '"' : '';
|
|
114
114
|
|
|
115
|
-
// Inline initialization script embedded in HTML
|
|
115
|
+
// Inline initialization script to be embedded in HTML
|
|
116
116
|
$initScript = "document.addEventListener('DOMContentLoaded', function() {\n"
|
|
117
117
|
. " const config = " . $configJson . ";\n"
|
|
118
118
|
. " if (window.ClientLibrary) {\n"
|
|
@@ -129,7 +129,7 @@ class FingerprintClient
|
|
|
129
129
|
. " }\n"
|
|
130
130
|
. "});";
|
|
131
131
|
|
|
132
|
-
// Combine library loader script and inline initialization
|
|
132
|
+
// Combine the main library loader script and the inline initialization script
|
|
133
133
|
// phpcs:ignore WordPress.WP.EnqueuedResources.NonEnqueuedScript -- Polymorphic dynamic client script tag generation
|
|
134
134
|
return '<script src="' . htmlspecialchars($this->clientScriptPath) . '"' . $nonceAttr . '></script>'
|
|
135
135
|
. '<script' . $nonceAttr . '>' . $initScript . '</script>';
|