@anonympins/fingerprint 0.3.2 → 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 +276 -34
- package/composer.json +38 -0
- package/index.js +5 -0
- package/package.json +23 -18
- package/phpunit.xml +20 -0
- package/public/fp.js +2 -0
- package/src/js/build-client.js +69 -0
- package/{fingerprint.builder.js → src/js/fingerprint.builder.js} +168 -175
- package/{fingerprint.client.js → src/js/fingerprint.client.js} +634 -538
- package/src/js/fingerprint.client.obfuscated.js +1 -0
- package/{fingerprint.js → src/js/fingerprint.js} +255 -101
- package/{library.js → src/js/library.js} +1729 -1729
- 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
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
<?php
|
|
2
|
+
|
|
3
|
+
declare(strict_types=1);
|
|
4
|
+
|
|
5
|
+
namespace Anonympins\Fingerprint\Optimization;
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Registre pour exposer de manière contrôlée les fonctions de la bibliothèque d'optimisation.
|
|
9
|
+
*/
|
|
10
|
+
class FunctionRegistry
|
|
11
|
+
{
|
|
12
|
+
/** @var array<string, callable> */
|
|
13
|
+
private static array $functions = [];
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Initialise le registre avec les fonctions disponibles.
|
|
17
|
+
*/
|
|
18
|
+
private static function initialize(): void
|
|
19
|
+
{
|
|
20
|
+
if (empty(self::$functions)) {
|
|
21
|
+
// Fonctions de "Scoring"
|
|
22
|
+
self::$functions['tsp.calculateEnergy'] = [OptimizationUtils::class, 'evaluatePathDistance'];
|
|
23
|
+
self::$functions['portfolio.calculateMetrics'] = [OptimizationOperators::class, 'createPortfolioAllocator'];
|
|
24
|
+
|
|
25
|
+
// Fonctions de "Résolution"
|
|
26
|
+
self::$functions['tsp.solve'] = [OptimizationOperators::class, 'solveTSP'];
|
|
27
|
+
self::$functions['portfolio.solve'] = [OptimizationOperators::class, 'solvePortfolio'];
|
|
28
|
+
self::$functions['fraud.solve'] = [OptimizationOperators::class, 'solveFraudDetection'];
|
|
29
|
+
self::$functions['facility.solve'] = [OptimizationOperators::class, 'solveFacilityLocation'];
|
|
30
|
+
self::$functions['security.tune'] = [OptimizationOperators::class, 'solveFullSecurityTuning'];
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Récupère une fonction depuis le registre.
|
|
36
|
+
*/
|
|
37
|
+
public static function get(string $name): ?callable
|
|
38
|
+
{
|
|
39
|
+
self::initialize();
|
|
40
|
+
return self::$functions[$name] ?? null;
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* Enregistre une nouvelle fonction. Principalement pour les tests.
|
|
44
|
+
* @internal
|
|
45
|
+
* @param string $name
|
|
46
|
+
* @param callable $function
|
|
47
|
+
* @return void
|
|
48
|
+
*/
|
|
49
|
+
public static function register(string $name, callable $function): void
|
|
50
|
+
{
|
|
51
|
+
self::initialize();
|
|
52
|
+
self::$functions[$name] = $function;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* Réinitialise le registre. Uniquement pour les tests.
|
|
57
|
+
* @internal
|
|
58
|
+
*/
|
|
59
|
+
public static function __internal_resetRegistry(): void
|
|
60
|
+
{
|
|
61
|
+
self::$functions = [];
|
|
62
|
+
}
|
|
63
|
+
}
|
|
@@ -0,0 +1,256 @@
|
|
|
1
|
+
<?php
|
|
2
|
+
|
|
3
|
+
declare(strict_types=1);
|
|
4
|
+
|
|
5
|
+
namespace Anonympins\Fingerprint\Optimization;
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Bibliothèque d'algorithmes d'optimisation.
|
|
9
|
+
*/
|
|
10
|
+
class Optimization
|
|
11
|
+
{
|
|
12
|
+
/**
|
|
13
|
+
* Génère un nombre flottant aléatoire cryptographiquement sûr entre 0 (inclus) et 1 (exclus).
|
|
14
|
+
*/
|
|
15
|
+
private static function secureRandom(): float
|
|
16
|
+
{
|
|
17
|
+
try {
|
|
18
|
+
return random_int(0, PHP_INT_MAX - 1) / PHP_INT_MAX;
|
|
19
|
+
} catch (\Exception $e) {
|
|
20
|
+
return (float)mt_rand() / (float)mt_getrandmax(); // Fallback
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Algorithme génétique multi-objectifs (inspiré de NSGA-II).
|
|
26
|
+
* @param callable $createIndividual
|
|
27
|
+
* @param callable $fitnessFunction
|
|
28
|
+
* @param callable $crossover
|
|
29
|
+
* @param callable $mutate
|
|
30
|
+
* @param array $options
|
|
31
|
+
* @return array<int, array{solution: mixed, objectives: array<float>}>
|
|
32
|
+
*/
|
|
33
|
+
public static function geneticAlgorithmMultiObjective(
|
|
34
|
+
callable $createIndividual,
|
|
35
|
+
callable $fitnessFunction,
|
|
36
|
+
callable $crossover,
|
|
37
|
+
callable $mutate,
|
|
38
|
+
array $options = []
|
|
39
|
+
): array {
|
|
40
|
+
$generations = $options['generations'] ?? 150;
|
|
41
|
+
$populationSize = $options['populationSize'] ?? 60;
|
|
42
|
+
$mutationRate = $options['mutationRate'] ?? 0.1;
|
|
43
|
+
|
|
44
|
+
$population = [];
|
|
45
|
+
for ($i = 0; $i < $populationSize; $i++) {
|
|
46
|
+
$individual = $createIndividual();
|
|
47
|
+
$population[] = [
|
|
48
|
+
'individual' => $individual,
|
|
49
|
+
'objectives' => $fitnessFunction($individual)
|
|
50
|
+
];
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
for ($gen = 0; $gen < $generations; $gen++) {
|
|
54
|
+
// 1. Créer une population d'enfants
|
|
55
|
+
$offspring = [];
|
|
56
|
+
for ($i = 0; $i < $populationSize; $i++) {
|
|
57
|
+
$parent1 = $population[random_int(0, count($population) - 1)];
|
|
58
|
+
$parent2 = $population[random_int(0, count($population) - 1)];
|
|
59
|
+
$childIndividual = $crossover($parent1['individual'], $parent2['individual']);
|
|
60
|
+
if (self::secureRandom() < $mutationRate) {
|
|
61
|
+
$childIndividual = $mutate($childIndividual);
|
|
62
|
+
}
|
|
63
|
+
$offspring[] = [
|
|
64
|
+
'individual' => $childIndividual,
|
|
65
|
+
'objectives' => $fitnessFunction($childIndividual)
|
|
66
|
+
];
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
// 2. Combiner parents et enfants
|
|
70
|
+
$combinedPopulation = array_merge($population, $offspring);
|
|
71
|
+
|
|
72
|
+
// 3. Trier la population combinée en fronts
|
|
73
|
+
$fronts = self::nonDominatedSort($combinedPopulation);
|
|
74
|
+
|
|
75
|
+
// 4. Construire la nouvelle population
|
|
76
|
+
$newPopulation = [];
|
|
77
|
+
foreach ($fronts as $front) {
|
|
78
|
+
if (count($newPopulation) + count($front) <= $populationSize) {
|
|
79
|
+
$newPopulation = array_merge($newPopulation, $front);
|
|
80
|
+
} else {
|
|
81
|
+
self::calculateCrowdingDistance($front);
|
|
82
|
+
// Trier par distance décroissante
|
|
83
|
+
usort($front, fn ($a, $b) => $b['crowdingDistance'] <=> $a['crowdingDistance']);
|
|
84
|
+
$remaining = $populationSize - count($newPopulation);
|
|
85
|
+
$newPopulation = array_merge($newPopulation, array_slice($front, 0, $remaining));
|
|
86
|
+
break;
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
$population = $newPopulation;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
// Retourner le premier front de la population finale
|
|
93
|
+
$finalFronts = self::nonDominatedSort($population);
|
|
94
|
+
$bestFront = $finalFronts[0] ?? [];
|
|
95
|
+
|
|
96
|
+
// Filtrer pour ne garder que les solutions avec des objectifs uniques
|
|
97
|
+
$uniqueSolutionsMap = [];
|
|
98
|
+
foreach ($bestFront as $p) {
|
|
99
|
+
$key = json_encode($p['objectives']);
|
|
100
|
+
if (!isset($uniqueSolutionsMap[$key])) {
|
|
101
|
+
$uniqueSolutionsMap[$key] = [
|
|
102
|
+
'solution' => $p['individual'],
|
|
103
|
+
'objectives' => $p['objectives'],
|
|
104
|
+
];
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
return array_values($uniqueSolutionsMap);
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
/**
|
|
111
|
+
* Détermine si la solution A domine la solution B.
|
|
112
|
+
* @param array<float> $objectivesA
|
|
113
|
+
* @param array<float> $objectivesB
|
|
114
|
+
*/
|
|
115
|
+
private static function paretoDominates(array $objectivesA, array $objectivesB): bool
|
|
116
|
+
{
|
|
117
|
+
$aIsBetterInOne = false;
|
|
118
|
+
for ($i = 0; $i < count($objectivesA); $i++) {
|
|
119
|
+
if ($objectivesA[$i] > $objectivesB[$i]) {
|
|
120
|
+
return false; // A est pire sur au moins un objectif
|
|
121
|
+
}
|
|
122
|
+
if ($objectivesA[$i] < $objectivesB[$i]) {
|
|
123
|
+
$aIsBetterInOne = true; // A est strictement meilleur sur au moins un
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
return $aIsBetterInOne;
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
/**
|
|
130
|
+
* Trie une population en fronts de Pareto non-dominés.
|
|
131
|
+
* @param array<int, array> &$populationWithObjectives
|
|
132
|
+
* @return array<int, array>
|
|
133
|
+
*/
|
|
134
|
+
private static function nonDominatedSort(array &$populationWithObjectives): array
|
|
135
|
+
{
|
|
136
|
+
$fronts = [[]];
|
|
137
|
+
$n = count($populationWithObjectives);
|
|
138
|
+
|
|
139
|
+
for ($i = 0; $i < $n; $i++) {
|
|
140
|
+
$p1 = &$populationWithObjectives[$i];
|
|
141
|
+
$p1['dominationCount'] = 0;
|
|
142
|
+
$p1['dominatedSolutions'] = [];
|
|
143
|
+
|
|
144
|
+
for ($j = 0; $j < $n; $j++) {
|
|
145
|
+
if ($i === $j) continue;
|
|
146
|
+
$p2 = &$populationWithObjectives[$j];
|
|
147
|
+
|
|
148
|
+
if (self::paretoDominates($p1['objectives'], $p2['objectives'])) {
|
|
149
|
+
$p1['dominatedSolutions'][] = $j;
|
|
150
|
+
} elseif (self::paretoDominates($p2['objectives'], $p1['objectives'])) {
|
|
151
|
+
$p1['dominationCount']++;
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
if ($p1['dominationCount'] === 0) {
|
|
156
|
+
$p1['rank'] = 0;
|
|
157
|
+
$fronts[0][] = $p1;
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
$i = 0;
|
|
162
|
+
while (!empty($fronts[$i])) {
|
|
163
|
+
$nextFront = [];
|
|
164
|
+
foreach ($fronts[$i] as $p1) {
|
|
165
|
+
foreach ($p1['dominatedSolutions'] as $p2_idx) {
|
|
166
|
+
$p2 = &$populationWithObjectives[$p2_idx];
|
|
167
|
+
$p2['dominationCount']--;
|
|
168
|
+
if ($p2['dominationCount'] === 0) {
|
|
169
|
+
$p2['rank'] = $i + 1;
|
|
170
|
+
$nextFront[] = $p2;
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
$i++;
|
|
175
|
+
if (!empty($nextFront)) {
|
|
176
|
+
$fronts[$i] = $nextFront;
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
return $fronts;
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
/**
|
|
183
|
+
* Calcule la distance de promiscuité (crowding distance) pour un front.
|
|
184
|
+
* @param array<int, array> &$front
|
|
185
|
+
*/
|
|
186
|
+
private static function calculateCrowdingDistance(array &$front): void
|
|
187
|
+
{
|
|
188
|
+
if (empty($front)) return;
|
|
189
|
+
|
|
190
|
+
$numObjectives = count($front[0]['objectives']);
|
|
191
|
+
$l = count($front);
|
|
192
|
+
|
|
193
|
+
foreach ($front as &$p) {
|
|
194
|
+
$p['crowdingDistance'] = 0;
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
for ($i = 0; $i < $numObjectives; $i++) {
|
|
198
|
+
// Trier le front par l'objectif courant
|
|
199
|
+
usort($front, fn ($a, $b) => $a['objectives'][$i] <=> $b['objectives'][$i]);
|
|
200
|
+
|
|
201
|
+
$minObj = $front[0]['objectives'][$i];
|
|
202
|
+
$maxObj = $front[$l - 1]['objectives'][$i];
|
|
203
|
+
|
|
204
|
+
// Les solutions aux extrémités ont une distance infinie
|
|
205
|
+
$front[0]['crowdingDistance'] = INF;
|
|
206
|
+
$front[$l - 1]['crowdingDistance'] = INF;
|
|
207
|
+
|
|
208
|
+
if ($maxObj === $minObj) continue;
|
|
209
|
+
|
|
210
|
+
for ($j = 1; $j < $l - 1; $j++) {
|
|
211
|
+
$front[$j]['crowdingDistance'] +=
|
|
212
|
+
($front[$j + 1]['objectives'][$i] - $front[$j - 1]['objectives'][$i]) /
|
|
213
|
+
($maxObj - $minObj);
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
/**
|
|
219
|
+
* Calcule la déviation d'une série de chiffres par rapport à la loi de Benford.
|
|
220
|
+
* @public
|
|
221
|
+
* @param array<int|float> $numbers
|
|
222
|
+
*/
|
|
223
|
+
public static function benfordTest(array $numbers): float // Rendre la méthode publique et statique
|
|
224
|
+
{
|
|
225
|
+
if (count($numbers) < 10) {
|
|
226
|
+
return 0.0; // Pas assez de données
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
$leadingDigits = array_map(function ($n) {
|
|
230
|
+
$str = ltrim((string)$n, '0.');
|
|
231
|
+
return $str[0] ?? '';
|
|
232
|
+
}, $numbers);
|
|
233
|
+
|
|
234
|
+
$leadingDigits = array_filter($leadingDigits, fn ($d) => $d >= '1' && $d <= '9');
|
|
235
|
+
|
|
236
|
+
if (count($leadingDigits) < 10) {
|
|
237
|
+
return 0.0;
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
$counts = array_fill(1, 9, 0);
|
|
241
|
+
foreach ($leadingDigits as $digit) {
|
|
242
|
+
$counts[(int)$digit]++;
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
$benfordDistribution = [1 => 30.1, 2 => 17.6, 3 => 12.5, 4 => 9.7, 5 => 7.9, 6 => 6.7, 7 => 5.8, 8 => 5.1, 9 => 4.6];
|
|
246
|
+
|
|
247
|
+
$totalDeviation = 0.0;
|
|
248
|
+
for ($i = 1; $i <= 9; $i++) {
|
|
249
|
+
$observedFrequency = ($counts[$i] / count($leadingDigits)) * 100;
|
|
250
|
+
$expectedFrequency = $benfordDistribution[$i];
|
|
251
|
+
$totalDeviation += pow($observedFrequency - $expectedFrequency, 2);
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
return sqrt($totalDeviation) / 50.0;
|
|
255
|
+
}
|
|
256
|
+
}
|
|
@@ -0,0 +1,305 @@
|
|
|
1
|
+
<?php
|
|
2
|
+
|
|
3
|
+
declare(strict_types=1);
|
|
4
|
+
|
|
5
|
+
namespace Anonympins\Fingerprint\Optimization;
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Opérateurs pour les problèmes d'optimisation.
|
|
9
|
+
*/
|
|
10
|
+
class OptimizationOperators
|
|
11
|
+
{
|
|
12
|
+
/**
|
|
13
|
+
* Génère un nombre flottant aléatoire cryptographiquement sûr entre 0 (inclus) et 1 (exclus).
|
|
14
|
+
*/
|
|
15
|
+
private static function secureRandom(): float
|
|
16
|
+
{
|
|
17
|
+
try {
|
|
18
|
+
return random_int(0, PHP_INT_MAX - 1) / PHP_INT_MAX;
|
|
19
|
+
} catch (\Exception $e) {
|
|
20
|
+
return (float)mt_rand() / (float)mt_getrandmax(); // Fallback
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Crée un évaluateur pour l'optimisation de portefeuille.
|
|
26
|
+
* @param array $config
|
|
27
|
+
* @return callable
|
|
28
|
+
*/
|
|
29
|
+
public static function createPortfolioAllocator(array $config): callable
|
|
30
|
+
{
|
|
31
|
+
// Cette fonction est un placeholder. Une implémentation complète nécessiterait
|
|
32
|
+
// une logique de calcul de rendement et de volatilité de portefeuille.
|
|
33
|
+
return function (array $weights) use ($config): float {
|
|
34
|
+
// Minimiser le rendement négatif (donc maximiser le rendement)
|
|
35
|
+
return -array_sum($weights);
|
|
36
|
+
};
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Crée un évaluateur pour l'auto-tuning complet de la configuration de sécurité.
|
|
41
|
+
* @param array $context
|
|
42
|
+
* @return callable
|
|
43
|
+
*/
|
|
44
|
+
public static function createFullSecurityConfigEvaluator(array $context): callable
|
|
45
|
+
{
|
|
46
|
+
$trafficData = $context['trafficData'];
|
|
47
|
+
|
|
48
|
+
return function (array $config) use ($trafficData): array {
|
|
49
|
+
$falsePositives = 0;
|
|
50
|
+
$falseNegatives = 0;
|
|
51
|
+
$totalHumans = 0;
|
|
52
|
+
$totalBots = 0;
|
|
53
|
+
|
|
54
|
+
$calculateScore = function (array $log) use ($config): float {
|
|
55
|
+
$score = 0.0;
|
|
56
|
+
foreach ($config['weights'] as $key => $weight) {
|
|
57
|
+
$score += ($log['vector'][$key] ?? 0) * $weight;
|
|
58
|
+
}
|
|
59
|
+
return $score;
|
|
60
|
+
};
|
|
61
|
+
|
|
62
|
+
$confidenceWeights = [
|
|
63
|
+
'request_passed' => 0.7,
|
|
64
|
+
'challenge_issued' => 1.0,
|
|
65
|
+
'request_blocked' => 1.0,
|
|
66
|
+
'challenge_solved' => 1.5,
|
|
67
|
+
'trap_triggered' => 2.0,
|
|
68
|
+
];
|
|
69
|
+
|
|
70
|
+
foreach ($trafficData as $log) {
|
|
71
|
+
$confidence = $confidenceWeights[$log['type']] ?? 1.0;
|
|
72
|
+
$isLikelyBot = in_array($log['type'], ['challenge_issued', 'request_blocked', 'trap_triggered']);
|
|
73
|
+
$isLikelyHuman = in_array($log['type'], ['request_passed', 'challenge_solved']);
|
|
74
|
+
|
|
75
|
+
if ($isLikelyBot) {
|
|
76
|
+
$totalBots += $confidence;
|
|
77
|
+
$score = $calculateScore($log);
|
|
78
|
+
if ($score < $config['thresholds']['low']) {
|
|
79
|
+
$falseNegatives += $confidence;
|
|
80
|
+
}
|
|
81
|
+
} elseif ($isLikelyHuman) {
|
|
82
|
+
$totalHumans += $confidence;
|
|
83
|
+
$score = $calculateScore($log);
|
|
84
|
+
if ($score >= $config['thresholds']['low']) {
|
|
85
|
+
$falsePositives += $confidence;
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
$falsePositiveRate = $totalHumans > 0 ? $falsePositives / $totalHumans : 0;
|
|
91
|
+
$falseNegativeRate = $totalBots > 0 ? $falseNegatives / $totalBots : 0;
|
|
92
|
+
|
|
93
|
+
return [$falsePositiveRate, $falseNegativeRate];
|
|
94
|
+
};
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
/**
|
|
98
|
+
* Résout le problème de l'auto-tuning complet de la configuration de sécurité.
|
|
99
|
+
* @param array $context
|
|
100
|
+
* @param array $options
|
|
101
|
+
* @return array
|
|
102
|
+
*/
|
|
103
|
+
public static function solveFullSecurityTuning(array $context, array $options = []): array
|
|
104
|
+
{
|
|
105
|
+
$fitnessFunction = self::createFullSecurityConfigEvaluator($context);
|
|
106
|
+
|
|
107
|
+
$createIndividual = function (): array {
|
|
108
|
+
return [
|
|
109
|
+
'thresholds' => [
|
|
110
|
+
'low' => 15 + self::secureRandom() * 20,
|
|
111
|
+
'medium' => 40 + self::secureRandom() * 25,
|
|
112
|
+
'high' => 70 + self::secureRandom() * 20,
|
|
113
|
+
],
|
|
114
|
+
'weights' => [
|
|
115
|
+
'historyScore' => self::secureRandom(),
|
|
116
|
+
'rotationScore' => self::secureRandom(),
|
|
117
|
+
'headerAnomalyScore' => self::secureRandom(),
|
|
118
|
+
'requestPatternScore' => 0.5 + self::secureRandom(),
|
|
119
|
+
'inconsistencyScore' => self::secureRandom(),
|
|
120
|
+
'honeypotScore' => 1.0,
|
|
121
|
+
'behaviorScore' => self::secureRandom(),
|
|
122
|
+
'crossLayerInconsistencyScore' => self::secureRandom(),
|
|
123
|
+
'timeInconsistencyScore' => self::secureRandom(),
|
|
124
|
+
'tlsSpoofingScore' => self::secureRandom(),
|
|
125
|
+
'botScore' => self::secureRandom(),
|
|
126
|
+
'cookieDroppingScore' => self::secureRandom(),
|
|
127
|
+
'threatIntelScore' => self::secureRandom(),
|
|
128
|
+
],
|
|
129
|
+
'patterns' => [
|
|
130
|
+
'velocityThreshold' => 100 + self::secureRandom() * 400,
|
|
131
|
+
'burstThreshold' => 300 + self::secureRandom() * 700,
|
|
132
|
+
'scrapeThreshold' => 500 + self::secureRandom() * 1000,
|
|
133
|
+
'regularityThreshold' => 50 + self::secureRandom() * 200,
|
|
134
|
+
'decayFactor' => 0.85 + self::secureRandom() * 0.14,
|
|
135
|
+
'inactivityReset' => 15000 + self::secureRandom() * 45000,
|
|
136
|
+
]
|
|
137
|
+
];
|
|
138
|
+
};
|
|
139
|
+
|
|
140
|
+
$crossover = function (array $c1, array $c2): array {
|
|
141
|
+
$child = $c1;
|
|
142
|
+
foreach (['thresholds', 'weights', 'patterns'] as $section) {
|
|
143
|
+
foreach ($child[$section] as $key => $value) {
|
|
144
|
+
if ($key !== 'honeypotScore') {
|
|
145
|
+
$child[$section][$key] = ($c1[$section][$key] + $c2[$section][$key]) / 2;
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
return $child;
|
|
150
|
+
};
|
|
151
|
+
|
|
152
|
+
$mutate = function (array $c): array {
|
|
153
|
+
$newConfig = $c;
|
|
154
|
+
$sections = [
|
|
155
|
+
['name' => 'patterns', 'weight' => 0.5],
|
|
156
|
+
['name' => 'weights', 'weight' => 0.35],
|
|
157
|
+
['name' => 'thresholds', 'weight' => 0.15]
|
|
158
|
+
];
|
|
159
|
+
$rand = self::secureRandom();
|
|
160
|
+
$cumulativeWeight = 0;
|
|
161
|
+
$sectionToMutate = 'patterns';
|
|
162
|
+
foreach ($sections as $section) {
|
|
163
|
+
$cumulativeWeight += $section['weight'];
|
|
164
|
+
if ($rand < $cumulativeWeight) {
|
|
165
|
+
$sectionToMutate = $section['name'];
|
|
166
|
+
break;
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
$keys = array_keys($newConfig[$sectionToMutate]);
|
|
171
|
+
$keyToMutate = $keys[random_int(0, count($keys) - 1)];
|
|
172
|
+
|
|
173
|
+
if ($keyToMutate === 'honeypotScore') return $newConfig;
|
|
174
|
+
|
|
175
|
+
$mutationAmount = (self::secureRandom() - 0.5) * 0.4;
|
|
176
|
+
$newConfig[$sectionToMutate][$keyToMutate] *= (1 + $mutationAmount);
|
|
177
|
+
|
|
178
|
+
if ($sectionToMutate === 'weights') {
|
|
179
|
+
$newConfig[$sectionToMutate][$keyToMutate] = max(0, min(1.5, $newConfig[$sectionToMutate][$keyToMutate]));
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
return $newConfig;
|
|
183
|
+
};
|
|
184
|
+
|
|
185
|
+
return Optimization::geneticAlgorithmMultiObjective(
|
|
186
|
+
$createIndividual,
|
|
187
|
+
$fitnessFunction,
|
|
188
|
+
$crossover,
|
|
189
|
+
$mutate,
|
|
190
|
+
array_merge(['generations' => 50, 'populationSize' => 50], $options)
|
|
191
|
+
);
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
/**
|
|
195
|
+
* Crée un évaluateur pour trouver les seuils de détection de fraude optimaux.
|
|
196
|
+
* @param array $context
|
|
197
|
+
* @return callable
|
|
198
|
+
*/
|
|
199
|
+
public static function createFraudThresholdEvaluator(array $context): callable
|
|
200
|
+
{
|
|
201
|
+
$legitimateClicks = $context['legitimateClicks'] ?? [];
|
|
202
|
+
$fraudulentClicks = $context['fraudulentClicks'] ?? [];
|
|
203
|
+
|
|
204
|
+
return function (array $solution) use ($legitimateClicks, $fraudulentClicks): array {
|
|
205
|
+
[$minTimeToClick, $maxClickVariance, $minMouseEntropy, $minScrollEvents] = $solution;
|
|
206
|
+
|
|
207
|
+
$truePositives = 0;
|
|
208
|
+
$falsePositives = 0;
|
|
209
|
+
|
|
210
|
+
foreach ($fraudulentClicks as $click) {
|
|
211
|
+
if (
|
|
212
|
+
($click['timeToClick'] < $minTimeToClick) ||
|
|
213
|
+
($click['mouseEntropy'] < $minMouseEntropy)
|
|
214
|
+
) {
|
|
215
|
+
$truePositives++;
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
foreach ($legitimateClicks as $click) {
|
|
220
|
+
if (
|
|
221
|
+
($click['timeToClick'] < $minTimeToClick) ||
|
|
222
|
+
($click['mouseEntropy'] < $minMouseEntropy)
|
|
223
|
+
) {
|
|
224
|
+
$falsePositives++;
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
$totalFraudulent = count($fraudulentClicks) ?: 1;
|
|
229
|
+
$totalLegitimate = count($legitimateClicks) ?: 1;
|
|
230
|
+
|
|
231
|
+
$objective1 = 1 - ($truePositives / $totalFraudulent);
|
|
232
|
+
$objective2 = $falsePositives / $totalLegitimate;
|
|
233
|
+
|
|
234
|
+
return [$objective1, $objective2];
|
|
235
|
+
};
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
/**
|
|
239
|
+
* Résout le problème de la détection de fraude.
|
|
240
|
+
* @param array $context
|
|
241
|
+
* @param array $options
|
|
242
|
+
* @return array
|
|
243
|
+
*/
|
|
244
|
+
public static function solveFraudDetection(array $context, array $options = []): array
|
|
245
|
+
{
|
|
246
|
+
$fitnessFunction = self::createFraudThresholdEvaluator($context);
|
|
247
|
+
|
|
248
|
+
$createIndividual = function (): array {
|
|
249
|
+
return [
|
|
250
|
+
100 + self::secureRandom() * 4900, // minTimeToClick
|
|
251
|
+
1 + self::secureRandom() * 9999, // maxClickVariance
|
|
252
|
+
self::secureRandom() * 0.5, // minMouseEntropy
|
|
253
|
+
floor(self::secureRandom() * 10) // minScrollEvents
|
|
254
|
+
];
|
|
255
|
+
};
|
|
256
|
+
|
|
257
|
+
$crossover = fn ($s1, $s2) => array_map(fn ($a, $b) => ($a + $b) / 2, $s1, $s2);
|
|
258
|
+
|
|
259
|
+
$mutate = function (array $solution): array {
|
|
260
|
+
$i = random_int(0, 3);
|
|
261
|
+
$mutationFactors = [500, 1000, 0.1, 2];
|
|
262
|
+
$solution[$i] += (self::secureRandom() - 0.5) * $mutationFactors[$i];
|
|
263
|
+
return $solution;
|
|
264
|
+
};
|
|
265
|
+
|
|
266
|
+
return Optimization::geneticAlgorithmMultiObjective(
|
|
267
|
+
$createIndividual,
|
|
268
|
+
$fitnessFunction,
|
|
269
|
+
$crossover,
|
|
270
|
+
$mutate,
|
|
271
|
+
array_merge(['generations' => 80, 'populationSize' => 60], $options)
|
|
272
|
+
);
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
/**
|
|
276
|
+
* Placeholder pour le solveur TSP.
|
|
277
|
+
* @param array $cities
|
|
278
|
+
* @param array $options
|
|
279
|
+
* @return array
|
|
280
|
+
*/
|
|
281
|
+
public static function solveTSP(array $cities, array $options = []): array
|
|
282
|
+
{
|
|
283
|
+
// Implémentation factice pour la complétude
|
|
284
|
+
return ['solution' => array_keys($cities), 'energy' => 100];
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
/**
|
|
288
|
+
* Placeholder pour le solveur de portefeuille.
|
|
289
|
+
* @param array $assets
|
|
290
|
+
* @param float $maxVolatility
|
|
291
|
+
* @param array $options
|
|
292
|
+
* @return array
|
|
293
|
+
*/
|
|
294
|
+
public static function solvePortfolio(array $assets, float $maxVolatility, array $options = []): array
|
|
295
|
+
{
|
|
296
|
+
// Implémentation factice pour la complétude
|
|
297
|
+
return ['solution' => array_fill(0, count($assets), 1 / count($assets)), 'fitness' => -0.1];
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
public static function solveFacilityLocation(array $customers, int $numFacilities, array $bounds, array $options = []): array
|
|
301
|
+
{
|
|
302
|
+
// Implémentation factice pour la complétude
|
|
303
|
+
return ['solution' => [], 'energy' => 0];
|
|
304
|
+
}
|
|
305
|
+
}
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
<?php
|
|
2
|
+
|
|
3
|
+
declare(strict_types=1);
|
|
4
|
+
|
|
5
|
+
namespace Anonympins\Fingerprint\Optimization;
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Fonctions pour générer dynamiquement les données d'un problème.
|
|
9
|
+
*/
|
|
10
|
+
class ProblemInitializers
|
|
11
|
+
{
|
|
12
|
+
/** @var array<string, callable> */
|
|
13
|
+
private static array $initializers = [];
|
|
14
|
+
|
|
15
|
+
private static function initialize(): void
|
|
16
|
+
{
|
|
17
|
+
if (empty(self::$initializers)) {
|
|
18
|
+
self::$initializers['generate:randomPoints'] = function (array $params): array {
|
|
19
|
+
$count = $params['count'] ?? 0;
|
|
20
|
+
$bounds = $params['bounds'] ?? ['x' => 1000, 'y' => 1000];
|
|
21
|
+
if (!is_numeric($count)) return [];
|
|
22
|
+
$points = [];
|
|
23
|
+
for ($i = 0; $i < $count; $i++) {
|
|
24
|
+
$points[] = [
|
|
25
|
+
'x' => mt_rand() / mt_getrandmax() * $bounds['x'],
|
|
26
|
+
'y' => mt_rand() / mt_getrandmax() * $bounds['y']
|
|
27
|
+
];
|
|
28
|
+
}
|
|
29
|
+
return $points;
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
self::$initializers['generate:randomAssets'] = function (array $params): array {
|
|
33
|
+
$count = $params['count'] ?? 0;
|
|
34
|
+
if (!is_numeric($count)) return [];
|
|
35
|
+
$assets = [];
|
|
36
|
+
for ($i = 0; $i < $count; $i++) {
|
|
37
|
+
$assets[] = [
|
|
38
|
+
'name' => 'Asset ' . ($i + 1),
|
|
39
|
+
'expectedReturn' => mt_rand() / mt_getrandmax() * 0.2,
|
|
40
|
+
'volatility' => 0.1 + mt_rand() / mt_getrandmax() * 0.3
|
|
41
|
+
];
|
|
42
|
+
}
|
|
43
|
+
return $assets;
|
|
44
|
+
};
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
public static function get(string $name): ?callable
|
|
49
|
+
{
|
|
50
|
+
self::initialize();
|
|
51
|
+
return self::$initializers[$name] ?? null;
|
|
52
|
+
}
|
|
53
|
+
}
|