cipher-shield 1.0.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/LICENSE +21 -0
- package/README.md +666 -0
- package/index.js +75 -0
- package/package.json +57 -0
- package/src/core/aesEngine.js +369 -0
- package/src/core/blacklistMem.js +510 -0
- package/src/core/defconSystem.js +301 -0
- package/src/magicAuth.js +272 -0
- package/src/modules/aiScanner.js +482 -0
- package/src/modules/ghostHandler.js +279 -0
- package/src/modules/shadowHandler.js +266 -0
- package/src/shield.js +694 -0
- package/src/smartLogger.js +268 -0
- package/src/sslManager.js +345 -0
package/src/shield.js
ADDED
|
@@ -0,0 +1,694 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* cipher-shield v2.1 - Production Security Middleware
|
|
3
|
+
*
|
|
4
|
+
* A comprehensive, multi-layered security middleware for Node.js/Express applications
|
|
5
|
+
* providing AES encryption, ghost route protection, shadow banning, and AI-powered
|
|
6
|
+
* threat detection with adaptive DEFCON escalation.
|
|
7
|
+
*
|
|
8
|
+
* @author Omindu Dissanayaka
|
|
9
|
+
* @version 1.0.0
|
|
10
|
+
* @license MIT
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
const ghostHandler = require('./modules/ghostHandler');
|
|
14
|
+
const shadowHandler = require('./modules/shadowHandler');
|
|
15
|
+
const aiScanner = require('./modules/aiScanner');
|
|
16
|
+
const aesEngine = require('./core/aesEngine');
|
|
17
|
+
const defconSystem = require('./core/defconSystem');
|
|
18
|
+
const blacklistMem = require('./core/blacklistMem');
|
|
19
|
+
const SSLManager = require('./sslManager');
|
|
20
|
+
const helmet = require('helmet');
|
|
21
|
+
const rateLimit = require('express-rate-limit');
|
|
22
|
+
const onHeaders = require('on-headers');
|
|
23
|
+
const fs = require('fs');
|
|
24
|
+
const path = require('path');
|
|
25
|
+
|
|
26
|
+
const packageJsonPath = path.join(__dirname, '..', 'package.json');
|
|
27
|
+
const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, 'utf8'));
|
|
28
|
+
const CIPHER_SHIELD_VERSION = packageJson.version;
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Generates a random security signature for header obfuscation
|
|
32
|
+
*
|
|
33
|
+
* @private
|
|
34
|
+
* @returns {string} Random security signature
|
|
35
|
+
*/
|
|
36
|
+
function generateRandomSignature() {
|
|
37
|
+
const signatures = [
|
|
38
|
+
'Cipher Shield',
|
|
39
|
+
'Security Shield',
|
|
40
|
+
'Defense Matrix',
|
|
41
|
+
'Protection Core',
|
|
42
|
+
'Security Engine',
|
|
43
|
+
'Defense System',
|
|
44
|
+
'Shield Protocol',
|
|
45
|
+
'Security Framework',
|
|
46
|
+
'Defense Network',
|
|
47
|
+
'Protection Layer',
|
|
48
|
+
'Security Vault',
|
|
49
|
+
'Defense Core',
|
|
50
|
+
'Shield Matrix',
|
|
51
|
+
'Security Grid',
|
|
52
|
+
'Defense Protocol'
|
|
53
|
+
];
|
|
54
|
+
|
|
55
|
+
return signatures[Math.floor(Math.random() * signatures.length)];
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* Default strict helmet configuration for maximum security
|
|
60
|
+
* Military-grade HTTP security headers
|
|
61
|
+
*/
|
|
62
|
+
const defaultStrictConfig = {
|
|
63
|
+
contentSecurityPolicy: {
|
|
64
|
+
directives: {
|
|
65
|
+
defaultSrc: ["'self'"],
|
|
66
|
+
scriptSrc: ["'self'"],
|
|
67
|
+
styleSrc: ["'self'", "'unsafe-inline'"],
|
|
68
|
+
imgSrc: ["'self'", "data:", "https:"],
|
|
69
|
+
fontSrc: ["'self'", "https:", "data:"],
|
|
70
|
+
connectSrc: ["'self'"],
|
|
71
|
+
mediaSrc: ["'self'"],
|
|
72
|
+
objectSrc: ["'none'"],
|
|
73
|
+
frameSrc: ["'none'"],
|
|
74
|
+
baseUri: ["'self'"],
|
|
75
|
+
formAction: ["'self'"],
|
|
76
|
+
upgradeInsecureRequests: []
|
|
77
|
+
}
|
|
78
|
+
},
|
|
79
|
+
hsts: {
|
|
80
|
+
maxAge: 31536000,
|
|
81
|
+
includeSubDomains: true,
|
|
82
|
+
preload: true
|
|
83
|
+
},
|
|
84
|
+
noSniff: true,
|
|
85
|
+
xssFilter: true,
|
|
86
|
+
frameguard: {
|
|
87
|
+
action: 'deny'
|
|
88
|
+
},
|
|
89
|
+
referrerPolicy: {
|
|
90
|
+
policy: 'strict-origin-when-cross-origin'
|
|
91
|
+
},
|
|
92
|
+
permissionsPolicy: {
|
|
93
|
+
features: {
|
|
94
|
+
camera: ["'none'"],
|
|
95
|
+
microphone: ["'none'"],
|
|
96
|
+
geolocation: ["'none'"],
|
|
97
|
+
payment: ["'self'"]
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
};
|
|
101
|
+
|
|
102
|
+
/**
|
|
103
|
+
* Creates helmet middleware based on configuration
|
|
104
|
+
* @param {Object} config - Headers configuration
|
|
105
|
+
* @returns {Function} Helmet middleware function
|
|
106
|
+
*/
|
|
107
|
+
function createHelmetMiddleware(config) {
|
|
108
|
+
if (!config.enabled) {
|
|
109
|
+
return (req, res, next) => next();
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
let helmetConfig = {};
|
|
113
|
+
|
|
114
|
+
if (config.simple) {
|
|
115
|
+
helmetConfig = {};
|
|
116
|
+
} else if (config.security === 'max' || config.strict) {
|
|
117
|
+
helmetConfig = { ...defaultStrictConfig };
|
|
118
|
+
} else {
|
|
119
|
+
helmetConfig = {
|
|
120
|
+
contentSecurityPolicy: {
|
|
121
|
+
directives: {
|
|
122
|
+
defaultSrc: ["'self'"],
|
|
123
|
+
scriptSrc: ["'self'", "'unsafe-inline'"],
|
|
124
|
+
styleSrc: ["'self'", "'unsafe-inline'"],
|
|
125
|
+
imgSrc: ["'self'", "data:", "https:"],
|
|
126
|
+
connectSrc: ["'self'"],
|
|
127
|
+
objectSrc: ["'none'"]
|
|
128
|
+
}
|
|
129
|
+
},
|
|
130
|
+
hsts: {
|
|
131
|
+
maxAge: 86400,
|
|
132
|
+
includeSubDomains: false
|
|
133
|
+
},
|
|
134
|
+
noSniff: true,
|
|
135
|
+
xssFilter: true,
|
|
136
|
+
frameguard: {
|
|
137
|
+
action: 'sameorigin'
|
|
138
|
+
}
|
|
139
|
+
};
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
if (config.options && typeof config.options === 'object') {
|
|
143
|
+
helmetConfig = { ...helmetConfig, ...config.options };
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
return helmet(helmetConfig);
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
/**
|
|
150
|
+
* Creates rate limiter middleware based on configuration
|
|
151
|
+
* @param {Object} config - Rate limit configuration
|
|
152
|
+
* @param {Object} smartLogger - SmartLogger instance for logging (optional)
|
|
153
|
+
* @returns {Function} Rate limiter middleware function
|
|
154
|
+
*/
|
|
155
|
+
function createRateLimitMiddleware(config, smartLogger = null) {
|
|
156
|
+
if (!config.enabled) {
|
|
157
|
+
return (req, res, next) => next();
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
let rateLimitConfig = {
|
|
161
|
+
windowMs: config.windowMs,
|
|
162
|
+
max: config.max,
|
|
163
|
+
message: config.message,
|
|
164
|
+
standardHeaders: config.standardHeaders,
|
|
165
|
+
legacyHeaders: config.legacyHeaders,
|
|
166
|
+
skipSuccessfulRequests: config.skipSuccessfulRequests,
|
|
167
|
+
skipFailedRequests: config.skipFailedRequests
|
|
168
|
+
};
|
|
169
|
+
|
|
170
|
+
if (config.options && typeof config.options === 'object') {
|
|
171
|
+
rateLimitConfig = { ...rateLimitConfig, ...config.options };
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
return rateLimit(rateLimitConfig);
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
/**
|
|
178
|
+
* Utility function to extract client IP address from request
|
|
179
|
+
* Handles various proxy headers and fallbacks
|
|
180
|
+
*/
|
|
181
|
+
function extractClientIP(req) {
|
|
182
|
+
const forwarded = req.headers['x-forwarded-for'];
|
|
183
|
+
if (forwarded) {
|
|
184
|
+
return forwarded.split(',')[0].trim();
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
const realIP = req.headers['x-real-ip'];
|
|
188
|
+
if (realIP) {
|
|
189
|
+
return realIP;
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
const clientIP = req.headers['x-client-ip'];
|
|
193
|
+
if (clientIP) {
|
|
194
|
+
return clientIP;
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
const remoteAddress = req.connection?.remoteAddress ||
|
|
198
|
+
req.socket?.remoteAddress ||
|
|
199
|
+
req.connection?.socket?.remoteAddress;
|
|
200
|
+
|
|
201
|
+
if (remoteAddress) {
|
|
202
|
+
return remoteAddress.replace(/^::ffff:/, '');
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
return req.ip || 'unknown';
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
/**
|
|
209
|
+
* Validates IP address format to prevent injection attacks
|
|
210
|
+
*
|
|
211
|
+
* @private
|
|
212
|
+
* @param {string} ip - IP address to validate
|
|
213
|
+
* @returns {boolean} True if IP is valid format
|
|
214
|
+
*/
|
|
215
|
+
function isValidIP(ip) {
|
|
216
|
+
if (!ip || typeof ip !== 'string') return false;
|
|
217
|
+
|
|
218
|
+
const ipv4Regex = /^(\d{1,3})\.(\d{1,3})\.(\d{1,3})\.(\d{1,3})$/;
|
|
219
|
+
if (ipv4Regex.test(ip)) {
|
|
220
|
+
return ip.split('.').every(part => {
|
|
221
|
+
const num = parseInt(part, 10);
|
|
222
|
+
return num >= 0 && num <= 255;
|
|
223
|
+
});
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
const ipv6Regex = /^([0-9a-fA-F]{1,4}:){7}[0-9a-fA-F]{1,4}$/;
|
|
227
|
+
return ipv6Regex.test(ip);
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
/**
|
|
231
|
+
* Adds security branding to blocked response objects
|
|
232
|
+
*
|
|
233
|
+
* @private
|
|
234
|
+
* @param {Object} responseObj - The response object to brand
|
|
235
|
+
* @param {Object} signatureConfig - Signature configuration
|
|
236
|
+
* @returns {Object} Branded response object
|
|
237
|
+
*/
|
|
238
|
+
function addSecurityBranding(responseObj, signatureConfig) {
|
|
239
|
+
if (!signatureConfig.enabled || !signatureConfig.branding) {
|
|
240
|
+
return responseObj;
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
return {
|
|
244
|
+
...responseObj,
|
|
245
|
+
security_provider: signatureConfig.simple ?
|
|
246
|
+
signatureConfig.protectedBy :
|
|
247
|
+
signatureConfig.server
|
|
248
|
+
};
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
/**
|
|
252
|
+
* Validates and normalizes cipher-shield configuration
|
|
253
|
+
* Ensures all required settings are present and valid
|
|
254
|
+
*
|
|
255
|
+
* @private
|
|
256
|
+
* @param {Object} config - Raw configuration object
|
|
257
|
+
* @returns {Object} Normalized and validated configuration
|
|
258
|
+
* @throws {Error} If configuration is invalid
|
|
259
|
+
*/
|
|
260
|
+
function validateAndNormalizeConfig(config = {}) {
|
|
261
|
+
const settings = {
|
|
262
|
+
encryption: {
|
|
263
|
+
enabled: Boolean(config.encryption?.enabled),
|
|
264
|
+
secretKey: config.encryption?.secretKey || null,
|
|
265
|
+
algorithm: config.encryption?.algorithm || 'aes-256-gcm'
|
|
266
|
+
},
|
|
267
|
+
ghostRoutes: Array.isArray(config.ghostRoutes) ? config.ghostRoutes : [],
|
|
268
|
+
shadowBan: {
|
|
269
|
+
enabled: Boolean(config.shadowBan?.enabled),
|
|
270
|
+
delayTime: Math.max(1000, config.shadowBan?.delayTime || 20000)
|
|
271
|
+
},
|
|
272
|
+
aiGate: {
|
|
273
|
+
enabled: Boolean(config.aiGate?.enabled),
|
|
274
|
+
provider: config.aiGate?.provider || 'gemini',
|
|
275
|
+
gemini: {
|
|
276
|
+
apiKey: config.aiGate?.gemini?.apiKey || null,
|
|
277
|
+
model: config.aiGate?.gemini?.model || 'gemini-2.5-flash'
|
|
278
|
+
},
|
|
279
|
+
openai: {
|
|
280
|
+
apiKey: config.aiGate?.openai?.apiKey || null,
|
|
281
|
+
model: config.aiGate?.openai?.model || 'gpt-4o-mini',
|
|
282
|
+
organization: config.aiGate?.openai?.organization || null
|
|
283
|
+
},
|
|
284
|
+
scanRoutes: Array.isArray(config.aiGate?.scanRoutes) ? config.aiGate.scanRoutes : [],
|
|
285
|
+
failSafe: config.aiGate?.failSafe !== false
|
|
286
|
+
},
|
|
287
|
+
headers: {
|
|
288
|
+
enabled: config.headers?.enabled !== false,
|
|
289
|
+
simple: Boolean(config.headers?.simple),
|
|
290
|
+
security: config.headers?.security || 'standard',
|
|
291
|
+
strict: Boolean(config.headers?.strict),
|
|
292
|
+
options: config.headers?.options || {}
|
|
293
|
+
},
|
|
294
|
+
rateLimit: {
|
|
295
|
+
enabled: config.rateLimit?.enabled !== false,
|
|
296
|
+
simple: Boolean(config.rateLimit?.simple),
|
|
297
|
+
windowMs: config.rateLimit?.windowMs || 15 * 60 * 1000,
|
|
298
|
+
max: config.rateLimit?.max || 100,
|
|
299
|
+
message: config.rateLimit?.message || 'Too many requests from this IP, please try again later.',
|
|
300
|
+
standardHeaders: config.rateLimit?.standardHeaders !== false,
|
|
301
|
+
legacyHeaders: Boolean(config.rateLimit?.legacyHeaders),
|
|
302
|
+
skipSuccessfulRequests: Boolean(config.rateLimit?.skipSuccessfulRequests),
|
|
303
|
+
skipFailedRequests: Boolean(config.rateLimit?.skipFailedRequests),
|
|
304
|
+
options: config.rateLimit?.options || {}
|
|
305
|
+
},
|
|
306
|
+
signature: {
|
|
307
|
+
enabled: config.signature?.enabled !== false,
|
|
308
|
+
simple: Boolean(config.signature?.simple),
|
|
309
|
+
headerValue: config.signature?.headerValue || 'Cipher Shield',
|
|
310
|
+
randomize: Boolean(config.signature?.randomize),
|
|
311
|
+
customHeaderName: config.signature?.customHeaderName || 'X-Protected-By',
|
|
312
|
+
server: config.signature?.customServer ?
|
|
313
|
+
`${config.signature.customServer}/${CIPHER_SHIELD_VERSION}` :
|
|
314
|
+
`CipherShield/${CIPHER_SHIELD_VERSION}`,
|
|
315
|
+
removePoweredBy: config.signature?.removePoweredBy !== false,
|
|
316
|
+
branding: config.signature?.branding !== false
|
|
317
|
+
},
|
|
318
|
+
adaptiveDefcon: config.adaptiveDefcon !== false
|
|
319
|
+
};
|
|
320
|
+
|
|
321
|
+
if (settings.encryption.enabled) {
|
|
322
|
+
try {
|
|
323
|
+
const algoInfo = aesEngine.getAlgorithmInfo(settings.encryption.algorithm);
|
|
324
|
+
|
|
325
|
+
if (!settings.encryption.secretKey || settings.encryption.secretKey.length !== algoInfo.keyLength) {
|
|
326
|
+
throw new Error(
|
|
327
|
+
`${settings.encryption.algorithm} requires a ${algoInfo.keyLength}-character hex-encoded secret key`
|
|
328
|
+
);
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
if (!/^[0-9a-fA-F]+$/.test(settings.encryption.secretKey)) {
|
|
332
|
+
throw new Error(`Secret key must be valid hexadecimal for ${settings.encryption.algorithm}`);
|
|
333
|
+
}
|
|
334
|
+
} catch (error) {
|
|
335
|
+
throw new Error(`cipher-shield: ${error.message}`);
|
|
336
|
+
}
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
if (settings.aiGate.enabled) {
|
|
340
|
+
if (settings.aiGate.provider === 'gemini' && !settings.aiGate.gemini.apiKey) {
|
|
341
|
+
throw new Error('Gemini API key is required when using Gemini provider');
|
|
342
|
+
}
|
|
343
|
+
if (settings.aiGate.provider === 'openai' && !settings.aiGate.openai.apiKey) {
|
|
344
|
+
throw new Error('OpenAI API key is required when using OpenAI provider');
|
|
345
|
+
}
|
|
346
|
+
if (!['gemini', 'openai'].includes(settings.aiGate.provider)) {
|
|
347
|
+
throw new Error('AI provider must be either "gemini" or "openai"');
|
|
348
|
+
}
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
if (settings.shadowBan.enabled && settings.shadowBan.delayTime > 45000) {
|
|
352
|
+
throw new Error('Shadow ban delay time cannot exceed 45 seconds (45000ms)');
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
return settings;
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
/**
|
|
359
|
+
* CipherShield - Production Security Middleware Class
|
|
360
|
+
*
|
|
361
|
+
* A comprehensive, multi-layered security middleware for Node.js/Express applications
|
|
362
|
+
* providing AES encryption, ghost route protection, shadow banning, AI-powered
|
|
363
|
+
* threat detection, adaptive DEFCON escalation, and automated SSL certificate management.
|
|
364
|
+
*
|
|
365
|
+
* @class CipherShield
|
|
366
|
+
* @version 1.0.0
|
|
367
|
+
* @author Omindu Dissanayaka
|
|
368
|
+
* @license MIT
|
|
369
|
+
*/
|
|
370
|
+
class CipherShield {
|
|
371
|
+
/**
|
|
372
|
+
* Creates a CipherShield instance
|
|
373
|
+
*
|
|
374
|
+
* @param {Object} config - Configuration object
|
|
375
|
+
*/
|
|
376
|
+
constructor(config = {}) {
|
|
377
|
+
this.config = validateAndNormalizeConfig(config);
|
|
378
|
+
this.sslManager = null;
|
|
379
|
+
|
|
380
|
+
this.helmetMiddleware = createHelmetMiddleware(this.config.headers);
|
|
381
|
+
|
|
382
|
+
this.rateLimitMiddleware = createRateLimitMiddleware(this.config.rateLimit);
|
|
383
|
+
|
|
384
|
+
this.algoInfo = this.config.encryption.enabled ?
|
|
385
|
+
aesEngine.getAlgorithmInfo(this.config.encryption.algorithm) : null;
|
|
386
|
+
|
|
387
|
+
if (this.config.adaptiveDefcon) {
|
|
388
|
+
defconSystem.initialize();
|
|
389
|
+
}
|
|
390
|
+
}
|
|
391
|
+
|
|
392
|
+
/**
|
|
393
|
+
* Get the Express middleware function
|
|
394
|
+
*
|
|
395
|
+
* @returns {Function} Express middleware function
|
|
396
|
+
*/
|
|
397
|
+
middleware() {
|
|
398
|
+
return this.createMiddleware();
|
|
399
|
+
}
|
|
400
|
+
|
|
401
|
+
/**
|
|
402
|
+
* Enable automated SSL certificate management
|
|
403
|
+
*
|
|
404
|
+
* @param {Object} sslConfig - SSL configuration
|
|
405
|
+
* @param {string} sslConfig.email - Email for Let's Encrypt account
|
|
406
|
+
* @param {string|string[]} sslConfig.domains - Domain(s) to obtain certificates for
|
|
407
|
+
* @param {boolean} [sslConfig.staging=true] - Use staging environment
|
|
408
|
+
* @param {string} [sslConfig.certDir] - Certificate storage directory
|
|
409
|
+
* @param {Object} [sslConfig.expressApp] - Express app for HTTP-01 challenges
|
|
410
|
+
* @returns {SSLManager} SSL Manager instance
|
|
411
|
+
*/
|
|
412
|
+
async enableSSL(sslConfig) {
|
|
413
|
+
if (!sslConfig.email) {
|
|
414
|
+
throw new Error('SSL configuration requires an email address');
|
|
415
|
+
}
|
|
416
|
+
if (!sslConfig.domains || (Array.isArray(sslConfig.domains) && sslConfig.domains.length === 0)) {
|
|
417
|
+
throw new Error('SSL configuration requires at least one domain');
|
|
418
|
+
}
|
|
419
|
+
|
|
420
|
+
const fullSSLConfig = {
|
|
421
|
+
...sslConfig,
|
|
422
|
+
expressApp: sslConfig.expressApp || this.config.expressApp
|
|
423
|
+
};
|
|
424
|
+
|
|
425
|
+
this.sslManager = new SSLManager(fullSSLConfig);
|
|
426
|
+
await this.sslManager.initialize();
|
|
427
|
+
|
|
428
|
+
return this.sslManager;
|
|
429
|
+
}
|
|
430
|
+
|
|
431
|
+
/**
|
|
432
|
+
* Get SSL certificates
|
|
433
|
+
*
|
|
434
|
+
* @param {string} [domain] - Specific domain, or all if not provided
|
|
435
|
+
* @returns {Object|Object[]|null} Certificate data
|
|
436
|
+
*/
|
|
437
|
+
getSSLCertificates(domain) {
|
|
438
|
+
if (!this.sslManager) {
|
|
439
|
+
return null;
|
|
440
|
+
}
|
|
441
|
+
|
|
442
|
+
if (domain) {
|
|
443
|
+
return this.sslManager.getCertificate(domain);
|
|
444
|
+
}
|
|
445
|
+
|
|
446
|
+
return this.sslManager.getAllCertificates();
|
|
447
|
+
}
|
|
448
|
+
|
|
449
|
+
/**
|
|
450
|
+
* Shutdown all services
|
|
451
|
+
*/
|
|
452
|
+
async shutdown() {
|
|
453
|
+
if (this.sslManager) {
|
|
454
|
+
await this.sslManager.shutdown();
|
|
455
|
+
}
|
|
456
|
+
}
|
|
457
|
+
|
|
458
|
+
/**
|
|
459
|
+
* Create the middleware function
|
|
460
|
+
*
|
|
461
|
+
* @private
|
|
462
|
+
* @returns {Function} Express middleware function
|
|
463
|
+
*/
|
|
464
|
+
createMiddleware() {
|
|
465
|
+
return async (req, res, next) => {
|
|
466
|
+
const startTime = process.hrtime.bigint();
|
|
467
|
+
|
|
468
|
+
if (this.config.signature.enabled) {
|
|
469
|
+
onHeaders(res, () => {
|
|
470
|
+
if (this.config.signature.removePoweredBy) {
|
|
471
|
+
res.removeHeader('X-Powered-By');
|
|
472
|
+
}
|
|
473
|
+
|
|
474
|
+
let headerValue = this.config.signature.headerValue;
|
|
475
|
+
if (this.config.signature.randomize) {
|
|
476
|
+
headerValue = generateRandomSignature();
|
|
477
|
+
}
|
|
478
|
+
|
|
479
|
+
if (this.config.signature.simple) {
|
|
480
|
+
res.setHeader(this.config.signature.customHeaderName, headerValue);
|
|
481
|
+
} else {
|
|
482
|
+
res.setHeader('Server', this.config.signature.server);
|
|
483
|
+
res.setHeader(this.config.signature.customHeaderName, headerValue);
|
|
484
|
+
}
|
|
485
|
+
});
|
|
486
|
+
}
|
|
487
|
+
};
|
|
488
|
+
}
|
|
489
|
+
|
|
490
|
+
/**
|
|
491
|
+
* Execute active defense logic (Layers 1-4)
|
|
492
|
+
* @private
|
|
493
|
+
*/
|
|
494
|
+
async executeActiveDefense(req, res, next, startTime) {
|
|
495
|
+
const clientIP = extractClientIP(req);
|
|
496
|
+
|
|
497
|
+
try {
|
|
498
|
+
if (this.config.ghostRoutes.length > 0) {
|
|
499
|
+
const isGhostRoute = ghostHandler.detect(req.path, this.config.ghostRoutes);
|
|
500
|
+
|
|
501
|
+
if (isGhostRoute) {
|
|
502
|
+
blacklistMem.add(clientIP, 'ghost_route_access');
|
|
503
|
+
|
|
504
|
+
if (this.config.adaptiveDefcon) {
|
|
505
|
+
defconSystem.escalate('GHOST_ROUTE_HIT');
|
|
506
|
+
}
|
|
507
|
+
|
|
508
|
+
}
|
|
509
|
+
}
|
|
510
|
+
|
|
511
|
+
if (this.config.shadowBan.enabled && blacklistMem.isBlacklisted(clientIP)) {
|
|
512
|
+
const currentDefcon = this.config.adaptiveDefcon ? defconSystem.getState() : 'GREEN';
|
|
513
|
+
const shouldBlock = await shadowHandler.delay(
|
|
514
|
+
clientIP,
|
|
515
|
+
this.config.shadowBan.delayTime,
|
|
516
|
+
currentDefcon
|
|
517
|
+
);
|
|
518
|
+
|
|
519
|
+
if (shouldBlock) {
|
|
520
|
+
const processingTime = Number(process.hrtime.bigint() - startTime) / 1000000;
|
|
521
|
+
console.log(`[cipher-shield] Blocked ${clientIP} after ${processingTime.toFixed(2)}ms`);
|
|
522
|
+
|
|
523
|
+
return res.status(408).json(addSecurityBranding({
|
|
524
|
+
error: 'Request Timeout',
|
|
525
|
+
message: 'The server timed out waiting for the request'
|
|
526
|
+
}, this.config.signature));
|
|
527
|
+
}
|
|
528
|
+
}
|
|
529
|
+
|
|
530
|
+
if (this.config.encryption.enabled && (req.method === 'POST' || req.method === 'PUT')) {
|
|
531
|
+
if (!req.body || typeof req.body !== 'object') {
|
|
532
|
+
blacklistMem.add(clientIP, 'invalid_request_body');
|
|
533
|
+
if (this.config.adaptiveDefcon) {
|
|
534
|
+
defconSystem.escalate('INVALID_REQUEST_BODY');
|
|
535
|
+
}
|
|
536
|
+
return res.status(400).json(addSecurityBranding({
|
|
537
|
+
error: 'Bad Request',
|
|
538
|
+
message: 'Invalid request body'
|
|
539
|
+
}, this.config.signature));
|
|
540
|
+
}
|
|
541
|
+
|
|
542
|
+
if (req.body.payload) {
|
|
543
|
+
if (typeof req.body.payload !== 'string' || req.body.payload.length > 10000) {
|
|
544
|
+
blacklistMem.add(clientIP, 'invalid_payload_format');
|
|
545
|
+
if (this.config.adaptiveDefcon) {
|
|
546
|
+
defconSystem.escalate('INVALID_PAYLOAD_FORMAT');
|
|
547
|
+
}
|
|
548
|
+
return res.status(400).json(addSecurityBranding({
|
|
549
|
+
error: 'Bad Request',
|
|
550
|
+
message: 'Invalid payload format'
|
|
551
|
+
}, this.config.signature));
|
|
552
|
+
}
|
|
553
|
+
|
|
554
|
+
try {
|
|
555
|
+
const decryptedData = aesEngine.decrypt(
|
|
556
|
+
req.body.payload,
|
|
557
|
+
this.config.encryption.secretKey
|
|
558
|
+
);
|
|
559
|
+
|
|
560
|
+
let parsedData;
|
|
561
|
+
try {
|
|
562
|
+
const tempObj = {};
|
|
563
|
+
parsedData = JSON.parse(decryptedData);
|
|
564
|
+
|
|
565
|
+
if (parsedData === null || typeof parsedData !== 'object') {
|
|
566
|
+
throw new Error('Invalid decrypted data structure');
|
|
567
|
+
}
|
|
568
|
+
|
|
569
|
+
if ('__proto__' in parsedData || 'constructor' in parsedData || 'prototype' in parsedData) {
|
|
570
|
+
throw new Error('Potential prototype pollution detected');
|
|
571
|
+
}
|
|
572
|
+
|
|
573
|
+
function checkPrototypePollution(obj, depth = 0) {
|
|
574
|
+
if (depth > 10) throw new Error('Maximum object depth exceeded');
|
|
575
|
+
if (obj === null || typeof obj !== 'object') return;
|
|
576
|
+
|
|
577
|
+
if ('__proto__' in obj || 'constructor' in obj || 'prototype' in obj) {
|
|
578
|
+
throw new Error('Prototype pollution detected in nested object');
|
|
579
|
+
}
|
|
580
|
+
|
|
581
|
+
for (const key in obj) {
|
|
582
|
+
if (obj.hasOwnProperty(key)) {
|
|
583
|
+
checkPrototypePollution(obj[key], depth + 1);
|
|
584
|
+
}
|
|
585
|
+
}
|
|
586
|
+
}
|
|
587
|
+
|
|
588
|
+
checkPrototypePollution(parsedData);
|
|
589
|
+
|
|
590
|
+
} catch (parseError) {
|
|
591
|
+
throw new Error(`JSON parsing failed: ${parseError.message}`);
|
|
592
|
+
}
|
|
593
|
+
|
|
594
|
+
req.body = parsedData;
|
|
595
|
+
|
|
596
|
+
} catch (decryptError) {
|
|
597
|
+
blacklistMem.add(clientIP, `aes_decryption_failure:${this.config.encryption.algorithm}`);
|
|
598
|
+
|
|
599
|
+
if (this.config.adaptiveDefcon) {
|
|
600
|
+
defconSystem.escalate('AES_DECRYPTION_FAIL');
|
|
601
|
+
}
|
|
602
|
+
|
|
603
|
+
const processingTime = Number(process.hrtime.bigint() - startTime) / 1000000;
|
|
604
|
+
console.log(`[cipher-shield] Decryption failed for ${clientIP} after ${processingTime.toFixed(2)}ms`);
|
|
605
|
+
|
|
606
|
+
return res.status(400).json(addSecurityBranding({
|
|
607
|
+
error: 'Bad Request',
|
|
608
|
+
message: 'Invalid payload format'
|
|
609
|
+
}, this.config.signature));
|
|
610
|
+
}
|
|
611
|
+
}
|
|
612
|
+
}
|
|
613
|
+
|
|
614
|
+
if (this.config.aiGate.enabled) {
|
|
615
|
+
const shouldScanRoute = this.config.aiGate.scanRoutes.some(route =>
|
|
616
|
+
req.path === route || req.path.startsWith(route)
|
|
617
|
+
);
|
|
618
|
+
|
|
619
|
+
if (shouldScanRoute) {
|
|
620
|
+
const currentDefcon = this.config.adaptiveDefcon ? defconSystem.getState() : 'GREEN';
|
|
621
|
+
|
|
622
|
+
if (currentDefcon !== 'RED' || !this.config.adaptiveDefcon) {
|
|
623
|
+
try {
|
|
624
|
+
const aiConfig = {
|
|
625
|
+
provider: this.config.aiGate.provider,
|
|
626
|
+
gemini: this.config.aiGate.gemini,
|
|
627
|
+
openai: this.config.aiGate.openai
|
|
628
|
+
};
|
|
629
|
+
|
|
630
|
+
const aiResult = await aiScanner.scan(req.body, aiConfig);
|
|
631
|
+
|
|
632
|
+
if (!aiResult.safe) {
|
|
633
|
+
const threatLevel = aiResult.threatLevel || 0;
|
|
634
|
+
|
|
635
|
+
if (threatLevel >= 7) {
|
|
636
|
+
blacklistMem.add(clientIP, 'ai_high_threat');
|
|
637
|
+
|
|
638
|
+
if (this.config.adaptiveDefcon) {
|
|
639
|
+
defconSystem.escalate('AI_HIGH_THREAT');
|
|
640
|
+
}
|
|
641
|
+
|
|
642
|
+
const processingTime = Number(process.hrtime.bigint() - startTime) / 1000000;
|
|
643
|
+
console.log(`[cipher-shield] Blocked high threat from ${clientIP} after ${processingTime.toFixed(2)}ms`);
|
|
644
|
+
|
|
645
|
+
return res.status(403).json(addSecurityBranding({
|
|
646
|
+
error: 'Forbidden',
|
|
647
|
+
message: 'Request blocked by security policy'
|
|
648
|
+
}, this.config.signature));
|
|
649
|
+
|
|
650
|
+
} else if (threatLevel >= 4) {
|
|
651
|
+
blacklistMem.add(clientIP, 'ai_medium_threat');
|
|
652
|
+
}
|
|
653
|
+
}
|
|
654
|
+
|
|
655
|
+
} catch (aiError) {
|
|
656
|
+
if (this.config.aiGate.failSafe) {
|
|
657
|
+
if (this.config.adaptiveDefcon) {
|
|
658
|
+
defconSystem.escalate('AI_FAILURE');
|
|
659
|
+
}
|
|
660
|
+
} else {
|
|
661
|
+
const processingTime = Number(process.hrtime.bigint() - startTime) / 1000000;
|
|
662
|
+
console.log(`[cipher-shield] AI failure for ${clientIP} after ${processingTime.toFixed(2)}ms`);
|
|
663
|
+
|
|
664
|
+
return res.status(503).json(addSecurityBranding({
|
|
665
|
+
error: 'Service Unavailable',
|
|
666
|
+
message: 'Security validation unavailable'
|
|
667
|
+
}, this.config.signature));
|
|
668
|
+
}
|
|
669
|
+
}
|
|
670
|
+
}
|
|
671
|
+
}
|
|
672
|
+
}
|
|
673
|
+
|
|
674
|
+
const totalTime = Number(process.hrtime.bigint() - startTime) / 1000000;
|
|
675
|
+
|
|
676
|
+
if (process.env.NODE_ENV === 'development') {
|
|
677
|
+
console.log(`[cipher-shield] ✅ ${clientIP} passed all checks in ${totalTime.toFixed(2)}ms`);
|
|
678
|
+
}
|
|
679
|
+
|
|
680
|
+
next();
|
|
681
|
+
|
|
682
|
+
} catch (error) {
|
|
683
|
+
const processingTime = Number(process.hrtime.bigint() - startTime) / 1000000;
|
|
684
|
+
|
|
685
|
+
const sanitizedError = error.message ? error.message.substring(0, 100) : 'Unknown error';
|
|
686
|
+
|
|
687
|
+
console.error(`[cipher-shield] Middleware error for ${clientIP} after ${processingTime.toFixed(2)}ms: ${sanitizedError}`);
|
|
688
|
+
|
|
689
|
+
next();
|
|
690
|
+
}
|
|
691
|
+
}
|
|
692
|
+
}
|
|
693
|
+
|
|
694
|
+
module.exports = CipherShield;
|