@anonympins/fingerprint 0.3.1 → 0.3.3
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 +172 -0
- package/README.md +298 -37
- package/composer.json +38 -0
- package/index.js +5 -0
- package/package.json +100 -94
- package/phpunit.xml +20 -0
- package/public/fp.js +2 -0
- package/public/fp.wasm +0 -0
- package/src/js/build-client.js +69 -0
- package/{fingerprint.builder.js → src/js/fingerprint.builder.js} +168 -171
- package/{fingerprint.client.js → src/js/fingerprint.client.js} +634 -483
- package/src/js/fingerprint.client.obfuscated.js +1 -0
- package/{fingerprint.js → src/js/fingerprint.js} +338 -120
- package/{library.js → src/js/library.js} +1729 -1727
- package/{problem-manager.js → src/js/problem-manager.js} +539 -522
- package/src/php/AutoTuner.php +155 -0
- package/src/php/Challenge/ChallengeUtils.php +306 -0
- package/src/php/Config/SecurityProfiles.php +257 -0
- package/src/php/DirectFingerprint.php +81 -0
- package/src/php/FingerprintBuilder.php +185 -0
- package/src/php/FingerprintClient.php +118 -0
- package/src/php/FingerprintEngine.php +850 -0
- package/src/php/Optimization/FunctionRegistry.php +63 -0
- package/src/php/Optimization/Optimization.php +256 -0
- package/src/php/Optimization/OptimizationOperators.php +305 -0
- package/src/php/Optimization/ProblemInitializers.php +53 -0
- package/src/php/ProblemManager.php +255 -0
- package/src/php/RequestContext.php +87 -0
- package/src/php/Store/IStore.php +42 -0
- package/src/php/Store/InMemoryStore.php +67 -0
- package/src/php/Store/StoreManager.php +26 -0
- package/src/php/Tests/ChallengeUtilsTest.php +82 -0
- package/src/php/Tests/FingerprintBuilderTest.php +58 -0
- package/src/php/Tests/FingerprintEngineTest.php +219 -0
- package/src/php/Tests/PowTest.php +40 -0
- package/src/php/Tests/ProblemManagerTest.php +295 -0
- package/src/php/Tests/RequestUtilsTest.php +81 -0
- package/src/php/Tests/problems.config.json +9 -0
- package/src/php/Utils/BigInt.php +102 -0
- package/src/php/Utils/BlockList.php +100 -0
- package/src/php/Utils/Logger.php +30 -0
- package/src/php/Utils/MaliciousPatterns.php +59 -0
- package/src/php/Utils/RequestUtils.php +673 -0
- package/fingerprint.client.obfuscated.js +0 -1
- /package/{mongodb-store.js → src/js/mongodb-store.js} +0 -0
- /package/{optimization.worker.js → src/js/optimization.worker.js} +0 -0
- /package/{pow.solver.inline.js → src/js/pow.solver.inline.js} +0 -0
- /package/{pow.solver.js → src/js/pow.solver.js} +0 -0
- /package/{pow.worker.js → src/js/pow.worker.js} +0 -0
- /package/{redis-store.js → src/js/redis-store.js} +0 -0
- /package/{sql-store.js → src/js/sql-store.js} +0 -0
|
@@ -1,172 +1,169 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Algorithme de hachage cyrb53 (rapide et faible taux de collision).
|
|
3
|
-
*/
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
Math.imul(
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
Math.imul(
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
*
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
console.
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
//
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
const volatileKeys = new Set([
|
|
114
|
-
'ch_ua', 'ch_platform', 'ch_mobile', 'ch_model', 'ch_arch', 'ch_bitness',
|
|
115
|
-
'cookie_keys', 'upgrade',
|
|
116
|
-
|
|
117
|
-
'
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
//
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
//
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
//
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
//
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
if (
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
return totalWeight === 0 ? 0 : weightedMatches / totalWeight;
|
|
171
|
-
}
|
|
1
|
+
/**
|
|
2
|
+
* Algorithme de hachage cyrb53 (rapide et faible taux de collision).
|
|
3
|
+
*/
|
|
4
|
+
// Exporté pour être utilisé comme fallback par fingerprint.client.js
|
|
5
|
+
export const cyrb53 = (str, seed = 0) => {
|
|
6
|
+
let h1 = 0xdeadbeef ^ seed,
|
|
7
|
+
h2 = 0x41c6ce57 ^ seed;
|
|
8
|
+
for (let i = 0, ch; i < str.length; i++) {
|
|
9
|
+
ch = str.charCodeAt(i);
|
|
10
|
+
h1 = Math.imul(h1 ^ ch, 2654435761); // Use Math.imul for 32-bit multiplication
|
|
11
|
+
h2 = Math.imul(h2 ^ ch, 1597334677);
|
|
12
|
+
}
|
|
13
|
+
h1 =
|
|
14
|
+
Math.imul(h1 ^ (h1 >>> 16), 2246822507) ^
|
|
15
|
+
Math.imul(h2 ^ (h2 >>> 13), 3266489909);
|
|
16
|
+
h2 =
|
|
17
|
+
Math.imul(h2 ^ (h2 >>> 16), 2246822507) ^
|
|
18
|
+
Math.imul(h1 ^ (h1 >>> 13), 3266489909);
|
|
19
|
+
return 4294967296 * (2097151 & h2) + (h1 >>> 0);
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Classe pour construire une empreinte composite (Multi-Hash).
|
|
24
|
+
* Format de sortie : "grp1:hash1|grp2:hash2|grp3:hash3"
|
|
25
|
+
*/
|
|
26
|
+
export class FingerprintBuilder {
|
|
27
|
+
constructor() {
|
|
28
|
+
// Le hasher est maintenant une propriété pour pouvoir être surchargé par le client WASM.
|
|
29
|
+
this.components = new Map();
|
|
30
|
+
// FIX: Initialiser le hasher par défaut à l'implémentation JS.
|
|
31
|
+
this.hasher = cyrb53;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Ajoute un composant au hash global.
|
|
36
|
+
* @param {string} group - Le nom du groupe (ex: 'hw', 'screen', 'geo')
|
|
37
|
+
* @param {string|number|boolean} value - La valeur brute à hasher
|
|
38
|
+
*/
|
|
39
|
+
add(group, value) {
|
|
40
|
+
if (value === undefined || value === null) return this;
|
|
41
|
+
// On hash la valeur individuellement pour l'anonymiser et réduire sa taille
|
|
42
|
+
this.components.set(group, this.hasher(String(value)));
|
|
43
|
+
return this;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* Adds a raw component without hashing it.
|
|
48
|
+
* Useful for metrics that need to be read on the server.
|
|
49
|
+
* @param {string} group - The name of the group.
|
|
50
|
+
* @param {string|number} value - The raw value.
|
|
51
|
+
*/
|
|
52
|
+
addRaw(group, value) {
|
|
53
|
+
if (value === undefined || value === null) return this;
|
|
54
|
+
this.components.set(group, value);
|
|
55
|
+
return this;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* Affiche les composants actuels dans la console.
|
|
60
|
+
* @param {string} [title='FingerprintBuilder Components'] - Un titre pour le log.
|
|
61
|
+
*/
|
|
62
|
+
log(title = 'FingerprintBuilder Components') {
|
|
63
|
+
console.log(`--- ${title} ---`);
|
|
64
|
+
const sortedComponents = Array.from(this.components.entries())
|
|
65
|
+
.sort((a, b) => a[0].localeCompare(b[0]));
|
|
66
|
+
|
|
67
|
+
console.table(Object.fromEntries(sortedComponents));
|
|
68
|
+
console.log(`Final string: ${this.toString()}`);
|
|
69
|
+
console.log(`---------------------------------${'-'.repeat(title.length)}`);
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* Génère la chaîne de signature finale.
|
|
74
|
+
* Trie les clés pour garantir un ordre déterministe.
|
|
75
|
+
*/
|
|
76
|
+
toString() {
|
|
77
|
+
return Array.from(this.components.entries())
|
|
78
|
+
.sort((a, b) => a[0].localeCompare(b[0])) // Tri alphabétique des clés
|
|
79
|
+
.map(([key, hash]) => `${key}:${hash}`)
|
|
80
|
+
.join("|");
|
|
81
|
+
}
|
|
82
|
+
/**
|
|
83
|
+
* Adds a raw component without hashing it.
|
|
84
|
+
* Useful for metrics that need to be read on the server.
|
|
85
|
+
* @param {string} group - The name of the group.
|
|
86
|
+
* @param {string|number} value - The raw value.
|
|
87
|
+
*/
|
|
88
|
+
addRaw(group, value) {
|
|
89
|
+
if (value === undefined || value === null) return this;
|
|
90
|
+
this.components.set(group, value);
|
|
91
|
+
return this;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
/**
|
|
95
|
+
* Compares two fingerprints and returns a similarity score (0 to 1).
|
|
96
|
+
* Uses weights to give more importance to strong invariants (Canvas, GPU).
|
|
97
|
+
* @param {string} fpString1 - Fingerprint A
|
|
98
|
+
* @param {string} fpString2 - Fingerprint B
|
|
99
|
+
*/
|
|
100
|
+
// Note on `volatileKeys`: These keys are ignored during the comparison between the fingerprint
|
|
101
|
+
// of the request that *triggered* a challenge and the fingerprint of the request that *submits*
|
|
102
|
+
// the solution. This is because headers like Client-Hints (ch_*), cookie presence, and upgrade-insecure-requests
|
|
103
|
+
// can legitimately change or be absent on the subsequent request, especially after a redirect.
|
|
104
|
+
// By ignoring them, we focus the comparison on more stable identifiers like UA, JA3, GPU, etc.
|
|
105
|
+
static compare(fpString1, fpString2) {
|
|
106
|
+
if (!fpString1 || !fpString2) return 0;
|
|
107
|
+
|
|
108
|
+
const parse = (str) => new Map(str.split("|").map(part => part.split(":")).filter(([k,v]) => k && v));
|
|
109
|
+
|
|
110
|
+
const map1 = parse(fpString1);
|
|
111
|
+
const map2 = parse(fpString2);
|
|
112
|
+
|
|
113
|
+
const volatileKeys = new Set([
|
|
114
|
+
'ch_ua', 'ch_platform', 'ch_mobile', 'ch_model', 'ch_arch', 'ch_bitness',
|
|
115
|
+
'cookie_keys', 'upgrade',
|
|
116
|
+
'network', 'http_ver',
|
|
117
|
+
'x_forwarded_for', 'x_real_ip', 'cf_connecting_ip'
|
|
118
|
+
]);
|
|
119
|
+
|
|
120
|
+
// Poids de "véracité" (Entropie/Stabilité)
|
|
121
|
+
// Les poids sont augmentés pour donner plus d'importance aux signaux forts.
|
|
122
|
+
const weights = {
|
|
123
|
+
// --- Signaux très forts (difficiles à usurper) ---
|
|
124
|
+
cvs: 5.0, // Canvas: Très haute entropie (Rendu unique du GPU/driver)
|
|
125
|
+
gpu: 4.0, // GPU: Haute entropie (Matériel spécifique)
|
|
126
|
+
ja3: 3.5, // JA3: Identifie la librairie TLS (très stable pour un client donné)
|
|
127
|
+
ja4: 4.0, // JA4: Plus moderne, inclut HTTP/2
|
|
128
|
+
h2: 3.0, // HTTP/2 settings frame fingerprint
|
|
129
|
+
tcp: 2.5, // TCP/IP fingerprint
|
|
130
|
+
ua: 2.0, // User-Agent: Signal fort, bien que modifiable
|
|
131
|
+
|
|
132
|
+
// --- Signaux composites et dérivés ---
|
|
133
|
+
client_fp_hash: 3.0, // Le hash de l'empreinte client est un signal très fort.
|
|
134
|
+
browser: 1.5, // Le navigateur extrait du UA.
|
|
135
|
+
os_version: 1.5, // L'OS extrait du UA.
|
|
136
|
+
device_type: 1.0, // Le type d'appareil extrait du UA.
|
|
137
|
+
|
|
138
|
+
// --- Signaux moyens ---
|
|
139
|
+
hw: 1.5, // Hardware (CPU, RAM): Stabilité moyenne
|
|
140
|
+
scr: 1.0, // Screen: Stabilité moyenne
|
|
141
|
+
// 'os' est souvent la même chose que 'ch_platform', on peut le déprécier ou lui donner un poids faible.
|
|
142
|
+
os: 0.8, // OS (nav.platform): Assez stable
|
|
143
|
+
geo: 0.5, // Geo/Langue: Peut changer (VPN, voyage)
|
|
144
|
+
};
|
|
145
|
+
|
|
146
|
+
let weightedMatches = 0;
|
|
147
|
+
let totalWeight = 0;
|
|
148
|
+
|
|
149
|
+
const allKeys = new Set([...map1.keys(), ...map2.keys()]);
|
|
150
|
+
|
|
151
|
+
allKeys.forEach((key) => {
|
|
152
|
+
// On ignore les clés volatiles pour cette comparaison spécifique.
|
|
153
|
+
if (volatileKeys.has(key)) {
|
|
154
|
+
return;
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
const weight = weights[key] ?? 0;
|
|
158
|
+
// On ne compte une clé dans le poids total que si elle est présente dans au moins une des deux empreintes.
|
|
159
|
+
if (!map1.has(key) && !map2.has(key)) return;
|
|
160
|
+
|
|
161
|
+
totalWeight += weight; // N'incrémenter que si la clé est pertinente.
|
|
162
|
+
if (map1.get(key) === map2.get(key)) {
|
|
163
|
+
weightedMatches += weight;
|
|
164
|
+
}
|
|
165
|
+
});
|
|
166
|
+
|
|
167
|
+
return totalWeight === 0 ? 0 : weightedMatches / totalWeight;
|
|
168
|
+
}
|
|
172
169
|
}
|