You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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'

问题分析与解决方案

可能原因

  1. 桶策略优先级覆盖对象ACL:如果桶策略中存在允许所有用户访问private路径对象的规则,即使对象本身设置了private ACL,也会被强制公开。
  2. 历史对象ACL未更新:若在调整PrivateMediaStorage的default_acl设置前就上传了private路径的文件,这些文件的ACL可能仍为public-read或继承了旧的桶默认权限。
  3. 全局AWS_DEFAULT_ACL的影响:全局配置AWS_DEFAULT_ACL = None时,对象会使用桶的默认权限,若桶默认权限为公开,则私有路径的对象也会被公开访问。

解决方案

  1. 修正桶策略
    确保桶策略仅对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/*"
        }
    ]
}
  1. 更新现有私有对象的ACL
    对于已上传到private路径的文件,可通过S3控制台手动修改其ACL为私有(仅对象所有者有访问权限),或使用配置正确的PrivateMediaStorage重新上传这些文件。

  2. 验证存储后端ACL生效情况
    上传新的私有文件后,在S3控制台查看其权限,确认default_acl = 'private'是否正确应用。

  3. 检查桶的默认权限
    在S3控制台的桶权限设置中,确认桶的默认访问权限为私有,避免全局AWS_DEFAULT_ACL = None时对象继承公开权限。


内容的提问来源于stack exchange,提问作者Tejaswin

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.21 15:03:23