安全加固

AuraBoot 出厂自带多层纵深防御。加固的过程是把每一层从插件作者期间宽松的默认值调整为生产取向的配置。
防御分层
| 层 | 防护对象 | 落在哪里 |
|---|---|---|
| 认证 | 调用方身份 | JWT 签发 + security.jwt.secret |
| Permission code | 动作授权 | @RequirePermission(MetaPermission.X) + 角色绑定 |
| 行级数据域 | 记录级可见性 | 按角色绑定的 DataScopeType + model 的 ownerField |
| Admin Guard v2 | 高危 endpoint | 路径范围白名单 + ab_admin_action_log |
| 输入校验 | 注入、路径穿越 | Bean Validation + 参数化 JDBC |
| 审计日志 | 取证记录 | ab_audit_trail append-only(hash 链) |
何时使用
- 将部署暴露给非内部用户之前。
- 导入第三方插件之后:核对它声明的 permission 与行策略。
- 定期安全 review(建议每季度一次)。
- 任何对认证、JWT、permission code 接线的改动之后。
Permission code
每个 permission code 形如 <module>.<resource>.<action>,必须在 default-bootstrap.json 或某个插件的 permissions.json 中注册。CI 的 permission-code 闸门 fail-closed;任何无法解析的引用都是构建中断,而不是 warning。
// 正确:引用已注册 code 的类型化常量
@RequirePermission(MetaPermission.META_CHANGELOG_READ)
public PageResult<ChangelogEntry> list(...) { ... }
// 禁止:裸字符串绕过注册表
@RequirePermission("system.changelog.read")禁止的模式:
- 新增
system.*前缀的 code(已废弃)。 @RequirePermission中的裸字符串字面量。commands.json/menus.json引用未注册的 code。
Admin Guard v2
Admin Guard v2 通过三道控制保护高危 endpoint(数据导出、批量删除、租户管理):
- 按路径范围分配角色(
/api/admin/infrastructure/**、/api/admin/cloud-config/**、/api/admin/bootstrap/**需platform_admin,其余/api/admin/**需tenant_admin;两个角色互不蕴含)。 - Caffeine 60 秒缓存压制角色查询滥用。
- 每次调用追加一条
ab_admin_action_log审计行。
理论上属于 admin-only 但没出现在白名单里的路径是缺陷,不是 feature。
行级数据域(Data Scope)
数据域作用在动态表查询层——行过滤在 SQL 层追加,不会被直接的 DSL 查询绕过。机制不是自由文本 filter,而是按角色绑定的 DataScopeType 枚举(com.auraboot.framework.permission.enums.DataScopeType):
| DataScopeType | 含义 |
|---|---|
self | 只看自己的记录(按 model 的 ownerField 与当前用户比较) |
dept / dept_and_sub | 本部门 / 本部门及下属组织 |
all | 全部 |
model 声明 ownerField(如 crm_lead 的 crm_lead_assigned_to);角色绑定 self 时,平台在查询层自动追加 <ownerField> = <当前用户> 的行过滤。数据域与 permission code 复合:调用方既要有 permission,数据域也要命中。详见 权限。
SQL 与异常红线
- 所有参数值都走
DynamicDataMapper的参数化绑定。把字符串拼进 SQL 是 P0 finding。 - 动态表名(
mt_前缀约定)在用作FROM之前会按标识符白名单[a-zA-Z0-9_]+校验,拒绝任何含特殊字符的表名。 @Transactional方法中的catch (Exception e)会在下一次 commit 时把事务置为 rollback-only。辅助操作请用@Transactional(propagation = NOT_SUPPORTED),并在每个新增 catch 块上写注释说明为什么安全。- 不允许对缺失数据做
ensureXxx()自愈;让错误冒上来,从源头修 bootstrap 链路。
配置
security:
jwt:
secret: ${JWT_SECRET:dev-only-secret-key-replace-in-production-min-32-chars}
kid: ${JWT_KID:key-1}
expiration: ${JWT_EXPIRATION:86400} # 单位:秒(默认 24 小时)
previous-secret: ${JWT_PREVIOUS_SECRET:} # 密钥轮换时校验旧 token
previous-kid: ${JWT_PREVIOUS_KID:}
password:
min-length: 8
max-length: 128
require-uppercase: true
require-lowercase: true
require-digit: true
require-special: false
history-count: 5
expiry-days: 90
lockout:
max-attempts: 5
duration-minutes: 30Admin Guard v2 不通过 YAML 配置:路径范围(/api/admin/infrastructure/**、/api/admin/cloud-config/**、/api/admin/bootstrap/** 需 platform_admin,其余 /api/admin/** 需 tenant_admin)固化在 AdminRoleInterceptor 中,每次调用都会写一条 ab_admin_action_log。CORS、TLS、安全响应头按渗透测试清单在反向代理层配置。
渗透测试清单
[ ] Auth: tokens expire; refresh rotates; revoked tokens are rejected within 60s
[ ] Auth: brute-force on /api/auth/login is rate-limited per IP and per user
[ ] AuthZ: every Controller has @RequirePermission or explicit @PermitAll comment
[ ] AuthZ: row-level filters cannot be bypassed via /api/<pageKey>/list
[ ] Input: parameterized SQL only; dynamic table names regex-validated
[ ] Input: file upload restricts content-type, size, and rewrites filenames
[ ] Crypto: TLS 1.2+ only; HSTS; secure cookies; no plaintext secrets in logs
[ ] Headers: X-Frame-Options, X-Content-Type-Options, CSP set in reverse proxy
[ ] Errors: 5xx responses do not leak stack traces; 4xx do not echo SQL
[ ] Audit: every admin action lands in ab_admin_action_log with actor + payload hash
[ ] Dependencies: SCA scan green; no critical CVEs in the last release
[ ] Secrets: rotation rehearsed; vault access logs reviewed验证
node scripts/validate-permission-codes.mjs在 OSS 与企业仓库均 exit 0。- 针对最新 release 的渗透测试每一项都通过。
- 日志中每一次 admin 操作都有 actor、target 与结果。
- 调用未授权命令返回 403 且不泄漏细节。
- 通过列表 filter 尝试 SQL 注入会得到校验错误,而不是
BadSqlGrammar。