API 密钥的定位
API 密钥是面向开发者的凭证形态:它用来识别“调用方是哪个应用”,而不是认证“哪个用户正在操作”。因此它适合服务端到服务端的集成与配额归属,不适合放进浏览器或移动端代码里。一个可持续的密钥体系要支持签发、轮换、吊销与权限绑定四件事,并且只把密钥的哈希存在服务端——明文只在创建时展示一次。
基本信息
| 签发 | 按应用签发,绑定权限范围与配额 |
|---|---|
| 存储 | 服务端存哈希;仅在创建时展示一次明文 |
| 治理 | 定期轮换、异常调用审计、离职与下线回收 |
| 辨识 | 密钥前缀加环境标识,便于日志里快速定位泄漏来源 |
典型使用场景
- 开放平台的开发者接入:按应用签发密钥并绑定调用配额
- 服务端到服务端集成:内部系统与第三方系统之间的固定凭证
- 计费与限流归属:按密钥统计调用量,用于配额与账单
- 灰度或试用版本:给特定调用方单独签发可随时吊销的凭证
- 数据同步任务:定时任务使用独立密钥,便于审计与单独吊销
密钥治理清单
- 签发:按应用与用途签发,绑定权限与配额
- 存储:服务端只存哈希,明文仅展示一次
- 轮换:到期前双密钥并行,平滑切换
- 吊销:即时生效并保留历史调用审计
- 传输:仅通过 TLS,禁止出现在 URL 查询串里
- 审计:记录调用方的密钥标识与来源 IP,便于异常追溯
校验流程(伪代码)
校验流程(伪代码)
key = read_header('Authorization')
if not key.startsWith('sk_live_'): return 401
row = db.find_by_hash(sha256(key)) # 只按哈希查库
if not row or row.revoked_at is not None: return 401
if not row.scopes.contains(required_scope): return 403
rate_limit(row.app_id, row.quota) # 配额按应用维度统计
# 日志只记录密钥前缀与哈希前若干位,绝不记录完整密钥常见风险与对策
| 风险 | 表现 | 对策 |
|---|---|---|
| 明文落库 | 数据库或备份泄漏即全面失守 | 只存哈希,明文仅创建时展示 |
| 权限过大 | 一个密钥能调所有接口,无法按调用方审计 | 按应用绑定最小权限范围与配额 |
| 无法轮换 | 更换密钥需要调用方停机改配置 | 支持双密钥并行期,平滑替换 |
| 写入仓库或前端 | 密钥随代码或页面传播到不受控环境 | 服务端调用,密钥走环境变量或密钥管理服务 |
| 无吊销与审计 | 离职或应用下线后凭证仍然有效 | 提供即时吊销入口并保留调用审计 |
API Key 放进前端或仓库会泄露;公开场景优先用带用户上下文的 OAuth/OIDC 方案,密钥只用于服务端之间的固定身份。
密钥出现在 URL 查询串里会被网关、代理与浏览器历史记录留存:认证信息必须走请求头,日志与监控里也要对密钥做脱敏。