@anonympins/fingerprint 0.0.1 → 0.0.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/README.md +175 -44
- package/fingerprint.js +565 -504
- package/library.js +1 -825
- package/package.json +5 -4
package/README.md
CHANGED
|
@@ -1,12 +1,15 @@
|
|
|
1
1
|
# fingerprint
|
|
2
|
-
](https://github.com/anonympins/fingerprint/actions/workflows/ci.yml)
|
|
3
|
+
[](https://github.com/anonympins/fingerprint/releases)
|
|
4
|
+
[](https://github.com/anonympins/fingerprint/blob/main/LICENSE)
|
|
5
|
+
[](https://github.com/anonympins/fingerprint/releases)
|
|
6
|
+
[](https://github.com/anonympins/fingerprint/watchers)
|
|
4
7
|
|
|
5
8
|
An HTTP(S) client mitigation and anti-bot protection library for Node.js/Express, based on digital fingerprinting and dynamic Proof-of-Work (PoW) challenges.
|
|
6
9
|
|
|
7
10
|
## How It Works
|
|
8
11
|
|
|
9
|
-
This system
|
|
12
|
+
This system identifies and slows down bots and automated scripts by evaluating the "suspicion" level of each incoming request. Instead of outright blocking, it imposes challenges with a difficulty proportional to the suspicion score, penalizing bots without significantly impacting legitimate users.
|
|
10
13
|
|
|
11
14
|
The process unfolds in three steps:
|
|
12
15
|
|
|
@@ -17,24 +20,19 @@ The process unfolds in three steps:
|
|
|
17
20
|
* **IP Behavior**: An excessive number of different devices seen from the same IP, or a single device using a large number of IPs (proxy rotation).
|
|
18
21
|
* **Inconsistency**: A low similarity score between the current fingerprint and the initial one associated with the `device_id` (cookie theft detection).
|
|
19
22
|
3. **Dynamic Challenge**: If the suspicion score exceeds a certain threshold, a challenge is presented to the user. The difficulty and type of challenge depend on the score:
|
|
20
|
-
* **
|
|
21
|
-
* **
|
|
22
|
-
* **Level 3 (High Suspicion)**: Complex challenge (e.g., TSP - Traveling Salesperson Problem) or a CAPTCHA.
|
|
23
|
+
* **Low to Medium Suspicion**: A combined CPU and Memory Proof-of-Work (PoW) challenge is issued. The difficulty of both the CPU (hash calculation) and Memory (allocation and computation) components scales progressively with the suspicion score. For low scores, the memory challenge is negligible, making it primarily a CPU task.
|
|
24
|
+
* **High Suspicion**: For the most suspicious requests, the system issues a high-difficulty combined CPU/Memory challenge. The architecture allows for plugging in more complex challenges like CAPTCHAs if needed.
|
|
23
25
|
|
|
24
|
-
Once the challenge is solved, a clearance "ticket" is issued via a cookie, exempting the user from new challenges for a set period.
|
|
26
|
+
Once the challenge is solved, a clearance "ticket" is issued via a secure cookie, exempting the user from new challenges for a set period.
|
|
25
27
|
|
|
26
28
|
## Features
|
|
27
29
|
|
|
28
30
|
- **Multi-Factor Fingerprinting**: Combines client-side data (`hardwareConcurrency`, `deviceMemory`, `screen`, `canvas`, `webgl`) and server-side data (`User-Agent`, `Client-Hints`).
|
|
29
|
-
- **Weighted Suspicion Engine**: Calculates a score based on behavioral and technical indicators.
|
|
30
|
-
- **Variable-Difficulty Proof-of-Work Challenges**:
|
|
31
|
-
- `cpu_target`: An "analog" CPU challenge where difficulty is finely tuned to the suspicion score.
|
|
32
|
-
- `memory`: A challenge that allocates an amount of memory proportional to the suspicion level.
|
|
33
|
-
- `tsp`: An optimization challenge (Traveling Salesperson Problem) for the most suspicious cases.
|
|
34
31
|
- **Secure Ticket System**: Uses HMAC-SHA256 signatures to validate clearances and prevent tampering.
|
|
35
32
|
- **Pluggable Datastore**: Supports external datastores like Redis for state persistence and scalability across multiple server instances.
|
|
36
33
|
- **Express.js Middleware**: Easy integration into an Express application with `powMiddleware`.
|
|
37
34
|
- **Timing Attack Protection**: Uses `crypto.timingSafeEqual` for secure ticket validation.
|
|
35
|
+
- **Automatic Threshold Tuning**: Includes a genetic algorithm-based optimizer (`startThresholdAutoTuning`) that analyzes real traffic to dynamically adjust suspicion thresholds (`low`, `medium`, `high`), improving bot detection accuracy and reducing false positives over time.
|
|
38
36
|
|
|
39
37
|
## Installation and Usage
|
|
40
38
|
|
|
@@ -54,13 +52,11 @@ export POW_SECRET="your_secret_key_of_at_least_32_characters"
|
|
|
54
52
|
|
|
55
53
|
### Integration Example
|
|
56
54
|
|
|
57
|
-
|
|
55
|
+
The `powMiddleware` requires a configuration object defining the weights of suspicion indicators and the challenge trigger thresholds.
|
|
58
56
|
|
|
59
57
|
```javascript
|
|
60
58
|
import express from 'express';
|
|
61
59
|
import cookieParser from 'cookie-parser';
|
|
62
|
-
// The `configurePow` function is a conceptual example. In the actual implementation,
|
|
63
|
-
// you would pass the configuration to the middleware, for example, via a factory function.
|
|
64
60
|
import { powMiddleware /*, configurePow */ } from './fingerprint.js'; // Adjust the path
|
|
65
61
|
|
|
66
62
|
const app = express();
|
|
@@ -69,33 +65,39 @@ app.use(cookieParser());
|
|
|
69
65
|
// Configuration of weights and thresholds for calculating the suspicion score.
|
|
70
66
|
// These values should be adjusted based on traffic and expected user behavior.
|
|
71
67
|
const securityConfig = {
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
68
|
+
weights: {
|
|
69
|
+
historyScore: 0.3, // Penalizes IP rotation (proxy)
|
|
70
|
+
rotationScore: 0.5, // Penalizes rapid fingerprint changes (user-agent, etc.)
|
|
71
|
+
headerAnomalyScore: 0.1, // Penalizes abnormal headers (missing UA, etc.)
|
|
72
|
+
inconsistencyScore: 0.8 // Strongly penalizes inconsistency between the current and initial fingerprint (stolen cookie)
|
|
73
|
+
},
|
|
74
|
+
thresholds: {
|
|
75
|
+
low: 20, // Score from which a CPU challenge is issued
|
|
76
|
+
medium: 45, // Score for a more difficult combined CPU/Memory challenge
|
|
77
|
+
high: 75, // Score for a very difficult challenge
|
|
78
|
+
block: 95 // Score above which the request is blocked outright (HTTP 403)
|
|
79
|
+
}
|
|
83
80
|
};
|
|
84
81
|
|
|
85
|
-
//
|
|
86
|
-
// For example: const configuredPowMiddleware = createPowMiddleware(securityConfig);
|
|
82
|
+
// Create an instance of the middleware with your security configuration.
|
|
87
83
|
const powMiddlewareInstance = powMiddleware(securityConfig);
|
|
88
84
|
|
|
89
85
|
// Enable trust proxy if your app is behind a reverse proxy (Nginx, etc.)
|
|
90
86
|
// to correctly retrieve the client's IP.
|
|
91
87
|
app.set('trust proxy', 1);
|
|
92
88
|
|
|
93
|
-
// Apply the protection middleware to all routes or to specific
|
|
94
|
-
// You would use the configured middleware here.
|
|
89
|
+
// Apply the protection middleware to all routes or to specific ones.
|
|
95
90
|
app.use(powMiddlewareInstance);
|
|
96
91
|
|
|
97
92
|
app.get('/', (req, res) => {
|
|
98
|
-
|
|
93
|
+
res.send('Welcome to the protected page!');
|
|
94
|
+
});
|
|
95
|
+
|
|
96
|
+
// Example of accessing the suspicion score in a subsequent middleware or route.
|
|
97
|
+
// The `fingerprint` object is attached to the request object by the middleware.
|
|
98
|
+
app.use((req, res, next) => {
|
|
99
|
+
console.log(`Request from ${req.ip} has a suspicion score of: ${req.fingerprint?.score}`);
|
|
100
|
+
next();
|
|
99
101
|
});
|
|
100
102
|
|
|
101
103
|
app.listen(3000, () => console.log('Server started on port 3000'));
|
|
@@ -107,7 +109,7 @@ In addition to the main middleware, several functions are exported to allow for
|
|
|
107
109
|
|
|
108
110
|
### Main Functions
|
|
109
111
|
|
|
110
|
-
#### `powMiddleware(
|
|
112
|
+
#### `powMiddleware(securityConfig)`
|
|
111
113
|
The main Express middleware. It orchestrates identification, suspicion calculation, and challenge issuance. It is the main entry point of the library.
|
|
112
114
|
|
|
113
115
|
#### `configureStore(store)`
|
|
@@ -115,7 +117,7 @@ Allows replacing the in-memory store with an external datastore (like Redis) for
|
|
|
115
117
|
|
|
116
118
|
```javascript
|
|
117
119
|
import { configureStore } from './fingerprint.js';
|
|
118
|
-
import { createRedisStore } from './redis-store.js'; // Assuming
|
|
120
|
+
import { createRedisStore } from './redis-store.js'; // Assuming a redis store implementation exists
|
|
119
121
|
|
|
120
122
|
const redisStore = createRedisStore(process.env.REDIS_URL);
|
|
121
123
|
configureStore(redisStore);
|
|
@@ -129,19 +131,19 @@ import { RateLimiterMemory } from 'rate-limiter-flexible';
|
|
|
129
131
|
import { identifyRequest } from './fingerprint.js';
|
|
130
132
|
|
|
131
133
|
const rateLimiter = new RateLimiterMemory({
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
134
|
+
keyPrefix: 'rate_limit',
|
|
135
|
+
points: 10,
|
|
136
|
+
duration: 1,
|
|
135
137
|
});
|
|
136
138
|
|
|
137
139
|
app.use(async (req, res, next) => {
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
140
|
+
try {
|
|
141
|
+
const key = await identifyRequest(req, res);
|
|
142
|
+
await rateLimiter.consume(key);
|
|
143
|
+
next();
|
|
144
|
+
} catch (err) {
|
|
145
|
+
res.status(429).send('Too Many Requests');
|
|
146
|
+
}
|
|
145
147
|
});
|
|
146
148
|
```
|
|
147
149
|
|
|
@@ -150,7 +152,7 @@ app.use(async (req, res, next) => {
|
|
|
150
152
|
#### `isTicketValid(ip, ticket)`
|
|
151
153
|
Checks the validity of a `pow_clearance` cookie. Returns `true` if the ticket is present, not expired, and correctly signed for the given IP.
|
|
152
154
|
|
|
153
|
-
#### `FingerprintBuilder`
|
|
155
|
+
#### `FingerprintBuilder`
|
|
154
156
|
A class for building granular server-side fingerprints.
|
|
155
157
|
|
|
156
158
|
```javascript
|
|
@@ -162,6 +164,135 @@ const fp = builder.toString(); // "os:hash1|ua:hash2"
|
|
|
162
164
|
|
|
163
165
|
#### `getDeviceFingerprint()`
|
|
164
166
|
*Client-side function only.* Generates a detailed browser fingerprint using APIs like Canvas, WebGL, etc.
|
|
167
|
+
This is the primary function for client-side identification.
|
|
168
|
+
|
|
169
|
+
#### `generateRequestSignature(payload)`
|
|
170
|
+
*Client-side function only.* Creates a signature for an outgoing request. It combines the device fingerprint with a hash of the request's `payload`. This can be used on the server-side to verify that a request comes from a recognized device and that its payload has not been trivially altered.
|
|
171
|
+
|
|
172
|
+
```javascript
|
|
173
|
+
// On the client
|
|
174
|
+
const signature = generateRequestSignature({ action: 'update', id: 123 });
|
|
175
|
+
// Send signature in headers...
|
|
176
|
+
```
|
|
177
|
+
|
|
178
|
+
#### `generateClientSideSignature(payload, secret)`
|
|
179
|
+
*Client-side function only.* Generates a secure HMAC-SHA256 signature for a given `payload` using a `secret`.
|
|
180
|
+
**Security Note:** This function is powerful but should be used with caution. The `secret` must be managed securely. It is typically used with a temporary, single-use secret provided by the server for a specific action, rather than a long-lived shared secret embedded in the client-side code.
|
|
181
|
+
|
|
182
|
+
---
|
|
183
|
+
## Advanced Features
|
|
184
|
+
|
|
185
|
+
### Architecture: `FingerprintEngine`
|
|
186
|
+
|
|
187
|
+
The core logic of the library is encapsulated within the `FingerprintEngine` class. The `powMiddleware` is essentially a lightweight wrapper that adapts this engine for use with Express.js.
|
|
188
|
+
|
|
189
|
+
The engine is responsible for:
|
|
190
|
+
1. Receiving a `requestContext` (IP, headers, cookies, etc.).
|
|
191
|
+
2. Calculating the suspicion score using the configured weights.
|
|
192
|
+
3. Making a decision: `next`, `challenge`, or `redirect` (after solving a challenge).
|
|
193
|
+
|
|
194
|
+
Although not exported for direct public use, understanding its role can be useful for advanced integrations or debugging.
|
|
195
|
+
|
|
196
|
+
### Manual Integration (outside Express.js)
|
|
197
|
+
|
|
198
|
+
While `powMiddleware` is convenient for Express, you can use the `FingerprintEngine` directly in any Node.js server environment (e.g., native `http`, Fastify, Koa). This gives you full control over the request/response cycle.
|
|
199
|
+
|
|
200
|
+
The engine is available via the internal exports: `import { __internal } from './fingerprint.js'`.
|
|
201
|
+
|
|
202
|
+
**Workflow:**
|
|
203
|
+
|
|
204
|
+
1. **Instantiate the Engine**: Create an instance with your `securityConfig`.
|
|
205
|
+
2. **Build the `requestContext`**: On each request, manually create a context object. It must include `clientIp`, `path`, `cookies`, `query`, `headers`, and mock `rawReq`/`rawRes` objects for cookie handling.
|
|
206
|
+
3. **Process the Request**: Call `engine.processRequest(requestContext)`.
|
|
207
|
+
4. **Handle the Decision**: The engine returns a decision object (`{ action: 'challenge' | 'redirect' | 'next', ... }`). You are responsible for implementing the corresponding HTTP response.
|
|
208
|
+
|
|
209
|
+
**Example with native Node.js `http` server:**
|
|
210
|
+
|
|
211
|
+
```javascript
|
|
212
|
+
import http from 'http';
|
|
213
|
+
import { __internal } from './fingerprint.js'; // Adjust path
|
|
214
|
+
|
|
215
|
+
const { FingerprintEngine } = __internal;
|
|
216
|
+
const securityConfig = { /* ... your config ... */ };
|
|
217
|
+
const engine = new FingerprintEngine(securityConfig);
|
|
218
|
+
|
|
219
|
+
const server = http.createServer(async (req, res) => {
|
|
220
|
+
// 1. Manually build the context
|
|
221
|
+
const requestContext = {
|
|
222
|
+
clientIp: req.socket.remoteAddress,
|
|
223
|
+
path: req.url.split('?')[0],
|
|
224
|
+
cookies: {}, // Parse cookies from req.headers.cookie
|
|
225
|
+
query: {}, // Parse query string from req.url
|
|
226
|
+
headers: req.headers,
|
|
227
|
+
isStatic: /\.(js|css|png)$/.test(req.url),
|
|
228
|
+
rawReq: req, // Pass the raw request
|
|
229
|
+
rawRes: res, // Pass the raw response for cookie setting
|
|
230
|
+
};
|
|
231
|
+
|
|
232
|
+
// 2. Process and get a decision
|
|
233
|
+
const decision = await engine.processRequest(requestContext);
|
|
234
|
+
|
|
235
|
+
// The decision object now contains the score and the raw suspicion vector.
|
|
236
|
+
// You can use it for logging or custom logic.
|
|
237
|
+
console.log(`Request from ${requestContext.clientIp} processed with score: ${decision.score}`);
|
|
238
|
+
|
|
239
|
+
// 3. Act on the decision
|
|
240
|
+
if (decision.action === 'challenge') {
|
|
241
|
+
res.writeHead(decision.status, { 'Content-Type': 'text/html' });
|
|
242
|
+
res.end(decision.body);
|
|
243
|
+
} else if (decision.action === 'redirect') {
|
|
244
|
+
// The engine sets the cookie directly on `res` via `rawRes`
|
|
245
|
+
res.writeHead(302, { 'Location': decision.path });
|
|
246
|
+
res.end();
|
|
247
|
+
} else { // 'next'
|
|
248
|
+
res.writeHead(200, { 'Content-Type': 'text/plain' });
|
|
249
|
+
res.end('Welcome to the protected page!');
|
|
250
|
+
}
|
|
251
|
+
});
|
|
252
|
+
|
|
253
|
+
server.listen(3000, () => console.log('Server with manual fingerprint engine started on port 3000'));
|
|
254
|
+
```
|
|
255
|
+
|
|
256
|
+
### Automatic Threshold Tuning
|
|
257
|
+
|
|
258
|
+
Manually setting the `low`, `medium`, and `high` thresholds can be challenging. This library provides a powerful tool to automate this process based on real traffic data. It uses a genetic algorithm to find the optimal thresholds that maximize bot detection while minimizing the impact on legitimate users.
|
|
259
|
+
|
|
260
|
+
#### How to use it:
|
|
261
|
+
|
|
262
|
+
1. **Enable Logging**: The auto-tuner needs data. You must provide a `logger` function in your security configuration. This function will be called for significant events (`challenge_issued`, `challenge_solved`, etc.).
|
|
263
|
+
|
|
264
|
+
2. **Start the Tuner**: Call `startThresholdAutoTuning` with your live security configuration and the array where logs are stored.
|
|
265
|
+
|
|
266
|
+
```javascript
|
|
267
|
+
import { powMiddleware, startThresholdAutoTuning } from './fingerprint.js';
|
|
268
|
+
|
|
269
|
+
// Array to store traffic analysis data. In a real application, this could be
|
|
270
|
+
// a more robust logging system.
|
|
271
|
+
const trafficData = [];
|
|
272
|
+
|
|
273
|
+
const securityConfig = {
|
|
274
|
+
weights: { /* ... your weights ... */ },
|
|
275
|
+
thresholds: {
|
|
276
|
+
low: 20, // Initial values, will be optimized
|
|
277
|
+
medium: 45,
|
|
278
|
+
high: 75
|
|
279
|
+
},
|
|
280
|
+
// The logger is required for auto-tuning
|
|
281
|
+
logger: (log) => trafficData.push(log)
|
|
282
|
+
};
|
|
283
|
+
|
|
284
|
+
// Start the background optimization process.
|
|
285
|
+
// The `securityConfig.thresholds` object will be mutated with optimized values.
|
|
286
|
+
startThresholdAutoTuning({
|
|
287
|
+
securityConfig: securityConfig, // The config object to be updated
|
|
288
|
+
trafficData: trafficData, // The data source for the algorithm
|
|
289
|
+
interval: 1800000, // Optimization cycle every 30 minutes
|
|
290
|
+
minDataPoints: 200 // Minimum requests before starting optimization
|
|
291
|
+
});
|
|
292
|
+
|
|
293
|
+
const powMiddlewareInstance = powMiddleware(securityConfig);
|
|
294
|
+
app.use(powMiddlewareInstance);
|
|
295
|
+
```
|
|
165
296
|
|
|
166
297
|
---
|
|
167
298
|
|