部署为AWS Lambda HTTP API的.NET 6 Minimal Api文件上传POST返回403
使用AWS Lambda HTTP API部署.NET 6 Minimal Api,已启用Function URL,包含GET诊断接口和POST文件上传接口。本地调试时两个接口均正常工作,但线上环境中GET接口调用正常,POST文件上传接口始终返回403 InvalidSignatureException,错误信息如下:
{"message":"The request signature we calculated does not match the signature you provided. Check your AWS Secret Access Key and signing method. Consult the service documentation for details."}
相关代码
public async Task<IActionResult> UploadFileAsync([FromForm]RetailerUploadDetailsDto request) { try { var result = await _fileService.UploadFileAsync(request.File.FileName, _webHostEnvironment.EnvironmentName, request.File.OpenReadStream().ToByteArray()); if (result.IsError) { return StatusCode(result.StatusCode, result.Error); } return Ok(); } catch (Exception e) { _logger.LogError(e, "Failed to upload file"); return StatusCode((int)HttpStatusCode.InternalServerError, e.Message); } }
测试用curl命令
正常工作的GET请求
curl --location 'https://xxxxxxxxxxxx.lambda-url.ap-southeast-2.on.aws/Upload/Diagnostic' --header 'X-Amz-Date: 20231017T031151Z' --header 'Authorization: AWS4-HMAC-SHA256 Credential=xxxxxxxxxxxx/20231017/ap-southeast-2/lambda/aws4_request, SignedHeaders=host;x-amz-date, Signature=xxxxxxxxxxxxxxxxxxxxxxxxx'
返回403的POST请求
curl --location 'https://***********.lambda-url.ap-southeast-2.on.aws/Upload' \ --header 'X-Amz-Date: 20231023T193740Z' \ --header 'Authorization: AWS4-HMAC-SHA256 Credential=*************/20231023/ap-southeast-2/lambda/aws4_request, SignedHeaders=content-length;content-type;host;x-amz-date, Signature=*********************' \ --form 'RetailerId=""' \ --form 'ShouldDeleteProducts="false"' \ --form 'File=@"Retailer Upload Sheet_v1.4.xlsx"' -v
严格匹配Content-Type的签名值:
multipart/form-data请求的Content-Type会自动包含boundary(如multipart/form-data; boundary=------------------------d7780c363dd73797),签名计算时必须使用实际发送的完整Content-Type字符串,不能仅用multipart/form-data。curl使用--form时会自动生成boundary,需确保签名时的content-type参数和请求头完全一致。校验签名参数的一致性:
- 确认
X-Amz-Date的日期与Credential中的日期(如20231023)完全匹配,不能有时间差或格式错误; - 检查SignedHeaders中的头列表与实际请求发送的头完全对应,且顺序一致;
- 验证IAM用户的Secret Access Key无错误,且该用户拥有
lambda:InvokeFunctionUrl权限。
- 确认
简化请求逐步排查:
先发送不带文件的POST表单请求(仅传RetailerId和ShouldDeleteProducts),验证是否仍返回403。若正常,再添加文件测试,排查是否为文件名含特殊字符(如空格)或文件内容导致签名计算异常,必要时对文件名进行URL编码。检查Function URL权限配置:
确认Function URL的授权类型为AWS_IAM,同时验证IAM用户的权限策略包含对该Lambda函数的InvokeFunctionUrl权限,示例策略如下:{ "Version": "2012-10-17", "Statement": [ { "Effect": "Allow", "Action": "lambda:InvokeFunctionUrl", "Resource": "arn:aws:lambda:ap-southeast-2:ACCOUNT_ID:function:FUNCTION_NAME", "Condition": { "StringEquals": { "lambda:FunctionUrlAuthType": "AWS_IAM" } } } ] }查看CloudWatch日志:
启用Lambda的CloudWatch日志,查看是否有签名计算过程中的细节差异,帮助定位具体不匹配的部分。
内容的提问来源于stack exchange,提问作者Madhav Shenoy

