桶策略五要素:Principal、Action、Resource、Condition、Effect 深度拆解

桶策略看起来就是一段 JSON,但真正要把它写对、写安全,你得理解背后的五个核心元素。我把它们叫做策略的五根骨头:Principal、Action、Resource、Condition、Effect。缺一个,策略可能就是废的;配错了,权限可能就漏了。

这五个元素怎么协同工作的?我画了张图,你看看就明白了。

桶策略五要素框架图 桶策略 (Bucket Policy) Principal Action Resource Condition Effect 各元素作用简述 1. Principal:谁可以访问?用户、角色、账号、服务。 2. Action:允许或拒绝哪些操作?读、写、删、列举。 3. Resource:作用于哪些资源?桶本身、特定对象、前缀路径。 4. Condition:在什么条件下生效?IP范围、时间、加密、标签等。 5. Effect:结果是允许还是拒绝?Allow 或 Deny。 核心逻辑:五者缺一不可,联合决定最终授权结果。

1. Principal:谁可以访问?

Principal 就是策略的「主语」。说白了,这条策略是写给谁看的?可以是某个具体的 IAM 用户、一个角色、整个 AWS 账号,甚至是匿名用户(所有人)。

我在项目中见过最多的坑,就是把Principal写成 "AWS": "*",结果整个互联网都能访问。嗯,这种配置你要非常谨慎。我一般建议,如果你不确定,先用特定的 ARN 测试,确认无误再考虑放开。

{
    "Principal": {
        "AWS": "arn:aws:iam::123456789012:user/zhangsan"
    }
}
个人小建议: 能用具体 ARN 就别用通配符。即使要跨账号授权,也写清楚对方的账号 ID。你想想看,万一哪个账号被攻破了,你的桶就是第一个目标。

2. Action:允许或拒绝哪些操作?

Action 定义了你能对桶或对象做什么。常见的包括:s3:GetObject(读对象)、s3:PutObject(上传)、s3:DeleteObject(删除)、s3:ListBucket(列举桶内容)等。

Action 可以写多个,用数组方式,也可以用通配符 "s3:*" 代表所有操作。但说真话,给通配符风险很高。我曾经在审计时发现一个策略给了 s3:*,结果某个实习生写脚本把整桶数据删了……虽然最后救了回来,但那种惊心动魄,我不想再体验。

一个靠谱的做法:只授权你确实需要的动作。比如,只读的客户端,你给它 s3:GetObject 就足够了,别给 s3:DeleteObject。

{
    "Action": [
        "s3:GetObject",
        "s3:ListBucket"
    ]
}

3. Resource:作用于哪些资源?

Resource 就是你策略落地的「地盘」。它是 ARN(Amazon Resource Name)格式。比如桶本身:"arn:aws:s3:::my-bucket";桶里的所有对象:"arn:aws:s3:::my-bucket/*"。

这里有个关键点:Action 和 Resource 要匹配才能生效。比如 ListBucket 动作,它的 Resource 应该是桶本身的 ARN,而不是某个对象。而 GetObject 动作,Resource 应该指向对象(用 /* 或具体路径)。配错的话,策略明明看起来合理,但就是不生效。

我记得有一次排查了半天,才发现是 Resource 写错了范围。你想想看,这个错误很隐蔽,IAM 模拟器也不一定会报错,只有执行时才知道。

最佳实践:
  • 桶操作(如 ListBucket)的 Resource 写桶 ARN:"arn:aws:s3:::my-bucket"
  • 对象操作(如 GetObject)的 Resource 写对象路径:"arn:aws:s3:::my-bucket/*"
  • 如果需要两者兼顾,可以同时写出两个 Resource

4. Condition:在什么条件下生效?

Condition 是策略中最灵活、也最容易写错的地方。它允许你附加额外的约束条件。常见的条件包括:IP 地址范围、TLS 版本、请求时间、MFA 与否、对象标签等。

Condition 的语法是:"Condition": { "条件运算符": { "条件键": "条件值" } }。举个例子,我只允许来自公司内部 IP 的写入操作:

{
    "Condition": {
        "IpAddress": {
            "aws:SourceIp": "203.0.113.0/24"
        }
    }
}

另一个常见场景:强制要求使用 HTTPS。我曾经帮一个客户配置桶策略,只允许通过 HTTPS 访问。如果不加这个条件,用户可以用 HTTP 明文传输数据,非常危险。写起来很简单:

{
    "Condition": {
        "Bool": {
            "aws:SecureTransport": "true"
        }
    }
}
注意: 条件键的写法非常严谨。比如 IP 条件是 aws:SourceIp,不是 aws:sourceip。大小写错一个,策略就不起作用。我曾经见过一个客户配了三个月没生效,最后发现是字母大小写的问题。别笑,真的很容易犯。

5. Effect:允许还是拒绝?

Effect 只有两个值:Allow 或 Deny。它决定了策略的最终结果是「放行」还是「拦下」。默认情况下,所有请求都是隐式拒绝的。你写 Allow 就是显式允许,写 Deny 就是显式拒绝。

有一个优先级问题:显式 Deny 的优先级永远高于任何 Allow。也就是说,哪怕你写了一万个 Allow,只要存在一条匹配的 Deny,请求就会被拒绝。项目中我经常用这个特性来做“保底安全策略”。

比如,我想确保任何人都不能删除某个关键目录下的文件,就算管理员账号也不行。可以写一条 Deny,指定 Action 为 s3:DeleteObject,Resource 为 arn:aws:s3:::my-bucket/important/*,Principal 为 "*"。这样任何用户、任何角色都无法删除那个目录下的文件,包括我自己。

{
    "Effect": "Deny",
    "Principal": "*",
    "Action": "s3:DeleteObject",
    "Resource": "arn:aws:s3:::my-bucket/important/*"
}
一个小技巧: 利用 Deny 来创建黑名单。例如,你允许某个用户组访问桶里的所有数据,但唯独拒绝一个离职员工的访问权限。只需要在 Principal 里指定那个用户的 ARN,配一个 Deny 策略,其他人不受影响。这个做法我在权限回收的场景里用过很多次,很实用。

好了,五大元素就这些。每个元素单独看都不难,但组合在一起,策略的复杂度就上来了。记住:Principal 是“谁”,Action 是“做什么”,Resource 是“对什么做”,Condition 是“在什么条件下”,Effect 是“结论”。掌握好这个框架,再复杂的权限场景你都能拆解清楚。