Django结合S3、boto3时自定义存储后端类异常:私有文件可公开访问
Django S3存储配置问题排查
已解决问题记录
- 执行
collectstatic命令时触发botocore.exceptions.ClientError: An error occurred (AccessDenied) when calling the PutObject operation: Access Denied错误,通过设置AWS_DEFAULT_ACL = None解决。 - 静态文件成功上传S3但网页无法加载,直接访问对象URL返回AccessDenied:关闭桶的
Block all public access设置并添加桶策略后,静态图片和样式可正常加载;后续字体文件出现跨域错误,添加对应CORS配置后解决。
当前待解决问题
配置三个继承自S3Boto3Storage的存储后端类(StaticStorage、PublicMediaStorage、PrivateMediaStorage)后,PublicMediaStorage的文件可正常公开访问,但PrivateMediaStorage的文件也能通过普通URL(如https://<bucket-name>.s3.eu-north-1.amazonaws.com/private/hello.txt)直接公开访问,不符合私有存储的预期。
相关代码
存储后端类
class StaticStorage(S3Boto3Storage): location = 'static' default_acl = 'public-read' class PublicMediaStorage(S3Boto3Storage): location = 'media' default_acl = 'public-read' file_overwrite = False class PrivateMediaStorage(S3Boto3Storage): location = 'private' default_acl = 'private' file_overwrite = False custom_domain = False
settings.py 配置
# settings.py 配置内容 if USE_S3: # aws settings AWS_ACCESS_KEY_ID = os.getenv('AWS_ACCESS_KEY_ID') AWS_SECRET_ACCESS_KEY = os.getenv('AWS_SECRET_ACCESS_KEY') AWS_STORAGE_BUCKET_NAME = os.getenv('AWS_STORAGE_BUCKET_NAME') AWS_S3_REGION_NAME = os.getenv('AWS_S3_REGION_NAME') AWS_DEFAULT_ACL = None AWS_S3_CUSTOM_DOMAIN = f'{AWS_STORAGE_BUCKET_NAME}.s3.amazonaws.com' AWS_S3_OBJECT_PARAMETERS = {'CacheControl': 'max-age=86400'} # s3 static settings STATIC_LOCATION = 'public-read' STATIC_URL = f'https://{AWS_S3_CUSTOM_DOMAIN}/{STATIC_LOCATION}/' STATICFILES_STORAGE = 'myproject.storage_backends.StaticStorage' # s3 public media settings PUBLIC_MEDIA_LOCATION = 'media' MEDIA_URL = f'https://{AWS_S3_CUSTOM_DOMAIN}/{PUBLIC_MEDIA_LOCATION}/' DEFAULT_FILE_STORAGE = 'myproject.storage_backends.PublicMediaStorage' # s3 private media settings PRIVATE_MEDIA_LOCATION = 'private' PRIVATE_FILE_STORAGE = 'myproject.storage_backends.PrivateMediaStorage' else: STATIC_URL = 'static/' STATIC_ROOT = BASE_DIR / 'staticfiles' MEDIA_URL = 'media/' MEDIA_ROOT = BASE_DIR / 'mediafiles'
问题分析与解决方案
可能原因
- 桶策略优先级覆盖对象ACL:如果桶策略中存在允许所有用户访问
private路径对象的规则,即使对象本身设置了privateACL,也会被强制公开。 - 历史对象ACL未更新:若在调整
PrivateMediaStorage的default_acl设置前就上传了private路径的文件,这些文件的ACL可能仍为public-read或继承了旧的桶默认权限。 - 全局AWS_DEFAULT_ACL的影响:全局配置
AWS_DEFAULT_ACL = None时,对象会使用桶的默认权限,若桶默认权限为公开,则私有路径的对象也会被公开访问。
解决方案
- 修正桶策略
确保桶策略仅对static和media路径开放公开访问,移除任何涉及private/*的公开规则。示例策略如下:
{ "Version": "2012-10-17", "Statement": [ { "Effect": "Allow", "Principal": "*", "Action": "s3:GetObject", "Resource": "arn:aws:s3:::your-bucket-name/static/*" }, { "Effect": "Allow", "Principal": "*", "Action": "s3:GetObject", "Resource": "arn:aws:s3:::your-bucket-name/media/*" } ] }
更新现有私有对象的ACL
对于已上传到private路径的文件,可通过S3控制台手动修改其ACL为私有(仅对象所有者有访问权限),或使用配置正确的PrivateMediaStorage重新上传这些文件。验证存储后端ACL生效情况
上传新的私有文件后,在S3控制台查看其权限,确认default_acl = 'private'是否正确应用。检查桶的默认权限
在S3控制台的桶权限设置中,确认桶的默认访问权限为私有,避免全局AWS_DEFAULT_ACL = None时对象继承公开权限。
内容的提问来源于stack exchange,提问作者Tejaswin
相关产品推荐
相关产品推荐

