@anonympins/fingerprint 0.1.3 → 0.2.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/README.md CHANGED
@@ -18,14 +18,20 @@ The process unfolds in three steps:
18
18
  * **Header Anomalies**: Missing `User-Agent`, `Accept-Language`, etc.
19
19
  * **Device Behavior**: Rapid fingerprint changes (User-Agent rotation).
20
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).
21
- * **Inconsistency**: A low similarity score between the current fingerprint and the initial one associated with the `device_id` (cookie theft detection).
21
+ * **Inconsistency**: A low similarity score between the current fingerprint and the one initially associated with the `device_id` (cookie theft detection).
22
+ * **Cross-Layer Inconsistency**: Mismatches between client-side data (e.g., OS reported by the browser) and server-side headers (e.g., `User-Agent`).
22
23
  * **Request Patterns**: Repetitive, rapid-fire, or sequential requests typical of scraping bots. The parameters for detecting these patterns (e.g., request velocity, burst detection) are dynamically adjusted by the auto-tuner for optimal performance.
23
24
  * **Honeypot Trap**: Detection of bots that automatically fill hidden form fields or probe for common but unused URL parameters (e.g., `?debug=true`).
24
25
  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:
25
- * **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.
26
+ * **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.
26
27
  * **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.
28
+ * **New Devices**: To increase the cost for bots that simply clear their cookies, new (unseen) devices are systematically presented with a minimal, almost imperceptible challenge on their first visit, even if their suspicion score is low.
27
29
 
28
- Once the challenge is solved, a clearance "ticket" is issued via a secure cookie, exempting the user from new challenges for a set period. For API clients, the challenge is delivered as a `429` JSON response, and the client is expected to solve it and retry the request.
30
+ Once the challenge is solved, a clearance "ticket" is issued via a secure cookie, exempting the user from new challenges. The duration of this ticket is dynamic:
31
+ - **Probationary Ticket**: If the request was moderately suspicious, a very short-lived "probationary" ticket (e.g., 30 seconds) is issued. This forces the client to be re-evaluated quickly, increasing security.
32
+ - **Optimal TTL Ticket**: For less suspicious requests, a genetic algorithm calculates the optimal ticket duration, balancing security (shorter TTL for higher risk) and user experience (longer TTL for lower risk).
33
+
34
+ For API clients, the challenge is delivered as a `404` JSON response, and the client library can automatically solve it and retry the original request.
29
35
 
30
36
  ## Features
31
37
 
@@ -33,7 +39,7 @@ Once the challenge is solved, a clearance "ticket" is issued via a secure cookie
33
39
  - **Secure Ticket System**: Uses HMAC-SHA256 signatures to validate clearances and prevent tampering.
34
40
  - **Pluggable Datastore**: Supports external datastores like Redis for state persistence and scalability across multiple server instances.
35
41
  - **Express.js Middleware**: Easy integration into an Express application with `powMiddleware`. The datastore must support setting a Time-To-Live (TTL) for challenge secrets.
36
- - **Timing Attack Protection**: Uses `crypto.timingSafeEqual` for secure ticket validation.
42
+ - **Timing Attack Protection**: Uses `crypto.timingSafeEqual` for secure validation of tickets and other signatures.
37
43
  - **Bot Whitelisting**: Includes a DNS-based verification mechanism to reliably identify and whitelist legitimate crawlers like Googlebot and Bingbot, preventing them from being challenged. The results are cached for optimal performance.
38
44
  - **Automatic Parameter Tuning**: Includes a genetic algorithm-based optimizer (`startThresholdAutoTuning`) that analyzes real traffic to dynamically adjust not only suspicion thresholds (`low`, `medium`, `high`) but also the parameters for behavioral pattern detection, improving accuracy and reducing false positives over time.
39
45
 
@@ -61,7 +67,7 @@ The `powMiddleware` requires a configuration object defining the weights of susp
61
67
  import express from 'express';
62
68
  import bodyParser from 'body-parser';
63
69
  import cookieParser from 'cookie-parser';
64
- import { powMiddleware, default_whitelist } from './fingerprint.js'; // Adjust the path
70
+ import { powMiddleware, default_whitelist, default_analyzers } from './fingerprint.js'; // Adjust the path
65
71
 
66
72
  const app = express();
67
73
  app.use(cookieParser());
@@ -81,32 +87,27 @@ const securityConfig = {
81
87
  headerAnomalyScore: 0.1, // Penalizes abnormal headers (missing UA, etc.)
82
88
  requestPatternScore: 0.6,// Penalizes bot-like request sequences (scraping, etc.)
83
89
  inconsistencyScore: 0.8, // Strongly penalizes inconsistency between the current and initial fingerprint (stolen cookie)
84
- behaviorScore: 0.7, // Penalizes non-human interactions (no mouse/keyboard activity)
85
- honeypotScore: 1.0 // Strongly penalizes bots filling hidden form fields
90
+ behaviorScore: 0.7, // Penalizes non-human interactions (no mouse/keyboard activity)
91
+ honeypotScore: 1.0, // Strongly penalizes bots filling hidden form fields
92
+ crossLayerInconsistencyScore: 0.4, // Penalizes mismatches between client-side data (e.g., OS) and server-side headers (e.g., User-Agent)
93
+ timeInconsistencyScore: 0.9 // Strongly penalizes large time gaps between client metric collection and server reception (replay attack)
86
94
  },
87
- // A new, non-suspicious device will always have its score adjusted to a minimum of 1, ensuring it receives a minimal, almost imperceptible challenge on its first visit.
88
95
  thresholds: {
89
96
  low: 20, // Score from which a CPU challenge is issued
90
97
  medium: 45, // Score for a more difficult combined CPU/Memory challenge
91
98
  high: 75, // Score for a very difficult challenge
92
- block: 95, // Score above which the request is blocked outright (HTTP 403)
93
- isStaticResource: (req) => req.path.startsWith('/static/'), // Optional: Custom function to identify static resources
94
- isApiRequest: (req) => req.path.startsWith('/api/') || req.headers.accept?.includes('application/json') // Optional: Custom function to identify API requests
99
+ block: 95, // Score above which the request is blocked outright (HTTP 404)
95
100
  },
96
101
  // (Optional) Configure the duration (in milliseconds) for various temporary data.
97
102
  ticketMaxAge: 3600000, // 1 hour. Duration for which a solved challenge ticket is valid.
98
103
  challengeTtl: 300000, // 5 minutes. Time during which a challenge nonce is valid.
99
104
  deviceIdCookieMaxAge: undefined, // By default, it's a session cookie. Set a value in ms for a persistent cookie.
100
105
  challengePagePath: './path/to/your/custom-challenge-page.html', // (Optional) Path to a custom HTML template for the challenge page.
101
- verbose: true, // set to true to log for fingerprint detection output
106
+ verbose: process.env.NODE_ENV !== 'production', // Log detailed info in development, but not in production.
102
107
  patterns: { // (Optional) Initial values for request pattern detection, optimized by auto-tuner if enabled.
103
108
  velocityThreshold: 800, // ms between requests to be considered "fast"
104
109
  burstThreshold: 1500, // ms for identical requests to be a "burst"
105
110
  scrapeThreshold: 1000, // ms for sequential requests to be "scraping"
106
- scrapeBurstWeight: 40, // Additional weight for repeated scraping patterns
107
- sequenceLength: 3, // Length of a request sequence to detect (e.g., A->B->C)
108
- sequenceWeight: 60, // Penalty for repeating a sequence
109
- sequenceMinTimeSpan: 2000, // Sequence min time span
110
111
  historySize: 10, // Number of requests to keep for pattern analysis
111
112
  decayFactor: 0.9, // How quickly the pattern score decays over time
112
113
  inactivityReset: 30000, // ms of inactivity after which the pattern score is reset
@@ -118,27 +119,20 @@ const securityConfig = {
118
119
  // List of URL paths that should never be accessed by a legitimate user.
119
120
  // A request to one of these paths will immediately flag the device as malicious.
120
121
  trapUrls: ['/wp-admin', '/.env', '/admin.php', '/phpmyadmin'], // (Optional)
121
- // Automatically detect common SQL/NoSQL injection and RCE patterns in request values. (Optional, default: true)
122
- detectInjections: true,
123
- // (Optional) Plug in external, more robust analyzers. This allows you to extend the default detection with specialized libraries (e.g., WAFs, anti-spam) or your own custom logic.
122
+ // Automatically detect common injection patterns. Can be a boolean or an array of specific types.
123
+ // - `true`: Enables all available detections (default).
124
+ // - `false`: Disables injection detection.
125
+ // - `['sql', 'rce']`: Enables only SQL injection and Remote Command Execution detection.
126
+ detectInjections: ['sql', 'rce', 'traversal', 'xxe', 'ssti', 'log4shell'], // (Optional, default: true)
127
+ // (Optional) Plug in external analyzers. This allows you to extend detection with specialized libraries or custom logic.
124
128
  // Each function receives an object with all query and body data and should return `true` if a threat is detected.
125
129
  analyzers: [
126
- // Example 1: Using a general-purpose WAF library.
127
- // (npm install generic-waf)
128
- (data) => {
129
- const WAF = require('generic-waf');
130
- const waf = new WAF();
131
- // This WAF expects a string, so we stringify the data to check all values at once.
132
- return waf.isMalicious(JSON.stringify(data));
133
- },
134
- // Example 2: Using a specialized library for XSS detection.
135
- // (npm install xss)
136
- (data) => {
137
- const xss = require('xss');
138
- const originalData = JSON.stringify(data);
139
- // If the sanitized string is different from the original, it means malicious HTML/JS was found and removed.
140
- return xss(originalData) !== originalData;
141
- },
130
+ ...default_analyzers(), // Includes the default XSS analyzer.
131
+
132
+ // Example 2: Enable a powerful WAF with ModSecurity and the OWASP Core Rule Set.
133
+ // Requires `npm install modsecurity-nodejs` and downloading the OWASP CRS rules.
134
+ // modsecurity_analyzer('/path/to/owasp-crs/crs-setup.conf'),
135
+
142
136
  // Example 3: A custom function to detect specific keywords (e.g., for anti-spam).
143
137
  (data) => {
144
138
  const spamKeywords = ['viagra', 'free money', 'crypto pump'];
@@ -165,8 +159,10 @@ const securityConfig = {
165
159
  ...default_whitelist(), // Use the defaults
166
160
  { userAgent: 'MyIndustrySpecificBot', hostnameSuffix: '.my-bot-verifier.com' }, // Add a custom bot
167
161
  ],
168
- // Or, if you only want the defaults:
169
- // whitelist: default_whitelist(),
162
+ // Optional: Custom function to identify static resources
163
+ isStaticResource: (req) => req.path.startsWith('/static/'),
164
+ // Optional: Custom function to identify API requests
165
+ isApiRequest: (req) => req.path.startsWith('/api/') || req.headers.accept?.includes('application/json'),
170
166
  // The logger is required for auto-tuning. It collects data on requests.
171
167
  logger: (log) => trafficData.push(log),
172
168
  // (Optional) Configuration for the automatic threshold and pattern tuning.
@@ -175,6 +171,8 @@ const securityConfig = {
175
171
  interval: 1800000, // Optimization cycle every 30 minutes (in ms).
176
172
  minDataPoints: 200 // Minimum requests before starting an optimization cycle.
177
173
  },
174
+ // Enables problem solving for suspicious activity (configurable in problems.config.json)
175
+ enableUsefulWork: true
178
176
  };
179
177
 
180
178
  // Create an instance of the middleware with your security configuration.
@@ -200,7 +198,39 @@ app.use((req, res, next) => {
200
198
 
201
199
  app.listen(3000, () => console.log('Server started on port 3000'));
202
200
  ```
203
- ### Customizing the Challenge Page
201
+
202
+
203
+ ---
204
+
205
+ ## Advanced Behavioral Analysis
206
+
207
+ The FingerprintEngine includes sophisticated behavioral analysis to detect non-human patterns. This analysis is performed by the `getRequestPatternScore` function, which is a stateful check that looks for repetitive or unnaturally fast requests from a single device.
208
+
209
+ This function uses several configurable parameters to identify suspicious behavior:
210
+
211
+ ### Core Pattern Detection
212
+
213
+ These parameters form the basis of the request pattern analysis:
214
+
215
+ * `velocityThreshold`: (Default: 800ms) Penalizes requests that are too fast to be humanly possible. If the time since the last request from a device is less than this value, the suspicion score increases.
216
+ * `burstThreshold`: (Default: 1500ms) Adds a significant penalty for multiple identical requests (same path and query parameters) occurring in a very short time frame. This is a strong indicator of automated retries or brute-force attacks.
217
+ * `scrapeThreshold`: (Default: 1000ms) Penalizes sequential requests to the same path but with different query parameters. This pattern is typical of scraping bots that iterate through pages or product IDs.
218
+ * `sequenceLength`: (Default: 3) Detects repetitive sequences of requests (e.g., A -> B -> C -> A -> B -> C), which is a common pattern for scripted bots navigating a site.
219
+
220
+ ### Statistical Analysis (Benford's Law)
221
+
222
+ To counter more advanced bots that might try to randomize their request timings, the engine employs statistical analysis based on Benford's Law.
223
+
224
+ * **How it works**: Benford's Law states that in many naturally occurring sets of numbers, the leading digit is more likely to be small. For example, the number 1 appears as the leading digit about 30% of the time, while 9 appears less than 5% of the time. The timings between a human's requests tend to follow this natural distribution, whereas a bot's randomized delays often do not.
225
+
226
+ * `benfordMinSamples`: (Default: 15) The minimum number of request timings to collect before performing a Benford's Law test.
227
+ * `benfordWeight`: (Default: 50) The weight applied to the suspicion score if the distribution of timings significantly deviates from Benford's Law.
228
+
229
+ ### Configuration and Auto-Tuning
230
+
231
+ All these parameters are part of the `patterns` object within the main security configuration and can be fine-tuned.
232
+
233
+ ## Customizing the Challenge Page
204
234
 
205
235
  You can provide your own HTML template for the Proof-of-Work challenge page to maintain a consistent user experience with your brand.
206
236
 
@@ -255,7 +285,7 @@ The main Express middleware. It orchestrates identification, suspicion calculati
255
285
 
256
286
  #### `configureStore(store)`
257
287
  Allows replacing the in-memory store with an external datastore (like Redis) for persistence and scaling.
258
- The library provides ready-to-use adapters for popular datastores like Redis and MongoDB, which automatically handle the Time-To-Live (TTL) required for temporary data like challenge secrets.
288
+ The library provides ready-to-use adapters for popular datastores like **Redis**, **MongoDB**, and any **SQL database** supported by Knex.js. These adapters automatically handle the Time-To-Live (TTL) required for temporary data like challenge secrets.
259
289
 
260
290
  **Redis Example:**
261
291
 
@@ -290,6 +320,32 @@ configureStore(mongoStore);
290
320
  // db.sessions.createIndex({ "expiresAt": 1 }, { expireAfterSeconds: 0 })
291
321
  ```
292
322
 
323
+ **SQL Example (with Knex.js):**
324
+
325
+ ```javascript
326
+ import { configureStore } from './fingerprint.js';
327
+ import { createSqlStore } from './sql-store.js';
328
+ import knex from 'knex';
329
+
330
+ const knexClient = knex({
331
+ client: 'pg', // or 'mysql', 'sqlite3', etc.
332
+ connection: process.env.DATABASE_URL,
333
+ });
334
+
335
+ const sqlStore = createSqlStore(knexClient, 'fingerprint_sessions'); // 'fingerprint_sessions' is the table name
336
+ configureStore(sqlStore);
337
+
338
+ // IMPORTANT: For automatic expiration of challenges and other temporary data to work,
339
+ // your table must have an `expiresAt` column. The store will handle cleanup of expired rows,
340
+ // but you must create the table yourself.
341
+ // Example schema for PostgreSQL:
342
+ // CREATE TABLE fingerprint_sessions (
343
+ // "key" VARCHAR(255) PRIMARY KEY,
344
+ // "value" TEXT NOT NULL,
345
+ // "expiresAt" TIMESTAMPTZ
346
+ // );
347
+ ```
348
+
293
349
  #### `identifyRequest(req, res)`
294
350
  An asynchronous function that returns an identification string for a given request, based on its suspicion level (`device:<id>`, `suspicious_medium:<ip>`, etc.). Useful for integration with a custom rate-limiter.
295
351
 
@@ -476,7 +532,7 @@ Although not exported for direct public use, understanding its role can be usefu
476
532
 
477
533
  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.
478
534
 
479
- **For concrete examples with Koa and Fastify, see our Framework Integration Guide.**
535
+ **For concrete examples with Koa and Fastify, see our [Framework Integration Guide](https://github.com/anonympins/fingerprint/blob/main/INTEGRATION.md).**
480
536
 
481
537
  The engine is a named export from the main module.
482
538
 
@@ -502,7 +558,7 @@ const server = http.createServer(async (req, res) => {
502
558
  clientIp: req.socket.remoteAddress,
503
559
  path: req.url.split('?')[0],
504
560
  cookies: {}, // Parse cookies from req.headers.cookie
505
- query: new URL(req.url, `http://${req.headers.host}`).searchParams,
561
+ query: Object.fromEntries(new URL(req.url, `http://${req.headers.host}`).searchParams),
506
562
  headers: req.headers,
507
563
  rawReq: req, // Pass the raw request
508
564
  rawRes: res, // Pass the raw response for cookie setting