s3-bucket-policy
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChineseAWS S3 Bucket Policy Expert
AWS S3存储桶策略专家
Expert guidance on creating, analyzing, and optimizing AWS S3 bucket policies with focus on security, access control, and compliance.
为创建、分析和优化AWS S3存储桶策略提供专业指导,重点关注安全性、访问控制与合规性。
Policy Structure
策略结构
json
{
"Version": "2012-10-17",
"Id": "PolicyIdentifier",
"Statement": [
{
"Sid": "StatementIdentifier",
"Effect": "Allow | Deny",
"Principal": {
"AWS": "arn:aws:iam::account-id:root"
},
"Action": [
"s3:GetObject",
"s3:PutObject"
],
"Resource": [
"arn:aws:s3:::bucket-name",
"arn:aws:s3:::bucket-name/*"
],
"Condition": {
"StringEquals": {
"s3:x-amz-acl": "bucket-owner-full-control"
}
}
}
]
}json
{
"Version": "2012-10-17",
"Id": "PolicyIdentifier",
"Statement": [
{
"Sid": "StatementIdentifier",
"Effect": "Allow | Deny",
"Principal": {
"AWS": "arn:aws:iam::account-id:root"
},
"Action": [
"s3:GetObject",
"s3:PutObject"
],
"Resource": [
"arn:aws:s3:::bucket-name",
"arn:aws:s3:::bucket-name/*"
],
"Condition": {
"StringEquals": {
"s3:x-amz-acl": "bucket-owner-full-control"
}
}
}
]
}Core Principles
核心原则
yaml
security_principles:
least_privilege:
description: "Grant only minimum necessary permissions"
practice: "Start with deny all, add specific allows"
explicit_deny:
description: "Deny always overrides Allow"
practice: "Use Deny for security guardrails"
defense_in_depth:
description: "Multiple layers of security"
practice: "Combine bucket policy + IAM + ACL + encryption"
avoid_wildcards:
bad: '"Principal": "*"'
better: '"Principal": {"AWS": "arn:aws:iam::123456789012:root"}'
common_mistakes:
- "Using Principal: * without conditions"
- "Missing resource ARN for objects (/*)"
- "Forgetting to block public access"
- "Not enabling versioning before policies"yaml
security_principles:
least_privilege:
description: "仅授予必要的最小权限"
practice: "从拒绝所有权限开始,逐步添加特定的允许规则"
explicit_deny:
description: "拒绝权限始终优先于允许权限"
practice: "使用拒绝规则作为安全防护措施"
defense_in_depth:
description: "多层安全防护"
practice: "结合存储桶策略 + IAM + ACL + 加密"
avoid_wildcards:
bad: '"Principal": "*"'
better: '"Principal": {"AWS": "arn:aws:iam::123456789012:root"}'
common_mistakes:
- "使用不带条件的Principal: *"
- "遗漏对象的资源ARN (/*)"
- "忘记阻止公共访问"
- "在配置策略前未启用版本控制"Common Policy Patterns
常见策略模式
Public Read for Static Website
静态网站公共读取权限
json
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "PublicReadGetObject",
"Effect": "Allow",
"Principal": "*",
"Action": "s3:GetObject",
"Resource": "arn:aws:s3:::my-website-bucket/*",
"Condition": {
"StringEquals": {
"s3:ExistingObjectTag/public": "true"
}
}
}
]
}json
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "PublicReadGetObject",
"Effect": "Allow",
"Principal": "*",
"Action": "s3:GetObject",
"Resource": "arn:aws:s3:::my-website-bucket/*",
"Condition": {
"StringEquals": {
"s3:ExistingObjectTag/public": "true"
}
}
}
]
}Cross-Account Access
跨账户访问
json
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "CrossAccountAccess",
"Effect": "Allow",
"Principal": {
"AWS": "arn:aws:iam::987654321098:root"
},
"Action": [
"s3:GetObject",
"s3:PutObject",
"s3:ListBucket"
],
"Resource": [
"arn:aws:s3:::shared-bucket",
"arn:aws:s3:::shared-bucket/*"
],
"Condition": {
"StringEquals": {
"s3:x-amz-acl": "bucket-owner-full-control"
}
}
}
]
}json
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "CrossAccountAccess",
"Effect": "Allow",
"Principal": {
"AWS": "arn:aws:iam::987654321098:root"
},
"Action": [
"s3:GetObject",
"s3:PutObject",
"s3:ListBucket"
],
"Resource": [
"arn:aws:s3:::shared-bucket",
"arn:aws:s3:::shared-bucket/*"
],
"Condition": {
"StringEquals": {
"s3:x-amz-acl": "bucket-owner-full-control"
}
}
}
]
}CloudFront Origin Access Control
CloudFront源访问控制
json
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "AllowCloudFrontServicePrincipal",
"Effect": "Allow",
"Principal": {
"Service": "cloudfront.amazonaws.com"
},
"Action": "s3:GetObject",
"Resource": "arn:aws:s3:::my-cdn-bucket/*",
"Condition": {
"StringEquals": {
"AWS:SourceArn": "arn:aws:cloudfront::123456789012:distribution/EDFDVBD6EXAMPLE"
}
}
}
]
}json
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "AllowCloudFrontServicePrincipal",
"Effect": "Allow",
"Principal": {
"Service": "cloudfront.amazonaws.com"
},
"Action": "s3:GetObject",
"Resource": "arn:aws:s3:::my-cdn-bucket/*",
"Condition": {
"StringEquals": {
"AWS:SourceArn": "arn:aws:cloudfront::123456789012:distribution/EDFDVBD6EXAMPLE"
}
}
}
]
}Enforce Encryption
强制加密
json
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "DenyUnencryptedUploads",
"Effect": "Deny",
"Principal": "*",
"Action": "s3:PutObject",
"Resource": "arn:aws:s3:::secure-bucket/*",
"Condition": {
"StringNotEquals": {
"s3:x-amz-server-side-encryption": "aws:kms"
}
}
},
{
"Sid": "DenyIncorrectKMSKey",
"Effect": "Deny",
"Principal": "*",
"Action": "s3:PutObject",
"Resource": "arn:aws:s3:::secure-bucket/*",
"Condition": {
"StringNotEquals": {
"s3:x-amz-server-side-encryption-aws-kms-key-id": "arn:aws:kms:us-east-1:123456789012:key/12345678-1234-1234-1234-123456789012"
}
}
}
]
}json
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "DenyUnencryptedUploads",
"Effect": "Deny",
"Principal": "*",
"Action": "s3:PutObject",
"Resource": "arn:aws:s3:::secure-bucket/*",
"Condition": {
"StringNotEquals": {
"s3:x-amz-server-side-encryption": "aws:kms"
}
}
},
{
"Sid": "DenyIncorrectKMSKey",
"Effect": "Deny",
"Principal": "*",
"Action": "s3:PutObject",
"Resource": "arn:aws:s3:::secure-bucket/*",
"Condition": {
"StringNotEquals": {
"s3:x-amz-server-side-encryption-aws-kms-key-id": "arn:aws:kms:us-east-1:123456789012:key/12345678-1234-1234-1234-123456789012"
}
}
}
]
}IP-Based Restrictions
基于IP的访问限制
json
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "AllowFromCorporateNetwork",
"Effect": "Allow",
"Principal": "*",
"Action": [
"s3:GetObject",
"s3:PutObject"
],
"Resource": "arn:aws:s3:::internal-bucket/*",
"Condition": {
"IpAddress": {
"aws:SourceIp": [
"192.0.2.0/24",
"203.0.113.0/24"
]
}
}
},
{
"Sid": "DenyFromOtherIPs",
"Effect": "Deny",
"Principal": "*",
"Action": "s3:*",
"Resource": [
"arn:aws:s3:::internal-bucket",
"arn:aws:s3:::internal-bucket/*"
],
"Condition": {
"NotIpAddress": {
"aws:SourceIp": [
"192.0.2.0/24",
"203.0.113.0/24"
]
}
}
}
]
}json
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "AllowFromCorporateNetwork",
"Effect": "Allow",
"Principal": "*",
"Action": [
"s3:GetObject",
"s3:PutObject"
],
"Resource": "arn:aws:s3:::internal-bucket/*",
"Condition": {
"IpAddress": {
"aws:SourceIp": [
"192.0.2.0/24",
"203.0.113.0/24"
]
}
}
},
{
"Sid": "DenyFromOtherIPs",
"Effect": "Deny",
"Principal": "*",
"Action": "s3:*",
"Resource": [
"arn:aws:s3:::internal-bucket",
"arn:aws:s3:::internal-bucket/*"
],
"Condition": {
"NotIpAddress": {
"aws:SourceIp": [
"192.0.2.0/24",
"203.0.113.0/24"
]
}
}
}
]
}VPC Endpoint Access Only
仅允许VPC端点访问
json
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "DenyNonVPCAccess",
"Effect": "Deny",
"Principal": "*",
"Action": "s3:*",
"Resource": [
"arn:aws:s3:::private-bucket",
"arn:aws:s3:::private-bucket/*"
],
"Condition": {
"StringNotEquals": {
"aws:SourceVpce": "vpce-1234567890abcdef0"
}
}
}
]
}json
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "DenyNonVPCAccess",
"Effect": "Deny",
"Principal": "*",
"Action": "s3:*",
"Resource": [
"arn:aws:s3:::private-bucket",
"arn:aws:s3:::private-bucket/*"
],
"Condition": {
"StringNotEquals": {
"aws:SourceVpce": "vpce-1234567890abcdef0"
}
}
}
]
}MFA Delete Protection
MFA删除保护
json
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "RequireMFAForDelete",
"Effect": "Deny",
"Principal": "*",
"Action": [
"s3:DeleteObject",
"s3:DeleteObjectVersion"
],
"Resource": "arn:aws:s3:::critical-bucket/*",
"Condition": {
"Bool": {
"aws:MultiFactorAuthPresent": "false"
}
}
}
]
}json
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "RequireMFAForDelete",
"Effect": "Deny",
"Principal": "*",
"Action": [
"s3:DeleteObject",
"s3:DeleteObjectVersion"
],
"Resource": "arn:aws:s3:::critical-bucket/*",
"Condition": {
"Bool": {
"aws:MultiFactorAuthPresent": "false"
}
}
}
]
}Time-Based Access
基于时间的访问控制
json
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "BusinessHoursOnly",
"Effect": "Deny",
"Principal": "*",
"Action": "s3:*",
"Resource": [
"arn:aws:s3:::business-bucket",
"arn:aws:s3:::business-bucket/*"
],
"Condition": {
"DateGreaterThan": {
"aws:CurrentTime": "2024-01-01T18:00:00Z"
},
"DateLessThan": {
"aws:CurrentTime": "2024-01-02T09:00:00Z"
}
}
}
]
}json
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "BusinessHoursOnly",
"Effect": "Deny",
"Principal": "*",
"Action": "s3:*",
"Resource": [
"arn:aws:s3:::business-bucket",
"arn:aws:s3:::business-bucket/*"
],
"Condition": {
"DateGreaterThan": {
"aws:CurrentTime": "2024-01-01T18:00:00Z"
},
"DateLessThan": {
"aws:CurrentTime": "2024-01-02T09:00:00Z"
}
}
}
]
}CloudTrail Logging
CloudTrail日志记录
json
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "AWSCloudTrailAclCheck",
"Effect": "Allow",
"Principal": {
"Service": "cloudtrail.amazonaws.com"
},
"Action": "s3:GetBucketAcl",
"Resource": "arn:aws:s3:::cloudtrail-logs-bucket",
"Condition": {
"StringEquals": {
"AWS:SourceArn": "arn:aws:cloudtrail:us-east-1:123456789012:trail/mytrail"
}
}
},
{
"Sid": "AWSCloudTrailWrite",
"Effect": "Allow",
"Principal": {
"Service": "cloudtrail.amazonaws.com"
},
"Action": "s3:PutObject",
"Resource": "arn:aws:s3:::cloudtrail-logs-bucket/AWSLogs/123456789012/*",
"Condition": {
"StringEquals": {
"s3:x-amz-acl": "bucket-owner-full-control",
"AWS:SourceArn": "arn:aws:cloudtrail:us-east-1:123456789012:trail/mytrail"
}
}
}
]
}json
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "AWSCloudTrailAclCheck",
"Effect": "Allow",
"Principal": {
"Service": "cloudtrail.amazonaws.com"
},
"Action": "s3:GetBucketAcl",
"Resource": "arn:aws:s3:::cloudtrail-logs-bucket",
"Condition": {
"StringEquals": {
"AWS:SourceArn": "arn:aws:cloudtrail:us-east-1:123456789012:trail/mytrail"
}
}
},
{
"Sid": "AWSCloudTrailWrite",
"Effect": "Allow",
"Principal": {
"Service": "cloudtrail.amazonaws.com"
},
"Action": "s3:PutObject",
"Resource": "arn:aws:s3:::cloudtrail-logs-bucket/AWSLogs/123456789012/*",
"Condition": {
"StringEquals": {
"s3:x-amz-acl": "bucket-owner-full-control",
"AWS:SourceArn": "arn:aws:cloudtrail:us-east-1:123456789012:trail/mytrail"
}
}
}
]
}Condition Keys Reference
条件键参考
yaml
condition_keys:
global:
aws:SourceIp: "IP address or CIDR"
aws:SourceVpc: "VPC ID"
aws:SourceVpce: "VPC endpoint ID"
aws:PrincipalOrgID: "AWS Organization ID"
aws:CurrentTime: "ISO 8601 datetime"
aws:MultiFactorAuthPresent: "true/false"
aws:SecureTransport: "true/false"
s3_specific:
s3:x-amz-acl: "ACL to apply"
s3:x-amz-server-side-encryption: "AES256 or aws:kms"
s3:x-amz-server-side-encryption-aws-kms-key-id: "KMS key ARN"
s3:ExistingObjectTag/<key>: "Object tag value"
s3:RequestObjectTagKeys: "Tags being set"
s3:prefix: "Object key prefix"
s3:max-keys: "Max keys in ListBucket"
s3:object-lock-mode: "GOVERNANCE or COMPLIANCE"
operators:
StringEquals: "Exact match"
StringNotEquals: "Not equal"
StringLike: "Wildcard match (*)"
IpAddress: "IP in CIDR"
NotIpAddress: "IP not in CIDR"
DateGreaterThan: "After date"
DateLessThan: "Before date"
Bool: "Boolean check"
Null: "Key exists/not exists"yaml
condition_keys:
global:
aws:SourceIp: "IP地址或CIDR段"
aws:SourceVpc: "VPC ID"
aws:SourceVpce: "VPC端点ID"
aws:PrincipalOrgID: "AWS组织ID"
aws:CurrentTime: "ISO 8601格式时间"
aws:MultiFactorAuthPresent: "true/false"
aws:SecureTransport: "true/false"
s3_specific:
s3:x-amz-acl: "要应用的ACL"
s3:x-amz-server-side-encryption: "AES256或aws:kms"
s3:x-amz-server-side-encryption-aws-kms-key-id: "KMS密钥ARN"
s3:ExistingObjectTag/<key>: "对象标签值"
s3:RequestObjectTagKeys: "正在设置的标签"
s3:prefix: "对象键前缀"
s3:max-keys: "ListBucket中的最大键数"
s3:object-lock-mode: "GOVERNANCE或COMPLIANCE"
operators:
StringEquals: "精确匹配"
StringNotEquals: "不匹配"
StringLike: "通配符匹配(*)"
IpAddress: "IP在CIDR段内"
NotIpAddress: "IP不在CIDR段内"
DateGreaterThan: "晚于指定日期"
DateLessThan: "早于指定日期"
Bool: "布尔值检查"
Null: "键存在/不存在"Security Best Practices
安全最佳实践
yaml
security_checklist:
block_public_access:
setting: "Block all public access"
how: |
aws s3api put-public-access-block \
--bucket my-bucket \
--public-access-block-configuration \
"BlockPublicAcls=true,IgnorePublicAcls=true,BlockPublicPolicy=true,RestrictPublicBuckets=true"
enable_versioning:
purpose: "Protect against accidental deletion"
how: |
aws s3api put-bucket-versioning \
--bucket my-bucket \
--versioning-configuration Status=Enabled
enable_logging:
purpose: "Audit access"
how: |
aws s3api put-bucket-logging \
--bucket my-bucket \
--bucket-logging-status '{"LoggingEnabled":{"TargetBucket":"log-bucket","TargetPrefix":"s3-access/"}}'
default_encryption:
purpose: "Encrypt at rest"
how: |
aws s3api put-bucket-encryption \
--bucket my-bucket \
--server-side-encryption-configuration \
'{"Rules":[{"ApplyServerSideEncryptionByDefault":{"SSEAlgorithm":"aws:kms","KMSMasterKeyID":"alias/s3-key"}}]}'
lifecycle_policy:
purpose: "Manage object lifecycle"
example: "Transition to Glacier after 90 days, delete after 365"yaml
security_checklist:
block_public_access:
setting: "阻止所有公共访问"
how: |
aws s3api put-public-access-block \
--bucket my-bucket \
--public-access-block-configuration \
"BlockPublicAcls=true,IgnorePublicAcls=true,BlockPublicPolicy=true,RestrictPublicBuckets=true"
enable_versioning:
purpose: "防止意外删除"
how: |
aws s3api put-bucket-versioning \
--bucket my-bucket \
--versioning-configuration Status=Enabled
enable_logging:
purpose: "审计访问记录"
how: |
aws s3api put-bucket-logging \
--bucket my-bucket \
--bucket-logging-status '{"LoggingEnabled":{"TargetBucket":"log-bucket","TargetPrefix":"s3-access/"}}'
default_encryption:
purpose: "静态加密"
how: |
aws s3api put-bucket-encryption \
--bucket my-bucket \
--server-side-encryption-configuration \
'{"Rules":[{"ApplyServerSideEncryptionByDefault":{"SSEAlgorithm":"aws:kms","KMSMasterKeyID":"alias/s3-key"}}]}'
lifecycle_policy:
purpose: "管理对象生命周期"
example: "90天后转换到Glacier存储,365天后删除"Troubleshooting
故障排查
yaml
common_issues:
access_denied:
symptoms: "403 AccessDenied error"
checks:
- "Verify IAM user/role permissions"
- "Check bucket policy allows action"
- "Verify resource ARN is correct"
- "Check for explicit Deny statements"
- "Verify bucket block public access settings"
debug: |
# Check effective policy
aws s3api get-bucket-policy --bucket my-bucket
# Test access
aws s3api head-object --bucket my-bucket --key test.txt
policy_too_large:
limit: "20 KB maximum"
solutions:
- "Use IAM policies instead"
- "Consolidate statements"
- "Use conditions instead of listing principals"
- "Reference IAM roles instead of users"
invalid_principal:
symptoms: "MalformedPolicy error"
common_causes:
- "Account ID doesn't exist"
- "Role/user doesn't exist"
- "Typo in ARN format"
format: "arn:aws:iam::ACCOUNT-ID:root/role/user"
condition_not_working:
checks:
- "Verify condition key spelling"
- "Check operator type matches value type"
- "Ensure condition applies to correct action"yaml
common_issues:
access_denied:
symptoms: "403 AccessDenied错误"
checks:
- "验证IAM用户/角色权限"
- "检查存储桶策略是否允许该操作"
- "验证资源ARN是否正确"
- "检查是否存在显式拒绝语句"
- "验证存储桶阻止公共访问设置"
debug: |
# 检查有效策略
aws s3api get-bucket-policy --bucket my-bucket
# 测试访问权限
aws s3api head-object --bucket my-bucket --key test.txt
policy_too_large:
limit: "最大20 KB"
solutions:
- "改用IAM策略"
- "合并语句"
- "使用条件而非列出所有主体"
- "引用IAM角色而非用户"
invalid_principal:
symptoms: "MalformedPolicy错误"
common_causes:
- "账户ID不存在"
- "角色/用户不存在"
- "ARN格式输入错误"
format: "arn:aws:iam::ACCOUNT-ID:root/role/user"
condition_not_working:
checks:
- "验证条件键拼写"
- "检查运算符类型与值类型匹配"
- "确保条件应用于正确的操作"Policy Validation
策略验证
bash
undefinedbash
undefinedValidate policy syntax
验证策略语法
aws iam simulate-custom-policy
--policy-input-list file://policy.json
--action-names s3:GetObject
--resource-arns arn:aws:s3:::my-bucket/test.txt
--policy-input-list file://policy.json
--action-names s3:GetObject
--resource-arns arn:aws:s3:::my-bucket/test.txt
aws iam simulate-custom-policy
--policy-input-list file://policy.json
--action-names s3:GetObject
--resource-arns arn:aws:s3:::my-bucket/test.txt
--policy-input-list file://policy.json
--action-names s3:GetObject
--resource-arns arn:aws:s3:::my-bucket/test.txt
Test policy with IAM Policy Simulator
使用IAM策略模拟器测试
Check for public access
检查公共访问状态
aws s3api get-bucket-policy-status --bucket my-bucket
aws s3api get-bucket-policy-status --bucket my-bucket
List bucket policies
列出存储桶策略
aws s3api get-bucket-policy --bucket my-bucket --output text
undefinedaws s3api get-bucket-policy --bucket my-bucket --output text
undefinedTerraform Example
Terraform示例
hcl
resource "aws_s3_bucket" "example" {
bucket = "my-secure-bucket"
}
resource "aws_s3_bucket_public_access_block" "example" {
bucket = aws_s3_bucket.example.id
block_public_acls = true
block_public_policy = true
ignore_public_acls = true
restrict_public_buckets = true
}
resource "aws_s3_bucket_policy" "example" {
bucket = aws_s3_bucket.example.id
policy = jsonencode({
Version = "2012-10-17"
Statement = [
{
Sid = "EnforceSSL"
Effect = "Deny"
Principal = "*"
Action = "s3:*"
Resource = [
aws_s3_bucket.example.arn,
"${aws_s3_bucket.example.arn}/*"
]
Condition = {
Bool = {
"aws:SecureTransport" = "false"
}
}
}
]
})
}hcl
resource "aws_s3_bucket" "example" {
bucket = "my-secure-bucket"
}
resource "aws_s3_bucket_public_access_block" "example" {
bucket = aws_s3_bucket.example.id
block_public_acls = true
block_public_policy = true
ignore_public_acls = true
restrict_public_buckets = true
}
resource "aws_s3_bucket_policy" "example" {
bucket = aws_s3_bucket.example.id
policy = jsonencode({
Version = "2012-10-17"
Statement = [
{
Sid = "EnforceSSL"
Effect = "Deny"
Principal = "*"
Action = "s3:*"
Resource = [
aws_s3_bucket.example.arn,
"${aws_s3_bucket.example.arn}/*"
]
Condition = {
Bool = {
"aws:SecureTransport" = "false"
}
}
}
]
})
}Лучшие практики
最佳实践
- Least privilege — минимальные необходимые права
- Block public access — блокируй публичный доступ по умолчанию
- Use conditions — добавляй условия для дополнительной защиты
- Enable logging — логируй все обращения к bucket
- Version control — храни политики в git
- Regular audits — проверяй политики регулярно
- 最小权限原则——仅授予必要的最小权限
- 阻止公共访问——默认情况下禁止公共访问
- 使用条件规则——添加条件以增强安全性
- 启用日志记录——记录所有对存储桶的访问请求
- 版本控制——将策略存储在Git中
- 定期审计——定期检查策略