如何防止通过Curl向S3预签名URL上传恶意文件?
解决S3预签名URL上传时绕过Content-Type限制的问题
核心原因
你遇到的问题根源在于:预签名URL仅验证请求头中的Content-Type是否与签名时指定的一致,但不会校验文件的实际内容。攻击者可以伪造请求头的Content-Type为你指定的Word类型,同时上传.exe文件内容,S3只会检查请求头匹配,不会验证文件真实类型。
可行解决方案
1. 强制校验文件实际内容(推荐)
通过S3事件联动后端服务,在文件上传完成后立即校验真实类型:
- 配置S3事件通知,当有文件上传时触发Lambda函数;
- 在Lambda中读取文件头部字节(无需全量读取),检测实际MIME类型(比如用
magic库); - 若实际类型不在允许列表内,直接删除该文件并记录告警。
示例Lambda伪代码:
import boto3 import magic s3 = boto3.client('s3') ALLOWED_MIMES = ['application/msword', 'application/vnd.openxmlformats-officedocument.wordprocessingml.document'] def lambda_handler(event, context): record = event['Records'][0]['s3'] bucket = record['bucket']['name'] key = record['object']['key'] # 读取文件前1KB用于类型检测 file_head = s3.get_object(Bucket=bucket, Key=key)['Body'].read(1024) actual_mime = magic.from_buffer(file_head, mime=True) if actual_mime not in ALLOWED_MIMES: s3.delete_object(Bucket=bucket, Key=key) print(f"Removed malicious file: {key}, detected type: {actual_mime}")
2. 缩小预签名URL的滥用范围
- 缩短有效期:将URL过期时间设为5-10分钟,减少被滥用的窗口;
- 绑定IP段:生成URL时添加
SourceIp条件,仅允许指定IP/IP段的请求使用(需在签名时附带条件)。
示例带IP限制的预签名URL生成代码(boto3):
import boto3 s3 = boto3.client('s3') presigned_url = s3.generate_presigned_url( 'put_object', Params={'Bucket': 'your-bucket', 'Key': 'target-file.docx', 'ContentType': 'application/vnd.openxmlformats-officedocument.wordprocessingml.document'}, ExpiresIn=300, Conditions=[{'IpAddress': {'aws:SourceIp': '192.168.1.0/24'}}] )
3. 客户端辅助校验(仅作补充)
在前端上传页面通过JS检测文件扩展名和本地读取的MIME类型,拦截明显不符合的文件,但这只能防普通用户,攻击者可直接绕过前端调用URL,因此必须配合后端校验。
关键提醒
预签名URL的Content-Type校验仅停留在请求头层面,无法替代真实的文件内容验证。只有结合上传后的内容检测,才能有效阻止恶意文件上传。
内容的提问来源于stack exchange,提问作者Hà Lê
相关产品推荐
相关产品推荐

