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
@@ -16,34 +16,200 @@ function getClientIP(req) {
16
16
  if (ip && ip.split(',').length > 0) {
17
17
  ip = ip.split(',')[0]; // 取第一个IP地址
18
18
  }
19
+ // 处理IPv6本地地址
20
+ if (ip === '::1' || ip === '::ffff:127.0.0.1') {
21
+ ip = '127.0.0.1';
22
+ }
23
+ // 移除可能的端口号
24
+ if (ip && ip.includes(':')) {
25
+ ip = ip.split(':')[0];
26
+ }
19
27
  return ip;
20
28
  };
21
29
 
30
+ /**
31
+ * 检查IP是否在白名单中
32
+ * @param {String} ip IP地址
33
+ * @param {Array} whitelist 白名单IP数组
34
+ * @returns {Boolean} 是否在白名单中
35
+ */
36
+ function isInWhitelist(ip, whitelist) {
37
+ if (!whitelist || !Array.isArray(whitelist)) return false;
38
+ return whitelist.some(whiteIP => {
39
+ // 支持CIDR格式(如192.168.1.0/24)
40
+ if (whiteIP.includes('/')) {
41
+ return ipInCidr(ip, whiteIP);
42
+ }
43
+ return ip === whiteIP;
44
+ });
45
+ }
46
+
47
+ /**
48
+ * 检查IP是否在CIDR范围内
49
+ * @param {String} ip IP地址
50
+ * @param {String} cidr CIDR格式字符串(如192.168.1.0/24)
51
+ * @returns {Boolean} 是否在范围内
52
+ */
53
+ function ipInCidr(ip, cidr) {
54
+ const [network, prefixLength] = cidr.split('/');
55
+ const netmask = -1 << (32 - parseInt(prefixLength, 10));
56
+
57
+ function ipToInt(ip) {
58
+ return ip.split('.').reduce((acc, octet) => (acc << 8) + parseInt(octet, 10), 0) >>> 0;
59
+ }
60
+
61
+ return (ipToInt(ip) & netmask) === (ipToInt(network) & netmask);
62
+ }
63
+
22
64
  /**
23
65
  * 设置黑名单
24
66
  * @param {String} ip IP地址
67
+ * @param {Object} config 配置参数
25
68
  */
26
- function setting_blacklist(ip) {
69
+ async function setting_blacklist(ip, config) {
70
+ // 检查是否已在黑名单中
71
+ const blacklistKey = `blacklist_${ip}`;
72
+ try {
73
+ const isBlacklisted = await $.cache.get(blacklistKey);
74
+ if (isBlacklisted) {
75
+ $.log.info(`IP ${ip} 已在黑名单中,无需重复添加`);
76
+ return;
77
+ }
78
+ } catch (error) {
79
+ $.log.error('检查黑名单缓存错误:', error);
80
+ }
81
+
27
82
  var cmd;
28
83
  if (platform == "win32") {
29
- // window 系统
30
- cmd =
31
- `netsh advfirewall firewall add rule name="Blacklist ${ip}" dir=in action=block remoteip="${ip}" protocol=any`
84
+ // Windows系统
85
+ cmd = `netsh advfirewall firewall add rule name="Blacklist ${ip}" dir=in action=block remoteip="${ip}" protocol=any`;
32
86
  } else {
33
- // linux 系统
34
- cmd = `sudo iptables -A INPUT -s ${ip} -j DROP`;
87
+ // Linux系统 - 不使用sudo,让系统确保权限
88
+ cmd = `iptables -A INPUT -s ${ip} -j DROP`;
35
89
  }
36
90
 
37
- exec(cmd, (error, stdout, stderr) => {
38
- if (error) {
39
- console.error(`执行的错误: ${error}`);
40
- return;
91
+ try {
92
+ // 使用Promise包装exec以支持异步操作
93
+ await new Promise((resolve, reject) => {
94
+ exec(cmd, (error, stdout, stderr) => {
95
+ if (error) {
96
+ console.error(`执行黑名单命令错误: ${error}`);
97
+ reject(error);
98
+ return;
99
+ }
100
+ $.log.info(`IP ${ip} 已加入防火墙黑名单`);
101
+ if (stderr) {
102
+ console.error(`黑名单命令标准错误: ${stderr}`);
103
+ }
104
+ resolve();
105
+ });
106
+ });
107
+
108
+ // 记录到缓存,支持黑名单过期时间
109
+ const expireTime = config.blacklist_expire_time || 86400000; // 默认1天
110
+ await $.cache.set(blacklistKey, true, expireTime);
111
+
112
+ // 记录黑名单IP,便于管理
113
+ const blacklistIPsKey = 'blacklisted_ips';
114
+ let blacklistIPs = [];
115
+ try {
116
+ const existing = await $.cache.get(blacklistIPsKey);
117
+ if (existing) {
118
+ blacklistIPs = typeof existing === 'string' ? JSON.parse(existing) : existing;
119
+ }
120
+ if (!Array.isArray(blacklistIPs)) {
121
+ blacklistIPs = [];
122
+ }
123
+ if (!blacklistIPs.includes(ip)) {
124
+ blacklistIPs.push(ip);
125
+ await $.cache.set(blacklistIPsKey, blacklistIPs, 0); // 永不过期
126
+ }
127
+ } catch (cacheError) {
128
+ $.log.error('保存黑名单IP列表错误:', cacheError);
129
+ }
130
+ } catch (error) {
131
+ $.log.error(`添加IP ${ip} 到黑名单失败:`, error);
132
+ }
133
+ }
134
+
135
+ /**
136
+ * 从防火墙中移除黑名单IP
137
+ * @param {String} ip IP地址
138
+ */
139
+ async function remove_blacklist(ip) {
140
+ var cmd;
141
+ if (platform == "win32") {
142
+ // Windows系统
143
+ cmd = `netsh advfirewall firewall delete rule name="Blacklist ${ip}"`;
144
+ } else {
145
+ // Linux系统
146
+ cmd = `iptables -D INPUT -s ${ip} -j DROP`;
147
+ }
148
+
149
+ try {
150
+ await new Promise((resolve, reject) => {
151
+ exec(cmd, (error, stdout, stderr) => {
152
+ if (error) {
153
+ console.error(`移除黑名单命令错误: ${error}`);
154
+ // 对于Windows,如果规则不存在,delete命令也会返回错误,但这不是严重问题
155
+ if (platform !== "win32") {
156
+ reject(error);
157
+ return;
158
+ }
159
+ }
160
+ $.log.info(`IP ${ip} 已从防火墙黑名单中移除`);
161
+ if (stderr) {
162
+ console.error(`移除黑名单命令标准错误: ${stderr}`);
163
+ }
164
+ resolve();
165
+ });
166
+ });
167
+
168
+ // 从缓存中移除
169
+ await $.cache.del(`blacklist_${ip}`);
170
+
171
+ // 从黑名单列表中移除
172
+ const blacklistIPsKey = 'blacklisted_ips';
173
+ try {
174
+ const existing = await $.cache.get(blacklistIPsKey);
175
+ if (existing) {
176
+ let blacklistIPs = typeof existing === 'string' ? JSON.parse(existing) : existing;
177
+ if (Array.isArray(blacklistIPs)) {
178
+ blacklistIPs = blacklistIPs.filter(ipItem => ipItem !== ip);
179
+ await $.cache.set(blacklistIPsKey, blacklistIPs, 0);
180
+ }
181
+ }
182
+ } catch (cacheError) {
183
+ $.log.error('更新黑名单IP列表错误:', cacheError);
41
184
  }
42
- $.log.info(`加入黑名单: ${ip}`);
43
- if (stderr) {
44
- console.error(`标准错误输出: ${stderr}`);
185
+ } catch (error) {
186
+ $.log.error(`移除IP ${ip} 从黑名单失败:`, error);
187
+ }
188
+ }
189
+
190
+ /**
191
+ * 初始化功能 - 加载持久化的黑名单并应用
192
+ * @param {Object} config 配置参数
193
+ */
194
+ async function initBlacklist(config) {
195
+ if (!config.persist_blacklist) return;
196
+
197
+ try {
198
+ const blacklistIPsKey = 'blacklisted_ips';
199
+ const existing = await $.cache.get(blacklistIPsKey);
200
+ if (existing) {
201
+ let blacklistIPs = typeof existing === 'string' ? JSON.parse(existing) : existing;
202
+ if (Array.isArray(blacklistIPs) && blacklistIPs.length > 0) {
203
+ $.log.info(`正在应用 ${blacklistIPs.length} 个持久化的黑名单IP规则...`);
204
+ for (const ip of blacklistIPs) {
205
+ // 重新应用黑名单规则
206
+ await setting_blacklist(ip, config);
207
+ }
208
+ }
45
209
  }
46
- });
210
+ } catch (error) {
211
+ $.log.error('初始化持久化黑名单失败:', error);
212
+ }
47
213
  }
48
214
 
49
215
  /**
@@ -51,10 +217,19 @@ function setting_blacklist(ip) {
51
217
  * @param {Object} server 服务
52
218
  * @param {Object} config 配置参数
53
219
  */
54
- module.exports = function(server, config) {
55
- var limit = config.request_limit || 0;
56
- var duration = config.request_duration || 0;
57
- var block = config.request_block || false;
220
+ module.exports = async function(server, config) {
221
+ // 从配置中获取参数,提供默认值
222
+ var limit = config.request_limit || 1000; // 默认每分钟1000请求
223
+ var duration = config.request_duration || 60000; // 默认60秒
224
+ var block = config.request_block || true; // 默认启用黑名单
225
+ var whitelist = config.ip_whitelist || []; // IP白名单
226
+ var blacklist_expire_time = config.blacklist_expire_time || 86400000; // 默认黑名单1天过期
227
+ var persist_blacklist = config.persist_blacklist !== undefined ? config.persist_blacklist : true; // 是否持久化黑名单
228
+
229
+ // 初始化持久化黑名单
230
+ if (persist_blacklist) {
231
+ await initBlacklist(config);
232
+ }
58
233
 
59
234
  if (limit && duration) {
60
235
  /* WAF(web防火墙) */
@@ -63,11 +238,33 @@ module.exports = function(server, config) {
63
238
  var pass = true;
64
239
  // 获取IP
65
240
  var ip = getClientIP(ctx.req);
241
+
242
+ // 检查白名单
243
+ if (isInWhitelist(ip, whitelist)) {
244
+ ctx.request.ip = ip;
245
+ ctx.ip = ip;
246
+ await next();
247
+ return;
248
+ }
249
+
250
+ // 检查是否已被黑名单
251
+ try {
252
+ const isBlacklisted = await $.cache.get(`blacklist_${ip}`);
253
+ if (isBlacklisted) {
254
+ ctx.status = 403;
255
+ ctx.body = '您的IP已被禁止访问';
256
+ return;
257
+ }
258
+ } catch (blacklistCheckError) {
259
+ $.log.error('检查黑名单状态错误:', blacklistCheckError);
260
+ }
261
+
66
262
  var num = 1;
67
263
  var now = new Date();
68
264
  var date = now.toStr('yyyy-MM-dd');
69
265
  var time;
70
266
  var json;
267
+
71
268
  try {
72
269
  var str = await $.cache.get("ip_" + ip);
73
270
  if (str) {
@@ -81,6 +278,7 @@ module.exports = function(server, config) {
81
278
  } else {
82
279
  json = str;
83
280
  }
281
+
84
282
  if (json) {
85
283
  try {
86
284
  if (json.date !== date) {
@@ -100,7 +298,7 @@ module.exports = function(server, config) {
100
298
  // 超出上限禁止访问,并加入黑名单
101
299
  pass = false;
102
300
  if (block) {
103
- setting_blacklist(ip);
301
+ await setting_blacklist(ip, config);
104
302
  }
105
303
  }
106
304
  }
@@ -115,7 +313,7 @@ module.exports = function(server, config) {
115
313
  // 超出上限禁止访问,并加入黑名单
116
314
  pass = false;
117
315
  if (block) {
118
- setting_blacklist(ip);
316
+ await setting_blacklist(ip, config);
119
317
  }
120
318
  }
121
319
  }
@@ -135,9 +333,11 @@ module.exports = function(server, config) {
135
333
  $.log.error('WAF IP中间件缓存操作错误:', cacheError);
136
334
  // 缓存出错时,默认允许请求通过
137
335
  }
336
+
138
337
  if (!time) {
139
338
  time = now.toStr('yyyy-MM-dd hh:mm:ss');
140
339
  }
340
+
141
341
  if (pass) {
142
342
  try {
143
343
  await $.cache.set("ip_" + ip, JSON.stringify({
@@ -164,5 +364,16 @@ module.exports = function(server, config) {
164
364
  }
165
365
  });
166
366
  }
367
+
368
+ // 提供手动管理黑名单的API方法
369
+ if (!$.waf_ip) {
370
+ $.waf_ip = {
371
+ addBlacklist: setting_blacklist,
372
+ removeBlacklist: remove_blacklist,
373
+ getClientIP: getClientIP,
374
+ isInWhitelist: isInWhitelist
375
+ };
376
+ }
377
+
167
378
  return server;
168
379
  };
@@ -1,10 +1,49 @@
1
1
  {
2
2
  "name": "web_waf_ip",
3
- "title": "IP防火墙",
4
- "description": "用于防止DOS攻击",
5
- "version": "1.0",
3
+ "title": "IP防火墙增强版",
4
+ "description": "增强版IP防火墙,支持IP白名单、黑名单持久化、自动过期等功能,有效防止DOS攻击",
5
+ "version": "2.0",
6
6
  "type": "web",
7
7
  "process_type": "common_before",
8
8
  "sort": 10,
9
- "state": 0
9
+ "state": 1,
10
+ "config": {
11
+ "request_limit": {
12
+ "type": "number",
13
+ "title": "请求限制数量",
14
+ "description": "在指定时间内允许的最大请求数",
15
+ "value": 1000,
16
+ "placeholder": "请输入数字"
17
+ },
18
+ "request_duration": {
19
+ "type": "number",
20
+ "title": "统计时间窗口(毫秒)",
21
+ "description": "请求计数的时间窗口,单位为毫秒",
22
+ "value": 60000,
23
+ "placeholder": "请输入毫秒数"
24
+ },
25
+ "request_block": {
26
+ "type": "boolean",
27
+ "title": "启用黑名单封禁",
28
+ "description": "是否将超限IP添加到系统防火墙黑名单",
29
+ "value": true
30
+ },
31
+ "ip_whitelist": [
32
+ "192.168.31.5",
33
+ "127.0.0.1"
34
+ ],
35
+ "blacklist_expire_time": {
36
+ "type": "number",
37
+ "title": "黑名单过期时间(毫秒)",
38
+ "description": "IP被添加到黑名单后自动失效的时间,单位为毫秒",
39
+ "value": 86400000,
40
+ "placeholder": "请输入毫秒数"
41
+ },
42
+ "persist_blacklist": {
43
+ "type": "boolean",
44
+ "title": "持久化黑名单",
45
+ "description": "系统重启后是否自动重新应用已保存的黑名单规则",
46
+ "value": true
47
+ }
48
+ }
10
49
  }
@@ -0,0 +1,269 @@
1
+ const mm_expand = require('mm_expand');
2
+
3
+ /**
4
+ * XSS攻击防护中间件
5
+ */
6
+ class Middleware {
7
+ /**
8
+ * 构造函数
9
+ */
10
+ constructor() {
11
+ this.default = {
12
+ // 过滤模式: sanitize(清理) | encode(编码) | both(同时进行)
13
+ mode: 'both',
14
+ // 是否过滤URL参数
15
+ query: true,
16
+ // 是否过滤表单数据
17
+ body: true,
18
+ // 是否过滤Cookie
19
+ cookie: true,
20
+ // 白名单标签
21
+ allowedTags: ['p', 'span', 'b', 'i', 'u', 'strong', 'em', 'br'],
22
+ // 白名单属性
23
+ allowedAttributes: ['id', 'class', 'style'],
24
+ // 是否记录XSS攻击尝试
25
+ log: true,
26
+ // 是否阻止恶意请求
27
+ block: true
28
+ };
29
+ }
30
+
31
+ /**
32
+ * 初始化中间件
33
+ * @param {Object} config 配置项
34
+ */
35
+ init(config) {
36
+ config = Object.assign({}, this.default, config);
37
+ this.config = config;
38
+ return function() {
39
+ // 返回中间件的run方法作为实际的处理函数
40
+ return function(ctx, next) {
41
+ return middleware.run(ctx, next);
42
+ };
43
+ };
44
+ }
45
+
46
+ /**
47
+ * 获取客户端真实IP
48
+ * 在代理环境中,优先从代理头获取真实IP
49
+ * @param {Object} ctx Koa上下文
50
+ * @returns {String} 客户端真实IP
51
+ */
52
+ getClientIp(ctx) {
53
+ const headers = ctx.headers;
54
+ // 优先检查常用代理头获取真实IP
55
+ // 注意:X-Forwarded-For 可能包含多个IP,取第一个非未知IP的值
56
+ const xForwardedFor = headers['x-forwarded-for'];
57
+ if (xForwardedFor) {
58
+ // X-Forwarded-For 格式通常为: client, proxy1, proxy2
59
+ const ips = xForwardedFor.split(',').map(ip => ip.trim());
60
+ for (let i = 0; i < ips.length; i++) {
61
+ const ip = ips[i];
62
+ if (ip && ip !== 'unknown' && ip !== '127.0.0.1' && ip !== '::1') {
63
+ return ip;
64
+ }
65
+ }
66
+ }
67
+
68
+ // 检查其他常见的代理头
69
+ return headers['x-real-ip'] ||
70
+ headers['x-client-ip'] ||
71
+ headers['cf-connecting-ip'] || // CloudFlare
72
+ headers['fastly-client-ip'] || // Fastly
73
+ headers['true-client-ip'] || // Akamai and Cloudflare
74
+ ctx.ip; // 默认回退到ctx.ip
75
+ }
76
+
77
+ /**
78
+ * 执行中间件
79
+ * @param {Object} ctx Koa上下文
80
+ * @param {Function} next 下一个中间件
81
+ */
82
+ async run(ctx, next) {
83
+ const config = this.config;
84
+ let hasXSS = false;
85
+ let attackInfo = {};
86
+
87
+ // 清理URL查询参数
88
+ if (config.query && ctx.query) {
89
+ for (let key in ctx.query) {
90
+ const original = ctx.query[key];
91
+ const sanitized = this.sanitizeInput(original, config);
92
+ if (original !== sanitized) {
93
+ ctx.query[key] = sanitized;
94
+ hasXSS = true;
95
+ attackInfo[`query.${key}`] = { original, sanitized };
96
+ }
97
+ }
98
+ }
99
+
100
+ // 清理请求体数据
101
+ if (config.body && ctx.request.body) {
102
+ this.cleanObject(ctx.request.body, config, attackInfo, 'body', hasXSS);
103
+ }
104
+
105
+ // 清理Cookie
106
+ if (config.cookie && ctx.cookies) {
107
+ const cookies = ctx.headers.cookie;
108
+ if (cookies) {
109
+ // 记录Cookie中的XSS尝试但不修改,因为修改Cookie需要重新设置
110
+ const cookieArr = cookies.split(';');
111
+ for (let cookie of cookieArr) {
112
+ const [name, value] = cookie.split('=').map(item => item.trim());
113
+ const sanitized = this.sanitizeInput(decodeURIComponent(value), config);
114
+ if (value !== sanitized) {
115
+ hasXSS = true;
116
+ attackInfo[`cookie.${name}`] = { original: value, sanitized };
117
+ }
118
+ }
119
+ }
120
+ }
121
+
122
+ // 记录XSS攻击尝试
123
+ if (hasXSS && config.log && $.log) {
124
+ $.log.warn('XSS Attack Attempt Detected:', {
125
+ ip: this.getClientIp(ctx),
126
+ path: ctx.path,
127
+ method: ctx.method,
128
+ attackInfo
129
+ });
130
+ }
131
+
132
+ // 阻止恶意请求
133
+ if (hasXSS && config.block) {
134
+ ctx.status = 403;
135
+ ctx.body = {
136
+ code: 403,
137
+ msg: 'Forbidden: Potential XSS attack detected'
138
+ };
139
+ return;
140
+ }
141
+
142
+ // 添加安全响应头
143
+ this.addSecurityHeaders(ctx);
144
+
145
+ await next();
146
+ }
147
+
148
+ /**
149
+ * 递归清理对象中的XSS内容
150
+ * @param {Object} obj 要清理的对象
151
+ * @param {Object} config 配置项
152
+ * @param {Object} attackInfo 攻击信息收集对象
153
+ * @param {String} prefix 路径前缀
154
+ * @param {Boolean} hasXSS 是否已发现XSS
155
+ */
156
+ cleanObject(obj, config, attackInfo, prefix, hasXSS) {
157
+ if (!obj || typeof obj !== 'object') return;
158
+
159
+ for (let key in obj) {
160
+ const path = `${prefix}.${key}`;
161
+ if (typeof obj[key] === 'string') {
162
+ const original = obj[key];
163
+ const sanitized = this.sanitizeInput(original, config);
164
+ if (original !== sanitized) {
165
+ obj[key] = sanitized;
166
+ hasXSS = true;
167
+ attackInfo[path] = { original, sanitized };
168
+ }
169
+ } else if (typeof obj[key] === 'object') {
170
+ this.cleanObject(obj[key], config, attackInfo, path, hasXSS);
171
+ }
172
+ }
173
+ }
174
+
175
+ /**
176
+ * 清理输入内容
177
+ * @param {String} input 输入内容
178
+ * @param {Object} config 配置项
179
+ * @returns {String} 清理后的内容
180
+ */
181
+ sanitizeInput(input, config) {
182
+ if (!input || typeof input !== 'string') return input;
183
+
184
+ let output = input;
185
+
186
+ // 根据模式选择清理方式
187
+ if (config.mode === 'sanitize' || config.mode === 'both') {
188
+ output = this.sanitizeHTML(output, config);
189
+ }
190
+
191
+ if (config.mode === 'encode' || config.mode === 'both') {
192
+ output = this.encodeHTML(output);
193
+ }
194
+
195
+ return output;
196
+ }
197
+
198
+ /**
199
+ * 清理HTML内容
200
+ * @param {String} html HTML内容
201
+ * @param {Object} config 配置项
202
+ * @returns {String} 清理后的内容
203
+ */
204
+ sanitizeHTML(html, config) {
205
+ // 简单的HTML标签清理实现
206
+ // 在生产环境中,可以使用更强大的库如DOMPurify
207
+ let clean = html;
208
+
209
+ // 移除所有脚本标签
210
+ clean = clean.replace(/<script[^>]*>.*?<\/script>/gi, '');
211
+
212
+ // 移除所有事件处理器
213
+ clean = clean.replace(/\s+on\w+\s*=\s*["\']?[^"\'>\s]+/gi, '');
214
+
215
+ // 移除危险的URL协议
216
+ clean = clean.replace(/(src|href|data|action)\s*=\s*["\']?(javascript|data|vbscript):/gi, '$1=');
217
+
218
+ return clean;
219
+ }
220
+
221
+ /**
222
+ * 编码HTML特殊字符
223
+ * @param {String} html HTML内容
224
+ * @returns {String} 编码后的内容
225
+ */
226
+ encodeHTML(html) {
227
+ return String(html)
228
+ .replace(/&/g, '&amp;')
229
+ .replace(/</g, '&lt;')
230
+ .replace(/>/g, '&gt;')
231
+ .replace(/"/g, '&quot;')
232
+ .replace(/'/g, '&#39;');
233
+ }
234
+
235
+ /**
236
+ * 添加安全响应头
237
+ * @param {Object} ctx Koa上下文
238
+ */
239
+ addSecurityHeaders(ctx) {
240
+ // X-XSS-Protection 头
241
+ ctx.set('X-XSS-Protection', '1; mode=block');
242
+
243
+ // Content-Security-Policy 头
244
+ ctx.set('Content-Security-Policy', "default-src 'self'; script-src 'self'; style-src 'self' 'unsafe-inline'; img-src 'self' data:;");
245
+ }
246
+ }
247
+
248
+ // 创建中间件实例
249
+ const middleware = new Middleware();
250
+
251
+ // 导出符合系统期望的函数
252
+ exports = module.exports = function(server, config) {
253
+ // 初始化中间件
254
+ const handlerFactory = middleware.init(config);
255
+ const middlewareHandler = handlerFactory();
256
+
257
+ // 直接使用server.use注册中间件
258
+ server.use(middlewareHandler);
259
+
260
+ // 记录中间件初始化信息
261
+ if ($.log && $.log.info) {
262
+ $.log.info(`XSS防护中间件已加载: 模式=${middleware.config.mode}, 过滤=${middleware.config.query ? 'query' : ''}${middleware.config.body ? ' body' : ''}${middleware.config.cookie ? ' cookie' : ''}`);
263
+ }
264
+
265
+ return server;
266
+ };
267
+
268
+ // 保留原始实例,以便其他方式调用
269
+ exports.middleware = middleware;
@@ -0,0 +1,18 @@
1
+ {
2
+ "name": "waf_xss",
3
+ "title": "XSS攻击防护中间件",
4
+ "type": "web_before",
5
+ "status": 1,
6
+ "sort": 10,
7
+ "desc": "提供全面的XSS攻击防护,包括输入过滤和安全响应头设置",
8
+ "config": {
9
+ "mode": "both",
10
+ "query": true,
11
+ "body": true,
12
+ "cookie": true,
13
+ "allowedTags": ["p", "span", "b", "i", "u", "strong", "em", "br"],
14
+ "allowedAttributes": ["id", "class", "style"],
15
+ "log": true,
16
+ "block": true
17
+ }
18
+ }
@@ -1,5 +1,5 @@
1
1
  {
2
- "name": "web_berofe",
2
+ "name": "web_before",
3
3
  "title": "web请求前",
4
4
  "description": "用于",
5
5
  "version": "1.0",
@@ -1,6 +1,6 @@
1
1
  {
2
- "name": "web_scoket",
3
- "title": "webscoket通讯",
2
+ "name": "web_socket",
3
+ "title": "websocket通讯",
4
4
  "description": "用于即时通讯",
5
5
  "version": "1.0",
6
6
  "type": "web",