Files
2026-10-20 21:53:26 +08:00

97 lines
5.8 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 4.10 安全架构设计
:::tip 概述
安全设计按威胁面组织:每项威胁对应明确控制措施与代码落点。本节给出威胁→控制总表、四条安全红线、部署基线与已知边界(诚实声明),可直接用于安全评审自查。
:::
## 威胁 → 控制总表
| 威胁面 | 控制措施 | 代码落点 |
|--------|---------|---------|
| 身份伪造 | HS256 签名校验 + iss/exp 强校验(限定 HS256 防算法混淆) | utility/jwt Parse |
| 令牌重放(登出后) | jti 黑名单,TTL=剩余有效期 | `blacklist:{jti}` |
| Refresh 重放 | 轮换:旧 refresh 入黑名单,已黑即拒(1008) | logic/auth Refresh |
| 被踢会话复活 | 强退同时拉黑 access+refresh 双令牌 | online_user ForceLogout |
| 口令窃取/泄露 | bcrypt(password+salt);日志脱敏(password/salt/token → ***) | utility/crypto; maskSecret |
| 暴力破解 | 登录/验证码/令牌兑换独立严阈值 10/min/IP | rateLimit.authLimit |
| 水平越权(跨租户) | dao.Ctx 注入 tenant_id IN(0,自身) | hooks.ApplyCtx |
| 行级越权(部门/本人) | 角色 data_scope 四档叠加过滤(仅列表/导出;写操作由 permission 兜底) | logic/data_scope.go |
| 垂直越权 | gmeta permission + HasPerm;超管放行 | Bind 包装层 |
| SQL 注入 | 全链 gdb 参数化;无方言拼接红线 | dao/Where 参数化 |
| 参数篡改 | gvalid 标签校验(Parse 统一触发) | api/v1 结构体标签 |
| 存储型 XSS | .svg 强制 attachment+nosniff;富文本外链不本地化、占位符契约 | middleware/response; base.go |
| SSRF | 富文本仅处理本站 URL,外站一律不抓取 | localizeOne 判定 |
| CSRF | Bearer 头令牌(非 Cookie)+ CORS 白名单 | JWT 中间件/CORS |
| IP 伪造 | 仅受信代理(api.proxyIps,支持 CIDR)的 XFF/X-Real-IP 生效 | realClientIP |
| 限流绕过 | 计数经 Redis 跨实例共享;窗口起点对齐 | RateLimit incrCounter |
| 越权写(演示站) | Demo 中间件拦截全部写操作 | middleware/demo |
| 审计缺失 | 操作日志异步全量记录(脱敏+响应+耗时) | OperationLog |
| 会话固定 | 无服务端 Cookie 会话,令牌全部服务端签发 | auth Login |
| 敏感信息出栈 | GetUser 永不返回 password/salt;令牌原文仅在线用户详情(需权限) | index/online_user |
## 四条租户隔离红线(安全审计重点)
::: warning 红线清单
1. 业务代码禁止 `g.DB()` 直查业务表(绕过钩子与过滤)
2. 业务代码禁止手写 del_flag / tenant_id 条件(应由 dao.Ctx 注入)
3. 新增租户级表必须登记 hooks.tenantTables(**漏登=跨租户泄漏**)
4. 唯一预检范围必须等于唯一索引范围(否则 1062 漏报/误报)
另:平台级(超管)全量可见是设计预期,管理端点须收紧 permission。
:::
## 文件上传安全链
```text
大小限制 → 扩展名白名单 → 图片魔数嗅探(前 512 字节须 image/*)
→ 随机文件名(防猜路径/覆盖)
→ 临时/正式两段式(未过校验不进正式目录)
→ 静态响应 svg 附件化 + nosniff
→ 定期清理 temp(每日 03:30,tempExpireDays 默认 7 天)
```
注意:fileExt 白名单含 js/css/sql 等——对公网用户提供上传时,建议按环境收紧 fileExt(配置层控制,无需改码)。
## 认证与授权安全细节
```text
认证:
* 令牌 HS256;密钥/签发者可配置;exp 必填
* 双令牌分工:access 2h 只作身份;refresh 7d 仅 /api/oauth2/token 可用(typ 校验)
* 验证码:一次性(命中即删)、TTL 300s、bypassCode 生产必须留空
* 密码:bcrypt DefaultCost;外部 salt 防彩虹;旧 md5 数据被 IsBcrypt 拒绝(强制重置)
授权:
* 权限点粒度到接口级(sys:module:action),按钮级由前端控制;
新增接口必须声明 permission 或显式留空(留空=登录即可,评审需明确该选择)
* 隐藏节点:menu hide=1 的权限节点不进 Perms——"禁用权限"用 status 而非 hide
* 权限缓存一致性:保存权限/分配角色/改数据范围主动清 identity 缓存,最迟 300s 过期
```
## 部署安全基线(生产)
```text
1) 修改 jwt.secret 为高强度随机值且与环境隔离
2) captcha.bypassCode=""; app.demo=false; app.errorMsg=false; errorStack=false
3) database/redis 使用独立账号与内网地址;不暴露公网
4) api.proxyIps 只列真实反代出口;未用反代时保持默认(含 127.0.0.1)
5) file.fileExt 按业务收紧;uploadDir 权限最小化;domainUrl 走 CDN/域名隔离
6) rateLimit 按压测调参(防误伤;authLimit 不建议调大)
7) HTTPS 由前置代理终止;服务本身 HTTP 内网
8) 定期核对 /api.json 与权限点清单(防新接口漏配 permission)
```
## 已知边界与取舍(诚实声明)
| 项 | 边界 |
|----|------|
| CheckUnique | 先查后插的 UX 预检,存在 TOCTOU——并发唯一性以 uk_* 索引为最终防线 |
| 数据权限 | 仅约束 Page/List/导出;Detail/Update/Delete 防线是 permission 节点 + 租户隔离 |
| 接线范围 | 数据权限当前仅 user 模块接线(dept_id 列);其他模块行级可见性由租户隔离单层兜底 |
| 部门子树 | deptSubtreeIds 每次全量拉部门表 BFS 无缓存——数百行内无感知,上万行需加短时缓存 |
| 身份缓存窗口 | 300s 内直接改库的 dept_id/data_scope 不经 API 不会即时生效(API 路径均主动清缓存) |
| 在线用户详情 | 返回令牌原文(便于运维排查),依赖 sys:onlineUser:detail 权限收口 |
| 日志截断 | param/result 截断 8KB,超长报文不完整留存 |
| 密码策略 | 无独立复杂度/定期更换强制(可按需在密码路径追加规则) |
| 防重放 | jti 黑名单依赖 TTL 自然过期,无物理防重放窗口 |