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

如何验证用户通过Presigned URL上传至S3 Bucket的文件?(Serverless框架)

如何在S3预签名URL上传时验证数据内容

针对你遇到的预签名URL无上传限制问题,提供以下几种落地解决方案:


一、预签名阶段强制绑定验证参数(客户端上传时直接拦截)

生成预签名URL时,将需要验证的元数据(如文件类型、大小范围)加入签名参数,客户端上传时必须严格匹配这些参数,否则S3会直接拒绝请求。

修改后的Lambda代码:

module.exports.uploadLarge = async (event) => {
    console.log({ event })
    try {
        const body = JSON.parse(event.body);
        const action = body.action;
        const type = body.type;
        const key = body.key;

        // 第一步:提前校验客户端传入的文件类型是否在允许列表内
        const allowedContentTypes = ['image/jpeg', 'image/png', 'application/pdf'];
        if (!allowedContentTypes.includes(type)) {
            return {
                statusCode: 400,
                headers: { 'Access-Control-Allow-Origin': '*' },
                body: JSON.stringify({ error: '不允许的文件类型' })
            };
        }

        const params = {
            Bucket: BucketName,
            Key: key,
            Expires: 10000,
        }
        if (action === "putObject") {
            // 强制将ContentType加入签名参数,客户端必须携带一致的请求头
            params.ContentType = type;
            // 可选:限制文件大小范围(0到50MB)
            params.ContentLengthRange = { Min: 0, Max: 1024 * 1024 * 50 };
        }
        console.log({ params })
        const u = S3.getSignedUrl(action, params);
        return {
            statusCode: 200,
            body: JSON.stringify({ u }),
            headers: { 'Access-Control-Allow-Origin': '*' }
        }
    } catch (err) {
        return {
            statusCode: 500,
            headers: {
                "Content-Type": "application/json",
                "Access-Control-Allow-Origin": "*"
            },
            body: JSON.stringify(err)
        }
    }
}

效果说明:

  • 客户端上传时必须使用预签名中指定的Content-Type,否则返回403错误
  • 如果设置了ContentLengthRange,文件大小超出范围会直接被S3拦截

二、上传完成后触发Lambda异步验证文件内容

如果需要验证文件真实内容(比如是否是真图片,而非改后缀的文件),可以通过S3事件触发Lambda,在后台校验并清理非法文件。

1. Serverless配置片段(添加S3事件触发)

functions:
  uploadLarge:
    # 你的现有函数配置
    events:
      - http:
          path: upload
          method: post
          cors: true
  validateUpload:
    handler: handlers.validateUpload.handler
    events:
      - s3:
          bucket: ${self:custom.BucketName}
          event: s3:ObjectCreated:*
          rules:
            - prefix: uploads/ # 只监听指定前缀的文件
          existing: true # 若桶已存在需添加此配置
    iamRoleStatements:
      - Effect: Allow
        Action:
          - s3:GetObject
          - s3:DeleteObject
        Resource: arn:aws:s3:::${self:custom.BucketName}/*

2. 验证Lambda代码示例

const AWS = require('aws-sdk');
const s3 = new AWS.S3();
const fileType = require('file-type'); // 需要安装该库:npm install file-type

module.exports.handler = async (event) => {
    const record = event.Records[0];
    const bucket = record.s3.bucket.name;
    const key = decodeURIComponent(record.s3.object.key.replace(/\+/g, ' '));
    
    try {
        // 获取文件元数据
        const head = await s3.headObject({ Bucket: bucket, Key: key }).promise();
        const declaredType = head.ContentType;

        // 下载文件前100字节检测真实类型
        const fileBuffer = await s3.getObject({
            Bucket: bucket,
            Key: key,
            Range: 'bytes=0-99'
        }).promise().then(res => res.Body);
        
        const detectedType = await fileType.fromBuffer(fileBuffer);

        // 校验真实类型与声明类型是否匹配
        const typeMap = {
            'image/jpeg': ['jpg', 'jpeg'],
            'image/png': ['png'],
            'application/pdf': ['pdf']
        };
        if (!typeMap[declaredType] || !typeMap[declaredType].includes(detectedType?.ext)) {
            // 删除非法文件
            await s3.deleteObject({ Bucket: bucket, Key: key }).promise();
            console.log(`已删除非法文件:${key},检测到真实类型:${detectedType?.ext}`);
            return { statusCode: 200, body: '非法文件已清理' };
        }

        console.log(`文件验证通过:${key}`);
        return { statusCode: 200, body: '文件验证通过' };
    } catch (err) {
        console.error('验证失败:', err);
        return { statusCode: 500, body: JSON.stringify(err) };
    }
};

三、S3桶策略直接限制上传条件

通过桶策略强制要求上传请求满足指定条件,作为前两种方案的补充。

示例桶策略:

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Principal": "*",
            "Action": "s3:PutObject",
            "Resource": "arn:aws:s3:::your-bucket/*",
            "Condition": {
                "StringEquals": {
                    "Content-Type": ["image/jpeg", "image/png", "application/pdf"]
                },
                "ContentLengthLessThan": 52428800 // 限制最大50MB
            }
        }
    ]
}

方案选择建议

  • 仅需限制文件类型、大小等元数据:用预签名绑定参数+桶策略组合,拦截成本最低
  • 需要验证文件真实内容:必须配合上传后Lambda异步验证,因为S3本身不支持内容级校验

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.21 11:30:52