125 lines
9.0 KiB
Plaintext
125 lines
9.0 KiB
Plaintext
================================================================================
|
||
EasyGoAdmin 后端开发者文档 · 17 安全设计专题
|
||
================================================================================
|
||
按威胁面组织,每项给出控制措施与代码落点,便于安全评审与自查。
|
||
|
||
--------------------------------------------------------------------------------
|
||
1. 威胁 → 控制总表
|
||
--------------------------------------------------------------------------------
|
||
威胁面 控制措施 代码落点
|
||
------------------ -------------------------------------- ------------------
|
||
身份伪造 HS256 签名校验+iss/exp 强校验 utility/jwt Parse
|
||
令牌重放(登出后) jti 黑名单,TTL=剩余有效期 blacklist:token:{jti}
|
||
Refresh 重放 轮换:旧 refresh 入黑名单,已黑即拒 logic/auth Refresh
|
||
被踢会话复活 强退同时拉黑 access+refresh 双令牌 online_user ForceLogout*
|
||
口令窃取/泄露 bcrypt(password+salt);日志脱敏 utility/crypto;
|
||
access_token/refresh_token/password 不落日志 middleware maskSecret
|
||
暴力破解 登录/验证码/令牌兑换独立严阈值 10/min/IP rateLimit.authLimit
|
||
水平越权(跨租户) dao.Ctx 注入 tenant_id IN(0,自身) hooks.ApplyCtx
|
||
行级越权(部门/本人) 角色 data_scope 四档叠加过滤 logic/data_scope.go
|
||
(仅列表/导出;写操作由 permission 兜底) ApplyDataScope
|
||
垂直越权 gmeta permission + HasPerm;超管放行 Bind 包装层
|
||
注入(SQL) 全链 gdb 参数化;无方言拼接红线 dao/Where 参数化
|
||
参数篡改 gvalid 标签校验(Parse 统一触发) api/v1 结构体标签
|
||
存储型 XSS .svg 强制 attachment+nosniff middleware/response
|
||
富文本外链不本地化、占位符契约 base.go Localize
|
||
SSRF 富文本仅处理本站 URL,外站一律不抓取 localizeOne 判定
|
||
CSRF Bearer 头令牌(非 Cookie)+ CORS 白名单 JWT 中间件/CORS
|
||
IP 伪造 仅受信代理(api.proxyIps,支持 CIDR) realClientIP
|
||
的 XFF/X-Real-IP 生效
|
||
限流绕过 计数经 Redis 跨实例共享;窗口起点对齐 RateLimit incrCounter
|
||
越权写(演示站) Demo 中间件拦截全部写操作 middleware/demo
|
||
审计缺失 操作日志异步全量记录(脱敏+响应+耗时) OperationLog
|
||
会话固定 无服务端 Cookie 会话,令牌全部服务端签发 auth Login
|
||
敏感信息出栈 GetUser 永不返回 password/salt;详情页
|
||
令牌原文仅在线用户详情(需权限) index/online_user
|
||
|
||
--------------------------------------------------------------------------------
|
||
2. 认证安全细节
|
||
--------------------------------------------------------------------------------
|
||
* 令牌:HS256,密钥与签发者可配置(jwt.secret/issuer);Parse 限定
|
||
WithValidMethods(HS256) 防算法混淆;exp 必填。
|
||
* 双令牌分工:access 2h 只作身份;refresh 7d 且仅 /api/oauth2/token 可用
|
||
(typ 校验,access 不能当 refresh 用)。
|
||
* 验证码:一次性(命中即删)、TTL 300s、bypassCode 生产必须留空。
|
||
* 密码:bcrypt DefaultCost;外部 salt 防彩虹;旧 md5 数据被 IsBcrypt 拒绝
|
||
(强制重置升级)。
|
||
* 万能码/调试口:仅 captcha.bypassCode 一处,发布检查清单强制清空。
|
||
|
||
--------------------------------------------------------------------------------
|
||
3. 授权安全细节
|
||
--------------------------------------------------------------------------------
|
||
* 权限点粒度:到接口级(sys:module:action),按钮级由前端按 permissions 控制;
|
||
后端不做按钮级校验——新增接口必须同时声明 permission 或显式留空(留空
|
||
= 登录即可,评审需明确该选择)。
|
||
* 数据权限(行级):租户隔离(硬规则,不可配置)+ 角色 data_scope(可配置,
|
||
两层独立叠加)。data_scope 四档:1-全部 2-本部门及以下 3-本部门 4-仅本人;
|
||
多角色取最宽松;超管/平台绕过;未分配部门用户挂部门档降级为仅本人;
|
||
仅作用于挂接 ApplyDataScope 的模块之 Page/List/导出(applyFilters 链),
|
||
Detail/Update/Delete 不过滤,由 permission 节点与租户隔离兜底。
|
||
* 权限缓存一致性:保存权限/分配角色/角色改数据范围主动清 identity 缓存,
|
||
最迟 300s 过期。
|
||
* 隐藏节点:menu hide=1 的权限节点不进 Perms(LoadIdentity 过滤 hide=0),
|
||
不能用 hide 表达“禁用权限”——用 status。
|
||
|
||
--------------------------------------------------------------------------------
|
||
4. 租户隔离红线(安全审计重点)
|
||
--------------------------------------------------------------------------------
|
||
1) 业务代码禁止 g.DB() 直查业务表(绕过钩子与过滤);
|
||
2) 业务代码禁止手写 del_flag / tenant_id 条件(应由 dao.Ctx 注入);
|
||
3) 新增租户级表必须登记 hooks.tenantTables(漏登=跨租户泄漏);
|
||
4) 唯一预检范围必须等于唯一索引范围(否则 1062 漏报/误报);
|
||
5) 平台级(超管)全量可见是设计预期,管理端点须收紧 permission。
|
||
|
||
--------------------------------------------------------------------------------
|
||
5. 文件上传安全链
|
||
--------------------------------------------------------------------------------
|
||
大小限制 → 扩展名白名单 → 图片魔数嗅探 → 随机文件名(防猜路径/覆盖)
|
||
→ 临时/正式两段式(未过校验不进正式目录)→ 静态响应 svg 附件化+nosniff
|
||
→ 定期清理 temp。注意:fileExt 白名单含 js/css/sql 等——如对公网用户提供
|
||
上传,建议按环境收紧 fileExt(配置层控制,无需改码)。
|
||
|
||
--------------------------------------------------------------------------------
|
||
6. 日志与审计合规
|
||
--------------------------------------------------------------------------------
|
||
* 操作日志:登录后全部带 log 标签接口的操作均可追溯(操作人/参数/响应/IP/
|
||
归属地/UA/耗时/结果);密码与令牌字段脱敏。
|
||
* 登录日志:成功/失败/退出全记录,失败含原因。
|
||
* 日志保留:job.logRetentionDays(默认 90 天)物理清理——合规要求更长
|
||
保留期时调大配置。
|
||
* 审计字段:create_user/update_user 由 hooks 强制盖章(取登录名),
|
||
业务不可伪造(经 ctx 注入;无 ctx 的后台任务写 daemon/空)。
|
||
|
||
--------------------------------------------------------------------------------
|
||
7. 部署安全基线(生产)
|
||
--------------------------------------------------------------------------------
|
||
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)
|
||
|
||
--------------------------------------------------------------------------------
|
||
8. 已知边界与取舍(诚实声明)
|
||
--------------------------------------------------------------------------------
|
||
* CheckUnique 为先查后插的 UX 预检,存在 TOCTOU——并发唯一性以 uk_* 索引
|
||
为最终防线(捕获 1062 翻译提示)。
|
||
* 数据权限(data_scope)仅约束 Page/List/导出;Detail/Update/Delete
|
||
不做行级过滤——越权改删他人记录的防线是接口 permission 节点 + 租户隔离。
|
||
管理端点授予权限时应评估该角色是否应拥有写权限。
|
||
* 数据权限当前仅 user 模块接线(dept_id 列);其他模块行级可见性由
|
||
租户隔离单层兜底。
|
||
* 部门子树查询(deptSubtreeIds)每次全量拉部门表 BFS,无缓存——部门表
|
||
数百行内无感知,成千上万行时需加短时缓存。
|
||
* 身份缓存(300s)窗口内,直接改库的 dept_id/data_scope 不经 API 不会
|
||
即时生效(API 路径均主动清缓存,见 15 章)。
|
||
* 在线用户详情返回令牌原文(便于运维排查),依赖 sys:onlineUser:detail
|
||
权限收口。
|
||
* 操作日志 param 截断 8KB、响应截断 8KB,超长报文不完整留存。
|
||
* 无独立的密码复杂度策略/定期更换策略(业务层未强制,可按需在 index/
|
||
user 密码路径追加规则)。
|
||
* 无物理防重放窗口(jti 黑名单依赖 TTL;时钟偏移极小场景不受影响)。
|