S3 REST API HEAD 请求上的 403 禁止错误
Posted
技术标签:
【中文标题】S3 REST API HEAD 请求上的 403 禁止错误【英文标题】:403 forbidden error on S3 REST API HEAD request 【发布时间】:2015-02-12 16:59:53 【问题描述】:我正在尝试向 S3 REST API 发出 HEAD Object 请求,但我不断收到 403 Forbidden 错误,即使我在 S3 上拥有具有必要权限的策略设置。响应正文是空的,所以我认为这不是签名问题。我已经尝试对政策进行多次更改,但似乎没有任何效果。我可以正常 PUT 对象和 DELETE 对象,只是 HEAD 不起作用。
这是我的存储桶政策:
"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/*"
]
有什么想法吗?
更新:
正如迈克尔指出的那样,我的签名似乎有问题,尽管我看不出是什么。
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
生成的要签名的字符串如下所示:
HEAD\n\n\n1418590715\n/video-thumbnails/1234.jpg"
解决方案:
似乎在开发文件 PUT 部分时,我实际上破坏了 GET 和 HEAD。我传递了一个空字符串作为请求的主体,而不是什么都不传递,而是在签名上创建了所需的 mime 类型并破坏了它,因为我没有提供 mime 类型。我只是删除了空的请求正文,它工作得很好。感谢 Michael 为我指出了错误的方向(我浪费了很多时间来更改存储桶策略)。
【问题讨论】:
那么,这个完全相同的代码适用于签署GET
请求,但不适用于同一对象的HEAD
请求?我认为“video-thumbnails”是bucket,1234.jpg是key,US-Standard(us-east-1)是bucket所在的区域?
正确。我使用基于查询的身份验证,这适用于 HEAD 请求吗?
【参考方案1】:
它仍然可能是您的签名,我怀疑它是,原因如下:
您对邮件正文的观察是一个很好的观察;但是,它并不意味着您已经得出的结论。
在这种情况下,缺少响应正文根本不会为您提供任何有关错误性质的信息,因为无论如何,Web 服务器都不应该返回正文以及 HEAD
响应:
HEAD
方法与GET
相同,只是服务器MUST NOT
在响应中返回消息体—http://www.w3.org/Protocols/rfc2616/rfc2616-sec9.html (RFC-2616)
在我这边进行测试后,我确认 S3 对未签名的 HEAD
请求和未正确签名的 HEAD
请求的响应没有什么不同:它始终是 HTTP/1.1 403 Forbidden
,没有消息正文。
另外请注意,GET
的签名 URL 对 HEAD
无效,反之亦然。
在 S3 Signature Version 2 和 S3 Signature Version 4 中,“要签名的字符串”包括“HTTP 动词”,即 GET
或 HEAD
,这意味着对 GET
有效的签名将对HEAD
无效,反之亦然...请求方法必须在签名时已知,因为它是在签名过程中使用的元素。
s3:GetObject
权限是使用HEAD
所需的唯一documented 权限,如果GET
正常工作,这似乎消除了权限问题,这表明签名是潜在的问题。
【讨论】:
迈克尔,感谢您指出。请查看已编辑的消息。 @Marcello 我看到你已经接受了这个答案。我很感激,但您发现问题了吗?【参考方案2】:确认 HEAD a presigned-URL 将获得 403 Forbidden。 如果设置自定义标头,例如对象的内容类型。 403 响应将不包含自定义标头,仍会获取 application/xml。
【讨论】:
【参考方案3】:对上述@Michael-sqlbot 答案的补充评论...
我面临相同的症状,但我有不同的根本原因。
如果您尝试 HEAD 一个不存在的文件,那么这也会返回 403 禁止错误,除非您拥有 s3:ListBucket 权限。
在我的例子中,我拥有 s3.GetObject、s3.PutObject 和 s3.HeadBucket 权限,但直到我添加了 s3.ListBucket,我才得到正确的 404 - 未找到错误。
这里也有解释:https://aws.amazon.com/premiumsupport/knowledge-center/s3-rest-api-cloudfront-error-403/
【讨论】:
【参考方案4】:有同样的问题,但根本原因不同 - 试图创建一个存储桶,而不是得到 404,得到了 403。由于 S3 是全局命名空间的,所以其他人创建了存储桶,所以虽然我有正确的我的帐户的权限和设置,我仍然会从 HEAD 请求中获得 403。解决方法是先检查bucket是否全局存在,如果存在,换一个bucket名称。
【讨论】:
【参考方案5】:我也收到了这个错误作为一个红鲱鱼;在pytest
期间使用freezegun
。我已经冻结了过去的时间,并且收到了 403 错误。所以时钟偏差可能会导致这种情况。
我通过尝试另一个 API 调用发现了这一点,我收到了:
E botocore.exceptions.ClientError: An error occurred (RequestTimeTooSkewed) when calling the ListObjects operation: The difference between the request time and the current time is too large.
【讨论】:
以上是关于S3 REST API HEAD 请求上的 403 禁止错误的主要内容,如果未能解决你的问题,请参考以下文章
IIS Rest Api - 放置和删除请求不起作用 ERR_FAILED 403
Sharepoint 2013 通过 REST API:尝试创建项目时禁止出现错误 403
在 S3 上托管 React 页面并对 Elastic Beanstalk 上的服务器进行 REST api 调用