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

如何安全合理设置DigitalOcean Spaces上传文件的访问权限?

安全访问DigitalOcean Spaces(S3兼容)文件的正确方式

我用Python脚本把文件传到DigitalOcean Spaces(兼容AWS S3的对象存储),上传功能正常,但不知道怎么安全访问文件。目前必须设置IMAGES_STORE_S3_ACL = 'public-read',才能通过拼接URL(比如file_path = f'{storage_domain}/my_files/{file_name}')在浏览器显示图片,否则会出现Permission Denied错误。但这种方式等于任何人只要拼对URL就能访问,完全不安全。

上传时boto3模块输出了请求日志(内容如下),我不确定能否从Python脚本中访问这些数据:

Making request for OperationModel(name=PutObject) with params: {'url_path': '/path/to/file.jpg', 'query_string': {}, 'method': 'PUT', 'headers': {'x-amz-meta-width': '749', 'x-amz-meta-height': '562', 'x-amz-acl': 'public-read', 'Cache-Control': 'max-age=172800', 'Content-Type': 'image/jpeg', 'User-Agent': 'Botocore/1.34.83 ua/2.0 os/macos#23.4.0 md/arch#arm64 lang/python#3.11.8 md/pyimpl#CPython cfg/retry-mode#legacy', 'Content-MD5': 'something here==', 'Expect': '100-continue'}, 'body': <_io.BytesIO object at 0x10778fbf0>, 'auth_path': '/path/to/file.jpg', 'url': 'https://location.digitaloceanspaces.com/path/to/file.jpg', 'context': {'client_region': 'region', 'client_config': <botocore.config.Config object at 0x107189210>, 'has_streaming_input': True, 'auth_type': 'v4', 's3_redirect': {'redirected': False, 'bucket': 'bucket_name', 'params': {'Bucket': 'bucket_name', 'Key': 'path/to/file.jpg', 'Body': <_io.BytesIO object at 0x10778fbf0>, 'Metadata': {'width': '749', 'height': '562'}, 'ACL': 'public-read', 'CacheControl': 'max-age=172800', 'ContentType': 'image/jpeg'}}, 'input_params': {'Bucket': 'bucket_name', 'Key': 'path/to/file.jpg'}, 'signing': {'region': 'location', 'signing_name': 's3', 'disableDoubleEncoding': True}, 'endpoint_properties': {'authSchemes': [{'disableDoubleEncoding': True, 'name': 'sigv4', 'signingName': 's3', 'signingRegion': 'fra1'}]}}}
Event request-created.s3.PutObject: calling handler <bound method RequestSigner.handler of <botocore.signers.RequestSigner object at 0x1071891d0>>
Event choose-signer.s3.PutObject: calling handler <function set_operation_specific_signer at 0x106070fe0>
Event before-sign.s3.PutObject: calling handler <function remove_arn_from_signing_path at 0x106073560>
Event before-sign.s3.PutObject: calling handler <bound method S3ExpressIdentityResolver.resolve_s3express_identity of <botocore.utils.S3ExpressIdentityResolver object at 0x1071a8c10>>
Calculating signature using v4 auth.
CanonicalRequest:
PUT
/path/to/file.jpg

cache-control:max-age=172800
content-md5:something==
content-type:image/jpeg
host:location.digitaloceanspaces.com
x-amz-acl:public-read
x-amz-content-sha256:UNSIGNED-PAYLOAD
x-amz-date:20240412T204214Z
x-amz-meta-height:562
x-amz-meta-width:749

cache-control;content-md5;content-type;host;x-amz-acl;x-amz-content-sha256;x-amz-date;x-amz-meta-height;x-amz-meta-width
UNSIGNED-PAYLOAD
StringToSign:
AWS4-HMAC-SHA256
20240412T204214Z
20240412/location/s3/aws4_request
something_here
Signature:
something_here

替代公开访问的安全方案

1. 生成预签名URL(最常用)

这是S3兼容存储的标准安全访问方式,生成带签名的临时URL,只有持有该URL的用户能在有效期内访问,过期自动失效。

代码示例:

import boto3

# 初始化DigitalOcean Spaces客户端
s3 = boto3.client(
    's3',
    region_name='你的区域(比如fra1)',
    endpoint_url='https://你的区域.digitaloceanspaces.com',
    aws_access_key_id='你的Access Key',
    aws_secret_access_key='你的Secret Key'
)

# 生成预签名URL,默认有效期3600秒(1小时)
def generate_presigned_url(bucket_name, object_key, expires_in=3600):
    try:
        return s3.generate_presigned_url(
            'get_object',
            Params={'Bucket': bucket_name, 'Key': object_key},
            ExpiresIn=expires_in
        )
    except Exception as e:
        print(f"生成URL失败: {e}")
        return None

# 使用示例
bucket = "你的存储桶名称"
file_key = "path/to/file.jpg"
temp_url = generate_presigned_url(bucket, file_key)
print(f"临时访问链接: {temp_url}")
  • 注意:有效期可以根据场景调整,比如给用户展示图片设1小时,内部系统使用可设更久,但不要过长避免泄露风险。

2. 配置存储桶精细权限策略

如果需要固定的服务端(比如你的Web服务器)访问,可通过存储桶策略限制访问来源,比如只允许指定IP访问:

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Principal": "*",
            "Action": "s3:GetObject",
            "Resource": "arn:aws:s3:::你的存储桶名/*",
            "Condition": {
                "IpAddress": {
                    "aws:SourceIp": "你的服务器IP/32"
                }
            }
        }
    ]
}

这种方式下,只有你的服务器能直接访问文件,用户通过你的服务器间接获取,看不到Spaces的真实地址。

3. 应用层代理访问

完全通过你的应用接口中转文件请求:用户请求你的API,应用后台用自身权限从Spaces拉取文件,再返回给用户。好处是能在应用层做身份验证、权限校验、日志记录等,完全隐藏Spaces地址。

Flask示例代码:

from flask import Flask, send_file
import boto3
from io import BytesIO

app = Flask(__name__)
s3 = boto3.client('s3', 你的配置参数...)

@app.route('/images/<file_name>')
def serve_image(file_name):
    # 先做用户身份/权限校验,比如检查登录状态、用户是否有权限查看该文件
    # ...
    
    try:
        # 从Spaces获取文件
        response = s3.get_object(Bucket='你的存储桶名', Key=f'my_files/{file_name}')
        file_data = response['Body'].read()
        return send_file(BytesIO(file_data), mimetype=response['ContentType'])
    except Exception as e:
        return f"文件获取失败: {str(e)}", 404

if __name__ == '__main__':
    app.run()

关于boto3日志的说明

你上传时看到的日志是boto3内部的签名请求细节,这些数据不需要你手动处理,SDK已经完成了签名逻辑。如果需要获取上传后的文件元信息(比如ETag),可以直接从put_object的返回值中获取:

response = s3.put_object(Bucket=bucket_name, Key=object_key, Body=file_data, ACL='private')
print(f"文件ETag: {response['ETag']}")

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.26 00:14:54