- 缺陷:internal/auth/apikey.go 的 key 表是纯内存 map(注释自认 production should persist)⇒ 一次 restart 后所有 key 一律 401;实测挡住了大附件真机 e2e,也与「key 用于生产救火」的长凭据语义矛盾。 - 迁移 0016_api_keys:表 api_keys(id/name/key_hash/created_at/last_used_at/revoked_at)+ 唯一索引 idx_api_keys_key_hash;**只存 SHA-256 hash,无明文入口**。 - 纪律:Generate 先落库后改内存(失败 ⇒ 内存不变 + 返回错误);AttachKeyStore 启动时把库中未吊销 hash 载入内存(Load 失败仍接上 store ⇒ Generate 会响亮报错,而不是静默退回「不落盘」);Revoke/RevokeAll/RevokeByMask 先落库再移出内存(落库失败 ⇒ WARN + 内存不变,fail-closed:绝不让被吊销的 key 重启后复活)。 - 内存表也改为存 hash(不再存明文)⇒ 面板 id/掩码由 hash 派生:不再泄露明文首尾、且跨重启稳定(重启前记下的 id 重启后仍可吊销)。 - 契约零变化:HTTP 请求/响应形状、网关代码、既有内存用例断言全部未动(网关构造注入点只在组合根 wireAPIKeys)。 - 主会话独立验收:隔离 worktree(K1 的 7 文件)build/vet 绿、全树 59 包 0 FAIL、key 用例 24 PASS、双平台交叉编译绿;变异独立复现(跳过落库 ⇒ 重启存活用例红)。 - 取舍与遗留:吊销落库失败在 HTTP 层仍表现为 404/revoked:0(签名零改动的代价,日志可辨);last_used_at 列已建但恒 NULL(避免把读路径变写路径);旧内存 key 已被此前重启消灭,需重签一次后即长期有效。
31 lines
1.8 KiB
SQL
31 lines
1.8 KiB
SQL
-- 0016_api_keys: API key 哈希落盘(K1 缺陷修复)。
|
|
--
|
|
-- 为什么必须落盘:此前 key 表只活在 internal/auth 的内存 map 里,管理面板
|
|
-- POST /api/v1/admin/users/apikey 签出去的 key 一遇进程重启全部失效(X-API-Key
|
|
-- 一律 401)。而 design/11 §2 把 key 定位成「管理员调试、甚至生产环境救火」的
|
|
-- 长凭据 —— 签了就必须真能用,不能重启即作废。本表是那份凭据的权威载体:
|
|
-- 启动时 auth.SQLiteKeyStore 载入全部 revoked_at IS NULL 的行到内存表,之后
|
|
-- Generate/Revoke 先落库、成功后才改内存。
|
|
--
|
|
-- 口径:
|
|
-- * key_hash 是明文 key 的 **SHA-256 十六进制**,库里**只存哈希**;明文只在
|
|
-- 签发响应里出现一次,绝不落库、绝不落日志(与 0008 refresh_tokens 的
|
|
-- token_hash 同一纪律,且本包没有任何接受明文的写库入口)。
|
|
-- * revoked_at 为 NULL 表示仍有效;吊销只打时间戳、不删行,留审计痕迹。
|
|
-- * name 是可空备注;created_at / last_used_at 是运营时间戳。last_used_at
|
|
-- 当前**没有写入方**(最后一用时间不能靠「每次鉴权同步写库」拿,那样会把
|
|
-- 鉴权路径变成写路径),有意留 NULL,等异步/批量方案落地再填。
|
|
-- * 表名用复数,援引 AGENTS §10 已登记的例外(既有迁移口径一致)。
|
|
CREATE TABLE IF NOT EXISTS api_keys (
|
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
name TEXT,
|
|
key_hash TEXT NOT NULL,
|
|
created_at TEXT NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%fZ','now')),
|
|
last_used_at TEXT,
|
|
revoked_at TEXT
|
|
);
|
|
|
|
-- 唯一索引同时承担两件事:同一把 key 只能有一行;按 hash 查表/吊销有索引可走。
|
|
CREATE UNIQUE INDEX IF NOT EXISTS idx_api_keys_key_hash
|
|
ON api_keys (key_hash);
|