mm_os 3.3.0 → 3.3.1
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/core/base/mqtt/index.js +1109 -1106
- package/core/base/web/index.js +245 -243
- package/core/com/event/README.md +4 -4
- package/core/com/event/com.json +3 -3
- package/core/com/event/config.tpl.json +18 -18
- package/core/com/event/drive.js +132 -132
- package/core/com/event/index.js +344 -344
- package/core/com/event/script.js +25 -25
- package/core/com/middleware/com.js +1 -0
- package/core/com/sql/drive.js +1 -1
- package/core/com/static/index.js +1 -1
- package/index.js +50 -31
- package/middleware/cors/index.js +119 -0
- package/middleware/cors/middleware.json +20 -0
- package/middleware/csrf/index.js +202 -0
- package/middleware/csrf/middleware.json +24 -0
- package/middleware/ip_firewall/index.js +476 -0
- package/middleware/ip_firewall/middleware.json +109 -0
- package/middleware/mqtt_base/middleware.json +2 -1
- package/middleware/security_audit/index.js +13 -19
- package/middleware/security_audit/middleware.json +3 -3
- package/middleware/waf/index.js +27 -32
- package/middleware/waf_ddos/index.js +2 -2
- package/middleware/waf_ddos/middleware.json +3 -3
- package/middleware/waf_xss/index.js +1 -1
- package/middleware/waf_xss/middleware.json +1 -1
- package/middleware/web_after/middleware.json +2 -1
- package/middleware/web_base/middleware.json +2 -1
- package/middleware/web_before/middleware.json +2 -1
- package/middleware/web_check/middleware.json +2 -1
- package/middleware/web_main/middleware.json +2 -1
- package/middleware/web_proxy/middleware.json +2 -1
- package/middleware/web_render/middleware.json +2 -1
- package/middleware/web_socket/middleware.json +2 -1
- package/middleware/web_static/middleware.json +2 -1
- package/package.json +13 -11
- package/middleware/performance/index.js +0 -151
- package/middleware/performance/middleware.json +0 -16
- package/middleware/security_headers/index.js +0 -487
- package/middleware/security_headers/middleware.json +0 -45
- package/middleware/waf_ip/index.js +0 -379
- package/middleware/waf_ip/middleware.json +0 -49
package/core/com/event/script.js
CHANGED
|
@@ -1,26 +1,26 @@
|
|
|
1
|
-
// 使用api管理器
|
|
2
|
-
var api = $.api_admin('{0}', '{0}');
|
|
3
|
-
// 首次启动更新api接口;
|
|
4
|
-
api.update();
|
|
5
|
-
|
|
6
|
-
// 使用mysql数据库管理器
|
|
7
|
-
var sql = $.mysql_admin('sys', __dirname);
|
|
8
|
-
// sql.setConfig($.config.mysql);
|
|
9
|
-
// sql.open();
|
|
10
|
-
|
|
11
|
-
/**
|
|
12
|
-
* @description 接口主函数
|
|
13
|
-
* @param {Object} ctx HTTP上下文
|
|
14
|
-
* @param {Object} db 数据管理器,如: { next: async function{}, ret: {} }
|
|
15
|
-
* @return {Object} 执行结果
|
|
16
|
-
*/
|
|
17
|
-
async function main(ctx, db) {
|
|
18
|
-
// 使用模板引擎
|
|
19
|
-
db.tpl = new $.Tpl();
|
|
20
|
-
|
|
21
|
-
// 在这定义要访问的数据库 (分布式开发时设置不同的数据库名)
|
|
22
|
-
$.push(db, sql.db(), true);
|
|
23
|
-
return api.run(ctx, db);
|
|
24
|
-
};
|
|
25
|
-
|
|
1
|
+
// 使用api管理器
|
|
2
|
+
var api = $.api_admin('{0}', '{0}');
|
|
3
|
+
// 首次启动更新api接口;
|
|
4
|
+
api.update();
|
|
5
|
+
|
|
6
|
+
// 使用mysql数据库管理器
|
|
7
|
+
var sql = $.mysql_admin('sys', __dirname);
|
|
8
|
+
// sql.setConfig($.config.mysql);
|
|
9
|
+
// sql.open();
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* @description 接口主函数
|
|
13
|
+
* @param {Object} ctx HTTP上下文
|
|
14
|
+
* @param {Object} db 数据管理器,如: { next: async function{}, ret: {} }
|
|
15
|
+
* @return {Object} 执行结果
|
|
16
|
+
*/
|
|
17
|
+
async function main(ctx, db) {
|
|
18
|
+
// 使用模板引擎
|
|
19
|
+
db.tpl = new $.Tpl();
|
|
20
|
+
|
|
21
|
+
// 在这定义要访问的数据库 (分布式开发时设置不同的数据库名)
|
|
22
|
+
$.push(db, sql.db(), true);
|
|
23
|
+
return api.run(ctx, db);
|
|
24
|
+
};
|
|
25
|
+
|
|
26
26
|
exports.main = main;
|
package/core/com/sql/drive.js
CHANGED
package/core/com/static/index.js
CHANGED
|
@@ -117,7 +117,7 @@ Static.prototype.getObj = async function(dir) {
|
|
|
117
117
|
*/
|
|
118
118
|
Static.prototype.update_config_all = async function(path) {
|
|
119
119
|
if (!path) {
|
|
120
|
-
path =
|
|
120
|
+
path = './app'.fullname();
|
|
121
121
|
}
|
|
122
122
|
// 获取所有应用路径
|
|
123
123
|
var search_dir = "static";
|
package/index.js
CHANGED
|
@@ -98,7 +98,7 @@ class OS {
|
|
|
98
98
|
OS.prototype.initData = function(config) {
|
|
99
99
|
var p = config.runPath;
|
|
100
100
|
if (!p) {
|
|
101
|
-
config.runPath = $.runPath
|
|
101
|
+
config.runPath = $.runPath;
|
|
102
102
|
} else if (p.substring(p.length - 1) !== $.slash) {
|
|
103
103
|
config.runPath += $.slash;
|
|
104
104
|
}
|
|
@@ -216,34 +216,53 @@ OS.prototype.init = function(config) {
|
|
|
216
216
|
this.initErrorHandler();
|
|
217
217
|
}
|
|
218
218
|
|
|
219
|
-
/**
|
|
220
|
-
* 运行基础数据
|
|
221
|
-
* @param {Object} cg - 配置对象
|
|
222
|
-
*/
|
|
223
|
-
OS.prototype.initBase = function(cg) {
|
|
224
|
-
var middleware = $.middleware;
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
web
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
219
|
+
/**
|
|
220
|
+
* 运行基础数据
|
|
221
|
+
* @param {Object} cg - 配置对象
|
|
222
|
+
*/
|
|
223
|
+
OS.prototype.initBase = function(cg) {
|
|
224
|
+
var middleware = $.middleware;
|
|
225
|
+
// 更新中间件配置,并且加载JS文件
|
|
226
|
+
middleware.update();
|
|
227
|
+
var mqtt, web;
|
|
228
|
+
if (cg.web && cg.web.state) {
|
|
229
|
+
web = new WEB(cg.web);
|
|
230
|
+
// 过滤出web模式的中间件
|
|
231
|
+
web.list = middleware.list.filter((o) => {
|
|
232
|
+
return !o.mode || o.mode == "web"
|
|
233
|
+
});
|
|
234
|
+
// 预加载中间件函数,确保list中的每个中间件都有func属性
|
|
235
|
+
if (web.list && web.list.length > 0) {
|
|
236
|
+
for (var i = 0; i < web.list.length; i++) {
|
|
237
|
+
var o = web.list[i];
|
|
238
|
+
try {
|
|
239
|
+
if (o.func_file && !o.func) {
|
|
240
|
+
o.func = require(o.func_file);
|
|
241
|
+
}
|
|
242
|
+
} catch (error) {
|
|
243
|
+
if ($.log && $.log.error) {
|
|
244
|
+
$.log.error(`预加载中间件失败: ${o.name}`, error);
|
|
245
|
+
} else {
|
|
246
|
+
console.error(`预加载中间件失败: ${o.name}`, error);
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
web.init();
|
|
252
|
+
this.web = web;
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
if (cg.mqtt && cg.mqtt.state) {
|
|
256
|
+
mqtt = new MQTT(Object.assign(cg.mqtt, {
|
|
257
|
+
redis: cg.redis,
|
|
258
|
+
mongodb: cg.mongodb
|
|
259
|
+
}));
|
|
260
|
+
mqtt.list = middleware.list.filter((o) => {
|
|
261
|
+
return o.mode == "mqtt"
|
|
262
|
+
});
|
|
263
|
+
mqtt.init();
|
|
264
|
+
this.mqtt = mqtt;
|
|
265
|
+
}
|
|
247
266
|
}
|
|
248
267
|
|
|
249
268
|
/**
|
|
@@ -261,8 +280,8 @@ OS.prototype.main = async function(state) {
|
|
|
261
280
|
if (cg.web.socket) {
|
|
262
281
|
tip += " socket";
|
|
263
282
|
}
|
|
264
|
-
var
|
|
265
|
-
|
|
283
|
+
var evt = $.event_admin('api');
|
|
284
|
+
evt.update();
|
|
266
285
|
this.web.run();
|
|
267
286
|
}
|
|
268
287
|
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* CORS跨域中间件
|
|
3
|
+
* 处理跨域资源共享(CORS)配置
|
|
4
|
+
*/
|
|
5
|
+
class CorsMiddleware {
|
|
6
|
+
constructor() {
|
|
7
|
+
this.default = {
|
|
8
|
+
// 启用CORS
|
|
9
|
+
enable: true,
|
|
10
|
+
// 允许的源
|
|
11
|
+
origin: '*',
|
|
12
|
+
// 允许的请求头
|
|
13
|
+
headers: '*',
|
|
14
|
+
// 允许的HTTP方法
|
|
15
|
+
methods: ['GET', 'POST', 'PUT', 'DELETE', 'OPTIONS', 'HEAD'],
|
|
16
|
+
// 允许携带凭证
|
|
17
|
+
credentials: false,
|
|
18
|
+
// 预检请求的有效期(秒)
|
|
19
|
+
max_age: 3600,
|
|
20
|
+
// 暴露的响应头
|
|
21
|
+
expose_headers: [],
|
|
22
|
+
// 忽略的路径
|
|
23
|
+
ignore_paths: []
|
|
24
|
+
};
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
CorsMiddleware.prototype.init = function(config) {
|
|
29
|
+
this.config = Object.assign({}, this.default, config || {});
|
|
30
|
+
return this;
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
CorsMiddleware.prototype.run = async function(ctx, next) {
|
|
34
|
+
const config = this.config;
|
|
35
|
+
|
|
36
|
+
if (!config.enable) {
|
|
37
|
+
return await next();
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
// 检查是否应该忽略该路径
|
|
41
|
+
const path = ctx.path;
|
|
42
|
+
if (config.ignore_paths.some(p => path.startsWith(p))) {
|
|
43
|
+
return await next();
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
// 设置CORS头
|
|
47
|
+
this._setCorsHeaders(ctx, config);
|
|
48
|
+
|
|
49
|
+
// 处理预检请求
|
|
50
|
+
if (ctx.method === 'OPTIONS') {
|
|
51
|
+
ctx.status = 204;
|
|
52
|
+
return;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
await next();
|
|
56
|
+
};
|
|
57
|
+
|
|
58
|
+
CorsMiddleware.prototype._setCorsHeaders = function(ctx, config) {
|
|
59
|
+
// 设置Access-Control-Allow-Origin
|
|
60
|
+
if (config.origin === '*') {
|
|
61
|
+
ctx.set('Access-Control-Allow-Origin', '*');
|
|
62
|
+
} else if (Array.isArray(config.origin)) {
|
|
63
|
+
const requestOrigin = ctx.get('Origin');
|
|
64
|
+
if (config.origin.includes(requestOrigin)) {
|
|
65
|
+
ctx.set('Access-Control-Allow-Origin', requestOrigin);
|
|
66
|
+
}
|
|
67
|
+
} else {
|
|
68
|
+
ctx.set('Access-Control-Allow-Origin', config.origin);
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
// 设置Access-Control-Allow-Headers
|
|
72
|
+
if (config.headers === '*') {
|
|
73
|
+
ctx.set('Access-Control-Allow-Headers', '*');
|
|
74
|
+
} else if (Array.isArray(config.headers)) {
|
|
75
|
+
ctx.set('Access-Control-Allow-Headers', config.headers.join(', '));
|
|
76
|
+
} else {
|
|
77
|
+
ctx.set('Access-Control-Allow-Headers', config.headers);
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
// 设置Access-Control-Allow-Methods
|
|
81
|
+
ctx.set('Access-Control-Allow-Methods', config.methods.join(', '));
|
|
82
|
+
|
|
83
|
+
// 设置Access-Control-Allow-Credentials
|
|
84
|
+
if (config.credentials) {
|
|
85
|
+
ctx.set('Access-Control-Allow-Credentials', 'true');
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
// 设置Access-Control-Max-Age
|
|
89
|
+
if (config.max_age > 0) {
|
|
90
|
+
ctx.set('Access-Control-Max-Age', config.max_age.toString());
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
// 设置Access-Control-Expose-Headers
|
|
94
|
+
if (config.expose_headers.length > 0) {
|
|
95
|
+
ctx.set('Access-Control-Expose-Headers', config.expose_headers.join(', '));
|
|
96
|
+
}
|
|
97
|
+
};
|
|
98
|
+
|
|
99
|
+
// 创建中间件实例
|
|
100
|
+
const middleware = new CorsMiddleware();
|
|
101
|
+
|
|
102
|
+
// 导出符合系统期望的函数
|
|
103
|
+
exports = module.exports = function(server, config) {
|
|
104
|
+
// 初始化中间件
|
|
105
|
+
middleware.init(config);
|
|
106
|
+
|
|
107
|
+
// 注册中间件到服务器
|
|
108
|
+
server.use(middleware.run.bind(middleware));
|
|
109
|
+
|
|
110
|
+
// 记录中间件初始化信息
|
|
111
|
+
if ($.log && $.log.info) {
|
|
112
|
+
$.log.info(`CORS中间件已加载: 启用=${middleware.config.enable}, 源=${middleware.config.origin}`);
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
return server;
|
|
116
|
+
};
|
|
117
|
+
|
|
118
|
+
// 保留原始实例,以便其他方式调用
|
|
119
|
+
exports.middleware = middleware;
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "cors",
|
|
3
|
+
"title": "CORS跨域中间件",
|
|
4
|
+
"description": "处理跨域资源共享(CORS)配置,支持灵活的跨域策略",
|
|
5
|
+
"version": "1.0",
|
|
6
|
+
"type": "web",
|
|
7
|
+
"process_type": "common_before",
|
|
8
|
+
"sort": 15,
|
|
9
|
+
"state": 1,
|
|
10
|
+
"config": {
|
|
11
|
+
"enable": true,
|
|
12
|
+
"origin": "*",
|
|
13
|
+
"headers": "*",
|
|
14
|
+
"methods": ["GET", "POST", "PUT", "DELETE", "OPTIONS", "HEAD"],
|
|
15
|
+
"credentials": false,
|
|
16
|
+
"max_age": 3600,
|
|
17
|
+
"expose_headers": [],
|
|
18
|
+
"ignore_paths": []
|
|
19
|
+
}
|
|
20
|
+
}
|
|
@@ -0,0 +1,202 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* CSRF保护中间件
|
|
3
|
+
* 防止跨站请求伪造攻击
|
|
4
|
+
*/
|
|
5
|
+
class CsrfMiddleware {
|
|
6
|
+
constructor() {
|
|
7
|
+
this.default = {
|
|
8
|
+
// 启用CSRF保护
|
|
9
|
+
enable: true,
|
|
10
|
+
// CSRF令牌的Cookie名称
|
|
11
|
+
cookie_name: 'csrf_token',
|
|
12
|
+
// CSRF令牌的请求头名称
|
|
13
|
+
header_name: 'X-CSRF-Token',
|
|
14
|
+
// CSRF令牌的表单字段名称
|
|
15
|
+
form_field: '_csrf',
|
|
16
|
+
// CSRF令牌的过期时间(毫秒)
|
|
17
|
+
max_age: 3600000, // 1小时
|
|
18
|
+
// 忽略的HTTP方法
|
|
19
|
+
ignore_methods: ['GET', 'HEAD', 'OPTIONS'],
|
|
20
|
+
// 忽略的路径
|
|
21
|
+
ignore_paths: [],
|
|
22
|
+
// 是否生成新的令牌
|
|
23
|
+
generate_new: true,
|
|
24
|
+
// 是否验证来源
|
|
25
|
+
check_origin: true,
|
|
26
|
+
// 是否记录CSRF攻击尝试
|
|
27
|
+
log: true,
|
|
28
|
+
// 是否阻止恶意请求
|
|
29
|
+
block: true,
|
|
30
|
+
// 允许的来源域名
|
|
31
|
+
allowed_origins: []
|
|
32
|
+
};
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
CsrfMiddleware.prototype.init = function(config) {
|
|
37
|
+
this.config = Object.assign({}, this.default, config || {});
|
|
38
|
+
return this;
|
|
39
|
+
};
|
|
40
|
+
|
|
41
|
+
CsrfMiddleware.prototype.run = async function(ctx, next) {
|
|
42
|
+
const config = this.config;
|
|
43
|
+
|
|
44
|
+
if (!config.enable) {
|
|
45
|
+
return await next();
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
// 生成CSRF令牌
|
|
49
|
+
const token = await this._generateToken(ctx);
|
|
50
|
+
|
|
51
|
+
// 将令牌添加到上下文,供模板使用
|
|
52
|
+
ctx.state.csrf = token;
|
|
53
|
+
|
|
54
|
+
// 检查是否需要验证CSRF
|
|
55
|
+
if (!this._shouldSkipCsrfValidation(ctx, config)) {
|
|
56
|
+
// 验证来源
|
|
57
|
+
if (config.check_origin && !this._validateOrigin(ctx, config)) {
|
|
58
|
+
if (config.block) {
|
|
59
|
+
ctx.status = 403;
|
|
60
|
+
ctx.body = {
|
|
61
|
+
code: 403,
|
|
62
|
+
msg: 'Forbidden: Invalid request origin'
|
|
63
|
+
};
|
|
64
|
+
return;
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
// 验证CSRF令牌
|
|
69
|
+
if (!this._validateToken(ctx, config)) {
|
|
70
|
+
if (config.block) {
|
|
71
|
+
ctx.status = 403;
|
|
72
|
+
ctx.body = {
|
|
73
|
+
code: 403,
|
|
74
|
+
msg: 'Forbidden: Invalid CSRF token'
|
|
75
|
+
};
|
|
76
|
+
return;
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
// 如果需要生成新令牌
|
|
81
|
+
if (config.generate_new) {
|
|
82
|
+
await this._setNewToken(ctx, config);
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
await next();
|
|
87
|
+
};
|
|
88
|
+
|
|
89
|
+
CsrfMiddleware.prototype._generateToken = async function(ctx) {
|
|
90
|
+
let token = ctx.cookies.get(this.config.cookie_name);
|
|
91
|
+
|
|
92
|
+
// 如果没有令牌或需要生成新令牌,则创建一个新的
|
|
93
|
+
if (!token || this.config.generate_new) {
|
|
94
|
+
await this._setNewToken(ctx, this.config);
|
|
95
|
+
token = ctx.cookies.get(this.config.cookie_name);
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
return token;
|
|
99
|
+
};
|
|
100
|
+
|
|
101
|
+
CsrfMiddleware.prototype._setNewToken = async function(ctx, config) {
|
|
102
|
+
// 生成随机令牌
|
|
103
|
+
const token = this._createRandomToken();
|
|
104
|
+
|
|
105
|
+
// 将令牌存储到Cookie
|
|
106
|
+
ctx.cookies.set(config.cookie_name, token, {
|
|
107
|
+
httpOnly: false, // CSRF令牌需要从前端读取,所以不能设置httpOnly
|
|
108
|
+
maxAge: config.max_age,
|
|
109
|
+
sameSite: 'lax'
|
|
110
|
+
});
|
|
111
|
+
};
|
|
112
|
+
|
|
113
|
+
CsrfMiddleware.prototype._createRandomToken = function() {
|
|
114
|
+
return Math.random().toString(36).substring(2) +
|
|
115
|
+
Math.random().toString(36).substring(2) +
|
|
116
|
+
Math.random().toString(36).substring(2);
|
|
117
|
+
};
|
|
118
|
+
|
|
119
|
+
CsrfMiddleware.prototype._shouldSkipCsrfValidation = function(ctx, config) {
|
|
120
|
+
const method = ctx.method;
|
|
121
|
+
const path = ctx.path;
|
|
122
|
+
|
|
123
|
+
// 检查是否在忽略的方法列表中
|
|
124
|
+
if (config.ignore_methods.includes(method)) {
|
|
125
|
+
return true;
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
// 检查是否在忽略的路径列表中
|
|
129
|
+
if (config.ignore_paths.some(p => path.startsWith(p))) {
|
|
130
|
+
return true;
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
return false;
|
|
134
|
+
};
|
|
135
|
+
|
|
136
|
+
CsrfMiddleware.prototype._validateOrigin = function(ctx, config) {
|
|
137
|
+
const origin = ctx.get('Origin');
|
|
138
|
+
const host = ctx.get('Host');
|
|
139
|
+
|
|
140
|
+
// 如果没有Origin头,可能是同源请求
|
|
141
|
+
if (!origin) {
|
|
142
|
+
return true;
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
// 检查是否在允许的来源列表中
|
|
146
|
+
if (config.allowed_origins.length > 0) {
|
|
147
|
+
return config.allowed_origins.includes(origin);
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
// 默认情况下,只允许同源请求
|
|
151
|
+
return origin.includes(host);
|
|
152
|
+
};
|
|
153
|
+
|
|
154
|
+
CsrfMiddleware.prototype._validateToken = function(ctx, config) {
|
|
155
|
+
const tokenFromCookie = ctx.cookies.get(config.cookie_name);
|
|
156
|
+
|
|
157
|
+
// 从请求头获取令牌
|
|
158
|
+
const tokenFromHeader = ctx.get(config.header_name);
|
|
159
|
+
|
|
160
|
+
// 从请求体获取令牌
|
|
161
|
+
let tokenFromBody = null;
|
|
162
|
+
if (ctx.request.body && ctx.request.body[config.form_field]) {
|
|
163
|
+
tokenFromBody = ctx.request.body[config.form_field];
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
// 从查询参数获取令牌
|
|
167
|
+
const tokenFromQuery = ctx.query[config.form_field];
|
|
168
|
+
|
|
169
|
+
// 检查令牌是否匹配
|
|
170
|
+
const receivedToken = tokenFromHeader || tokenFromBody || tokenFromQuery;
|
|
171
|
+
|
|
172
|
+
if (!receivedToken || !tokenFromCookie) {
|
|
173
|
+
return false;
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
return receivedToken === tokenFromCookie;
|
|
177
|
+
};
|
|
178
|
+
|
|
179
|
+
// 创建中间件实例
|
|
180
|
+
const middleware = new CsrfMiddleware();
|
|
181
|
+
|
|
182
|
+
// 导出符合系统期望的函数
|
|
183
|
+
exports = module.exports = function(server, config) {
|
|
184
|
+
// 获取当前中间件的配置(从middleware.json加载的配置)
|
|
185
|
+
var middleware_config = this && this.config ? this.config : config;
|
|
186
|
+
|
|
187
|
+
// 初始化中间件
|
|
188
|
+
middleware.init(middleware_config);
|
|
189
|
+
|
|
190
|
+
// 注册中间件到服务器
|
|
191
|
+
server.use(middleware.run.bind(middleware));
|
|
192
|
+
|
|
193
|
+
// 记录中间件初始化信息
|
|
194
|
+
if ($.log && $.log.info) {
|
|
195
|
+
$.log.info(`CSRF中间件已加载: 启用=${middleware.config.enable}, 忽略路径=${middleware.config.ignore_paths.length}`);
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
return server;
|
|
199
|
+
};
|
|
200
|
+
|
|
201
|
+
// 保留原始实例,以便其他方式调用
|
|
202
|
+
exports.middleware = middleware;
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "csrf",
|
|
3
|
+
"title": "CSRF保护中间件",
|
|
4
|
+
"description": "防止跨站请求伪造攻击,提供安全的令牌验证机制",
|
|
5
|
+
"version": "1.0",
|
|
6
|
+
"type": "web",
|
|
7
|
+
"process_type": "common_before",
|
|
8
|
+
"sort": 20,
|
|
9
|
+
"state": 1,
|
|
10
|
+
"config": {
|
|
11
|
+
"enable": true,
|
|
12
|
+
"cookie_name": "csrf_token",
|
|
13
|
+
"header_name": "X-CSRF-Token",
|
|
14
|
+
"form_field": "_csrf",
|
|
15
|
+
"max_age": 3600000,
|
|
16
|
+
"ignore_methods": ["GET", "HEAD", "OPTIONS"],
|
|
17
|
+
"ignore_paths": ["/api/user/sign_in", "/user/login", "/login", "/admin/login"],
|
|
18
|
+
"generate_new": true,
|
|
19
|
+
"check_origin": true,
|
|
20
|
+
"log": true,
|
|
21
|
+
"block": true,
|
|
22
|
+
"allowed_origins": []
|
|
23
|
+
}
|
|
24
|
+
}
|