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 动词”,即 GETHEAD,这意味着对 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 调用

S3 REST API 和 POST 方法

使用预签名 URL 上传到 S3 存储桶会出现 403 禁止错误

选项请求的 AWS S3 CORS 403 错误