最小权限原则:给密钥策略“够用就好”的权限
做KMS密钥策略时,我最常听到的一句话是“先放开,后面再收紧”。但说实话,这在合规审计里是个坑。一旦权限放出去,想收回来往往要动一堆依赖方,而且审计日志里那堆“未被使用的操作”会被合规官追着问。
我自己的习惯是——从一开始就只给那个角色/用户当前确实要用到的操作,不留任何多余的“万一”。这就是最小权限原则在密钥策略里的落地方式。
为什么最小权限这么重要?
你想想看,一个密钥策略如果写得太宽,意味着什么?
- 攻击者拿到一个低风险凭证,就能直接调用
kms:Decrypt解密敏感数据 - 内部员工无意中执行了
kms:ScheduleKeyDeletion,导致密钥被计划删除,所有加密数据瞬间无法访问 - 合规扫描工具(比如AWS Config)会持续报警“过度授权”,审计过不了
我在一个金融项目的客户那边遇到过真实案例——他们给一个只做“加密上传”的服务授权了kms:Decrypt,结果一次配置错误,日志里出现了上百万次解密请求。虽然没出安全事故,但合规部门要求立即整改,花了一整周重新梳理所有策略。
最小权限的两个层次
说白了,最小权限不是一句口号,它分两个层次:
| 层次 | 说明 | 示例 |
|---|---|---|
| 操作级 | 只允许必要的KMS API操作 | 只给kms:Encrypt,不给kms:Decrypt |
| 资源级 | 只允许访问特定密钥(甚至特定密钥版本) | 只限定arn:aws:kms:...:key/xxx,不写* |
嗯,这里要注意:操作级和资源级要同时约束。光限制操作但不限制资源,等于说这个角色能用所有密钥干那件事,风险依然很大。
实战:从“全开”到“最小”三步走
我在团队里做策略审查时,通常用这三步来收窄权限:
- 列出现有策略里的所有操作 —— 把
Action列拿出来,一条条看 - 标记“必要”和“可选” —— 问使用方“你真的需要
kms:CreateGrant吗?” - 移除所有“可选”操作 —— 只保留当前功能必须的操作
举个例子:一个后端微服务需要“加密用户身份证号并存储”。常见的问题是,运维人员直接复制一个“全功能”策略,里面包含了Decrypt、GenerateDataKey、CreateGrant、ListKeys。实际最小策略只需要:
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"kms:Encrypt",
"kms:GenerateDataKey"
],
"Resource": "arn:aws:kms:ap-northeast-1:123456789012:key/abcd1234-a123-4567-a123-a123b456c789"
}
]
}
注意这里我没有写kms:Decrypt——因为微服务只做加密上传,解密由另一个独立服务负责。这样即使微服务被攻破,攻击者也拿不到明文数据。
常见过度授权“雷区”
- 写
"Action": "kms:*"—— 我最常看到的“偷懒写法”,千万别用 - 写
"Resource": "*"—— 让这个角色能操作所有密钥,等于没限制 - 给了
kms:ListKeys—— 虽然不是敏感操作,但会给攻击者提供密钥清单,辅助后续攻击 - 忘了加上
Condition约束 —— 比如来源IP、VPC、MFA等,进一步缩窄使用条件
kms:Decrypt和kms:ReEncrypt,结果任务脚本写错逻辑,循环解密了10万条记录后触发KMS限速,导致整个生产环境加密链路堵死。事后排查,发现那个任务其实只需要kms:ReEncrypt就够了。教训是:多一个操作就多一分风险。
用“权限矩阵”理清最小权限
我习惯在项目初期画一张权限矩阵表,把每个角色需要的操作和资源列清楚:
| 角色 | 需要操作 | 限制资源 | 备注 |
|---|---|---|---|
| 上传服务 | Encrypt, GenerateDataKey | Key-A (生产) | 不可解密 |
| 解密服务 | Decrypt | Key-A (生产) | 来自内部VPC |
| 审计员 | DescribeKey, GetKeyPolicy | 所有密钥(仅查看) | 只读,无写操作 |
| 管理员 | 所有操作 | 所有密钥 | 需MFA |
这张表定下来之后,直接转换成KMS策略JSON就行。每次有新角色加入,先问“最低要做什么”,再写策略。
我推荐的最小权限检查清单
写策略前,对着这个清单自检一遍:
- 这个角色是否真的需要
kms:Decrypt?能否用kms:Encrypt+kms:ReEncrypt替代? - 是否能用
kms:GenerateDataKey代替直接调用kms:Encrypt(性能更好)? - 资源ARN是否写死了具体密钥ID?还是写了
*? - 有没有加上
Condition限制来源IP、VPC、MFA? - 是否真的需要
kms:CreateGrant?这个操作可以扩展权限,通常用不上 - 是否真的需要
kms:ListKeys?大部分服务不需要枚举密钥
kms:SimulatePrincipalPolicy API模拟测试确认哪些操作真的被调用过,删掉那些从未出现的操作。我在项目里把这个写进了CI/CD流水线,每次部署前自动校验。
一张图看懂最小权限流程
避坑指南:我曾经踩过的三个坑
kms:Decrypt代替kms:GenerateDataKey做数据密钥派生。还记得有一次,一个同事为了少改代码,直接调Decrypt来解密数据密钥,结果一次误操作把加密数据也给解密了。其实应该用GenerateDataKey返回加密+明文数据密钥,解密工作交给单独服务。
"Condition": {"IpAddress": {...}}但忘记写"SourceVpc",导致密钥其实能被公网IP访问(如果网络层面没限制)。我后来学乖了:网络限制和访问控制要双保险。
kms:Decrypt,但Lambda函数里从没用过这个操作。审计工具告警后我查了半天,发现是开发者从旧例子直接复制了策略。后来我在团队里推行“策略即代码”+自动化审查,避免人肉复制。
总结一下,最小权限不是一锤子买卖。随着业务迭代,角色需要的操作会变。我的建议是:每季度做一次密钥策略审计,跟使用方对一次“你现在真的还需要这个操作吗?”。保持策略瘦身,审计才轻松。