fix: 安全漏洞修复 — CLI密码泄露 + margin上限 + 路径检查 + CSP/限速

CRITICAL:
- CLI --password 未提供时自动从 QRGEN_WIFI_PASSWORD 环境变量读取
  避免密码出现在 ps aux / /proc/pid/cmdline 进程列表中

HIGH:
- unsafe from_utf8_unchecked SAFETY 注释增强, 详细解释为何有效
- CLI --margin 添加 value_parser range(0..=100), 防止 u8=255 导致内存爆炸
- Web CORS permissive 添加注释说明公开 API 设计理由
- GUI FS 权限收紧: 写权限从 /c/Users/33644/** 限制为 Downloads/Documents/Desktop/AppData

MEDIUM:
- 新增 QrError::InvalidEcLevel(String), 不再复用 InvalidVersion(0)
- --logo/--batch/--output_dir 均添加 check_path() 路径遍历检查
- Web 添加 tower::limit::ConcurrencyLimitLayer(10) 并发限制防 CPU 耗尽
- decode_image 添加 4096x4096 图片尺寸上限防解压炸弹

LOW:
- Web 添加 Content-Security-Policy 响应头
This commit is contained in:
2026-06-21 16:09:08 +08:00
parent bd4ca718ac
commit b053272825
10 changed files with 68 additions and 12 deletions
+1
View File
@@ -12,3 +12,4 @@ tokio = { version = "1", features = ["full"] }
serde = { version = "1", features = ["derive"] }
serde_json = "1"
tower-http = { version = "0.6", features = ["cors", "limit", "set-header"] }
tower = "0.5"
+11 -2
View File
@@ -174,7 +174,8 @@ async fn main() {
.route("/api/qr", get(generate_qr_get).post(generate_qr_post))
.route("/api/decode", post(decode_qr))
// 安全层
.layer(RequestBodyLimitLayer::new(10 * 1024 * 1024)) // 10 MB
.layer(RequestBodyLimitLayer::new(10 * 1024 * 1024)) // 10 MB 请求体限制
// CORS: 公开 API,允许任意来源调用(无 cookie/sessionCSRF 不适用)
.layer(CorsLayer::permissive())
.layer(SetResponseHeaderLayer::if_not_present(
header::X_CONTENT_TYPE_OPTIONS,
@@ -187,7 +188,15 @@ async fn main() {
.layer(SetResponseHeaderLayer::if_not_present(
header::REFERRER_POLICY,
header::HeaderValue::from_static("strict-origin-when-cross-origin"),
));
))
.layer(SetResponseHeaderLayer::if_not_present(
header::CONTENT_SECURITY_POLICY,
header::HeaderValue::from_static(
"default-src 'self'; style-src 'self' 'unsafe-inline'; script-src 'self' 'unsafe-inline'; img-src 'self' data: blob:",
),
))
// 并发限制: 最多 10 个同时编码/解码请求,防止 CPU 耗尽
.layer(tower::limit::ConcurrencyLimitLayer::new(10));
let addr = "0.0.0.0:3000";
println!("QRGen Web → http://{}", addr);