Files
sub2api/backend/migrations/180_audit_logs.sql
T
shaw 0ddd58aaf9 feat(security): 操作审计日志 + 会话IP/UA绑定 + 敏感操作 step-up 2FA
应对管理员访问凭证失守导致的数据外泄风险,新增三层防护:

审计日志(admin-only 可见,用户不可见)
- 新增 append-only audit_logs 表(migration 180)+ 异步批量写入 + 保留期清理
- 审计中间件挂在 admin/user/auth/admin-payment 组认证之后:记录所有变更类
  请求 + 白名单敏感读取(账号/代理导出、备份下载、admin/user API key 读取)
- 请求头凭证首尾掩码;请求体 JSON 递归脱敏(api_key/password 等擦除,base_url
  保留以便追责);非 JSON body 不入库
- 无单条删除;全量清空需现场 TOTP 校验、拒绝 admin API key、未启用 2FA 不允许,
  清空后同步写入留痕记录

会话 IP/UA 绑定(默认开启,可在系统设置关闭)
- JWT 携带 session id + IP/UA 指纹哈希;IP 或 UA 任一变化即撤销会话家族并要求
  重新登录;旧 token 无指纹时放行以平滑升级

敏感操作 step-up 2FA(sudo 窗口 15 分钟)
- 账号/代理导出、DB 备份创建/下载、S3 目标修改要求近期 TOTP 二次验证;admin API
  key 一律拒绝;前端 useStepUp 组合式 + TotpStepUpDialog 弹码后自动重试
- API key 查看按需求暂不加强管控

前端:新增 /admin/audit-logs 操作日志页面(筛选/详情/2FA 清空)、侧边栏入口、
step-up 弹窗接入导出与备份流程、安全设置项(绑定开关 + 日志保留天数)、zh/en i18n
2026-07-16 13:47:50 +08:00

36 lines
1.7 KiB
SQL
Raw 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.
-- 管理面操作审计日志(append-only)
-- 记录管理员/用户在管理面(非 AI 网关转发面)的操作:
-- 变更类请求 + 敏感读取(导出/备份下载等) + 认证事件(登录/2FA/会话绑定失效)
-- 设计约束:
-- 1. 不提供单条删除;仅支持带 2FA 验证的全量清空(TRUNCATE),清空后写入留痕记录
-- 2. credential_masked 仅保存请求头凭证的首尾片段(中间以 * 遮蔽)
-- 3. request_body 为脱敏后的请求体(敏感键值已擦除、超长截断)
CREATE TABLE IF NOT EXISTS audit_logs (
id BIGSERIAL PRIMARY KEY,
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
actor_user_id BIGINT,
actor_email VARCHAR(255) NOT NULL DEFAULT '',
actor_role VARCHAR(32) NOT NULL DEFAULT '',
auth_method VARCHAR(32) NOT NULL DEFAULT '',
credential_masked VARCHAR(160) NOT NULL DEFAULT '',
action VARCHAR(128) NOT NULL DEFAULT '',
method VARCHAR(16) NOT NULL DEFAULT '',
path VARCHAR(512) NOT NULL DEFAULT '',
request_id VARCHAR(64) NOT NULL DEFAULT '',
client_ip VARCHAR(64) NOT NULL DEFAULT '',
user_agent VARCHAR(512) NOT NULL DEFAULT '',
request_body TEXT NOT NULL DEFAULT '',
status_code INT NOT NULL DEFAULT 0,
latency_ms BIGINT NOT NULL DEFAULT 0,
extra JSONB NOT NULL DEFAULT '{}'::jsonb
);
CREATE INDEX IF NOT EXISTS idx_audit_logs_created_at_id
ON audit_logs (created_at DESC, id DESC);
CREATE INDEX IF NOT EXISTS idx_audit_logs_actor_created
ON audit_logs (actor_user_id, created_at DESC);
CREATE INDEX IF NOT EXISTS idx_audit_logs_action
ON audit_logs (action);
CREATE INDEX IF NOT EXISTS idx_audit_logs_client_ip
ON audit_logs (client_ip);