mm_os 3.2.9 → 3.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.
Files changed (38) hide show
  1. package/README.md +47 -1
  2. package/core/base/mqtt/index.js +10 -10
  3. package/core/base/web/index.js +98 -11
  4. package/core/com/middleware/com.js +152 -152
  5. package/core/com/socket/config.tpl.json +2 -2
  6. package/core/com/socket/drive.js +2 -2
  7. package/core/com/socket/index.js +1 -1
  8. package/core/com/sql/drive.js +7 -7
  9. package/index.js +40 -30
  10. package/middleware/performance/index.js +150 -142
  11. package/middleware/security_audit/index.js +549 -0
  12. package/middleware/security_audit/middleware.json +48 -0
  13. package/middleware/security_headers/index.js +487 -0
  14. package/middleware/security_headers/middleware.json +45 -0
  15. package/middleware/waf/index.js +277 -6
  16. package/middleware/waf/middleware.json +2 -1
  17. package/middleware/waf_ddos/index.js +520 -0
  18. package/middleware/waf_ddos/middleware.json +38 -0
  19. package/middleware/waf_ip/index.js +231 -20
  20. package/middleware/waf_ip/middleware.json +43 -4
  21. package/middleware/waf_xss/index.js +269 -0
  22. package/middleware/waf_xss/middleware.json +18 -0
  23. package/middleware/web_before/middleware.json +1 -1
  24. package/middleware/web_socket/middleware.json +2 -2
  25. package/package.json +18 -7
  26. package/middleware/cors/index.js +0 -103
  27. package/middleware/cors/middleware.json +0 -9
  28. package/middleware/log/index.js +0 -32
  29. package/middleware/log/middleware.json +0 -9
  30. package/middleware/rate_limit/index.js +0 -112
  31. package/middleware/rate_limit/middleware.json +0 -10
  32. package/nodemon.json +0 -31
  33. package/package.txt +0 -1
  34. package/rps.bat +0 -3
  35. package/test.js +0 -10
  36. package/tps.bat +0 -3
  37. package/update.bat +0 -1
  38. package//347/263/273/347/273/237/346/236/266/346/236/204/350/257/204/344/274/260/344/270/216/344/274/230/345/214/226/345/273/272/350/256/256.md +0 -599
@@ -0,0 +1,487 @@
1
+ /**
2
+ * 安全头与跨域保护中间件
3
+ * 合并了CSRF保护和CORS功能的综合安全中间件
4
+ */
5
+ class Middleware {
6
+ /**
7
+ * 构造函数
8
+ */
9
+ constructor() {
10
+ this.default = {
11
+ // CSRF保护配置
12
+ csrf: {
13
+ // 启用CSRF保护
14
+ enable: true,
15
+ // CSRF令牌的Cookie名称
16
+ cookie_name: 'csrf_token',
17
+ // CSRF令牌的请求头名称
18
+ header_name: 'X-CSRF-Token',
19
+ // CSRF令牌的表单字段名称
20
+ form_field: '_csrf',
21
+ // CSRF令牌的过期时间(毫秒)
22
+ max_age: 3600000, // 1小时
23
+ // 忽略的HTTP方法
24
+ ignore_methods: ['GET', 'HEAD', 'OPTIONS'],
25
+ // 忽略的路径
26
+ ignore_paths: [],
27
+ // 是否生成新的令牌
28
+ generate_new: true,
29
+ // 是否验证来源
30
+ check_origin: true,
31
+ // 是否记录CSRF攻击尝试
32
+ log: true,
33
+ // 是否阻止恶意请求
34
+ block: true
35
+ },
36
+ // CORS配置
37
+ cors: {
38
+ // 启用CORS
39
+ enable: true,
40
+ // 允许的源
41
+ origin: '*',
42
+ // 允许的请求头
43
+ headers: '*',
44
+ // 允许的HTTP方法
45
+ methods: ['GET', 'POST', 'PUT', 'DELETE', 'OPTIONS', 'HEAD'],
46
+ // 允许携带凭证
47
+ credentials: false,
48
+ // 预检请求的有效期(秒)
49
+ max_age: 3600,
50
+ // 暴露的响应头
51
+ expose_headers: []
52
+ },
53
+ // 安全头配置
54
+ security_headers: {
55
+ // 启用安全头
56
+ enable: true,
57
+ // X-Content-Type-Options
58
+ content_type_options: 'nosniff',
59
+ // X-Frame-Options
60
+ frame_options: 'SAMEORIGIN',
61
+ // X-XSS-Protection
62
+ xss_protection: '1; mode=block',
63
+ // Content-Security-Policy
64
+ content_security_policy: "default-src 'self'; script-src 'self'; style-src 'self' 'unsafe-inline'; img-src 'self' data:",
65
+ // Strict-Transport-Security
66
+ strict_transport_security: 'max-age=31536000; includeSubDomains',
67
+ // Referrer-Policy
68
+ referrer_policy: 'strict-origin-when-cross-origin'
69
+ },
70
+ // 允许的来源域名(用于CSRF和CORS的共同配置)
71
+ allowed_origins: []
72
+ };
73
+ }
74
+
75
+ /**
76
+ * 初始化中间件
77
+ * @param {Object} config 配置项
78
+ */
79
+ init(config) {
80
+ // 合并默认配置和用户配置
81
+ config = this._deepMerge(this.default, config);
82
+
83
+ // 如果allowed_origins有值但csrf中没有设置,则复制到csrf配置
84
+ if (config.allowed_origins.length > 0 && config.csrf.allowed_origins === undefined) {
85
+ config.csrf.allowed_origins = [...config.allowed_origins];
86
+ }
87
+
88
+ this.config = config;
89
+ return this;
90
+ }
91
+
92
+ /**
93
+ * 执行中间件
94
+ * @param {Object} ctx Koa上下文
95
+ * @param {Function} next 下一个中间件
96
+ */
97
+ async run(ctx, next) {
98
+ const config = this.config;
99
+
100
+ // 强制输出日志
101
+ console.log('SECURITY HEADERS MIDDLEWARE RUNNING');
102
+ console.log('Config:', JSON.stringify(config.security_headers));
103
+ console.log('Security headers enabled:', config.security_headers.enable);
104
+
105
+ // 设置CORS头
106
+ if (config.cors.enable) {
107
+ console.log('Setting CORS headers');
108
+ await this._setCorsHeaders(ctx, config.cors);
109
+ }
110
+
111
+ // 设置安全头
112
+ if (config.security_headers.enable) {
113
+ console.log('Preparing to set security headers');
114
+ this._setSecurityHeaders(ctx, config.security_headers);
115
+ // 验证安全头是否已设置(在Koa中应该检查response.headers)
116
+ console.log('Security headers after setting (response.headers):', {
117
+ 'x-content-type-options': ctx.response.headers['x-content-type-options'],
118
+ 'x-frame-options': ctx.response.headers['x-frame-options'],
119
+ 'x-xss-protection': ctx.response.headers['x-xss-protection']
120
+ });
121
+ }
122
+
123
+ // 处理预检请求
124
+ if (ctx.method === 'OPTIONS' && config.cors.enable) {
125
+ ctx.status = 204;
126
+ return;
127
+ }
128
+
129
+ // CSRF保护
130
+ if (config.csrf.enable) {
131
+ // 生成CSRF令牌
132
+ const token = await this._generateToken(ctx);
133
+ console.log('CSRF token generated');
134
+
135
+ // 将令牌添加到上下文,供模板使用
136
+ ctx.state.csrf = token;
137
+
138
+ // 检查是否需要验证CSRF
139
+ if (!this._shouldSkipCsrfValidation(ctx, config.csrf)) {
140
+ // 验证来源
141
+ if (config.csrf.check_origin && !this._validateOrigin(ctx, config.csrf)) {
142
+ // 强制输出警告日志
143
+ console.warn('CSRF Origin Validation Failed:', {
144
+ ip: this._getClientIp(ctx),
145
+ path: ctx.path,
146
+ method: ctx.method,
147
+ origin: ctx.headers.origin,
148
+ referer: ctx.headers.referer
149
+ });
150
+
151
+ if (config.csrf.block) {
152
+ ctx.status = 403;
153
+ ctx.body = {
154
+ code: 403,
155
+ msg: 'Forbidden: Invalid request origin'
156
+ };
157
+ return;
158
+ }
159
+ }
160
+
161
+ // 验证CSRF令牌
162
+ if (!this._validateToken(ctx, config.csrf)) {
163
+ // 强制输出警告日志
164
+ console.warn('CSRF Token Validation Failed:', {
165
+ ip: this._getClientIp(ctx),
166
+ path: ctx.path,
167
+ method: ctx.method
168
+ });
169
+
170
+ if (config.csrf.block) {
171
+ ctx.status = 403;
172
+ ctx.body = {
173
+ code: 403,
174
+ msg: 'Forbidden: Invalid CSRF token'
175
+ };
176
+ return;
177
+ }
178
+ }
179
+
180
+ // 如果需要生成新令牌
181
+ if (config.csrf.generate_new) {
182
+ await this._setNewToken(ctx, config.csrf);
183
+ console.log('New CSRF token set');
184
+ }
185
+ }
186
+ }
187
+
188
+ await next();
189
+ }
190
+
191
+ /**
192
+ * 生成CSRF令牌
193
+ * @param {Object} ctx Koa上下文
194
+ * @returns {String} CSRF令牌
195
+ */
196
+ async _generateToken(ctx) {
197
+ const csrfConfig = this.config.csrf;
198
+ let token = ctx.cookies.get(csrfConfig.cookie_name);
199
+
200
+ // 如果没有令牌或需要生成新令牌,则创建一个新的
201
+ if (!token || csrfConfig.generate_new) {
202
+ await this._setNewToken(ctx, csrfConfig);
203
+ token = ctx.cookies.get(csrfConfig.cookie_name);
204
+ }
205
+
206
+ return token;
207
+ }
208
+
209
+ /**
210
+ * 设置新的CSRF令牌
211
+ * @param {Object} ctx Koa上下文
212
+ * @param {Object} csrfConfig CSRF配置项
213
+ */
214
+ async _setNewToken(ctx, csrfConfig) {
215
+ // 生成随机令牌
216
+ const token = this._createRandomToken();
217
+
218
+ // 将令牌存储到Cookie
219
+ ctx.cookies.set(csrfConfig.cookie_name, token, {
220
+ httpOnly: false, // CSRF令牌需要从前端读取,所以不能设置httpOnly
221
+ maxAge: csrfConfig.max_age,
222
+ sameSite: 'strict', // 防止跨站Cookie发送
223
+ secure: ctx.request.secure // 在HTTPS环境下使用secure标志
224
+ });
225
+ }
226
+
227
+ /**
228
+ * 创建随机令牌
229
+ * @returns {String} 随机令牌
230
+ */
231
+ _createRandomToken() {
232
+ const crypto = require('crypto');
233
+ return crypto.randomBytes(32).toString('hex');
234
+ }
235
+
236
+ /**
237
+ * 检查是否需要跳过CSRF验证
238
+ * @param {Object} ctx Koa上下文
239
+ * @param {Object} csrfConfig CSRF配置项
240
+ * @returns {Boolean} 是否跳过验证
241
+ */
242
+ _shouldSkipCsrfValidation(ctx, csrfConfig) {
243
+ // 检查HTTP方法是否在忽略列表中
244
+ if (csrfConfig.ignore_methods.includes(ctx.method)) {
245
+ return true;
246
+ }
247
+
248
+ // 检查路径是否在忽略列表中
249
+ for (const path of csrfConfig.ignore_paths) {
250
+ if (ctx.path.startsWith(path)) {
251
+ return true;
252
+ }
253
+ }
254
+
255
+ return false;
256
+ }
257
+
258
+ /**
259
+ * 验证来源
260
+ * @param {Object} ctx Koa上下文
261
+ * @param {Object} csrfConfig CSRF配置项
262
+ * @returns {Boolean} 验证是否通过
263
+ */
264
+ _validateOrigin(ctx, csrfConfig) {
265
+ const origin = ctx.headers.origin;
266
+ const referer = ctx.headers.referer;
267
+ const allowedOrigins = csrfConfig.allowed_origins || [];
268
+
269
+ // 如果没有配置允许的来源,则默认为通过
270
+ if (allowedOrigins.length === 0) {
271
+ return true;
272
+ }
273
+
274
+ // 检查origin
275
+ if (origin) {
276
+ const originHost = new URL(origin).hostname;
277
+ for (const allowed of allowedOrigins) {
278
+ if (originHost === allowed || originHost.endsWith('.' + allowed)) {
279
+ return true;
280
+ }
281
+ }
282
+ }
283
+
284
+ // 检查referer
285
+ if (referer) {
286
+ const refererHost = new URL(referer).hostname;
287
+ for (const allowed of allowedOrigins) {
288
+ if (refererHost === allowed || refererHost.endsWith('.' + allowed)) {
289
+ return true;
290
+ }
291
+ }
292
+ }
293
+
294
+ return false;
295
+ }
296
+
297
+ /**
298
+ * 验证CSRF令牌
299
+ * @param {Object} ctx Koa上下文
300
+ * @param {Object} csrfConfig CSRF配置项
301
+ * @returns {Boolean} 验证是否通过
302
+ */
303
+ _validateToken(ctx, csrfConfig) {
304
+ const token = ctx.cookies.get(csrfConfig.cookie_name);
305
+ if (!token) {
306
+ return false;
307
+ }
308
+
309
+ // 从请求头中获取令牌
310
+ const headerToken = ctx.headers[csrfConfig.header_name.toLowerCase()];
311
+
312
+ // 从请求体中获取令牌
313
+ let bodyToken;
314
+ if (ctx.request.body) {
315
+ bodyToken = ctx.request.body[csrfConfig.form_field];
316
+ }
317
+
318
+ // 从查询参数中获取令牌
319
+ const queryToken = ctx.query[csrfConfig.form_field];
320
+
321
+ // 验证令牌是否匹配
322
+ return headerToken === token || bodyToken === token || queryToken === token;
323
+ }
324
+
325
+ /**
326
+ * 获取客户端IP
327
+ * @param {Object} ctx Koa上下文
328
+ * @returns {String} 客户端IP
329
+ */
330
+ _getClientIp(ctx) {
331
+ return ctx.headers['x-forwarded-for'] ||
332
+ ctx.headers['X-Forwarded-For'] ||
333
+ ctx.headers['x-real-ip'] ||
334
+ ctx.connection.remoteAddress ||
335
+ ctx.socket.remoteAddress ||
336
+ ctx.connection.socket.remoteAddress;
337
+ }
338
+
339
+ /**
340
+ * 设置CORS头
341
+ * @param {Object} ctx Koa上下文
342
+ * @param {Object} corsConfig CORS配置项
343
+ */
344
+ async _setCorsHeaders(ctx, corsConfig) {
345
+ try {
346
+ // 设置允许的源
347
+ ctx.set('Access-Control-Allow-Origin', corsConfig.origin);
348
+
349
+ // 设置允许的HTTP方法
350
+ if (Array.isArray(corsConfig.methods) && corsConfig.methods.length > 0) {
351
+ ctx.set('Access-Control-Allow-Methods', corsConfig.methods.join(','));
352
+ }
353
+
354
+ // 设置允许的请求头
355
+ if (corsConfig.headers) {
356
+ ctx.set('Access-Control-Allow-Headers', corsConfig.headers);
357
+ }
358
+
359
+ // 设置是否允许携带凭证
360
+ if (corsConfig.credentials) {
361
+ ctx.set('Access-Control-Allow-Credentials', 'true');
362
+ }
363
+
364
+ // 设置预检请求的有效期
365
+ if (corsConfig.max_age) {
366
+ ctx.set('Access-Control-Max-Age', corsConfig.max_age.toString());
367
+ }
368
+
369
+ // 设置暴露的响应头
370
+ if (Array.isArray(corsConfig.expose_headers) && corsConfig.expose_headers.length > 0) {
371
+ ctx.set('Access-Control-Expose-Headers', corsConfig.expose_headers.join(','));
372
+ }
373
+ } catch (error) {
374
+ if ($.log) {
375
+ $.log.error('设置CORS头错误:', error);
376
+ }
377
+ }
378
+ }
379
+
380
+ /**
381
+ * 设置安全头
382
+ * @param {Object} ctx Koa上下文
383
+ * @param {Object} securityHeadersConfig 安全头配置项
384
+ */
385
+ _setSecurityHeaders(ctx, securityHeadersConfig) {
386
+ try {
387
+ // X-Content-Type-Options
388
+ if (securityHeadersConfig.content_type_options) {
389
+ ctx.set('X-Content-Type-Options', securityHeadersConfig.content_type_options);
390
+ }
391
+
392
+ // X-Frame-Options
393
+ if (securityHeadersConfig.frame_options) {
394
+ ctx.set('X-Frame-Options', securityHeadersConfig.frame_options);
395
+ }
396
+
397
+ // X-XSS-Protection
398
+ if (securityHeadersConfig.xss_protection) {
399
+ ctx.set('X-XSS-Protection', securityHeadersConfig.xss_protection);
400
+ }
401
+
402
+ // Content-Security-Policy
403
+ if (securityHeadersConfig.content_security_policy) {
404
+ ctx.set('Content-Security-Policy', securityHeadersConfig.content_security_policy);
405
+ }
406
+
407
+ // Strict-Transport-Security
408
+ if (securityHeadersConfig.strict_transport_security) {
409
+ ctx.set('Strict-Transport-Security', securityHeadersConfig.strict_transport_security);
410
+ }
411
+
412
+ // Referrer-Policy
413
+ if (securityHeadersConfig.referrer_policy) {
414
+ ctx.set('Referrer-Policy', securityHeadersConfig.referrer_policy);
415
+ }
416
+ } catch (error) {
417
+ if ($.log) {
418
+ $.log.error('设置安全头错误:', error);
419
+ }
420
+ }
421
+ }
422
+
423
+ /**
424
+ * 深度合并对象
425
+ * @param {Object} target 目标对象
426
+ * @param {Object} source 源对象
427
+ * @returns {Object} 合并后的对象
428
+ */
429
+ _deepMerge(target, source) {
430
+ const output = {
431
+ ...target
432
+ };
433
+
434
+ if (this._isObject(target) && this._isObject(source)) {
435
+ Object.keys(source).forEach(key => {
436
+ if (this._isObject(source[key])) {
437
+ if (!(key in target)) {
438
+ Object.assign(output, {
439
+ [key]: source[key]
440
+ });
441
+ } else {
442
+ output[key] = this._deepMerge(target[key], source[key]);
443
+ }
444
+ } else {
445
+ Object.assign(output, {
446
+ [key]: source[key]
447
+ });
448
+ }
449
+ });
450
+ }
451
+
452
+ return output;
453
+ }
454
+
455
+ /**
456
+ * 检查对象是否为纯对象
457
+ * @param {*} item 要检查的项
458
+ * @returns {Boolean} 是否为纯对象
459
+ */
460
+ _isObject(item) {
461
+ return item && typeof item === 'object' && !Array.isArray(item);
462
+ }
463
+ }
464
+
465
+ // 创建中间件实例
466
+ const middleware = new Middleware();
467
+
468
+ // 导出符合系统期望的函数
469
+ exports = module.exports = function(server, config) {
470
+ // 初始化中间件
471
+ middleware.init(config);
472
+
473
+ // 注册中间件到服务器
474
+ server.use(middleware.run.bind(middleware));
475
+
476
+ // 记录中间件初始化信息
477
+ if ($.log && $.log.info) {
478
+ $.log.info(
479
+ `安全头防护中间件已加载: CSRF=${middleware.config.csrf.enable ? '已启用' : '已禁用'}, CORS=${middleware.config.cors.enable ? '已启用' : '已禁用'}`
480
+ );
481
+ }
482
+
483
+ return server;
484
+ };
485
+
486
+ // 保留原始实例,以便其他方式调用
487
+ exports.middleware = middleware;
@@ -0,0 +1,45 @@
1
+ {
2
+ "name": "security_headers",
3
+ "title": "安全头与跨域保护",
4
+ "description": "综合安全中间件,合并了CSRF保护、CORS处理和安全头设置功能",
5
+ "version": "1.0",
6
+ "type": "web",
7
+ "process_type": "common_before",
8
+ "sort": 10,
9
+ "state": 1,
10
+ "config": {
11
+ "csrf": {
12
+ "enable": true,
13
+ "cookie_name": "csrf_token",
14
+ "header_name": "X-CSRF-Token",
15
+ "form_field": "_csrf",
16
+ "max_age": 3600000,
17
+ "ignore_methods": ["GET", "HEAD", "OPTIONS"],
18
+ "ignore_paths": ["/api/wx", "/api/wechat", "/api/alipay", "/api/baidu", "/api/tencent", "/api/qq", "/api/cloud", "/api/login", "/api/public"],
19
+ "generate_new": true,
20
+ "check_origin": true,
21
+ "log": true,
22
+ "block": true,
23
+ "allowed_origins": ["weixin.qq.com", "wx.qq.com", "servicewechat.com", "alipay.com", "taobao.com", "tmall.com", "baidu.com", "aliyun.com", "tencent.com", "tencentcloud.com", "qq.com"]
24
+ },
25
+ "cors": {
26
+ "enable": true,
27
+ "origin": "*",
28
+ "headers": "*",
29
+ "methods": ["GET", "POST", "PUT", "DELETE", "OPTIONS", "HEAD"],
30
+ "credentials": false,
31
+ "max_age": 3600,
32
+ "expose_headers": []
33
+ },
34
+ "security_headers": {
35
+ "enable": true,
36
+ "content_type_options": "nosniff",
37
+ "frame_options": "SAMEORIGIN",
38
+ "xss_protection": "1; mode=block",
39
+ "content_security_policy": "default-src 'self'; script-src 'self'; style-src 'self'; img-src 'self' data:; font-src 'self'; connect-src 'self'; frame-src 'none';",
40
+ "strict_transport_security": "max-age=31536000; includeSubDomains",
41
+ "referrer_policy": "strict-origin-when-cross-origin"
42
+ },
43
+ "allowed_origins": ["weixin.qq.com", "wx.qq.com", "servicewechat.com", "alipay.com", "taobao.com", "tmall.com", "baidu.com", "aliyun.com", "tencent.com", "tencentcloud.com", "qq.com"]
44
+ }
45
+ }