chanjs 2.5.6 → 2.5.8

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/App.js CHANGED
@@ -241,8 +241,7 @@ class Chan {
241
241
  * @description 设置 Express 应用配置
242
242
  */
243
243
  setApp() {
244
- this.app.set("trust proxy", Chan.config.PROXY === "true");
245
- this.app.disable("x-powered-by");
244
+ this.app.set("trust proxy", true);
246
245
  }
247
246
 
248
247
  /**
package/helper/ip.js CHANGED
@@ -17,8 +17,8 @@
17
17
  * - CF-Connecting-IP(Cloudflare CDN)
18
18
  *
19
19
  * 此方法按优先级从以下来源获取 IP 地址:
20
- * 1. req.ip(Express 自动解析,已包含代理头部处理)
21
- * 2. cf-connecting-ip 请求头(Cloudflare CDN,优先级更高)
20
+ * 1. cf-connecting-ip 请求头(Cloudflare CDN,优先级更高)
21
+ * 2. req.ip(Express 自动解析,已包含代理头部处理)
22
22
  * 3. 默认值 "0.0.0.0"
23
23
  *
24
24
  * 此方法适用于部署在反向代理(如 Nginx)或 CDN 后端的应用
@@ -30,13 +30,27 @@
30
30
  * });
31
31
  */
32
32
  export function getIp(req) {
33
+ // 1. 优先获取 Cloudflare CDN 透传的真实 IP(优先级最高)
34
+ const headers = req.headers || {};
35
+ let cfIp = headers["cf-connecting-ip"];
36
+ if (cfIp) {
37
+ // 处理可能的多 IP 拼接(部分场景下会传多个 IP,取第一个)
38
+ cfIp = cfIp.split(',').map(ip => ip.trim())[0];
39
+ if (cfIp) {
40
+ return cfIp;
41
+ }
42
+ }
43
+
44
+ // 2. 其次使用 Express 解析的 req.ip(已处理代理头部)
33
45
  if (req.ip) {
34
46
  let ip = req.ip;
35
47
 
48
+ // 处理 IPv6 映射的 IPv4 地址(如 ::ffff:192.168.1.1 → 192.168.1.1)
36
49
  if (ip.startsWith('::ffff:')) {
37
50
  ip = ip.substring(7);
38
51
  }
39
52
 
53
+ // 处理本地回环地址(::1 → 127.0.0.1)
40
54
  if (ip === '::1') {
41
55
  ip = '127.0.0.1';
42
56
  }
@@ -44,10 +58,6 @@ export function getIp(req) {
44
58
  return ip;
45
59
  }
46
60
 
47
- const headers = req.headers || {};
48
- if (headers["cf-connecting-ip"]) {
49
- return headers["cf-connecting-ip"];
50
- }
51
-
52
- return null;
53
- }
61
+ // 3. 无法获取时返回约定的默认值
62
+ return "0.0.0.0";
63
+ }
package/middleware/waf.js CHANGED
@@ -3,12 +3,25 @@ import { checkKeywords } from "../utils/checker.js";
3
3
  import { filterXSS } from "../utils/xss-filter.js";
4
4
  import { createRateLimitMiddleware } from "../utils/rate-limit.js";
5
5
 
6
+ function parseCookies(cookieHeader) {
7
+ if (!cookieHeader) return {};
8
+ const cookies = {};
9
+ cookieHeader.split(';').forEach(cookie => {
10
+ const [name, value] = cookie.trim().split('=');
11
+ if (name && value) {
12
+ cookies[name] = decodeURIComponent(value);
13
+ }
14
+ });
15
+ return cookies;
16
+ }
17
+
6
18
  /**
7
19
  * Web应用防火墙(WAF)中间件
8
20
  * 提供访问限流、关键词过滤、XSS防护等功能
9
21
  */
10
22
 
11
23
  const WAF_BLOCKED_KEY = `${process.env.APP_NAME || 'app'}_waf_blocked`;
24
+ const WAF_BLOCKED_COOKIE = 'waf_blocked';
12
25
  const WAF_BLOCKED_HEADER = 'x-waf-blocked';
13
26
 
14
27
  /**
@@ -47,7 +60,9 @@ function createWafMiddleware(wafConfig) {
47
60
 
48
61
  return async (req, res, next) => {
49
62
  try {
63
+ console.log('[WAF] 中间件开始执行,enabled:', wafConfig.enabled);
50
64
  if (!wafConfig.enabled) {
65
+ console.log('[WAF] WAF已禁用,跳过检查');
51
66
  return next();
52
67
  }
53
68
 
@@ -59,9 +74,12 @@ function createWafMiddleware(wafConfig) {
59
74
  res.setHeader("X-Frame-Options", "DENY");
60
75
 
61
76
  if (!isWhitelistedPath(requestPath)) {
62
- const isBlocked = req.headers[WAF_BLOCKED_HEADER];
63
- if (isBlocked === 'true') {
64
- console.error(`[WAF 永久封禁] IP:${clientIp} 路径:${requestPath} 原因:localStorage标记`);
77
+ const isBlockedHeader = req.headers[WAF_BLOCKED_HEADER];
78
+ const cookies = parseCookies(req.headers.cookie);
79
+ const isBlockedCookie = cookies[WAF_BLOCKED_COOKIE];
80
+
81
+ if (isBlockedHeader === 'true' || isBlockedCookie === 'true') {
82
+ console.error(`[WAF 永久封禁] IP:${clientIp} 路径:${requestPath} 原因:标记封禁`);
65
83
  return res.status(403).json({
66
84
  code: 403,
67
85
  success: false,
@@ -112,18 +130,25 @@ function createWafMiddleware(wafConfig) {
112
130
  <head>
113
131
  <meta charset="UTF-8">
114
132
  <title>访问受限</title>
133
+ <script>
134
+ (function() {
135
+ var expires = new Date();
136
+ expires.setTime(expires.getTime() + ${365 * 24 * 60 * 60 * 1000});
137
+ document.cookie = '${WAF_BLOCKED_COOKIE}=true; path=/; expires=' + expires.toUTCString() + '; SameSite=Lax';
138
+ localStorage.setItem('${WAF_BLOCKED_KEY}', 'true');
139
+ console.log('[WAF] Cookie已设置:', document.cookie);
140
+ })();
141
+ </script>
115
142
  </head>
116
143
  <body>
117
144
  <h1>非法请求</h1>
118
145
  <p>检测到恶意内容,您的访问已被限制。</p>
119
146
  <p>如需恢复请联系管理员。</p>
120
- <script>
121
- localStorage.setItem('${WAF_BLOCKED_KEY}', 'true');
122
- </script>
123
147
  </body>
124
148
  </html>
125
149
  `;
126
150
 
151
+ console.log('[WAF] 准备发送响应');
127
152
  return res.status(403).setHeader('Content-Type', 'text/html').send(htmlResponse);
128
153
  }
129
154
  }
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "type": "module",
3
3
  "name": "chanjs",
4
- "version": "2.5.6",
4
+ "version": "2.5.8",
5
5
  "description": "chanjs基于express5 纯js研发的轻量级mvc框架。",
6
6
  "main": "index.js",
7
7
  "module": "index.js",
Binary file