AWS S3 在 getSignedUrl 过期后优雅地处理 403

Posted

技术标签:

【中文标题】AWS S3 在 getSignedUrl 过期后优雅地处理 403【英文标题】:AWS S3 gracefully handle 403 after getSignedUrl expired 【发布时间】:2016-01-11 12:18:28 【问题描述】:

在通过过期 URL 访问 S3 资源时,我试图优雅地处理 403。目前它返回一个 amz xml 错误页面。我已经上传了一个 403.html 资源,并认为我可以重定向到那个。

存储桶资源是我的应用保存/获取的资产。尽管如此,阅读文档我设置存储桶属性以将存储桶作为静态网页页面处理,并将 403.html 上传到存储桶根目录。除对资源 403.html 的公共 GET 访问外,所有公共权限均被阻止。在存储桶属性、网站设置中,我将 403.html 指示为错误页面。访问http://<bucket>.s3-website-us-east-1.amazonaws.com/some-asset.html 会正确重定向到http://<bucket>.s3-website-us-east-1.amazonaws.com/403.html

但是,当我使用 aws-sdk js/node 并调用方法 getSignedUrl('getObject', params) 生成签名的 url 时,它返回不同的主机 url:https://<bucket>.s3.amazonaws.com/ 从该方法访问过期资源不会重定向到 403.html .我猜测由于主机地址不同,这就是它不会自动重定向的原因。

我还为条件设置了静态网站路由规则

<Condition>
  <HttpErrorCodeReturnedEquals>403</HttpErrorCodeReturnedEquals>
</Condition>
<Redirect>
  <ReplaceKeyWith>403.html</ReplaceKeyWith>
</Redirect>

仍然没有重定向签名的网址。所以我不知道如何优雅地处理这些过期的网址。任何帮助将不胜感激。

【问题讨论】:

【参考方案1】:

S3 存储桶有 2 个面向公众的接口,REST 和网站。这就是两个主机名之间的差异,以及您所看到的行为差异。

它们有两个不同的功能集。

feature          REST Endpoint       Website Endpoint
---------------- ------------------- -------------------
Access control   yes                 no, public content only
Error messages   XML                 HTML
Redirection      no                  yes, bucket, rule, and object-level
Request types    all supported       GET and HEAD only
Root of bucket   lists keys          returns index document
SSL              yes                 no

来源:http://docs.aws.amazon.com/AmazonS3/latest/dev/WebsiteEndpoints.html

因此,从表中可以看出,REST 端点支持签名 URL,但不支持友好错误,而网站端点支持友好错误,但不支持签名 URL。两者不能混合和匹配,因此 S3 本身不支持您尝试做的事情。


我已经解决了这个限制,方法是通过 EC2 实例上的 HAProxy 将存储桶的所有请求传递到存储桶的 REST 端点。

当返回 403 错误消息时,代理使用新的 embedded Lua interpreter 修改响应正文 XML,将其添加到 &lt;Error&gt; 标记之前。

<?xml-stylesheet type="text/xsl" href="/error.xsl"?>\n

文件/error.xsl 是公开可读的,并使用浏览器端XSLT 来呈现漂亮的错误响应。

代理还会在 xml 中注入几个额外的标签,&lt;ProxyTime&gt;&lt;ProxyHTTPCode&gt; 以在输出中使用。生成的 XML 如下所示:

<?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet type="text/xsl" href="/error.xsl"?>
<Error><ProxyTime>2015-10-13T17:36:01Z</ProxyTime><ProxyHTTPCode>403</ProxyHTTPCode><Code>AccessDenied</Code><Message>Access Denied</Message><RequestId>9D3E05D20C1BD6AC</RequestId><HostId>WvdkvIRIDMjfa/1Oi3DGVOTR0hABCDEFGHIJKLMNOPQRSTUVWXYZ+B8thZahg7W/I/ExAmPlEAQ=</HostId></Error>

然后我通过 XSL 测试改变向用户显示的输出以确定 S3 抛出了什么错误条件:

<xsl:if test="//Code = 'AccessDenied'">
  <p>It seems we may have provided you with a link to a resource to which you do not have access, or a resource which does not exist, or that our internal security mechanisms were unable to reach consensus on your authorization to view it.</p>
</xsl:if>

最后的结果是这样的:

以上是一般的“拒绝访问”,因为没有提供凭据。这是一个过期签名的示例。

我没有在输出中包含 HostId,因为它丑陋且嘈杂,而且,如果我需要它,代理会为我捕获并记录它,我可以交叉引用 request-id .

当然,作为奖励,通过我的代理运行请求意味着我可以在提供存储桶内容时使用我自己的域名我自己的 SSL 证书,并且我有实时访问日志毫不拖延。当代理与存储桶位于同一区域时,额外的数据传输步骤不会产生额外费用,我对这种设置非常满意。

【讨论】:

感谢您的详尽回答。我需要一些时间来实施和检查它;这当然很有帮助,同时我也投了赞成票。 太好了,谢谢。我一直想写一篇关于这个的博客文章,包括实现它所需的 Lua 源和 haproxy 配置。我会尽快把它放回我的列表中,并在此处添加一个链接。 源代码 -- HAProxy 的 XSL 文件和 Lua 脚本 -- gist.github.com/sqlbot/ce5388fd452eeb302dfd

以上是关于AWS S3 在 getSignedUrl 过期后优雅地处理 403的主要内容,如果未能解决你的问题,请参考以下文章

AWS S3 createPresignedPost 与 getSignedUrl。我应该使用哪一个从客户端上传各种文件?

在 s3 aws 中为对象创建签名 url 时如何设置内容类型?

如何使用 getSignedUrl 操作从 Amazon s3 访问对象

为特定文件设置 AWS S3 过期时间

AWS S3 上传。根据政策无效:政策已过期

如何在 AWS S3 生命周期过期前缀规则中使用正则表达式