403 Forbidden Error in S3 REST API HEAD Request
I'm trying to make a request for a HEAD object in the S3 REST API, but I still get a 403 Forbidden error even though I have a policy setting with the required permissions on S3. The body of the response is empty, so I don't think this is a signature issue. I tried several policy changes, nothing seemed to work. I can run objects and DELETE objects just fine, only HEAD doesn't work.
Here's my bucket policy:
{
"Statement": [
{
"Effect": "Allow",
"Principal": {
"AWS": "arn:aws:iam:: 999999999999:user/User"
},
"Action": "s3:ListBucket",
"Resource": "arn:aws:s3:::my-bucket"
},
{
"Effect": "Allow",
"Principal": {
"AWS": "*"
},
"Action": "s3:GetObject",
"Resource": "arn:aws:s3:::my-bucket/*"
},
{
"Sid": "",
"Effect": "Allow",
"Principal": {
"AWS": "arn:aws:iam::999999999999:user/User"
},
"Action": [
"s3:GetObject",
"s3:GetObjectVersion",
"s3:DeleteObject",
"s3:PutObject"
],
"Resource": "arn:aws:s3:::my-bucket/*"
}
]
}
Any ideas?
Update:
As Michael pointed out, it looks like this is a problem with my signature, although Im not been able to figure out what.
def generate_url options={}
options[:action] = options[:action].to_s.upcase
options[:expires] ||= Time.now.to_i + 100
file_path = "/" + @bucket_name + "/" + options[:file_name]
string_to_sign = ""
string_to_sign += options[:action]
string_to_sign += "\n\n#{options[:mime_type]}\n"
string_to_sign += options[:expires].to_s
string_to_sign += "\n"
string_to_sign += file_path
signature = CGI::escape(
Base64.strict_encode64(
OpenSSL::HMAC.digest('sha1', SECRET_KEY, string_to_sign)
)
)
url = "https://s3.amazonaws.com"
url += file_path
url += "?AWSAccessKeyId=#{ACCESS_KEY}"
url += "&Expires=#{options[:expires]}"
url += "&Signature=#{signature}"
url
end
The generated string looks like this:
HEAD\n\n\n1418590715\n/video-thumbnails/1234.jpg"
Decision:
It seems that at some point while developing the PUT part of the file I actually broke GET and HEAD. I was passing in an empty string as the request body instead of not skipping anything, creating the mimic type required for the signature and breaking it because I didn't provide the mime type. I just removed the empty request body and it worked fine. Thank you Michael for pointing me in the wrong direction (I've spent so much time changing the bucket policy).
source to share
It could still be your signature, and I suspect it is for the following reasons:
Your observation that the body of the message is a good observation; however, this does not mean that you did it.
The absence of a response body does not give you any information about the nature of the error, in this case, since the web server should not return the body along with the response HEAD
, no matter what
The method
HEAD
is identicalGET
, except that the serverMUST NOT
returns the message body in response- http://www.w3.org/Protocols/rfc2616/rfc2616-sec9.html (RFC-2616)
By checking this on my side, I have verified that the S3 response to the unsigned HEAD
request and the incorrectly signed HEAD
request is no different: it is always HTTP/1.1 403 Forbidden
without a message body.
Please note that the signed URL for is GET
not valid for HEAD
and vice versa.
In S3 Signature Version 2 and S3 Signature Version 4 "String to Sign" includes "HTTP-Verb", which will be GET
or HEAD
, which means that a signature valid for GET
, invalid for HEAD
and vice versa ... the request method must be known in time of signing, because it is an element that was used in the signing process.
The permission s3:GetObject
is the only documented permission required for use HEAD
, which seems to eliminate the permission as a problem if it GET
works, which indicates the signature as a potential problem.
source to share