@chdlabs/my-shield 0.1.3 → 0.3.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 +245 -113
- package/models/default-weights.json +682 -0
- package/package.json +19 -4
- package/src/admin/otpGate.js +370 -0
- package/src/biometrics/index.js +10 -0
- package/src/biometrics/minirocket.js +213 -0
- package/src/biometrics/ridge.js +144 -0
- package/src/client/collector.js +136 -0
- package/src/client/index.js +68 -0
- package/src/client/shieldClient.js +118 -0
- package/src/core/ipResolver.js +225 -0
- package/src/index.d.ts +181 -0
- package/src/index.js +138 -5
- package/src/middleware/behavioralGuard.js +304 -0
- package/src/middleware/honeypot.js +6 -4
- package/src/middleware/notFoundCollector.js +6 -4
- package/src/middleware/shieldGuard.js +3 -0
- package/src/storage/BaseAdapter.js +58 -0
- package/src/storage/CallbackStorageAdapter.js +69 -0
- package/src/storage/FileStorageAdapter.js +174 -0
- package/src/storage/MemoryAdapter.js +56 -0
- package/src/storage/index.js +53 -0
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# 🛡️ @chdlabs/my-shield
|
|
2
2
|
|
|
3
|
-
> High-performance, zero-latency security shield middleware for Express. Built with pure JavaScript probabilistic data structures (Bloom filter + LRU cache), honeypot traps, and
|
|
3
|
+
> High-performance, zero-latency security shield middleware for Express. Built with pure JavaScript probabilistic data structures (Bloom filter + LRU cache), honeypot traps, MiniRocket behavioral biometrics, pluggable threat persistence, and container-native enterprise IP resolution.
|
|
4
4
|
|
|
5
5
|
[](https://opensource.org/licenses/MIT)
|
|
6
6
|
[](https://nodejs.org/)
|
|
@@ -12,12 +12,16 @@
|
|
|
12
12
|
|
|
13
13
|
- **Sub-0.1ms Clean Pass**: Legitimate requests pass through the L1 Bloom filter in `< 0.1ms`.
|
|
14
14
|
- **Zero False-Positive Blocks**: Two-tiered L1 caching (Bloom filter + LRU confirmed threat cache) guarantees statistical Bloom false positives are never blocked.
|
|
15
|
-
- **
|
|
15
|
+
- **Enterprise Client IP Resolution (`IPResolver`)**: Production-tested parsing for Cloudflare (`CF-Connecting-IP`), Akamai (`True-Client-IP`), Nginx (`X-Real-IP`), and multi-hop HAProxy chains (`X-Forwarded-For`) with IPv6-mapped IPv4 normalization and port stripping.
|
|
16
|
+
- **Docker & Gateway Protection**: Docker bridge gateways (`172.17.0.1`) and loopback addresses (`127.0.0.1`, `::1`) are permanently immune from bans, preventing accidental container isolation.
|
|
17
|
+
- **Mission-Critical Route Protection**: First-class `shield.strictBotGuard()` to protect `/login`, `/checkout`, and `/register` against credential stuffing, carding (card testing), and automated account spam.
|
|
18
|
+
- **Dual-Mode Behavioral Biometrics**: Calibrated Ridge decision boundary (`0.50`) supporting non-blocking `'audit'` mode (for searches and queries) alongside strict `'block'` mode (for auth and checkout).
|
|
19
|
+
- **First-Party Client SDK (`@chdlabs/my-shield/client`)**: Full-featured `ShieldClient` with pointer freshness guards ($t_{\text{idle}} \le 2.5\text{s}$), buffer flushes on consumption, and automatic `fetch`/`Axios` interceptors.
|
|
20
|
+
- **Pluggable Threat Persistence**: Zero-overhead in-memory adapter, local JSON file persistence (`FileStorageAdapter`), or universal database hooks (`PostgreSQL`, `MySQL`, `Redis`, `Prisma`).
|
|
21
|
+
- **Built-in Admin OTP / MFA Challenge Gate**: Standalone, dark-mode OTP verification portal (`shield.otpGate()`) with timing-safe HMAC validation and brute-force protection.
|
|
22
|
+
- **Silent Honeypot Decoys**: Static trap routes (`/.env`, `/wp-login.php`, `/.git/config`, etc.) return a silent `200 OK` decoy response and instantly blacklist scanners.
|
|
16
23
|
- **404 Anomaly & Intelligence**: Captures every 404 URL probe, bans scanners probing $\ge 5$ unique paths in 60s, and auto-promotes frequent attack targets to active honeypots.
|
|
17
|
-
- **
|
|
18
|
-
- **Self-Contained Security Dashboard**: Built-in dark-themed telemetry console with real-time Server-Sent Events (SSE), charts, and active block management.
|
|
19
|
-
- **Safe By Default**: Admin dashboard is disabled by default (`admin.enabled: false`) with dedicated RBAC integration to prevent unauthorized telemetry access.
|
|
20
|
-
- **Zero Production Dependencies**: Pure JavaScript built exclusively with Node.js built-ins (`crypto`, `fs`, `path`, `events`).
|
|
24
|
+
- **Zero Production Dependencies**: Pure JavaScript built exclusively with Node.js built-ins (`crypto`, `fs`, `path`, `events`, `net`).
|
|
21
25
|
|
|
22
26
|
---
|
|
23
27
|
|
|
@@ -28,9 +32,14 @@ Incoming Request
|
|
|
28
32
|
│
|
|
29
33
|
▼
|
|
30
34
|
┌───────────────────────────────────────┐
|
|
35
|
+
│ Enterprise IPResolver │
|
|
36
|
+
│ Cloudflare / Nginx / HAProxy / Direct │
|
|
37
|
+
│ Gateway Protection (172.17.0.1 Immune)│
|
|
38
|
+
└──────────────────┬────────────────────┘
|
|
39
|
+
▼
|
|
40
|
+
┌───────────────────────────────────────┐
|
|
31
41
|
│ L1 Shield Guard │
|
|
32
|
-
│
|
|
33
|
-
│ 1. IP Whitelist? ──▶ Pass │
|
|
42
|
+
│ 1. IP Whitelisted? ──▶ Pass │
|
|
34
43
|
│ 2. Bloom Filter? ──▶ False = Pass │
|
|
35
44
|
│ 3. LRU Cache? ──▶ Found = Block │
|
|
36
45
|
└──────────────────┬────────────────────┘
|
|
@@ -42,13 +51,20 @@ Incoming Request
|
|
|
42
51
|
└──────────────────┬────────────────────┘
|
|
43
52
|
▼
|
|
44
53
|
┌───────────────────────────────────────┐
|
|
54
|
+
│ Route-Level Bot Defenses (Optional) │
|
|
55
|
+
│ • strictBotGuard() on /login & /pay │
|
|
56
|
+
│ • behavioralGuard({ mode: 'audit' }) │
|
|
57
|
+
│ • costThrottle() on high-cost routes │
|
|
58
|
+
└──────────────────┬────────────────────┘
|
|
59
|
+
▼
|
|
60
|
+
┌───────────────────────────────────────┐
|
|
45
61
|
│ Application Routes (Your API) │
|
|
46
62
|
└──────────────────┬────────────────────┘
|
|
47
63
|
▼
|
|
48
64
|
┌───────────────────────────────────────┐
|
|
49
65
|
│ 404 Intelligence Collector │
|
|
50
66
|
│ ≥ 5 unique 404s/60s? ──▶ Ban IP │
|
|
51
|
-
│
|
|
67
|
+
│ Auto-promotes frequent targets │
|
|
52
68
|
└───────────────────────────────────────┘
|
|
53
69
|
```
|
|
54
70
|
|
|
@@ -72,63 +88,40 @@ const app = express();
|
|
|
72
88
|
|
|
73
89
|
const shield = createShield({
|
|
74
90
|
whitelist: ['127.0.0.1', '::1'],
|
|
91
|
+
trustProxy: true, // Auto-resolve Cloudflare, Nginx, or HAProxy client IPs
|
|
92
|
+
protectGateways: true, // Prevents 172.17.0.1 & loopbacks from ever being banned
|
|
93
|
+
storage: 'file', // Persist bans to ./data/shield/blocked_ips.json across restarts
|
|
75
94
|
admin: {
|
|
76
|
-
enabled: true // Set to true to activate dashboard
|
|
77
|
-
},
|
|
78
|
-
bloom: {
|
|
79
|
-
expectedItems: 100_000,
|
|
80
|
-
falsePositiveRate: 0.01
|
|
81
|
-
},
|
|
82
|
-
lru: {
|
|
83
|
-
maxSize: 10_000,
|
|
84
|
-
ttlMs: 5 * 60 * 1000 // 5 minutes
|
|
85
|
-
},
|
|
86
|
-
honeypot: {
|
|
87
|
-
enabled: true,
|
|
88
|
-
delayMs: 500
|
|
89
|
-
},
|
|
90
|
-
intelligence: {
|
|
91
|
-
anomalyThreshold: 5, // 5 unique 404s in 60s -> BAN
|
|
92
|
-
anomalyWindowMs: 60_000,
|
|
93
|
-
captureAll: true,
|
|
94
|
-
filePersistence: {
|
|
95
|
-
enabled: true,
|
|
96
|
-
directory: './data/shield',
|
|
97
|
-
filename: '404_intelligence.json',
|
|
98
|
-
intervalMs: 60 * 60 * 1000, // 1 hour save interval
|
|
99
|
-
loadOnStartup: true
|
|
100
|
-
},
|
|
101
|
-
autoPromote: {
|
|
102
|
-
enabled: true,
|
|
103
|
-
minHits: 50,
|
|
104
|
-
minUniqueIPs: 10
|
|
105
|
-
}
|
|
95
|
+
enabled: true // Set to true to activate dashboard (Default: false)
|
|
106
96
|
}
|
|
107
97
|
});
|
|
108
98
|
|
|
99
|
+
// Hydrate persisted threats into Bloom and LRU on application startup
|
|
100
|
+
await shield.init();
|
|
101
|
+
|
|
109
102
|
// 1. Mount main defense guard (FIRST middleware after parsing)
|
|
110
103
|
app.use(shield.middleware());
|
|
111
104
|
|
|
112
105
|
// 2. Mount honeypot trap router (BEFORE application routes)
|
|
113
106
|
app.use(shield.honeypot());
|
|
114
107
|
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
app.get('/api/data', (req, res) => res.json({ status: 'ok' }));
|
|
118
|
-
// -----------------> ADD IN HERE ALL YOUR APPLICATION ROUTES <----------------//
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
108
|
+
// 3. Application Routes
|
|
109
|
+
app.get('/api/catalog', (req, res) => res.json({ items: [] }));
|
|
122
110
|
|
|
123
111
|
// 4. Mount 404 intelligence collector (AFTER application routes)
|
|
124
112
|
app.use(shield.notFoundCollector());
|
|
125
113
|
|
|
126
|
-
// 5.
|
|
127
|
-
app.use('/admin/shield', shield.
|
|
128
|
-
|
|
114
|
+
// 5. Standalone OTP Protected Admin Console
|
|
115
|
+
app.use('/admin/shield', shield.otpGate({
|
|
116
|
+
adminEmail: 'security@company.com',
|
|
117
|
+
async sendOtp(email, code) {
|
|
118
|
+
// Send OTP via your email service (Nodemailer, SendGrid, etc.)
|
|
119
|
+
console.log(`[OTP] Sent code ${code} to ${email}`);
|
|
120
|
+
}
|
|
129
121
|
}));
|
|
122
|
+
app.use('/admin/shield', shield.adminRouter());
|
|
130
123
|
|
|
131
|
-
// Graceful shutdown: flushes
|
|
124
|
+
// Graceful shutdown: flushes intelligence and threat persistence to disk
|
|
132
125
|
process.on('SIGTERM', async () => {
|
|
133
126
|
await shield.shutdown();
|
|
134
127
|
process.exit(0);
|
|
@@ -137,74 +130,217 @@ process.on('SIGTERM', async () => {
|
|
|
137
130
|
|
|
138
131
|
---
|
|
139
132
|
|
|
140
|
-
##
|
|
133
|
+
## 🛡️ Protecting Mission-Critical Routes (`/login`, `/checkout`, `/register`) From Bots
|
|
141
134
|
|
|
142
|
-
|
|
135
|
+
Automated bots attack high-value mutation endpoints in three distinct ways:
|
|
136
|
+
1. **Credential Stuffing & Brute Force (`/login`)**: Trying thousands of leaked email/password combinations.
|
|
137
|
+
2. **Card Testing / Carding (`/checkout`)**: Testing stolen credit card numbers, which triggers costly payment gateway decline fees and chargebacks.
|
|
138
|
+
3. **Account Farming (`/register`)**: Creating fake accounts for spamming or exploiting promotional credits.
|
|
139
|
+
|
|
140
|
+
### 1. Backend Route Protection (`shield.strictBotGuard()`)
|
|
141
|
+
|
|
142
|
+
Mount `shield.strictBotGuard()` directly on your sensitive POST endpoints. It verifies behavioral mouse dynamics, rejects synthetic or automated bot vectors with `403 Forbidden`, and **instantly auto-bans the attacker's IP**:
|
|
143
143
|
|
|
144
|
-
### 1. Enabling the Dashboard
|
|
145
|
-
By default, the admin dashboard is **disabled** (`admin.enabled: false`). If mounted while disabled, `/admin/shield` safely returns `404 Not Found`. To enable it, explicitly configure:
|
|
146
144
|
```javascript
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
145
|
+
import express from 'express';
|
|
146
|
+
import { createShield } from '@chdlabs/my-shield';
|
|
147
|
+
|
|
148
|
+
const app = express();
|
|
149
|
+
app.use(express.json());
|
|
150
|
+
|
|
151
|
+
const shield = createShield({ trustProxy: true });
|
|
152
|
+
await shield.init();
|
|
153
|
+
|
|
154
|
+
// Global ingress guard
|
|
155
|
+
app.use(shield.middleware());
|
|
156
|
+
|
|
157
|
+
// ── Protect High-Risk Mutations ──
|
|
158
|
+
|
|
159
|
+
// Protect Login against credential stuffing
|
|
160
|
+
app.post('/api/auth/login', shield.strictBotGuard(), (req, res) => {
|
|
161
|
+
// Only authentic human users reach this handler!
|
|
162
|
+
res.json({ success: true, token: 'session_token' });
|
|
151
163
|
});
|
|
152
|
-
```
|
|
153
164
|
|
|
154
|
-
|
|
155
|
-
|
|
165
|
+
// Protect User Registration against fake accounts
|
|
166
|
+
app.post('/api/auth/register', shield.strictBotGuard(), (req, res) => {
|
|
167
|
+
res.json({ success: true, user: req.body.email });
|
|
168
|
+
});
|
|
156
169
|
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
170
|
+
// Protect Checkout: Combine Strict Bot Defense with Cost Throttling
|
|
171
|
+
app.post('/api/checkout',
|
|
172
|
+
shield.strictBotGuard(),
|
|
173
|
+
shield.costThrottle({ weight: 5, maxCost: 50, windowMs: 60000 }),
|
|
174
|
+
(req, res) => {
|
|
175
|
+
res.json({ success: true, orderId: 'ord_123' });
|
|
176
|
+
}
|
|
177
|
+
);
|
|
178
|
+
|
|
179
|
+
// ── Protect High-Volume Reads with Audit Mode ──
|
|
180
|
+
// Does not reject searches, but logs kinematics and attaches X-Shield-Verdict header
|
|
181
|
+
app.get('/api/search', shield.behavioralGuard({ mode: 'audit' }), (req, res) => {
|
|
182
|
+
res.json({ results: [] });
|
|
183
|
+
});
|
|
162
184
|
```
|
|
163
185
|
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
3. **Graceful UI Feedback**: If a non-admin or unauthenticated visitor accesses the page, the dashboard renders an informative warning banner rather than a raw, broken JSON error.
|
|
186
|
+
### 2. Frontend Client Integration (`ShieldClient`)
|
|
187
|
+
|
|
188
|
+
Install `@chdlabs/my-shield` in your frontend application (or import `./client`).
|
|
168
189
|
|
|
169
|
-
|
|
190
|
+
The client captures human pointer micro-movements as the user fills out the form and clicks "Submit" or "Pay Now". When `instrumentFetch()` is enabled, it automatically attaches the `x-shield-telemetry` header to your API requests:
|
|
170
191
|
|
|
171
192
|
```javascript
|
|
172
|
-
//
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
193
|
+
// client/index.js (React, Vue, or Vanilla JS)
|
|
194
|
+
import { ShieldClient } from '@chdlabs/my-shield/client';
|
|
195
|
+
|
|
196
|
+
// 1. Initialize client SDK
|
|
197
|
+
const shieldClient = new ShieldClient({
|
|
198
|
+
sampleIntervalMs: 10,
|
|
199
|
+
windowLength: 128,
|
|
200
|
+
minPoints: 15, // Requires at least 15 raw events to prevent flatlines
|
|
201
|
+
maxIdleMs: 2500, // Freshness Guard: Discards trajectory if pointer idle > 2.5s
|
|
202
|
+
flushOnConsumption: true, // Prevents replaying identical trajectories
|
|
203
|
+
autoInjectTripwire: true // Injects invisible honeypot decoy trap into DOM
|
|
204
|
+
});
|
|
178
205
|
|
|
179
|
-
|
|
206
|
+
// 2. Start recording kinematics
|
|
207
|
+
shieldClient.start();
|
|
208
|
+
|
|
209
|
+
// 3. Transparently instrument fetch (or Axios)
|
|
210
|
+
shieldClient.instrumentFetch({
|
|
211
|
+
headerName: 'x-shield-telemetry',
|
|
212
|
+
endpoints: ['/api/auth/*', '/api/checkout*']
|
|
213
|
+
});
|
|
214
|
+
|
|
215
|
+
// Now standard fetch calls automatically include kinematics telemetry!
|
|
216
|
+
async function submitLogin(email, password) {
|
|
180
217
|
try {
|
|
181
|
-
const
|
|
182
|
-
|
|
183
|
-
|
|
218
|
+
const res = await fetch('/api/auth/login', {
|
|
219
|
+
method: 'POST',
|
|
220
|
+
headers: { 'Content-Type': 'application/json' },
|
|
221
|
+
body: JSON.stringify({ email, password })
|
|
222
|
+
});
|
|
223
|
+
|
|
224
|
+
if (res.status === 403) {
|
|
225
|
+
const err = await res.json();
|
|
226
|
+
if (err.shieldCode === 'AUTOMATED_KINEMATICS_DETECTED') {
|
|
227
|
+
alert('Bot behavior detected. Please interact with the page normally.');
|
|
228
|
+
return;
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
const data = await res.json();
|
|
233
|
+
console.log('Login successful:', data);
|
|
184
234
|
} catch (err) {
|
|
185
|
-
|
|
235
|
+
console.error('Request failed:', err);
|
|
186
236
|
}
|
|
187
237
|
}
|
|
238
|
+
```
|
|
239
|
+
|
|
240
|
+
---
|
|
241
|
+
|
|
242
|
+
## 🌐 Enterprise Reverse Proxies & Gateway Protection
|
|
243
|
+
|
|
244
|
+
When deploying in Docker, Kubernetes, AWS, or behind Cloudflare, standard `req.ip` lookups fail or ban internal container gateways. `IPResolver` resolves this natively:
|
|
245
|
+
|
|
246
|
+
```javascript
|
|
247
|
+
const shield = createShield({
|
|
248
|
+
// Trust proxy headers: true | false | 'loopback,linklocal,uniquelocal'
|
|
249
|
+
trustProxy: true,
|
|
250
|
+
|
|
251
|
+
// Permanently protects Docker bridge (172.17.0.1) & loopbacks from bans
|
|
252
|
+
protectGateways: true,
|
|
253
|
+
|
|
254
|
+
// Optional custom subnets to shield from accidental bans
|
|
255
|
+
protectedSubnets: ['10.0.0.1', '192.168.1.1']
|
|
256
|
+
});
|
|
257
|
+
```
|
|
258
|
+
|
|
259
|
+
### Supported Proxy Headers (in priority order):
|
|
260
|
+
1. `cf-connecting-ip` (Cloudflare)
|
|
261
|
+
2. `true-client-ip` (Akamai & Cloudflare Enterprise)
|
|
262
|
+
3. `x-real-ip` (Nginx `proxy_set_header X-Real-IP $remote_addr`)
|
|
263
|
+
4. `x-client-ip`
|
|
264
|
+
5. `x-forwarded-for` (Multi-hop chains are parsed to find the first non-internal public IP)
|
|
265
|
+
|
|
266
|
+
If `trustProxy: false` is set (direct internet exposure without reverse proxy), all proxy headers are ignored to prevent header spoofing injection attacks.
|
|
267
|
+
|
|
268
|
+
---
|
|
269
|
+
|
|
270
|
+
## 💾 Pluggable Threat Persistence
|
|
188
271
|
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
272
|
+
In containerized or serverless deployments, in-memory caches reset on every restart. Shield supports pluggable persistence adapters to maintain active threat bans:
|
|
273
|
+
|
|
274
|
+
### 1. Single-Container File Persistence (Zero External Dependencies)
|
|
275
|
+
```javascript
|
|
276
|
+
const shield = createShield({
|
|
277
|
+
storage: {
|
|
278
|
+
type: 'file',
|
|
279
|
+
directory: './data/shield',
|
|
280
|
+
filename: 'blocked_ips.json',
|
|
281
|
+
debounceMs: 500 // Debounced atomic writes
|
|
194
282
|
}
|
|
195
|
-
|
|
196
|
-
|
|
283
|
+
});
|
|
284
|
+
await shield.init();
|
|
285
|
+
```
|
|
197
286
|
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
287
|
+
### 2. Database & Cache Persistence (PostgreSQL, Redis, MongoDB, Prisma)
|
|
288
|
+
Plug any database into Shield with three async callbacks:
|
|
289
|
+
|
|
290
|
+
```javascript
|
|
291
|
+
const shield = createShield({
|
|
292
|
+
storage: {
|
|
293
|
+
async onLoad() {
|
|
294
|
+
// Return active unexpired blocked IPs on boot
|
|
295
|
+
const { rows } = await db.query(
|
|
296
|
+
'SELECT ip, reason, metadata, expires_at AS "expiresAt" FROM blocked_threats WHERE expires_at IS NULL OR expires_at > NOW()'
|
|
297
|
+
);
|
|
298
|
+
return rows;
|
|
299
|
+
},
|
|
300
|
+
async onBlock(ip, reason, metadata, expiresAt) {
|
|
301
|
+
// Save or update blocked threat
|
|
302
|
+
await db.query(
|
|
303
|
+
`INSERT INTO blocked_threats (ip, reason, metadata, expires_at)
|
|
304
|
+
VALUES ($1, $2, $3, $4)
|
|
305
|
+
ON CONFLICT (ip) DO UPDATE SET reason = $2, metadata = $3, expires_at = $4`,
|
|
306
|
+
[ip, reason, JSON.stringify(metadata), expiresAt ? new Date(expiresAt) : null]
|
|
307
|
+
);
|
|
308
|
+
},
|
|
309
|
+
async onUnblock(ip) {
|
|
310
|
+
await db.query('DELETE FROM blocked_threats WHERE ip = $1', [ip]);
|
|
311
|
+
}
|
|
312
|
+
}
|
|
313
|
+
});
|
|
314
|
+
|
|
315
|
+
// Restores unexpired database threats into Bloom filter and LRU cache
|
|
316
|
+
await shield.init();
|
|
202
317
|
```
|
|
203
318
|
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
319
|
+
---
|
|
320
|
+
|
|
321
|
+
## 🔑 Built-in Admin OTP / MFA Challenge Gate
|
|
322
|
+
|
|
323
|
+
Shield includes a standalone, zero-dependency dark-mode OTP challenge gate (`shield.otpGate()`) that protects `/admin/shield` without requiring external login libraries:
|
|
324
|
+
|
|
325
|
+
```javascript
|
|
326
|
+
app.use('/admin/shield', shield.otpGate({
|
|
327
|
+
adminEmail: 'admin@company.com',
|
|
328
|
+
jwtSecret: process.env.SHIELD_SECRET, // Signs session cookies
|
|
329
|
+
codeTtlMs: 5 * 60 * 1000, // Code valid for 5 minutes
|
|
330
|
+
maxAttempts: 5, // Locks out after 5 invalid tries
|
|
331
|
+
async sendOtp(email, code) {
|
|
332
|
+
// Deliver via email or SMS
|
|
333
|
+
await mailer.sendMail({
|
|
334
|
+
to: email,
|
|
335
|
+
subject: 'Shield Admin Passcode',
|
|
336
|
+
text: `Your one-time passcode is: ${code}`
|
|
337
|
+
});
|
|
338
|
+
}
|
|
339
|
+
}));
|
|
340
|
+
|
|
341
|
+
// Mount dashboard router
|
|
342
|
+
app.use('/admin/shield', shield.adminRouter());
|
|
343
|
+
```
|
|
208
344
|
|
|
209
345
|
---
|
|
210
346
|
|
|
@@ -213,25 +349,18 @@ The dashboard client automatically looks for the session token in the following
|
|
|
213
349
|
| Option | Type | Default | Description |
|
|
214
350
|
|---|---|---|---|
|
|
215
351
|
| `whitelist` | `string[]` | `[]` | List of IP addresses that are never blocked |
|
|
216
|
-
| `
|
|
217
|
-
| `
|
|
352
|
+
| `trustProxy` | `boolean\|string` | `'loopback,linklocal,uniquelocal'` | Reverse proxy trust configuration |
|
|
353
|
+
| `protectGateways` | `boolean` | `true` | Protects Docker bridge (`172.17.0.1`) and loopback from bans |
|
|
354
|
+
| `storage` | `string\|Object` | `'memory'` | Storage adapter (`'memory'`, `'file'`, or callback hooks) |
|
|
355
|
+
| `admin.enabled` | `boolean` | `false` | Explicit switch to enable admin dashboard & telemetry |
|
|
356
|
+
| `bloom.expectedItems` | `number` | `100000` | Estimated distinct IPs to hold in Bloom filter |
|
|
218
357
|
| `bloom.falsePositiveRate` | `number` | `0.01` | Target false positive probability (1%) |
|
|
219
|
-
| `bloom.sizeBytes` | `number` | `null` | Explicit bit array size in bytes |
|
|
220
358
|
| `lru.maxSize` | `number` | `10000` | Max entries in confirmed threat cache |
|
|
221
359
|
| `lru.ttlMs` | `number` | `300000` | Threat cache TTL in ms (5 minutes) |
|
|
222
|
-
| `honeypot.enabled` | `boolean` | `true` | Enable/disable honeypot traps |
|
|
360
|
+
| `honeypot.enabled` | `boolean` | `true` | Enable/disable static honeypot traps |
|
|
223
361
|
| `honeypot.delayMs` | `number` | `500` | Delay before sending silent 200 OK decoy |
|
|
224
|
-
| `honeypot.additionalRoutes` | `string[]` | `[]` | Custom trap path patterns |
|
|
225
362
|
| `intelligence.anomalyThreshold` | `number` | `5` | Unique 404s before IP is banned |
|
|
226
363
|
| `intelligence.anomalyWindowMs` | `number` | `60000` | Sliding window in ms (60 seconds) |
|
|
227
|
-
| `intelligence.filePersistence.enabled` | `boolean` | `true` | Enable periodic JSON snapshot saves |
|
|
228
|
-
| `intelligence.filePersistence.directory` | `string` | `'./data/shield'` | Destination directory for JSON snapshot |
|
|
229
|
-
| `intelligence.filePersistence.filename` | `string` | `'404_intelligence.json'` | Single deduplicated filename |
|
|
230
|
-
| `intelligence.filePersistence.intervalMs`| `number` | `3600000` | Snapshot save interval (1 hour) |
|
|
231
|
-
| `intelligence.filePersistence.loadOnStartup` | `boolean` | `true` | Restore intelligence on process start |
|
|
232
|
-
| `intelligence.autoPromote.enabled` | `boolean` | `true` | Auto-promote frequent 404s to traps |
|
|
233
|
-
| `intelligence.autoPromote.minHits` | `number` | `50` | Min 404 hits before promotion |
|
|
234
|
-
| `intelligence.autoPromote.minUniqueIPs` | `number` | `10` | Min distinct IPs probing path |
|
|
235
364
|
|
|
236
365
|
---
|
|
237
366
|
|
|
@@ -242,7 +371,6 @@ The dashboard client automatically looks for the session token in the following
|
|
|
242
371
|
shield.blockIP('198.51.100.42', 'manual_ban', { admin: 'dev1' });
|
|
243
372
|
shield.unblockIP('198.51.100.42');
|
|
244
373
|
shield.allowIP('192.168.1.50'); // Add to whitelist
|
|
245
|
-
shield.removeAllowedIP('192.168.1.50');
|
|
246
374
|
shield.isWhitelisted('192.168.1.50'); // Returns boolean
|
|
247
375
|
|
|
248
376
|
// Telemetry & Metrics
|
|
@@ -250,10 +378,14 @@ shield.getStats(); // Returns aggregate stats, LRU sizes, traps list
|
|
|
250
378
|
shield.getIntelligence(); // Full 404 intelligence snapshot
|
|
251
379
|
shield.getBloomHealth(); // Saturation percentage and estimated FPR
|
|
252
380
|
|
|
253
|
-
// Route-Level
|
|
381
|
+
// Route-Level Bot Defenses
|
|
382
|
+
app.post('/api/auth/login', shield.strictBotGuard(), handler);
|
|
383
|
+
app.get('/api/search', shield.behavioralGuard({ mode: 'audit' }), handler);
|
|
384
|
+
|
|
385
|
+
// Denial-of-Wallet Cost Throttling
|
|
254
386
|
app.post('/api/ai-generate', shield.costThrottle({ weight: 10, maxCost: 100 }), handler);
|
|
255
387
|
|
|
256
|
-
//
|
|
388
|
+
// API Key / Secret Leak Detector
|
|
257
389
|
app.use(shield.keyLeakDetector());
|
|
258
390
|
```
|
|
259
391
|
|