AWS签名V4预签名PUT请求出现SignatureDoesNotMatch问题
解决API Gateway PUT预签名URL(带请求体)的签名不匹配问题
API Gateway对UNSIGNED-PAYLOAD的支持
API Gateway完全支持UNSIGNED-PAYLOAD选项,当预签名URL生成时无法提前获取请求体内容(比如客户端后续上传任意二进制文件),可以通过该选项跳过请求体的哈希校验,避免签名不匹配的问题。
修改代码生成带UNSIGNED-PAYLOAD的预签名URL
你需要从@aws-sdk/signature-v4导入UNSIGNED_PAYLOAD常量,并在presign方法的请求参数中指定body: UNSIGNED_PAYLOAD,这样签名计算时会用固定字符串替代请求体哈希,客户端上传任意请求体都能通过校验。
修改后的TypeScript代码如下:
import { SignatureV4, UNSIGNED_PAYLOAD } from "@aws-sdk/signature-v4"; // 新增UNSIGNED_PAYLOAD导入 import { Sha256 } from "@aws-crypto/sha256-js"; import { formatUrl } from "@aws-sdk/util-format-url"; const EXPIRES_IN = 3600; // 示例过期时间,根据实际需求调整 const createSignedUrl = async (credentials: { accessKeyId: string, secretAccessKey: string, sessionToken: string, }, requestParams: { method: "GET" | "PUT", host: string, protocol: string, path: string, }) => { const sigv4 = new SignatureV4({ service: "execute-api", region: process.env.AWS_REGION!, credentials: { accessKeyId: credentials.accessKeyId, secretAccessKey: credentials.secretAccessKey, sessionToken: credentials.sessionToken, }, sha256: Sha256, applyChecksum: false }); const signedUrlRequest = await sigv4.presign({ method: requestParams.method, hostname: requestParams.host, path: requestParams.path, protocol: requestParams.protocol, headers: { host: requestParams.host, }, body: UNSIGNED_PAYLOAD, // 新增此行,指定使用UNSIGNED-PAYLOAD }, { expiresIn: EXPIRES_IN, }); const signedUrl = formatUrl(signedUrlRequest); return signedUrl; };
原理说明
当你设置body: UNSIGNED_PAYLOAD时,Signature V4签名计算过程中会将规范请求里的请求体哈希部分替换为固定的UNSIGNED_PAYLOAD字符串,而不是计算实际请求体的SHA-256哈希。API Gateway收到请求时,会识别这个标记,跳过请求体的哈希校验,从而允许客户端上传任意二进制文件而不会触发签名不匹配错误。
额外注意事项
- 确保你的API Gateway配置的IAM权限允许对应的PUT请求操作
- 确认
expiresIn设置的过期时间符合业务安全需求,避免URL被滥用 - 若后续需要对请求体进行校验,可以考虑在API Gateway集成S3时添加其他校验逻辑(比如文件大小、类型)
内容的提问来源于stack exchange,提问作者michaelb
相关产品推荐
相关产品推荐

