fix(saas): 安全修复 — IDOR防护、SSRF防护、JWT密钥强制、错误信息脱敏、CORS配置化

- account: admin 权限守卫 (list_accounts/get_account/update_status/list_logs)
- relay: SSRF 防护 (禁止内网地址、限制 http scheme、30s 超时)
- config: 生产环境强制 ZCLAW_SAAS_JWT_SECRET 环境变量
- error: 500 错误不再泄露内部细节给客户端
- main: CORS 支持配置白名单 origins
- 全部 21 个测试通过 (7 unit + 14 integration)
This commit is contained in:
iven
2026-03-27 13:07:20 +08:00
parent 00a08c9f9b
commit 94bf387aee
9 changed files with 134 additions and 31 deletions

View File

@@ -132,13 +132,27 @@ impl SaaSConfig {
Ok(config)
}
/// 获取 JWT 密钥 (从环境变量或生成默认值)
pub fn jwt_secret(&self) -> SecretString {
std::env::var("ZCLAW_SAAS_JWT_SECRET")
.map(SecretString::from)
.unwrap_or_else(|_| {
tracing::warn!("ZCLAW_SAAS_JWT_SECRET not set, using default (insecure!)");
SecretString::from("zclaw-saas-default-secret-change-in-production".to_string())
})
/// 获取 JWT 密钥 (从环境变量或生成临时值)
/// 生产环境必须设置 ZCLAW_SAAS_JWT_SECRET
pub fn jwt_secret(&self) -> anyhow::Result<SecretString> {
let is_dev = std::env::var("ZCLAW_SAAS_DEV")
.map(|v| v == "true" || v == "1")
.unwrap_or(false);
match std::env::var("ZCLAW_SAAS_JWT_SECRET") {
Ok(secret) => Ok(SecretString::from(secret)),
Err(_) => {
if is_dev {
tracing::warn!("ZCLAW_SAAS_JWT_SECRET not set, using development default (INSECURE)");
Ok(SecretString::from("zclaw-dev-only-secret-do-not-use-in-prod".to_string()))
} else {
anyhow::bail!(
"ZCLAW_SAAS_JWT_SECRET 环境变量未设置。\
请设置一个强随机密钥 (至少 32 字符)。\
开发环境可设置 ZCLAW_SAAS_DEV=true 使用默认值。"
)
}
}
}
}
}