mastercontroller 1.3.8 → 1.3.10
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/.claude/settings.local.json +4 -1
- package/FIXES_APPLIED.md +378 -0
- package/MasterAction.js +10 -263
- package/MasterControl.js +128 -27
- package/MasterRequest.js +6 -0
- package/MasterRouter.js +27 -32
- package/PERFORMANCE_SECURITY_AUDIT.md +677 -0
- package/README.md +117 -43
- package/monitoring/README.md +3112 -0
- package/package.json +1 -1
- package/security/README.md +1805 -0
- package/test-raw-body-preservation.js +128 -0
- package/MasterCors.js.tmp +0 -0
- package/MasterHtml.js +0 -649
- package/MasterPipeline.js.tmp +0 -0
- package/MasterRequest.js.tmp +0 -0
- package/MasterRouter.js.tmp +0 -0
- package/MasterSocket.js.tmp +0 -0
- package/MasterTemp.js.tmp +0 -0
- package/MasterTemplate.js +0 -230
- package/MasterTimeout.js.tmp +0 -0
- package/TemplateOverwrite.js +0 -41
- package/TemplateOverwrite.js.tmp +0 -0
- package/ssr/hydration-client.js +0 -93
- package/ssr/runtime-ssr.cjs +0 -553
- package/ssr/ssr-shims.js +0 -73
|
@@ -0,0 +1,1805 @@
|
|
|
1
|
+
# Security Architecture
|
|
2
|
+
|
|
3
|
+
**MasterController Security Layer** - Comprehensive security middleware and utilities protecting against OWASP Top 10 vulnerabilities.
|
|
4
|
+
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## 📋 Table of Contents
|
|
8
|
+
|
|
9
|
+
1. [Overview](#overview)
|
|
10
|
+
2. [Security Modules](#security-modules)
|
|
11
|
+
3. [Architecture & Integration](#architecture--integration)
|
|
12
|
+
4. [CSRF Protection](#csrf-protection)
|
|
13
|
+
5. [Rate Limiting](#rate-limiting)
|
|
14
|
+
6. [Session Security](#session-security)
|
|
15
|
+
7. [Input Validation](#input-validation)
|
|
16
|
+
8. [XSS Prevention](#xss-prevention)
|
|
17
|
+
9. [Event Handler Validation](#event-handler-validation)
|
|
18
|
+
10. [Content Security Policy](#content-security-policy)
|
|
19
|
+
11. [Configuration Guide](#configuration-guide)
|
|
20
|
+
12. [Best Practices](#best-practices)
|
|
21
|
+
|
|
22
|
+
---
|
|
23
|
+
|
|
24
|
+
## Overview
|
|
25
|
+
|
|
26
|
+
The MasterController security layer provides **defense-in-depth** protection through multiple coordinated modules. Every HTTP request passes through security middleware that validates, sanitizes, and enforces security policies before reaching application code.
|
|
27
|
+
|
|
28
|
+
### Key Features
|
|
29
|
+
|
|
30
|
+
- ✅ **CSRF Protection** - Token-based validation for state-changing requests
|
|
31
|
+
- ✅ **Rate Limiting** - Prevents brute force and DoS attacks
|
|
32
|
+
- ✅ **Session Security** - Hijacking detection via fingerprinting
|
|
33
|
+
- ✅ **Input Validation** - SQL/NoSQL/command injection detection
|
|
34
|
+
- ✅ **XSS Prevention** - Multi-layer HTML sanitization
|
|
35
|
+
- ✅ **Event Validation** - Component @event security
|
|
36
|
+
- ✅ **CSP Management** - Content Security Policy with nonce support
|
|
37
|
+
- ✅ **Security Headers** - X-Frame-Options, HSTS, etc.
|
|
38
|
+
|
|
39
|
+
### Threat Coverage (OWASP Top 10)
|
|
40
|
+
|
|
41
|
+
| Threat | Protection | Module |
|
|
42
|
+
|--------|-----------|---------|
|
|
43
|
+
| **A01: Broken Access Control** | CSRF tokens, session validation | SecurityMiddleware, SessionSecurity |
|
|
44
|
+
| **A02: Cryptographic Failures** | Secure session management | SessionSecurity |
|
|
45
|
+
| **A03: Injection** | SQL/NoSQL/command detection | MasterValidator |
|
|
46
|
+
| **A04: Insecure Design** | Defense-in-depth architecture | All modules |
|
|
47
|
+
| **A05: Security Misconfiguration** | Automatic enforcement | SecurityEnforcement |
|
|
48
|
+
| **A06: Vulnerable Components** | Event handler validation | EventHandlerValidator |
|
|
49
|
+
| **A07: Auth Failures** | Session fingerprinting | SessionSecurity |
|
|
50
|
+
| **A08: Data Integrity** | Input sanitization | MasterSanitizer |
|
|
51
|
+
| **A09: Security Logging** | Comprehensive logging | All modules |
|
|
52
|
+
| **A10: SSRF** | URL validation | MasterValidator |
|
|
53
|
+
|
|
54
|
+
---
|
|
55
|
+
|
|
56
|
+
## Security Modules
|
|
57
|
+
|
|
58
|
+
### 1. SecurityMiddleware.js (15 KB)
|
|
59
|
+
|
|
60
|
+
**Purpose:** Central security orchestration - CSRF, rate limiting, security headers, CORS
|
|
61
|
+
|
|
62
|
+
**Key Features:**
|
|
63
|
+
- CSRF token generation and validation
|
|
64
|
+
- Per-client rate limiting with time windows
|
|
65
|
+
- Security headers (X-Frame-Options, X-Content-Type-Options, etc.)
|
|
66
|
+
- CORS preflight handling
|
|
67
|
+
- Request logging and security auditing
|
|
68
|
+
|
|
69
|
+
**Used By:** MasterRouter.js (called on every HTTP request)
|
|
70
|
+
|
|
71
|
+
---
|
|
72
|
+
|
|
73
|
+
### 2. SessionSecurity.js (14 KB)
|
|
74
|
+
|
|
75
|
+
**Purpose:** Secure session management with hijacking detection
|
|
76
|
+
|
|
77
|
+
**Key Features:**
|
|
78
|
+
- Session fingerprinting (IP + User-Agent)
|
|
79
|
+
- Session regeneration on auth changes
|
|
80
|
+
- Session timeout enforcement
|
|
81
|
+
- Concurrent session detection
|
|
82
|
+
- Session fixation prevention
|
|
83
|
+
|
|
84
|
+
**Used By:** MasterAction.js (session creation/validation)
|
|
85
|
+
|
|
86
|
+
---
|
|
87
|
+
|
|
88
|
+
### 3. MasterValidator.js (13 KB)
|
|
89
|
+
|
|
90
|
+
**Purpose:** Input validation for injection attacks
|
|
91
|
+
|
|
92
|
+
**Key Features:**
|
|
93
|
+
- SQL injection detection (UNION, DROP, etc.)
|
|
94
|
+
- NoSQL injection detection (MongoDB operators)
|
|
95
|
+
- Command injection detection (shell metacharacters)
|
|
96
|
+
- Path traversal detection (../, directory escape)
|
|
97
|
+
- URL validation with protocol whitelist
|
|
98
|
+
- Email validation
|
|
99
|
+
|
|
100
|
+
**Used By:** MasterAction.js (validates user input before processing)
|
|
101
|
+
|
|
102
|
+
---
|
|
103
|
+
|
|
104
|
+
### 4. MasterSanitizer.js (11 KB)
|
|
105
|
+
|
|
106
|
+
**Purpose:** XSS prevention via HTML sanitization
|
|
107
|
+
|
|
108
|
+
**Key Features:**
|
|
109
|
+
- Dangerous tag removal (`<script>`, `<iframe>`, etc.)
|
|
110
|
+
- Event attribute removal (`onclick`, `onerror`, etc.)
|
|
111
|
+
- Protocol sanitization (blocks `javascript:`, `data:`)
|
|
112
|
+
- Whitelist-based approach
|
|
113
|
+
- CSS sanitization (removes `expression()`, `url()`)
|
|
114
|
+
|
|
115
|
+
**Used By:** MasterAction.js (sanitizes HTML before rendering)
|
|
116
|
+
|
|
117
|
+
---
|
|
118
|
+
|
|
119
|
+
### 5. EventHandlerValidator.js (12 KB)
|
|
120
|
+
|
|
121
|
+
**Purpose:** Validates @event bindings in Web Components
|
|
122
|
+
|
|
123
|
+
**Key Features:**
|
|
124
|
+
- Validates event handler syntax
|
|
125
|
+
- Checks for dangerous patterns
|
|
126
|
+
- Prevents arbitrary code execution
|
|
127
|
+
- Component security enforcement
|
|
128
|
+
|
|
129
|
+
**Used By:** runtime-ssr.cjs (SSR component rendering)
|
|
130
|
+
|
|
131
|
+
---
|
|
132
|
+
|
|
133
|
+
### 6. CSPConfig.js (7.8 KB)
|
|
134
|
+
|
|
135
|
+
**Purpose:** Content Security Policy configuration
|
|
136
|
+
|
|
137
|
+
**Key Features:**
|
|
138
|
+
- Development preset (permissive for debugging)
|
|
139
|
+
- Production preset (strict CSP)
|
|
140
|
+
- CDN preset (trusted external sources)
|
|
141
|
+
- Nonce generation for inline scripts
|
|
142
|
+
- CSP header generation
|
|
143
|
+
|
|
144
|
+
**Used By:** SecurityMiddleware.js (CSP header injection)
|
|
145
|
+
|
|
146
|
+
---
|
|
147
|
+
|
|
148
|
+
### 7. SecurityEnforcement.js (6.8 KB)
|
|
149
|
+
|
|
150
|
+
**Purpose:** Automatic security policy enforcement
|
|
151
|
+
|
|
152
|
+
**Key Features:**
|
|
153
|
+
- Auto-enables CSRF in production
|
|
154
|
+
- Auto-enables rate limiting
|
|
155
|
+
- Enforces security headers
|
|
156
|
+
- Validates security configuration
|
|
157
|
+
- Production hardening checks
|
|
158
|
+
|
|
159
|
+
**Used By:** MasterControl.js (initialization time)
|
|
160
|
+
|
|
161
|
+
---
|
|
162
|
+
|
|
163
|
+
## Architecture & Integration
|
|
164
|
+
|
|
165
|
+
### Request Flow
|
|
166
|
+
|
|
167
|
+
```
|
|
168
|
+
HTTP Request
|
|
169
|
+
↓
|
|
170
|
+
[SecurityMiddleware]
|
|
171
|
+
↓
|
|
172
|
+
┌─────────────────────────────────┐
|
|
173
|
+
│ 1. Rate Limiting │ → Block if exceeded
|
|
174
|
+
│ 2. CORS Preflight │ → Return 200 if OPTIONS
|
|
175
|
+
│ 3. Security Headers │ → Inject headers
|
|
176
|
+
│ 4. CSRF Validation (POST/PUT) │ → Block if invalid
|
|
177
|
+
└─────────────────────────────────┘
|
|
178
|
+
↓
|
|
179
|
+
[MasterRouter] → Route Resolution
|
|
180
|
+
↓
|
|
181
|
+
[MasterAction] → Controller Method
|
|
182
|
+
↓
|
|
183
|
+
┌─────────────────────────────────┐
|
|
184
|
+
│ Input Validation │ → MasterValidator
|
|
185
|
+
│ Input Sanitization │ → MasterSanitizer
|
|
186
|
+
│ Session Security │ → SessionSecurity
|
|
187
|
+
└─────────────────────────────────┘
|
|
188
|
+
↓
|
|
189
|
+
[View Rendering]
|
|
190
|
+
↓
|
|
191
|
+
┌─────────────────────────────────┐
|
|
192
|
+
│ Event Handler Validation │ → EventHandlerValidator
|
|
193
|
+
│ CSP Nonce Injection │ → CSPConfig
|
|
194
|
+
└─────────────────────────────────┘
|
|
195
|
+
↓
|
|
196
|
+
HTTP Response
|
|
197
|
+
```
|
|
198
|
+
|
|
199
|
+
### Initialization in MasterControl.js
|
|
200
|
+
|
|
201
|
+
```javascript
|
|
202
|
+
// Line 305-319: Security module registration
|
|
203
|
+
this.internalModules = [
|
|
204
|
+
"MasterTools",
|
|
205
|
+
"MasterAction",
|
|
206
|
+
"MasterRouter",
|
|
207
|
+
"MasterValidator", // ← Input validation
|
|
208
|
+
"MasterSanitizer", // ← XSS prevention
|
|
209
|
+
"SessionSecurity", // ← Session management
|
|
210
|
+
"SecurityMiddleware", // ← Central security
|
|
211
|
+
"SecurityEnforcement", // ← Auto-enforcement
|
|
212
|
+
"CSPConfig", // ← CSP management
|
|
213
|
+
"EventHandlerValidator" // ← Component security
|
|
214
|
+
];
|
|
215
|
+
|
|
216
|
+
// Line 324-336: Module loading
|
|
217
|
+
this.moduleRegistry = {
|
|
218
|
+
validator: './MasterValidator',
|
|
219
|
+
sanitizer: './MasterSanitizer',
|
|
220
|
+
sessionSecurity: './security/SessionSecurity',
|
|
221
|
+
securityMiddleware: './security/SecurityMiddleware',
|
|
222
|
+
securityEnforcement: './security/SecurityEnforcement',
|
|
223
|
+
cspConfig: './security/CSPConfig',
|
|
224
|
+
eventValidator: './security/EventHandlerValidator'
|
|
225
|
+
};
|
|
226
|
+
```
|
|
227
|
+
|
|
228
|
+
### Integration in MasterRouter.js
|
|
229
|
+
|
|
230
|
+
```javascript
|
|
231
|
+
// Line 95-110: Security middleware execution
|
|
232
|
+
routeMiddleware(requestObject, _loadEmit, routeList, middlewareList) {
|
|
233
|
+
// 1. Execute security middleware FIRST
|
|
234
|
+
this._master.securityMiddleware.checkRequest(requestObject);
|
|
235
|
+
|
|
236
|
+
// 2. Rate limiting
|
|
237
|
+
if (this._master.securityMiddleware.isRateLimited(requestObject)) {
|
|
238
|
+
requestObject.response.writeHead(429, {'Content-Type': 'text/plain'});
|
|
239
|
+
requestObject.response.end('Too Many Requests');
|
|
240
|
+
return;
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
// 3. CSRF validation for state-changing methods
|
|
244
|
+
if (['POST', 'PUT', 'DELETE'].includes(requestObject.request.method)) {
|
|
245
|
+
if (!this._master.securityMiddleware.validateCSRF(requestObject)) {
|
|
246
|
+
requestObject.response.writeHead(403, {'Content-Type': 'text/plain'});
|
|
247
|
+
requestObject.response.end('CSRF validation failed');
|
|
248
|
+
return;
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
// 4. Continue to route processing
|
|
253
|
+
this.processRoute(requestObject, _loadEmit, routeList);
|
|
254
|
+
}
|
|
255
|
+
```
|
|
256
|
+
|
|
257
|
+
---
|
|
258
|
+
|
|
259
|
+
## CSRF Protection
|
|
260
|
+
|
|
261
|
+
### Business Logic
|
|
262
|
+
|
|
263
|
+
**CSRF (Cross-Site Request Forgery)** protection prevents attackers from making unauthorized requests on behalf of authenticated users.
|
|
264
|
+
|
|
265
|
+
#### How It Works
|
|
266
|
+
|
|
267
|
+
1. **Token Generation** - Server generates unique token per session
|
|
268
|
+
2. **Token Embedding** - Token embedded in forms/AJAX headers
|
|
269
|
+
3. **Token Validation** - Server validates token on state-changing requests
|
|
270
|
+
4. **Token Rotation** - Tokens rotate periodically for security
|
|
271
|
+
|
|
272
|
+
### Workflow
|
|
273
|
+
|
|
274
|
+
```
|
|
275
|
+
User Visits Page
|
|
276
|
+
↓
|
|
277
|
+
[SecurityMiddleware.generateCSRFToken()]
|
|
278
|
+
↓
|
|
279
|
+
Token stored in session: {
|
|
280
|
+
token: "abc123...",
|
|
281
|
+
timestamp: 1706534400000
|
|
282
|
+
}
|
|
283
|
+
↓
|
|
284
|
+
Token embedded in HTML:
|
|
285
|
+
<input type="hidden" name="_csrf" value="abc123...">
|
|
286
|
+
↓
|
|
287
|
+
User Submits Form
|
|
288
|
+
↓
|
|
289
|
+
[SecurityMiddleware.validateCSRF()]
|
|
290
|
+
↓
|
|
291
|
+
✓ Token matches session
|
|
292
|
+
✓ Token not expired (< 1 hour)
|
|
293
|
+
✓ Token used only once
|
|
294
|
+
↓
|
|
295
|
+
Request Processed
|
|
296
|
+
```
|
|
297
|
+
|
|
298
|
+
### Implementation
|
|
299
|
+
|
|
300
|
+
**SecurityMiddleware.js (Lines 45-120)**
|
|
301
|
+
|
|
302
|
+
```javascript
|
|
303
|
+
generateCSRFToken(sessionId) {
|
|
304
|
+
// Generate cryptographically secure token
|
|
305
|
+
const token = crypto.randomBytes(32).toString('hex');
|
|
306
|
+
|
|
307
|
+
// Store in session
|
|
308
|
+
this.csrfTokens.set(sessionId, {
|
|
309
|
+
token: token,
|
|
310
|
+
timestamp: Date.now(),
|
|
311
|
+
used: false
|
|
312
|
+
});
|
|
313
|
+
|
|
314
|
+
return token;
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
validateCSRF(requestObject) {
|
|
318
|
+
const sessionId = requestObject.session.id;
|
|
319
|
+
const submittedToken = requestObject.body._csrf ||
|
|
320
|
+
requestObject.headers['x-csrf-token'];
|
|
321
|
+
|
|
322
|
+
const storedData = this.csrfTokens.get(sessionId);
|
|
323
|
+
|
|
324
|
+
// Validation checks
|
|
325
|
+
if (!storedData) return false;
|
|
326
|
+
if (storedData.token !== submittedToken) return false;
|
|
327
|
+
if (storedData.used) return false; // One-time use
|
|
328
|
+
if (Date.now() - storedData.timestamp > 3600000) return false; // 1 hour expiry
|
|
329
|
+
|
|
330
|
+
// Mark as used
|
|
331
|
+
storedData.used = true;
|
|
332
|
+
|
|
333
|
+
return true;
|
|
334
|
+
}
|
|
335
|
+
```
|
|
336
|
+
|
|
337
|
+
### Usage in Controllers
|
|
338
|
+
|
|
339
|
+
```javascript
|
|
340
|
+
class FormController {
|
|
341
|
+
// Display form with CSRF token
|
|
342
|
+
showForm(obj) {
|
|
343
|
+
const csrfToken = this.generateCSRFToken();
|
|
344
|
+
this.returnView({ csrfToken });
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
// Process form with CSRF validation
|
|
348
|
+
submitForm(obj) {
|
|
349
|
+
// CSRF automatically validated by SecurityMiddleware
|
|
350
|
+
// If we reach here, token was valid
|
|
351
|
+
|
|
352
|
+
const data = this.params;
|
|
353
|
+
// Process form...
|
|
354
|
+
}
|
|
355
|
+
}
|
|
356
|
+
```
|
|
357
|
+
|
|
358
|
+
### HTML Template
|
|
359
|
+
|
|
360
|
+
```html
|
|
361
|
+
<form method="POST" action="/form/submit">
|
|
362
|
+
<!-- CSRF token (auto-validated on submit) -->
|
|
363
|
+
<input type="hidden" name="_csrf" value="${csrfToken}">
|
|
364
|
+
|
|
365
|
+
<input type="text" name="username">
|
|
366
|
+
<button type="submit">Submit</button>
|
|
367
|
+
</form>
|
|
368
|
+
```
|
|
369
|
+
|
|
370
|
+
---
|
|
371
|
+
|
|
372
|
+
## Rate Limiting
|
|
373
|
+
|
|
374
|
+
### Business Logic
|
|
375
|
+
|
|
376
|
+
**Rate limiting** prevents brute force attacks, credential stuffing, and DoS by limiting requests per client per time window.
|
|
377
|
+
|
|
378
|
+
#### Strategy
|
|
379
|
+
|
|
380
|
+
- **Time Window:** 15 minutes (configurable)
|
|
381
|
+
- **Max Requests:** 100 per window (configurable)
|
|
382
|
+
- **Client Identification:** IP address + User-Agent
|
|
383
|
+
- **Storage:** In-memory Map (production: Redis)
|
|
384
|
+
|
|
385
|
+
### Workflow
|
|
386
|
+
|
|
387
|
+
```
|
|
388
|
+
Request arrives
|
|
389
|
+
↓
|
|
390
|
+
[SecurityMiddleware.isRateLimited()]
|
|
391
|
+
↓
|
|
392
|
+
Identify client: IP + User-Agent hash
|
|
393
|
+
↓
|
|
394
|
+
Check rate limit storage:
|
|
395
|
+
{
|
|
396
|
+
"192.168.1.1:Mozilla/5.0": {
|
|
397
|
+
count: 95,
|
|
398
|
+
windowStart: 1706534400000
|
|
399
|
+
}
|
|
400
|
+
}
|
|
401
|
+
↓
|
|
402
|
+
If count < 100:
|
|
403
|
+
Increment count → Allow request
|
|
404
|
+
If count >= 100:
|
|
405
|
+
Block request → Return 429 Too Many Requests
|
|
406
|
+
↓
|
|
407
|
+
If windowStart > 15 minutes ago:
|
|
408
|
+
Reset count → Allow request
|
|
409
|
+
```
|
|
410
|
+
|
|
411
|
+
### Implementation
|
|
412
|
+
|
|
413
|
+
**SecurityMiddleware.js (Lines 125-200)**
|
|
414
|
+
|
|
415
|
+
```javascript
|
|
416
|
+
isRateLimited(requestObject) {
|
|
417
|
+
// Client identification
|
|
418
|
+
const clientId = this._getClientId(requestObject);
|
|
419
|
+
|
|
420
|
+
const now = Date.now();
|
|
421
|
+
const windowMs = 15 * 60 * 1000; // 15 minutes
|
|
422
|
+
const maxRequests = 100;
|
|
423
|
+
|
|
424
|
+
// Get or create client record
|
|
425
|
+
let clientData = this.rateLimitStore.get(clientId);
|
|
426
|
+
|
|
427
|
+
if (!clientData) {
|
|
428
|
+
clientData = { count: 0, windowStart: now };
|
|
429
|
+
this.rateLimitStore.set(clientId, clientData);
|
|
430
|
+
}
|
|
431
|
+
|
|
432
|
+
// Check if window expired
|
|
433
|
+
if (now - clientData.windowStart > windowMs) {
|
|
434
|
+
clientData.count = 0;
|
|
435
|
+
clientData.windowStart = now;
|
|
436
|
+
}
|
|
437
|
+
|
|
438
|
+
// Increment and check
|
|
439
|
+
clientData.count++;
|
|
440
|
+
|
|
441
|
+
if (clientData.count > maxRequests) {
|
|
442
|
+
logger.warn({
|
|
443
|
+
code: 'RATE_LIMIT_EXCEEDED',
|
|
444
|
+
clientId,
|
|
445
|
+
count: clientData.count
|
|
446
|
+
});
|
|
447
|
+
return true; // Rate limited
|
|
448
|
+
}
|
|
449
|
+
|
|
450
|
+
return false; // Allowed
|
|
451
|
+
}
|
|
452
|
+
|
|
453
|
+
_getClientId(requestObject) {
|
|
454
|
+
const ip = requestObject.request.connection.remoteAddress;
|
|
455
|
+
const ua = requestObject.request.headers['user-agent'] || '';
|
|
456
|
+
return `${ip}:${crypto.createHash('md5').update(ua).digest('hex')}`;
|
|
457
|
+
}
|
|
458
|
+
```
|
|
459
|
+
|
|
460
|
+
### Configuration
|
|
461
|
+
|
|
462
|
+
```javascript
|
|
463
|
+
// config/security.js
|
|
464
|
+
module.exports = {
|
|
465
|
+
rateLimit: {
|
|
466
|
+
enabled: true,
|
|
467
|
+
windowMs: 15 * 60 * 1000, // 15 minutes
|
|
468
|
+
maxRequests: 100, // 100 requests per window
|
|
469
|
+
skipSuccessfulRequests: false,
|
|
470
|
+
skipFailedRequests: false,
|
|
471
|
+
whitelist: ['127.0.0.1'] // Exempt IPs
|
|
472
|
+
}
|
|
473
|
+
};
|
|
474
|
+
```
|
|
475
|
+
|
|
476
|
+
---
|
|
477
|
+
|
|
478
|
+
## Session Security
|
|
479
|
+
|
|
480
|
+
### Business Logic
|
|
481
|
+
|
|
482
|
+
**Session security** prevents session hijacking, fixation, and unauthorized access through fingerprinting and validation.
|
|
483
|
+
|
|
484
|
+
#### Security Measures
|
|
485
|
+
|
|
486
|
+
1. **Session Fingerprinting** - Bind session to IP + User-Agent
|
|
487
|
+
2. **Session Regeneration** - New session ID after authentication
|
|
488
|
+
3. **Timeout Enforcement** - Sessions expire after inactivity
|
|
489
|
+
4. **Concurrent Session Detection** - Limit sessions per user
|
|
490
|
+
5. **Secure Cookie Flags** - HttpOnly, Secure, SameSite
|
|
491
|
+
|
|
492
|
+
### Workflow
|
|
493
|
+
|
|
494
|
+
```
|
|
495
|
+
User Logs In
|
|
496
|
+
↓
|
|
497
|
+
[SessionSecurity.createSession()]
|
|
498
|
+
↓
|
|
499
|
+
Generate session ID: crypto.randomBytes(32)
|
|
500
|
+
↓
|
|
501
|
+
Create fingerprint:
|
|
502
|
+
IP: 192.168.1.1
|
|
503
|
+
User-Agent: Mozilla/5.0...
|
|
504
|
+
Hash: sha256(IP + UA)
|
|
505
|
+
↓
|
|
506
|
+
Store session:
|
|
507
|
+
{
|
|
508
|
+
id: "abc123...",
|
|
509
|
+
userId: 42,
|
|
510
|
+
fingerprint: "def456...",
|
|
511
|
+
createdAt: 1706534400000,
|
|
512
|
+
lastActivity: 1706534400000
|
|
513
|
+
}
|
|
514
|
+
↓
|
|
515
|
+
Set secure cookie:
|
|
516
|
+
HttpOnly: true
|
|
517
|
+
Secure: true (HTTPS only)
|
|
518
|
+
SameSite: Strict
|
|
519
|
+
↓
|
|
520
|
+
---
|
|
521
|
+
User Makes Request
|
|
522
|
+
↓
|
|
523
|
+
[SessionSecurity.validateSession()]
|
|
524
|
+
↓
|
|
525
|
+
Check fingerprint:
|
|
526
|
+
Stored: "def456..."
|
|
527
|
+
Current: "def456..."
|
|
528
|
+
✓ Match → Continue
|
|
529
|
+
✗ Mismatch → Destroy session → Redirect to login
|
|
530
|
+
↓
|
|
531
|
+
Check timeout:
|
|
532
|
+
Last activity: 30 minutes ago
|
|
533
|
+
Timeout: 60 minutes
|
|
534
|
+
✓ Active → Update lastActivity
|
|
535
|
+
✗ Expired → Destroy session → Redirect to login
|
|
536
|
+
↓
|
|
537
|
+
Request Processed
|
|
538
|
+
```
|
|
539
|
+
|
|
540
|
+
### Implementation
|
|
541
|
+
|
|
542
|
+
**SessionSecurity.js (Lines 40-250)**
|
|
543
|
+
|
|
544
|
+
```javascript
|
|
545
|
+
createSession(userId, requestObject) {
|
|
546
|
+
// Generate secure session ID
|
|
547
|
+
const sessionId = crypto.randomBytes(32).toString('hex');
|
|
548
|
+
|
|
549
|
+
// Create fingerprint
|
|
550
|
+
const fingerprint = this._createFingerprint(requestObject);
|
|
551
|
+
|
|
552
|
+
// Store session
|
|
553
|
+
const session = {
|
|
554
|
+
id: sessionId,
|
|
555
|
+
userId: userId,
|
|
556
|
+
fingerprint: fingerprint,
|
|
557
|
+
createdAt: Date.now(),
|
|
558
|
+
lastActivity: Date.now(),
|
|
559
|
+
data: {}
|
|
560
|
+
};
|
|
561
|
+
|
|
562
|
+
this.sessions.set(sessionId, session);
|
|
563
|
+
|
|
564
|
+
// Set secure cookie
|
|
565
|
+
requestObject.response.setHeader('Set-Cookie',
|
|
566
|
+
`sessionId=${sessionId}; HttpOnly; Secure; SameSite=Strict; Path=/; Max-Age=3600`
|
|
567
|
+
);
|
|
568
|
+
|
|
569
|
+
logger.info({
|
|
570
|
+
code: 'SESSION_CREATED',
|
|
571
|
+
userId,
|
|
572
|
+
sessionId: sessionId.substring(0, 8) + '...'
|
|
573
|
+
});
|
|
574
|
+
|
|
575
|
+
return sessionId;
|
|
576
|
+
}
|
|
577
|
+
|
|
578
|
+
validateSession(requestObject) {
|
|
579
|
+
const sessionId = this._extractSessionId(requestObject);
|
|
580
|
+
if (!sessionId) return false;
|
|
581
|
+
|
|
582
|
+
const session = this.sessions.get(sessionId);
|
|
583
|
+
if (!session) return false;
|
|
584
|
+
|
|
585
|
+
// Validate fingerprint
|
|
586
|
+
const currentFingerprint = this._createFingerprint(requestObject);
|
|
587
|
+
if (session.fingerprint !== currentFingerprint) {
|
|
588
|
+
logger.warn({
|
|
589
|
+
code: 'SESSION_HIJACK_DETECTED',
|
|
590
|
+
sessionId: sessionId.substring(0, 8) + '...'
|
|
591
|
+
});
|
|
592
|
+
this.destroySession(sessionId);
|
|
593
|
+
return false;
|
|
594
|
+
}
|
|
595
|
+
|
|
596
|
+
// Check timeout (60 minutes)
|
|
597
|
+
const timeout = 60 * 60 * 1000;
|
|
598
|
+
if (Date.now() - session.lastActivity > timeout) {
|
|
599
|
+
logger.info({
|
|
600
|
+
code: 'SESSION_TIMEOUT',
|
|
601
|
+
sessionId: sessionId.substring(0, 8) + '...'
|
|
602
|
+
});
|
|
603
|
+
this.destroySession(sessionId);
|
|
604
|
+
return false;
|
|
605
|
+
}
|
|
606
|
+
|
|
607
|
+
// Update activity
|
|
608
|
+
session.lastActivity = Date.now();
|
|
609
|
+
|
|
610
|
+
return true;
|
|
611
|
+
}
|
|
612
|
+
|
|
613
|
+
_createFingerprint(requestObject) {
|
|
614
|
+
const ip = requestObject.request.connection.remoteAddress;
|
|
615
|
+
const ua = requestObject.request.headers['user-agent'] || '';
|
|
616
|
+
return crypto.createHash('sha256').update(ip + ua).digest('hex');
|
|
617
|
+
}
|
|
618
|
+
|
|
619
|
+
regenerateSession(oldSessionId, requestObject) {
|
|
620
|
+
// Get old session data
|
|
621
|
+
const oldSession = this.sessions.get(oldSessionId);
|
|
622
|
+
if (!oldSession) return null;
|
|
623
|
+
|
|
624
|
+
// Create new session with same data
|
|
625
|
+
const newSessionId = this.createSession(oldSession.userId, requestObject);
|
|
626
|
+
const newSession = this.sessions.get(newSessionId);
|
|
627
|
+
newSession.data = oldSession.data;
|
|
628
|
+
|
|
629
|
+
// Destroy old session
|
|
630
|
+
this.destroySession(oldSessionId);
|
|
631
|
+
|
|
632
|
+
logger.info({
|
|
633
|
+
code: 'SESSION_REGENERATED',
|
|
634
|
+
oldId: oldSessionId.substring(0, 8) + '...',
|
|
635
|
+
newId: newSessionId.substring(0, 8) + '...'
|
|
636
|
+
});
|
|
637
|
+
|
|
638
|
+
return newSessionId;
|
|
639
|
+
}
|
|
640
|
+
```
|
|
641
|
+
|
|
642
|
+
### Usage in Authentication
|
|
643
|
+
|
|
644
|
+
```javascript
|
|
645
|
+
class AuthController {
|
|
646
|
+
async login(obj) {
|
|
647
|
+
const { username, password } = this.params;
|
|
648
|
+
|
|
649
|
+
// Validate credentials
|
|
650
|
+
const user = await User.findByUsername(username);
|
|
651
|
+
if (!user || !user.verifyPassword(password)) {
|
|
652
|
+
return this.returnError(401, 'Invalid credentials');
|
|
653
|
+
}
|
|
654
|
+
|
|
655
|
+
// Create secure session
|
|
656
|
+
const sessionId = this.createSession(user.id, this.__requestObject);
|
|
657
|
+
|
|
658
|
+
// Redirect to dashboard
|
|
659
|
+
this.redirectTo('/dashboard');
|
|
660
|
+
}
|
|
661
|
+
|
|
662
|
+
logout(obj) {
|
|
663
|
+
const sessionId = this._extractSessionId(this.__requestObject);
|
|
664
|
+
this.destroySession(sessionId);
|
|
665
|
+
this.redirectTo('/');
|
|
666
|
+
}
|
|
667
|
+
}
|
|
668
|
+
```
|
|
669
|
+
|
|
670
|
+
---
|
|
671
|
+
|
|
672
|
+
## Input Validation
|
|
673
|
+
|
|
674
|
+
### Business Logic
|
|
675
|
+
|
|
676
|
+
**Input validation** prevents injection attacks by detecting malicious patterns before data reaches the database or OS.
|
|
677
|
+
|
|
678
|
+
#### Validation Types
|
|
679
|
+
|
|
680
|
+
1. **SQL Injection** - Detects SQL keywords (UNION, DROP, etc.)
|
|
681
|
+
2. **NoSQL Injection** - Detects MongoDB operators ($where, $ne, etc.)
|
|
682
|
+
3. **Command Injection** - Detects shell metacharacters (|, &, `, etc.)
|
|
683
|
+
4. **Path Traversal** - Detects directory escape (../, ..\, etc.)
|
|
684
|
+
5. **URL Validation** - Protocol whitelist (http, https only)
|
|
685
|
+
|
|
686
|
+
### Workflow
|
|
687
|
+
|
|
688
|
+
```
|
|
689
|
+
User Input Received
|
|
690
|
+
↓
|
|
691
|
+
[MasterValidator.validateInput()]
|
|
692
|
+
↓
|
|
693
|
+
Check for SQL injection:
|
|
694
|
+
"'; DROP TABLE users; --"
|
|
695
|
+
✗ Contains SQL keywords → REJECT
|
|
696
|
+
↓
|
|
697
|
+
Check for NoSQL injection:
|
|
698
|
+
{"$where": "this.password == 'admin'"}
|
|
699
|
+
✗ Contains NoSQL operators → REJECT
|
|
700
|
+
↓
|
|
701
|
+
Check for command injection:
|
|
702
|
+
"test | rm -rf /"
|
|
703
|
+
✗ Contains shell metacharacters → REJECT
|
|
704
|
+
↓
|
|
705
|
+
Check for path traversal:
|
|
706
|
+
"../../../etc/passwd"
|
|
707
|
+
✗ Contains directory escape → REJECT
|
|
708
|
+
↓
|
|
709
|
+
✓ All checks passed → ALLOW
|
|
710
|
+
```
|
|
711
|
+
|
|
712
|
+
### Implementation
|
|
713
|
+
|
|
714
|
+
**MasterValidator.js (Lines 30-400)**
|
|
715
|
+
|
|
716
|
+
```javascript
|
|
717
|
+
validateInput(input, type = 'string') {
|
|
718
|
+
if (!input) return { valid: true };
|
|
719
|
+
|
|
720
|
+
// SQL injection check
|
|
721
|
+
const sqlCheck = this._checkSQLInjection(input);
|
|
722
|
+
if (!sqlCheck.valid) {
|
|
723
|
+
logger.warn({
|
|
724
|
+
code: 'SQL_INJECTION_DETECTED',
|
|
725
|
+
input: input.substring(0, 100),
|
|
726
|
+
pattern: sqlCheck.pattern
|
|
727
|
+
});
|
|
728
|
+
return sqlCheck;
|
|
729
|
+
}
|
|
730
|
+
|
|
731
|
+
// NoSQL injection check
|
|
732
|
+
const nosqlCheck = this._checkNoSQLInjection(input);
|
|
733
|
+
if (!nosqlCheck.valid) {
|
|
734
|
+
logger.warn({
|
|
735
|
+
code: 'NOSQL_INJECTION_DETECTED',
|
|
736
|
+
input: input.substring(0, 100)
|
|
737
|
+
});
|
|
738
|
+
return nosqlCheck;
|
|
739
|
+
}
|
|
740
|
+
|
|
741
|
+
// Command injection check
|
|
742
|
+
const cmdCheck = this._checkCommandInjection(input);
|
|
743
|
+
if (!cmdCheck.valid) {
|
|
744
|
+
logger.warn({
|
|
745
|
+
code: 'COMMAND_INJECTION_DETECTED',
|
|
746
|
+
input: input.substring(0, 100)
|
|
747
|
+
});
|
|
748
|
+
return cmdCheck;
|
|
749
|
+
}
|
|
750
|
+
|
|
751
|
+
// Path traversal check
|
|
752
|
+
const pathCheck = this._checkPathTraversal(input);
|
|
753
|
+
if (!pathCheck.valid) {
|
|
754
|
+
logger.warn({
|
|
755
|
+
code: 'PATH_TRAVERSAL_DETECTED',
|
|
756
|
+
input: input.substring(0, 100)
|
|
757
|
+
});
|
|
758
|
+
return pathCheck;
|
|
759
|
+
}
|
|
760
|
+
|
|
761
|
+
return { valid: true };
|
|
762
|
+
}
|
|
763
|
+
|
|
764
|
+
_checkSQLInjection(input) {
|
|
765
|
+
const sqlPatterns = [
|
|
766
|
+
/(\bUNION\b.*\bSELECT\b)/i,
|
|
767
|
+
/(\bDROP\b.*\bTABLE\b)/i,
|
|
768
|
+
/(\bINSERT\b.*\bINTO\b)/i,
|
|
769
|
+
/(\bUPDATE\b.*\bSET\b)/i,
|
|
770
|
+
/(\bDELETE\b.*\bFROM\b)/i,
|
|
771
|
+
/(--[^\r\n]*)/,
|
|
772
|
+
/('.*OR.*'.*=.*')/i,
|
|
773
|
+
/;\s*DROP/i,
|
|
774
|
+
/;\s*EXEC/i,
|
|
775
|
+
/xp_cmdshell/i
|
|
776
|
+
];
|
|
777
|
+
|
|
778
|
+
for (const pattern of sqlPatterns) {
|
|
779
|
+
if (pattern.test(input)) {
|
|
780
|
+
return {
|
|
781
|
+
valid: false,
|
|
782
|
+
error: 'SQL injection pattern detected',
|
|
783
|
+
pattern: pattern.toString()
|
|
784
|
+
};
|
|
785
|
+
}
|
|
786
|
+
}
|
|
787
|
+
|
|
788
|
+
return { valid: true };
|
|
789
|
+
}
|
|
790
|
+
|
|
791
|
+
_checkNoSQLInjection(input) {
|
|
792
|
+
// Check for MongoDB operators
|
|
793
|
+
const nosqlPatterns = [
|
|
794
|
+
/\$where/i,
|
|
795
|
+
/\$ne/i,
|
|
796
|
+
/\$gt/i,
|
|
797
|
+
/\$lt/i,
|
|
798
|
+
/\$regex/i,
|
|
799
|
+
/\$in/i,
|
|
800
|
+
/\$nin/i,
|
|
801
|
+
/\$or/i,
|
|
802
|
+
/\$and/i
|
|
803
|
+
];
|
|
804
|
+
|
|
805
|
+
const inputStr = typeof input === 'object' ? JSON.stringify(input) : input;
|
|
806
|
+
|
|
807
|
+
for (const pattern of nosqlPatterns) {
|
|
808
|
+
if (pattern.test(inputStr)) {
|
|
809
|
+
return {
|
|
810
|
+
valid: false,
|
|
811
|
+
error: 'NoSQL injection pattern detected'
|
|
812
|
+
};
|
|
813
|
+
}
|
|
814
|
+
}
|
|
815
|
+
|
|
816
|
+
return { valid: true };
|
|
817
|
+
}
|
|
818
|
+
|
|
819
|
+
_checkCommandInjection(input) {
|
|
820
|
+
const cmdChars = ['|', '&', ';', '`', '$', '(', ')', '<', '>', '\n', '\r'];
|
|
821
|
+
|
|
822
|
+
for (const char of cmdChars) {
|
|
823
|
+
if (input.includes(char)) {
|
|
824
|
+
return {
|
|
825
|
+
valid: false,
|
|
826
|
+
error: 'Command injection character detected',
|
|
827
|
+
char: char
|
|
828
|
+
};
|
|
829
|
+
}
|
|
830
|
+
}
|
|
831
|
+
|
|
832
|
+
return { valid: true };
|
|
833
|
+
}
|
|
834
|
+
|
|
835
|
+
_checkPathTraversal(input) {
|
|
836
|
+
const pathPatterns = [
|
|
837
|
+
/\.\.\//,
|
|
838
|
+
/\.\.\\/,
|
|
839
|
+
/%2e%2e%2f/i,
|
|
840
|
+
/%2e%2e%5c/i,
|
|
841
|
+
/\.\.%2f/i,
|
|
842
|
+
/\.\.%5c/i
|
|
843
|
+
];
|
|
844
|
+
|
|
845
|
+
for (const pattern of pathPatterns) {
|
|
846
|
+
if (pattern.test(input)) {
|
|
847
|
+
return {
|
|
848
|
+
valid: false,
|
|
849
|
+
error: 'Path traversal pattern detected'
|
|
850
|
+
};
|
|
851
|
+
}
|
|
852
|
+
}
|
|
853
|
+
|
|
854
|
+
return { valid: true };
|
|
855
|
+
}
|
|
856
|
+
```
|
|
857
|
+
|
|
858
|
+
### Usage in Controllers
|
|
859
|
+
|
|
860
|
+
```javascript
|
|
861
|
+
class UserController {
|
|
862
|
+
async search(obj) {
|
|
863
|
+
const query = this.params.q;
|
|
864
|
+
|
|
865
|
+
// Validate input
|
|
866
|
+
const validation = this.validateInput(query, 'string');
|
|
867
|
+
if (!validation.valid) {
|
|
868
|
+
return this.returnError(400, validation.error);
|
|
869
|
+
}
|
|
870
|
+
|
|
871
|
+
// Safe to query database
|
|
872
|
+
const results = await User.search(query);
|
|
873
|
+
this.returnJson(results);
|
|
874
|
+
}
|
|
875
|
+
|
|
876
|
+
async uploadFile(obj) {
|
|
877
|
+
const filename = this.params.filename;
|
|
878
|
+
|
|
879
|
+
// Check for path traversal
|
|
880
|
+
const validation = this.validateInput(filename, 'filename');
|
|
881
|
+
if (!validation.valid) {
|
|
882
|
+
return this.returnError(400, 'Invalid filename');
|
|
883
|
+
}
|
|
884
|
+
|
|
885
|
+
// Safe to write file
|
|
886
|
+
await fs.writeFile(`uploads/${filename}`, fileData);
|
|
887
|
+
}
|
|
888
|
+
}
|
|
889
|
+
```
|
|
890
|
+
|
|
891
|
+
---
|
|
892
|
+
|
|
893
|
+
## XSS Prevention
|
|
894
|
+
|
|
895
|
+
### Business Logic
|
|
896
|
+
|
|
897
|
+
**XSS (Cross-Site Scripting) prevention** protects against malicious JavaScript injection through multi-layer HTML sanitization.
|
|
898
|
+
|
|
899
|
+
#### Defense Layers
|
|
900
|
+
|
|
901
|
+
1. **Tag Removal** - Remove dangerous tags (`<script>`, `<iframe>`, `<object>`)
|
|
902
|
+
2. **Attribute Removal** - Remove event handlers (`onclick`, `onerror`)
|
|
903
|
+
3. **Protocol Sanitization** - Block dangerous protocols (`javascript:`, `data:`)
|
|
904
|
+
4. **CSS Sanitization** - Remove CSS expressions
|
|
905
|
+
5. **Whitelist Approach** - Only allow safe tags/attributes
|
|
906
|
+
|
|
907
|
+
### Workflow
|
|
908
|
+
|
|
909
|
+
```
|
|
910
|
+
User Input (HTML)
|
|
911
|
+
↓
|
|
912
|
+
[MasterSanitizer.sanitizeHTML()]
|
|
913
|
+
↓
|
|
914
|
+
Input: "<script>alert('XSS')</script><p onclick='hack()'>Hello</p>"
|
|
915
|
+
↓
|
|
916
|
+
Layer 1: Remove dangerous tags
|
|
917
|
+
→ <p onclick='hack()'>Hello</p>
|
|
918
|
+
↓
|
|
919
|
+
Layer 2: Remove event attributes
|
|
920
|
+
→ <p>Hello</p>
|
|
921
|
+
↓
|
|
922
|
+
Layer 3: Check protocols
|
|
923
|
+
→ (none to check)
|
|
924
|
+
↓
|
|
925
|
+
Layer 4: Sanitize CSS
|
|
926
|
+
→ (no inline styles)
|
|
927
|
+
↓
|
|
928
|
+
Output: "<p>Hello</p>"
|
|
929
|
+
↓
|
|
930
|
+
✓ Safe HTML
|
|
931
|
+
```
|
|
932
|
+
|
|
933
|
+
### Implementation
|
|
934
|
+
|
|
935
|
+
**MasterSanitizer.js (Lines 25-350)**
|
|
936
|
+
|
|
937
|
+
```javascript
|
|
938
|
+
sanitizeHTML(html) {
|
|
939
|
+
if (!html || typeof html !== 'string') return '';
|
|
940
|
+
|
|
941
|
+
let sanitized = html;
|
|
942
|
+
|
|
943
|
+
// Layer 1: Remove dangerous tags
|
|
944
|
+
sanitized = this._removeDangerousTags(sanitized);
|
|
945
|
+
|
|
946
|
+
// Layer 2: Remove event attributes
|
|
947
|
+
sanitized = this._removeEventAttributes(sanitized);
|
|
948
|
+
|
|
949
|
+
// Layer 3: Sanitize protocols
|
|
950
|
+
sanitized = this._sanitizeProtocols(sanitized);
|
|
951
|
+
|
|
952
|
+
// Layer 4: Sanitize CSS
|
|
953
|
+
sanitized = this._sanitizeCSS(sanitized);
|
|
954
|
+
|
|
955
|
+
return sanitized;
|
|
956
|
+
}
|
|
957
|
+
|
|
958
|
+
_removeDangerousTags(html) {
|
|
959
|
+
const dangerousTags = [
|
|
960
|
+
'script', 'iframe', 'object', 'embed',
|
|
961
|
+
'applet', 'meta', 'link', 'style',
|
|
962
|
+
'form', 'input', 'button', 'textarea',
|
|
963
|
+
'select', 'option', 'base'
|
|
964
|
+
];
|
|
965
|
+
|
|
966
|
+
let result = html;
|
|
967
|
+
|
|
968
|
+
for (const tag of dangerousTags) {
|
|
969
|
+
// Remove opening and closing tags (case-insensitive)
|
|
970
|
+
const openRegex = new RegExp(`<${tag}[^>]*>`, 'gi');
|
|
971
|
+
const closeRegex = new RegExp(`</${tag}>`, 'gi');
|
|
972
|
+
|
|
973
|
+
result = result.replace(openRegex, '');
|
|
974
|
+
result = result.replace(closeRegex, '');
|
|
975
|
+
}
|
|
976
|
+
|
|
977
|
+
return result;
|
|
978
|
+
}
|
|
979
|
+
|
|
980
|
+
_removeEventAttributes(html) {
|
|
981
|
+
// Remove all event handler attributes
|
|
982
|
+
const eventPattern = /\s+on\w+\s*=\s*["'][^"']*["']/gi;
|
|
983
|
+
return html.replace(eventPattern, '');
|
|
984
|
+
}
|
|
985
|
+
|
|
986
|
+
_sanitizeProtocols(html) {
|
|
987
|
+
// Block dangerous protocols
|
|
988
|
+
const dangerousProtocols = [
|
|
989
|
+
'javascript:',
|
|
990
|
+
'data:',
|
|
991
|
+
'vbscript:',
|
|
992
|
+
'file:',
|
|
993
|
+
'about:'
|
|
994
|
+
];
|
|
995
|
+
|
|
996
|
+
let result = html;
|
|
997
|
+
|
|
998
|
+
for (const protocol of dangerousProtocols) {
|
|
999
|
+
const regex = new RegExp(protocol, 'gi');
|
|
1000
|
+
result = result.replace(regex, '');
|
|
1001
|
+
}
|
|
1002
|
+
|
|
1003
|
+
return result;
|
|
1004
|
+
}
|
|
1005
|
+
|
|
1006
|
+
_sanitizeCSS(html) {
|
|
1007
|
+
// Remove dangerous CSS patterns
|
|
1008
|
+
const dangerousCSS = [
|
|
1009
|
+
/expression\s*\(/gi,
|
|
1010
|
+
/javascript\s*:/gi,
|
|
1011
|
+
/import\s+/gi,
|
|
1012
|
+
/@import/gi,
|
|
1013
|
+
/url\s*\(\s*["']?javascript:/gi
|
|
1014
|
+
];
|
|
1015
|
+
|
|
1016
|
+
let result = html;
|
|
1017
|
+
|
|
1018
|
+
for (const pattern of dangerousCSS) {
|
|
1019
|
+
result = result.replace(pattern, '');
|
|
1020
|
+
}
|
|
1021
|
+
|
|
1022
|
+
return result;
|
|
1023
|
+
}
|
|
1024
|
+
|
|
1025
|
+
// Whitelist approach for rich text
|
|
1026
|
+
sanitizeRichText(html) {
|
|
1027
|
+
const allowedTags = [
|
|
1028
|
+
'p', 'br', 'strong', 'em', 'u', 'h1', 'h2', 'h3',
|
|
1029
|
+
'ul', 'ol', 'li', 'a', 'img', 'blockquote', 'code', 'pre'
|
|
1030
|
+
];
|
|
1031
|
+
|
|
1032
|
+
const allowedAttributes = {
|
|
1033
|
+
'a': ['href', 'title', 'target'],
|
|
1034
|
+
'img': ['src', 'alt', 'width', 'height']
|
|
1035
|
+
};
|
|
1036
|
+
|
|
1037
|
+
// Parse HTML and rebuild with only allowed tags/attributes
|
|
1038
|
+
// (Implementation uses DOM parser or regex)
|
|
1039
|
+
|
|
1040
|
+
return sanitized;
|
|
1041
|
+
}
|
|
1042
|
+
```
|
|
1043
|
+
|
|
1044
|
+
### Usage in Controllers
|
|
1045
|
+
|
|
1046
|
+
```javascript
|
|
1047
|
+
class CommentController {
|
|
1048
|
+
async create(obj) {
|
|
1049
|
+
const content = this.params.content;
|
|
1050
|
+
|
|
1051
|
+
// Sanitize user HTML input
|
|
1052
|
+
const sanitizedContent = this.sanitizeHTML(content);
|
|
1053
|
+
|
|
1054
|
+
// Safe to store and display
|
|
1055
|
+
const comment = await Comment.create({
|
|
1056
|
+
content: sanitizedContent,
|
|
1057
|
+
userId: this.currentUser.id
|
|
1058
|
+
});
|
|
1059
|
+
|
|
1060
|
+
this.returnJson(comment);
|
|
1061
|
+
}
|
|
1062
|
+
}
|
|
1063
|
+
```
|
|
1064
|
+
|
|
1065
|
+
### Template Usage
|
|
1066
|
+
|
|
1067
|
+
```javascript
|
|
1068
|
+
// MasterTemplate automatically sanitizes variables
|
|
1069
|
+
class MasterTemplate {
|
|
1070
|
+
render(template, data) {
|
|
1071
|
+
return template.replace(/\${(\w+)}/g, (match, key) => {
|
|
1072
|
+
const value = data[key];
|
|
1073
|
+
// Automatic HTML escaping
|
|
1074
|
+
return this._escapeHTML(value);
|
|
1075
|
+
});
|
|
1076
|
+
}
|
|
1077
|
+
|
|
1078
|
+
_escapeHTML(str) {
|
|
1079
|
+
const htmlEscapes = {
|
|
1080
|
+
'&': '&',
|
|
1081
|
+
'<': '<',
|
|
1082
|
+
'>': '>',
|
|
1083
|
+
'"': '"',
|
|
1084
|
+
"'": '''
|
|
1085
|
+
};
|
|
1086
|
+
|
|
1087
|
+
return String(str).replace(/[&<>"']/g, char => htmlEscapes[char]);
|
|
1088
|
+
}
|
|
1089
|
+
}
|
|
1090
|
+
```
|
|
1091
|
+
|
|
1092
|
+
---
|
|
1093
|
+
|
|
1094
|
+
## Event Handler Validation
|
|
1095
|
+
|
|
1096
|
+
### Business Logic
|
|
1097
|
+
|
|
1098
|
+
**Event handler validation** ensures Web Component `@event` bindings are safe and don't allow arbitrary code execution.
|
|
1099
|
+
|
|
1100
|
+
#### Validation Rules
|
|
1101
|
+
|
|
1102
|
+
1. **Syntax Validation** - Ensure handler uses correct syntax
|
|
1103
|
+
2. **Dangerous Pattern Detection** - Block `eval()`, `Function()`, etc.
|
|
1104
|
+
3. **Scoped Execution** - Handlers can only call controller methods
|
|
1105
|
+
4. **No Inline Code** - Block inline JavaScript in attributes
|
|
1106
|
+
|
|
1107
|
+
### Implementation
|
|
1108
|
+
|
|
1109
|
+
**EventHandlerValidator.js (Lines 20-300)**
|
|
1110
|
+
|
|
1111
|
+
```javascript
|
|
1112
|
+
validateEventHandler(eventName, handlerCode, componentName) {
|
|
1113
|
+
// Check syntax
|
|
1114
|
+
if (!this._isValidSyntax(handlerCode)) {
|
|
1115
|
+
logger.error({
|
|
1116
|
+
code: 'INVALID_EVENT_SYNTAX',
|
|
1117
|
+
component: componentName,
|
|
1118
|
+
event: eventName,
|
|
1119
|
+
handler: handlerCode
|
|
1120
|
+
});
|
|
1121
|
+
return false;
|
|
1122
|
+
}
|
|
1123
|
+
|
|
1124
|
+
// Check for dangerous patterns
|
|
1125
|
+
if (this._containsDangerousPattern(handlerCode)) {
|
|
1126
|
+
logger.error({
|
|
1127
|
+
code: 'DANGEROUS_EVENT_HANDLER',
|
|
1128
|
+
component: componentName,
|
|
1129
|
+
event: eventName
|
|
1130
|
+
});
|
|
1131
|
+
return false;
|
|
1132
|
+
}
|
|
1133
|
+
|
|
1134
|
+
return true;
|
|
1135
|
+
}
|
|
1136
|
+
|
|
1137
|
+
_isValidSyntax(handlerCode) {
|
|
1138
|
+
// Valid: "methodName()" or "obj.methodName()"
|
|
1139
|
+
const validPattern = /^[a-zA-Z_$][a-zA-Z0-9_$]*(\.[a-zA-Z_$][a-zA-Z0-9_$]*)*\(\)$/;
|
|
1140
|
+
return validPattern.test(handlerCode);
|
|
1141
|
+
}
|
|
1142
|
+
|
|
1143
|
+
_containsDangerousPattern(handlerCode) {
|
|
1144
|
+
const dangerousPatterns = [
|
|
1145
|
+
/eval\(/,
|
|
1146
|
+
/Function\(/,
|
|
1147
|
+
/setTimeout\(/,
|
|
1148
|
+
/setInterval\(/,
|
|
1149
|
+
/new Function/,
|
|
1150
|
+
/constructor/,
|
|
1151
|
+
/__proto__/,
|
|
1152
|
+
/prototype/
|
|
1153
|
+
];
|
|
1154
|
+
|
|
1155
|
+
return dangerousPatterns.some(pattern => pattern.test(handlerCode));
|
|
1156
|
+
}
|
|
1157
|
+
```
|
|
1158
|
+
|
|
1159
|
+
### Usage in SSR
|
|
1160
|
+
|
|
1161
|
+
```javascript
|
|
1162
|
+
// runtime-ssr.cjs (Lines 450-500)
|
|
1163
|
+
function validateComponentEvents(component) {
|
|
1164
|
+
const eventAttributes = component.getAttributeNames()
|
|
1165
|
+
.filter(attr => attr.startsWith('@'));
|
|
1166
|
+
|
|
1167
|
+
for (const attr of eventAttributes) {
|
|
1168
|
+
const eventName = attr.substring(1); // Remove '@'
|
|
1169
|
+
const handlerCode = component.getAttribute(attr);
|
|
1170
|
+
|
|
1171
|
+
// Validate handler
|
|
1172
|
+
const isValid = eventValidator.validateEventHandler(
|
|
1173
|
+
eventName,
|
|
1174
|
+
handlerCode,
|
|
1175
|
+
component.tagName
|
|
1176
|
+
);
|
|
1177
|
+
|
|
1178
|
+
if (!isValid) {
|
|
1179
|
+
// Remove dangerous handler
|
|
1180
|
+
component.removeAttribute(attr);
|
|
1181
|
+
|
|
1182
|
+
logger.warn({
|
|
1183
|
+
code: 'EVENT_HANDLER_REMOVED',
|
|
1184
|
+
component: component.tagName,
|
|
1185
|
+
event: eventName
|
|
1186
|
+
});
|
|
1187
|
+
}
|
|
1188
|
+
}
|
|
1189
|
+
}
|
|
1190
|
+
```
|
|
1191
|
+
|
|
1192
|
+
---
|
|
1193
|
+
|
|
1194
|
+
## Content Security Policy
|
|
1195
|
+
|
|
1196
|
+
### Business Logic
|
|
1197
|
+
|
|
1198
|
+
**CSP (Content Security Policy)** defines approved sources for scripts, styles, images, and other resources, preventing XSS and data injection attacks.
|
|
1199
|
+
|
|
1200
|
+
### Presets
|
|
1201
|
+
|
|
1202
|
+
#### Development Preset (Permissive)
|
|
1203
|
+
```javascript
|
|
1204
|
+
{
|
|
1205
|
+
'default-src': ["'self'"],
|
|
1206
|
+
'script-src': ["'self'", "'unsafe-inline'", "'unsafe-eval'"],
|
|
1207
|
+
'style-src': ["'self'", "'unsafe-inline'"],
|
|
1208
|
+
'img-src': ["'self'", "data:", "https:"],
|
|
1209
|
+
'connect-src': ["'self'", "ws:", "wss:"]
|
|
1210
|
+
}
|
|
1211
|
+
```
|
|
1212
|
+
|
|
1213
|
+
#### Production Preset (Strict)
|
|
1214
|
+
```javascript
|
|
1215
|
+
{
|
|
1216
|
+
'default-src': ["'self'"],
|
|
1217
|
+
'script-src': ["'self'", "'nonce-{random}'"],
|
|
1218
|
+
'style-src': ["'self'", "'nonce-{random}'"],
|
|
1219
|
+
'img-src': ["'self'", "https:"],
|
|
1220
|
+
'connect-src': ["'self'"],
|
|
1221
|
+
'object-src': ["'none'"],
|
|
1222
|
+
'base-uri': ["'self'"],
|
|
1223
|
+
'form-action': ["'self'"]
|
|
1224
|
+
}
|
|
1225
|
+
```
|
|
1226
|
+
|
|
1227
|
+
#### CDN Preset
|
|
1228
|
+
```javascript
|
|
1229
|
+
{
|
|
1230
|
+
'default-src': ["'self'"],
|
|
1231
|
+
'script-src': [
|
|
1232
|
+
"'self'",
|
|
1233
|
+
"'nonce-{random}'",
|
|
1234
|
+
"https://cdn.jsdelivr.net",
|
|
1235
|
+
"https://unpkg.com"
|
|
1236
|
+
],
|
|
1237
|
+
'style-src': [
|
|
1238
|
+
"'self'",
|
|
1239
|
+
"'nonce-{random}'",
|
|
1240
|
+
"https://fonts.googleapis.com"
|
|
1241
|
+
],
|
|
1242
|
+
'font-src': [
|
|
1243
|
+
"'self'",
|
|
1244
|
+
"https://fonts.gstatic.com"
|
|
1245
|
+
]
|
|
1246
|
+
}
|
|
1247
|
+
```
|
|
1248
|
+
|
|
1249
|
+
### Implementation
|
|
1250
|
+
|
|
1251
|
+
**CSPConfig.js (Lines 15-250)**
|
|
1252
|
+
|
|
1253
|
+
```javascript
|
|
1254
|
+
class CSPConfig {
|
|
1255
|
+
constructor() {
|
|
1256
|
+
this.presets = {
|
|
1257
|
+
development: { /* ... */ },
|
|
1258
|
+
production: { /* ... */ },
|
|
1259
|
+
cdn: { /* ... */ }
|
|
1260
|
+
};
|
|
1261
|
+
}
|
|
1262
|
+
|
|
1263
|
+
generateCSPHeader(preset = 'production', customRules = {}) {
|
|
1264
|
+
const config = { ...this.presets[preset], ...customRules };
|
|
1265
|
+
|
|
1266
|
+
// Generate nonce for inline scripts
|
|
1267
|
+
const nonce = crypto.randomBytes(16).toString('base64');
|
|
1268
|
+
|
|
1269
|
+
// Build CSP directives
|
|
1270
|
+
const directives = [];
|
|
1271
|
+
|
|
1272
|
+
for (const [directive, sources] of Object.entries(config)) {
|
|
1273
|
+
const sourcesStr = sources
|
|
1274
|
+
.map(src => src.replace('{random}', nonce))
|
|
1275
|
+
.join(' ');
|
|
1276
|
+
directives.push(`${directive} ${sourcesStr}`);
|
|
1277
|
+
}
|
|
1278
|
+
|
|
1279
|
+
return {
|
|
1280
|
+
header: directives.join('; '),
|
|
1281
|
+
nonce: nonce
|
|
1282
|
+
};
|
|
1283
|
+
}
|
|
1284
|
+
}
|
|
1285
|
+
```
|
|
1286
|
+
|
|
1287
|
+
### Usage in Middleware
|
|
1288
|
+
|
|
1289
|
+
```javascript
|
|
1290
|
+
// SecurityMiddleware.js
|
|
1291
|
+
checkRequest(requestObject) {
|
|
1292
|
+
// Generate CSP
|
|
1293
|
+
const { header, nonce } = this.cspConfig.generateCSPHeader('production');
|
|
1294
|
+
|
|
1295
|
+
// Inject header
|
|
1296
|
+
requestObject.response.setHeader('Content-Security-Policy', header);
|
|
1297
|
+
|
|
1298
|
+
// Store nonce for template rendering
|
|
1299
|
+
requestObject.cspNonce = nonce;
|
|
1300
|
+
}
|
|
1301
|
+
```
|
|
1302
|
+
|
|
1303
|
+
### Template Usage
|
|
1304
|
+
|
|
1305
|
+
```html
|
|
1306
|
+
<!DOCTYPE html>
|
|
1307
|
+
<html>
|
|
1308
|
+
<head>
|
|
1309
|
+
<!-- Inline script with nonce -->
|
|
1310
|
+
<script nonce="${cspNonce}">
|
|
1311
|
+
console.log('Safe inline script');
|
|
1312
|
+
</script>
|
|
1313
|
+
|
|
1314
|
+
<!-- External script (allowed by CSP) -->
|
|
1315
|
+
<script src="/app.js"></script>
|
|
1316
|
+
</head>
|
|
1317
|
+
</html>
|
|
1318
|
+
```
|
|
1319
|
+
|
|
1320
|
+
---
|
|
1321
|
+
|
|
1322
|
+
## Configuration Guide
|
|
1323
|
+
|
|
1324
|
+
### Production Configuration
|
|
1325
|
+
|
|
1326
|
+
**config/security.js**
|
|
1327
|
+
|
|
1328
|
+
```javascript
|
|
1329
|
+
module.exports = {
|
|
1330
|
+
// CSRF Protection
|
|
1331
|
+
csrf: {
|
|
1332
|
+
enabled: true,
|
|
1333
|
+
tokenLength: 32,
|
|
1334
|
+
expiryMs: 3600000, // 1 hour
|
|
1335
|
+
oneTimeUse: true
|
|
1336
|
+
},
|
|
1337
|
+
|
|
1338
|
+
// Rate Limiting
|
|
1339
|
+
rateLimit: {
|
|
1340
|
+
enabled: true,
|
|
1341
|
+
windowMs: 15 * 60 * 1000, // 15 minutes
|
|
1342
|
+
maxRequests: 100, // 100 requests per window
|
|
1343
|
+
whitelist: ['127.0.0.1'], // Exempt IPs
|
|
1344
|
+
storage: 'redis', // redis or memory
|
|
1345
|
+
redisUrl: 'redis://localhost:6379'
|
|
1346
|
+
},
|
|
1347
|
+
|
|
1348
|
+
// Session Security
|
|
1349
|
+
session: {
|
|
1350
|
+
enabled: true,
|
|
1351
|
+
fingerprintEnabled: true,
|
|
1352
|
+
timeoutMs: 60 * 60 * 1000, // 60 minutes
|
|
1353
|
+
regenerateOnAuth: true,
|
|
1354
|
+
maxConcurrentSessions: 3,
|
|
1355
|
+
cookieOptions: {
|
|
1356
|
+
httpOnly: true,
|
|
1357
|
+
secure: true, // HTTPS only
|
|
1358
|
+
sameSite: 'strict',
|
|
1359
|
+
maxAge: 3600
|
|
1360
|
+
}
|
|
1361
|
+
},
|
|
1362
|
+
|
|
1363
|
+
// Input Validation
|
|
1364
|
+
validation: {
|
|
1365
|
+
enabled: true,
|
|
1366
|
+
strictMode: true,
|
|
1367
|
+
logBlocked: true
|
|
1368
|
+
},
|
|
1369
|
+
|
|
1370
|
+
// XSS Prevention
|
|
1371
|
+
sanitization: {
|
|
1372
|
+
enabled: true,
|
|
1373
|
+
allowRichText: false, // Strict sanitization
|
|
1374
|
+
whitelistTags: ['p', 'br', 'strong', 'em']
|
|
1375
|
+
},
|
|
1376
|
+
|
|
1377
|
+
// Content Security Policy
|
|
1378
|
+
csp: {
|
|
1379
|
+
enabled: true,
|
|
1380
|
+
preset: 'production',
|
|
1381
|
+
customRules: {
|
|
1382
|
+
'script-src': ["'self'", "https://trusted-cdn.com"]
|
|
1383
|
+
},
|
|
1384
|
+
reportUri: '/csp-report'
|
|
1385
|
+
},
|
|
1386
|
+
|
|
1387
|
+
// Security Headers
|
|
1388
|
+
headers: {
|
|
1389
|
+
'X-Frame-Options': 'DENY',
|
|
1390
|
+
'X-Content-Type-Options': 'nosniff',
|
|
1391
|
+
'X-XSS-Protection': '1; mode=block',
|
|
1392
|
+
'Strict-Transport-Security': 'max-age=31536000; includeSubDomains',
|
|
1393
|
+
'Referrer-Policy': 'no-referrer-when-downgrade'
|
|
1394
|
+
}
|
|
1395
|
+
};
|
|
1396
|
+
```
|
|
1397
|
+
|
|
1398
|
+
### Initialization
|
|
1399
|
+
|
|
1400
|
+
**config/initializers/security.js**
|
|
1401
|
+
|
|
1402
|
+
```javascript
|
|
1403
|
+
const master = require('mastercontroller');
|
|
1404
|
+
const securityConfig = require('../security');
|
|
1405
|
+
|
|
1406
|
+
// Initialize security modules
|
|
1407
|
+
master.securityMiddleware.configure(securityConfig);
|
|
1408
|
+
master.sessionSecurity.configure(securityConfig.session);
|
|
1409
|
+
master.cspConfig.setPreset(securityConfig.csp.preset);
|
|
1410
|
+
|
|
1411
|
+
// Enable automatic enforcement
|
|
1412
|
+
master.securityEnforcement.enable({
|
|
1413
|
+
autoCSRF: true,
|
|
1414
|
+
autoRateLimit: true,
|
|
1415
|
+
autoHeaders: true
|
|
1416
|
+
});
|
|
1417
|
+
|
|
1418
|
+
console.log('[Security] Initialized with production configuration');
|
|
1419
|
+
```
|
|
1420
|
+
|
|
1421
|
+
---
|
|
1422
|
+
|
|
1423
|
+
## Best Practices
|
|
1424
|
+
|
|
1425
|
+
### 1. Always Validate User Input
|
|
1426
|
+
|
|
1427
|
+
```javascript
|
|
1428
|
+
// ❌ BAD - No validation
|
|
1429
|
+
class UserController {
|
|
1430
|
+
async search(obj) {
|
|
1431
|
+
const query = this.params.q;
|
|
1432
|
+
const results = await User.search(query); // SQL injection risk!
|
|
1433
|
+
this.returnJson(results);
|
|
1434
|
+
}
|
|
1435
|
+
}
|
|
1436
|
+
|
|
1437
|
+
// ✅ GOOD - Validate before use
|
|
1438
|
+
class UserController {
|
|
1439
|
+
async search(obj) {
|
|
1440
|
+
const query = this.params.q;
|
|
1441
|
+
|
|
1442
|
+
// Validate input
|
|
1443
|
+
const validation = this.validateInput(query, 'string');
|
|
1444
|
+
if (!validation.valid) {
|
|
1445
|
+
return this.returnError(400, validation.error);
|
|
1446
|
+
}
|
|
1447
|
+
|
|
1448
|
+
const results = await User.search(query);
|
|
1449
|
+
this.returnJson(results);
|
|
1450
|
+
}
|
|
1451
|
+
}
|
|
1452
|
+
```
|
|
1453
|
+
|
|
1454
|
+
### 2. Sanitize HTML Output
|
|
1455
|
+
|
|
1456
|
+
```javascript
|
|
1457
|
+
// ❌ BAD - Raw HTML from user
|
|
1458
|
+
class CommentController {
|
|
1459
|
+
async show(obj) {
|
|
1460
|
+
const comment = await Comment.findById(this.params.id);
|
|
1461
|
+
// XSS risk if comment.content contains <script>
|
|
1462
|
+
this.returnView({ content: comment.content });
|
|
1463
|
+
}
|
|
1464
|
+
}
|
|
1465
|
+
|
|
1466
|
+
// ✅ GOOD - Sanitize HTML
|
|
1467
|
+
class CommentController {
|
|
1468
|
+
async show(obj) {
|
|
1469
|
+
const comment = await Comment.findById(this.params.id);
|
|
1470
|
+
const sanitized = this.sanitizeHTML(comment.content);
|
|
1471
|
+
this.returnView({ content: sanitized });
|
|
1472
|
+
}
|
|
1473
|
+
}
|
|
1474
|
+
```
|
|
1475
|
+
|
|
1476
|
+
### 3. Use CSRF Tokens for State-Changing Requests
|
|
1477
|
+
|
|
1478
|
+
```javascript
|
|
1479
|
+
// ❌ BAD - No CSRF protection
|
|
1480
|
+
class AccountController {
|
|
1481
|
+
deleteAccount(obj) {
|
|
1482
|
+
// Anyone can call this endpoint!
|
|
1483
|
+
User.delete(this.currentUser.id);
|
|
1484
|
+
this.redirectTo('/');
|
|
1485
|
+
}
|
|
1486
|
+
}
|
|
1487
|
+
|
|
1488
|
+
// ✅ GOOD - CSRF token required
|
|
1489
|
+
class AccountController {
|
|
1490
|
+
showDeleteForm(obj) {
|
|
1491
|
+
const csrfToken = this.generateCSRFToken();
|
|
1492
|
+
this.returnView({ csrfToken });
|
|
1493
|
+
}
|
|
1494
|
+
|
|
1495
|
+
deleteAccount(obj) {
|
|
1496
|
+
// CSRF automatically validated by SecurityMiddleware
|
|
1497
|
+
User.delete(this.currentUser.id);
|
|
1498
|
+
this.redirectTo('/');
|
|
1499
|
+
}
|
|
1500
|
+
}
|
|
1501
|
+
```
|
|
1502
|
+
|
|
1503
|
+
### 4. Regenerate Sessions After Authentication
|
|
1504
|
+
|
|
1505
|
+
```javascript
|
|
1506
|
+
// ❌ BAD - Session fixation risk
|
|
1507
|
+
class AuthController {
|
|
1508
|
+
async login(obj) {
|
|
1509
|
+
const user = await User.authenticate(this.params.username, this.params.password);
|
|
1510
|
+
this.__session.userId = user.id; // Session fixation!
|
|
1511
|
+
this.redirectTo('/dashboard');
|
|
1512
|
+
}
|
|
1513
|
+
}
|
|
1514
|
+
|
|
1515
|
+
// ✅ GOOD - Regenerate session
|
|
1516
|
+
class AuthController {
|
|
1517
|
+
async login(obj) {
|
|
1518
|
+
const user = await User.authenticate(this.params.username, this.params.password);
|
|
1519
|
+
|
|
1520
|
+
// Regenerate session ID
|
|
1521
|
+
const oldSessionId = this.__session.id;
|
|
1522
|
+
const newSessionId = this.regenerateSession(oldSessionId, this.__requestObject);
|
|
1523
|
+
|
|
1524
|
+
this.redirectTo('/dashboard');
|
|
1525
|
+
}
|
|
1526
|
+
}
|
|
1527
|
+
```
|
|
1528
|
+
|
|
1529
|
+
### 5. Use Parameterized Queries
|
|
1530
|
+
|
|
1531
|
+
```javascript
|
|
1532
|
+
// ❌ BAD - SQL injection risk
|
|
1533
|
+
class UserController {
|
|
1534
|
+
async search(obj) {
|
|
1535
|
+
const query = `SELECT * FROM users WHERE name = '${this.params.name}'`;
|
|
1536
|
+
const results = await db.query(query); // Dangerous!
|
|
1537
|
+
this.returnJson(results);
|
|
1538
|
+
}
|
|
1539
|
+
}
|
|
1540
|
+
|
|
1541
|
+
// ✅ GOOD - Parameterized query
|
|
1542
|
+
class UserController {
|
|
1543
|
+
async search(obj) {
|
|
1544
|
+
// ORM handles parameterization
|
|
1545
|
+
const results = await User.where({ name: this.params.name });
|
|
1546
|
+
this.returnJson(results);
|
|
1547
|
+
}
|
|
1548
|
+
}
|
|
1549
|
+
```
|
|
1550
|
+
|
|
1551
|
+
### 6. Set Secure Cookie Flags
|
|
1552
|
+
|
|
1553
|
+
```javascript
|
|
1554
|
+
// ❌ BAD - Insecure cookies
|
|
1555
|
+
response.setHeader('Set-Cookie', `sessionId=${id}; Path=/`);
|
|
1556
|
+
|
|
1557
|
+
// ✅ GOOD - Secure cookies
|
|
1558
|
+
response.setHeader('Set-Cookie',
|
|
1559
|
+
`sessionId=${id}; HttpOnly; Secure; SameSite=Strict; Path=/; Max-Age=3600`
|
|
1560
|
+
);
|
|
1561
|
+
```
|
|
1562
|
+
|
|
1563
|
+
### 7. Use CSP in Production
|
|
1564
|
+
|
|
1565
|
+
```javascript
|
|
1566
|
+
// ❌ BAD - No CSP
|
|
1567
|
+
// Vulnerable to XSS attacks
|
|
1568
|
+
|
|
1569
|
+
// ✅ GOOD - Strict CSP
|
|
1570
|
+
const { header, nonce } = this.cspConfig.generateCSPHeader('production');
|
|
1571
|
+
response.setHeader('Content-Security-Policy', header);
|
|
1572
|
+
```
|
|
1573
|
+
|
|
1574
|
+
### 8. Log Security Events
|
|
1575
|
+
|
|
1576
|
+
```javascript
|
|
1577
|
+
// ✅ GOOD - Comprehensive logging
|
|
1578
|
+
logger.warn({
|
|
1579
|
+
code: 'RATE_LIMIT_EXCEEDED',
|
|
1580
|
+
ip: requestObject.ip,
|
|
1581
|
+
userAgent: requestObject.headers['user-agent'],
|
|
1582
|
+
count: attempts,
|
|
1583
|
+
timestamp: Date.now()
|
|
1584
|
+
});
|
|
1585
|
+
```
|
|
1586
|
+
|
|
1587
|
+
---
|
|
1588
|
+
|
|
1589
|
+
## Monitoring & Logging
|
|
1590
|
+
|
|
1591
|
+
### Security Event Codes
|
|
1592
|
+
|
|
1593
|
+
| Code | Description | Severity | Action |
|
|
1594
|
+
|------|-------------|----------|--------|
|
|
1595
|
+
| `CSRF_VALIDATION_FAILED` | CSRF token invalid/missing | HIGH | Block request |
|
|
1596
|
+
| `RATE_LIMIT_EXCEEDED` | Too many requests | MEDIUM | Block request (429) |
|
|
1597
|
+
| `SESSION_HIJACK_DETECTED` | Fingerprint mismatch | CRITICAL | Destroy session |
|
|
1598
|
+
| `SESSION_TIMEOUT` | Session expired | LOW | Redirect to login |
|
|
1599
|
+
| `SQL_INJECTION_DETECTED` | SQL pattern found | CRITICAL | Block + alert |
|
|
1600
|
+
| `NOSQL_INJECTION_DETECTED` | NoSQL operator found | CRITICAL | Block + alert |
|
|
1601
|
+
| `COMMAND_INJECTION_DETECTED` | Shell metachar found | CRITICAL | Block + alert |
|
|
1602
|
+
| `PATH_TRAVERSAL_DETECTED` | Directory escape found | HIGH | Block request |
|
|
1603
|
+
| `XSS_SANITIZED` | Dangerous HTML removed | MEDIUM | Log + sanitize |
|
|
1604
|
+
| `INVALID_EVENT_SYNTAX` | Bad @event handler | MEDIUM | Remove handler |
|
|
1605
|
+
| `CSP_VIOLATION` | CSP rule broken | MEDIUM | Log + investigate |
|
|
1606
|
+
|
|
1607
|
+
### Log Analysis Queries
|
|
1608
|
+
|
|
1609
|
+
```bash
|
|
1610
|
+
# Failed CSRF attempts (potential attack)
|
|
1611
|
+
grep "CSRF_VALIDATION_FAILED" logs/security.log | wc -l
|
|
1612
|
+
|
|
1613
|
+
# Rate limit violations by IP
|
|
1614
|
+
grep "RATE_LIMIT_EXCEEDED" logs/security.log | awk '{print $4}' | sort | uniq -c | sort -nr
|
|
1615
|
+
|
|
1616
|
+
# Session hijack attempts
|
|
1617
|
+
grep "SESSION_HIJACK_DETECTED" logs/security.log
|
|
1618
|
+
|
|
1619
|
+
# Injection attack attempts
|
|
1620
|
+
grep -E "(SQL|NOSQL|COMMAND)_INJECTION_DETECTED" logs/security.log
|
|
1621
|
+
```
|
|
1622
|
+
|
|
1623
|
+
---
|
|
1624
|
+
|
|
1625
|
+
## Testing
|
|
1626
|
+
|
|
1627
|
+
### Security Test Suite
|
|
1628
|
+
|
|
1629
|
+
**test/security/csrf.test.js**
|
|
1630
|
+
|
|
1631
|
+
```javascript
|
|
1632
|
+
const request = require('supertest');
|
|
1633
|
+
const app = require('../../server');
|
|
1634
|
+
|
|
1635
|
+
describe('CSRF Protection', () => {
|
|
1636
|
+
it('should block POST without CSRF token', async () => {
|
|
1637
|
+
const res = await request(app)
|
|
1638
|
+
.post('/user/create')
|
|
1639
|
+
.send({ name: 'Test' });
|
|
1640
|
+
|
|
1641
|
+
expect(res.status).toBe(403);
|
|
1642
|
+
});
|
|
1643
|
+
|
|
1644
|
+
it('should allow POST with valid CSRF token', async () => {
|
|
1645
|
+
// Get token
|
|
1646
|
+
const tokenRes = await request(app).get('/form');
|
|
1647
|
+
const token = extractCSRFToken(tokenRes.text);
|
|
1648
|
+
|
|
1649
|
+
// Submit with token
|
|
1650
|
+
const res = await request(app)
|
|
1651
|
+
.post('/user/create')
|
|
1652
|
+
.send({ name: 'Test', _csrf: token });
|
|
1653
|
+
|
|
1654
|
+
expect(res.status).toBe(200);
|
|
1655
|
+
});
|
|
1656
|
+
});
|
|
1657
|
+
```
|
|
1658
|
+
|
|
1659
|
+
**test/security/rate-limit.test.js**
|
|
1660
|
+
|
|
1661
|
+
```javascript
|
|
1662
|
+
describe('Rate Limiting', () => {
|
|
1663
|
+
it('should block after 100 requests', async () => {
|
|
1664
|
+
// Make 100 requests
|
|
1665
|
+
for (let i = 0; i < 100; i++) {
|
|
1666
|
+
await request(app).get('/');
|
|
1667
|
+
}
|
|
1668
|
+
|
|
1669
|
+
// 101st request should be blocked
|
|
1670
|
+
const res = await request(app).get('/');
|
|
1671
|
+
expect(res.status).toBe(429);
|
|
1672
|
+
});
|
|
1673
|
+
});
|
|
1674
|
+
```
|
|
1675
|
+
|
|
1676
|
+
---
|
|
1677
|
+
|
|
1678
|
+
## Troubleshooting
|
|
1679
|
+
|
|
1680
|
+
### Common Issues
|
|
1681
|
+
|
|
1682
|
+
#### 1. CSRF Validation Failing
|
|
1683
|
+
|
|
1684
|
+
**Symptom:** Forms return 403 Forbidden
|
|
1685
|
+
|
|
1686
|
+
**Causes:**
|
|
1687
|
+
- Token not included in form
|
|
1688
|
+
- Token expired (> 1 hour)
|
|
1689
|
+
- Session not persisted
|
|
1690
|
+
|
|
1691
|
+
**Solution:**
|
|
1692
|
+
```javascript
|
|
1693
|
+
// Ensure token is embedded
|
|
1694
|
+
<input type="hidden" name="_csrf" value="${csrfToken}">
|
|
1695
|
+
|
|
1696
|
+
// Check session storage
|
|
1697
|
+
console.log(this.__session.id); // Should be defined
|
|
1698
|
+
```
|
|
1699
|
+
|
|
1700
|
+
#### 2. Rate Limit Too Aggressive
|
|
1701
|
+
|
|
1702
|
+
**Symptom:** Legitimate users blocked
|
|
1703
|
+
|
|
1704
|
+
**Solution:**
|
|
1705
|
+
```javascript
|
|
1706
|
+
// Increase limits in config/security.js
|
|
1707
|
+
rateLimit: {
|
|
1708
|
+
windowMs: 15 * 60 * 1000,
|
|
1709
|
+
maxRequests: 200, // Increase from 100
|
|
1710
|
+
whitelist: ['192.168.1.0/24'] // Exempt internal network
|
|
1711
|
+
}
|
|
1712
|
+
```
|
|
1713
|
+
|
|
1714
|
+
#### 3. Session Hijack False Positives
|
|
1715
|
+
|
|
1716
|
+
**Symptom:** Users logged out unexpectedly
|
|
1717
|
+
|
|
1718
|
+
**Causes:**
|
|
1719
|
+
- Mobile users switching networks
|
|
1720
|
+
- Users behind proxy with rotating IPs
|
|
1721
|
+
|
|
1722
|
+
**Solution:**
|
|
1723
|
+
```javascript
|
|
1724
|
+
// Disable fingerprinting or use less strict validation
|
|
1725
|
+
session: {
|
|
1726
|
+
fingerprintEnabled: false, // Disable IP check
|
|
1727
|
+
// OR
|
|
1728
|
+
fingerprintStrict: false // Only check User-Agent
|
|
1729
|
+
}
|
|
1730
|
+
```
|
|
1731
|
+
|
|
1732
|
+
#### 4. CSP Blocking Resources
|
|
1733
|
+
|
|
1734
|
+
**Symptom:** Scripts/styles not loading
|
|
1735
|
+
|
|
1736
|
+
**Solution:**
|
|
1737
|
+
```javascript
|
|
1738
|
+
// Add trusted sources to CSP
|
|
1739
|
+
csp: {
|
|
1740
|
+
customRules: {
|
|
1741
|
+
'script-src': ["'self'", "https://trusted-cdn.com"],
|
|
1742
|
+
'style-src': ["'self'", "https://fonts.googleapis.com"]
|
|
1743
|
+
}
|
|
1744
|
+
}
|
|
1745
|
+
|
|
1746
|
+
// OR use 'unsafe-inline' in development (NOT production!)
|
|
1747
|
+
csp: {
|
|
1748
|
+
preset: 'development'
|
|
1749
|
+
}
|
|
1750
|
+
```
|
|
1751
|
+
|
|
1752
|
+
---
|
|
1753
|
+
|
|
1754
|
+
## Migration Guide
|
|
1755
|
+
|
|
1756
|
+
### Upgrading Existing Projects
|
|
1757
|
+
|
|
1758
|
+
**Step 1:** Update dependencies
|
|
1759
|
+
```bash
|
|
1760
|
+
npm install mastercontroller@latest
|
|
1761
|
+
```
|
|
1762
|
+
|
|
1763
|
+
**Step 2:** Enable security modules in config
|
|
1764
|
+
```javascript
|
|
1765
|
+
// config/initializers/security.js
|
|
1766
|
+
const securityConfig = require('../security');
|
|
1767
|
+
master.securityMiddleware.configure(securityConfig);
|
|
1768
|
+
```
|
|
1769
|
+
|
|
1770
|
+
**Step 3:** Add CSRF tokens to forms
|
|
1771
|
+
```html
|
|
1772
|
+
<input type="hidden" name="_csrf" value="${csrfToken}">
|
|
1773
|
+
```
|
|
1774
|
+
|
|
1775
|
+
**Step 4:** Update controllers
|
|
1776
|
+
```javascript
|
|
1777
|
+
class FormController {
|
|
1778
|
+
showForm(obj) {
|
|
1779
|
+
const csrfToken = this.generateCSRFToken();
|
|
1780
|
+
this.returnView({ csrfToken });
|
|
1781
|
+
}
|
|
1782
|
+
}
|
|
1783
|
+
```
|
|
1784
|
+
|
|
1785
|
+
**Step 5:** Test thoroughly
|
|
1786
|
+
```bash
|
|
1787
|
+
npm test
|
|
1788
|
+
```
|
|
1789
|
+
|
|
1790
|
+
---
|
|
1791
|
+
|
|
1792
|
+
## Support
|
|
1793
|
+
|
|
1794
|
+
For security issues or questions:
|
|
1795
|
+
|
|
1796
|
+
1. Review this documentation
|
|
1797
|
+
2. Check logs: `logs/security.log`
|
|
1798
|
+
3. Run security tests: `npm run test:security`
|
|
1799
|
+
4. Report security vulnerabilities privately (do not open public issues)
|
|
1800
|
+
|
|
1801
|
+
---
|
|
1802
|
+
|
|
1803
|
+
**Last Updated:** 2026-01-29
|
|
1804
|
+
**Version:** 1.0.0
|
|
1805
|
+
**Maintained By:** MasterController Security Team
|