================================================================================ 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;时钟偏移极小场景不受影响)。