Reputation: 4146
I have the following aws s3 api
end point from third party application
https://xxx.s3.amazonaws.com/uploads/74d75512c28b49358846f959bd798536?Signature=lxBIZJD7DN4QK3LPmsHxR7D2eTA%3D&Expires=1506108780&AWSAccessKeyId=AKIAJG6Z6A5TUL7ULPXA
I am trying to make an PUT request
with this url to upload an File
, but always get the following error
#<Net::HTTPForbidden 403 Forbidden readbody=true>
Update And here is the response body
<?xml version="1.0" encoding="UTF-8"?>
<Error>
<Code>SignatureDoesNotMatch</Code>
<Message>
The request signature we calculated does not match the
signature you provided. Check your key and signing method.
</Message>
<AWSAccessKeyId>AKIAJG6Z6A5TUL7ULPXA</AWSAccessKeyId>
<StringToSign>
PUT
application/xml
1506144576
/xxx/uploads/93a64f8081804604be917b4185c5ed58
</StringToSign>
<SignatureProvided>8pwtC2vtN4LuOwGc887AlT2ZnUc=</SignatureProvided>
<StringToSignBytes>50 55 </StringToSignBytes>
<RequestId>B2B18369BA23A92F</RequestId>
<HostId>YCtjOJsfskITKxjW96ouZq1BV=</HostId>
</Error>
If I inspect
the response
it shows following data
, but did not understood what's wrong with this? no other clue, anyone can help will be appreciated.
{
"x-amz-request-id" => [
[0] "385D5CADFE6175FC"
],
"x-amz-id-2" => [
[0] "4uD51Gb/rJy0QooQVmF25Qbp0E568bB9v1P4Grg9CTM2dJ/Iiccad/IyuuEnWDphlGZrr8ZUnQw="
],
"content-type" => [
[0] "application/xml"
],
"transfer-encoding" => [
[0] "chunked"
],
"date" => [
[0] "Fri, 22 Sep 2017 19:28:01 GMT"
],
"server" => [
[0] "AmazonS3"
]
}
I am running the following code snippet
uri = URI.parse(aws_api_end_point)
request = Net::HTTP::Put.new(uri)
request.body = File.read(file_path)
request.content_type = 'application/xml'
http = Net::HTTP.start(uri.host, uri.port, :use_ssl => true)
response = http.request(request)
Note:: I am not using any aws SDK.
Upvotes: 0
Views: 912
Reputation: 178966
Request signing is deterministic -- for a given request at a given moment in time, there is exactly one valid signature. For all practical purposes, the opposite is also true -- for any signature, there's only one valid request you can make. Anything else, and the signature does not match.
The algorithm is designed not to be reverse-engineered, so we can't say what request they expected you to make when they gave you the signature.
But, we do have this. I believe some whitespace was lost, so I have added it back in:
<StringToSign>
PUT
application/x-www-form-urlencoded
1506144576
/xxx/uploads/93a64f8081804604be917b4185c5ed58
</StringToSign>
This is the request you made (not the request you were expected to make), converted to its canonical form using this pseudocode.
StringToSign =
HTTP-VERB + "\n" +
Content-MD5 + "\n" +
Content-Type + "\n" +
Expires + "\n" +
CanonicalizedAmzHeaders +
CanonicalizedResource;
The most obvious candidate for a problem is Content-Type
. It is almost a certainty that they did not expect you to use application/x-www-form-urlencoded
because the S3 PUT
Object operation does not use HTML Form uploads. S3 PUT
expects the raw octets of the object in the request body, and a Content-Type
header to match. S3 itself does not actually validate whether they match (header vs. body), but without at least the expected Content-Type
header, the upload will be blocked.
If you are providing a Content-Type
to the 3rd party API, then your upload needs to use that same type on the S3 upload, because the signature expects it.
Unfortunately, for troubleshooting purposes, there is an infinite number of things that can be done wrong to invalidate a request intended to be used with a pre-signed URL. The documentation from the third party should clarify the structure of the subsequent request they expect you to make.
Upvotes: 1