IAM 最小权限:大家常犯的错误(以及如何使用 Terraform 修复)
发布: (2025年12月17日 GMT+8 23:29)
6 min read
原文: Dev.to
Source: Dev.to
当我们面临压力时,最快的解决方案往往会获胜。有人需要访问权限,某些东西出错,截止日期临近。于是我们采取经典的做法:临时授予宽泛的权限。
问题在于,临时权限往往会变成永久的。
最小权限并不是出于偏执,而是出于有意。我们只授予必要的权限,这样错误的影响保持在小范围,安全性也更可预测。
什么是最小特权?
最小特权意味着:
- 只执行所需的操作(而不是所有操作)
- 只访问所需的资源(而不是“任何资源”)
- 只在需要时使用(而不是永久)
- 只针对正确的身份(角色、用户或服务)
有了这些要点,我总会自问以下问题:
- 这个工作负载需要做什么?
- 它需要在哪里执行?
- 什么是绝对不应被允许的?
这种思维方式很重要,因为 IAM 不仅关乎安全,还关乎可靠性。权限过大的 role 会更快导致更多问题。
为什么最小权限在规模化时重要
在小型环境中,宽泛的权限可能不会立即导致问题。
在大规模时,它们通常会导致问题。
最小权限可以保护您免受以下风险:
- 更大的影响范围: 一个被泄露的密钥可能影响所有资源
- 意外删除: 有人在生产环境中运行错误的命令
- 影子访问: 旧角色保留了无人记得授予的权限
- 审计难题: 像“为什么这个角色拥有此访问权限?”之类的棘手问题
当权限设置严格时,调试也会更容易。如果出现故障,我们知道访问边界是真实且有意义的。
团队常见错误
常见于真实 AWS 环境的模式:
- 通配符,如
* - 从互联网复制的策略,从未清理
- 在多个系统中重复使用同一个角色
- “临时”权限悄悄变为永久
- 部署权限与运行时权限之间没有分离
这些问题往往源于快速推进的压力。解决办法不是指责,而是采用正确的模式。
Bad Policy vs Good Policy Examples
示例 1:S3 访问
❌ 不良策略(范围过宽)
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": "s3:*",
"Resource": "*"
}
]
}
风险原因
- 可以删除任何存储桶
- 可以读取本应私有的数据
- 对特定路径或环境没有任何限制
✅ 良好策略(紧凑且实用)
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "ListBucketInPrefix",
"Effect": "Allow",
"Action": ["s3:ListBucket"],
"Resource": "arn:aws:s3:::my-app-data",
"Condition": {
"StringLike": {
"s3:prefix": ["public/*"]
}
}
},
{
"Sid": "ReadObjectsInPrefix",
"Effect": "Allow",
"Action": ["s3:GetObject"],
"Resource": "arn:aws:s3:::my-app-data/public/*"
}
]
}
优势说明
- 限定在单个存储桶内
- 限定在特定路径下
- 没有删除权限
示例 2:Lambda 日志记录
❌ 不良策略
{
"Effect": "Allow",
"Action": "logs:*",
"Resource": "*"
}
风险原因
- 允许在任何位置写入日志,既不必要又可能泄露敏感信息。
✅ 良好策略
{
"Effect": "Allow",
"Action": [
"logs:CreateLogStream",
"logs:PutLogEvents"
],
"Resource": "arn:aws:logs:us-east-1:123456789012:log-group:/aws/lambda/my-function:*"
}
以正确方式设计 IAM 的简易系统
在真实项目中有效的模式
分离角色
- 一个用于部署的角色
- 一个用于运行时的角色
- 一个用于监控的角色
从狭窄开始,仅在需要时扩展
- 当操作失败时,只添加修复所需的最小权限。
使用护栏
- 应用服务控制策略(SCP)和权限边界,以强制执行“绝不能发生”的规则。
定期审查
- 如果某项权限数月未被使用,便将其移除。
- 最小权限不是一次性设置,而是持续的维护习惯。
小项目:Lambda + S3 的最小权限 IAM 角色
目标
创建:
- 一个 S3 存储桶
- 一个 Lambda 执行角色
- 一个附加到该角色的最小权限 IAM 策略
Lambda 将能够:
- 仅从
s3://bucket/public/*读取 - 仅向
s3://bucket/results/*写入 - 将日志写入 CloudWatch
此动手示例可使用 Terraform 实现(稍后添加详细信息)。