如何将 API 网关的 AWS 签名 v4 预签名链接的过期时间从默认的 5 分钟增加?

How to increase expiration time in AWS signature v4 pre-signed links for API Gateway from default 5 min?

我正在尝试生成预签名的 link 到 API 网关(使用 IAM 身份验证),因此客户端可以访问此 API 网关后面的我的 Lambda 函数之一,而无需认证请求。这主要是为了方便客户端,因此它可能会透明地使用来自响应的一些 links,无论它们是否指向相同的经过身份验证的 API 网关、某些 S3 存储桶或互联网中的任意 URL .

为此,我使用查询参数制作了 API 签名 v4(参见 docs and example

因此,如果我尝试在 link 范围内签署 us-west-2 区域和 execute-api 服务:

https://example.com/some/path?some=params

我会得到以下结果(using Node.js aws4 library,但这里并不重要):

https://example.com/some/path?some=params&
X-Amz-Security-Token=<Session Token Removed>
X-Amz-Date=20210330T180303Z&
X-Amz-Algorithm=AWS4-HMAC-SHA256&
X-Amz-Credential=<Access Key Removed>%2F20210330%2Fus-west-2%2Fexecute-api%2Faws4_request&
X-Amz-SignedHeaders=host&
X-Amz-Signature=884f132ad6f0c7a850e6b1d22b5fed169c13e2189b6e0d0d568d11f967f4a8bd

而且有效!但只有生成后的前 5 分钟......

五分钟后,我将收到以下响应错误:

{"message":"Signature expired: 20210330T175821Z is now earlier than 20210330T180403Z (20210330T180903Z - 5 min.)"}

有关详细信息,请参阅

我尝试添加 X-Amz-Expires 文档中提到的具有各种值(小于和大于 300 秒)的查询参数,但没有成功:行为没有改变。

我至少需要几个小时,最多 allowed 6 hours for links generated by IAM instance credentials,因为 link 正在由另一个 Lambda 函数签名。

是否有任何方法可以增加 API 网关访问的预签名 link 有效期?

这是一个很有趣的问题! 起初,我认为 S3 文档中明确记录了所有服务(包括 API 网关)都支持 X-Amz-Expires。 [1][2]

经过更多的研究,结果是 如果 S3 以外的服务支持 X-Amz-Expires 参数

有各种消息来源声称只有 S3 遵守该参数。以下是 aws-sdk for go 工作的 AWS 员工的声明:

The expires time is only relevant for the S3 service. Other services have their own fixed expiration time. Generally this is 15 minutes, but it looks like IoT data service uses a 5 minute expiration time. [3]

他们跟进了:

The SDK doesn't have any metadata data available providing which services do or do not use the expiry value. [4]

然后在GitHub上相应的源代码中添加注释:

All other AWS services will use a fixed expiration time of 15 minutes. [5]

有大量示例表明 AWS 正在使用 S3 服务的参数,例如[1][6]。 但是,AWS 文档中也有一些示例显示了 IAM 服务参数的使用,例如[7][8]。 这很令人困惑。

AWS 的一位 SDE 发表评论可追溯到 2018 年,他在评论中发表了同样令人困惑的观察 [9]:

If S3 is the only service that supports this header I agree that the SDK's documentation should be updated to reflect that - including a note in the description for this header in S3's SigV4 documentation stating that this header is exclusive to presigned URLs for this service would also be helpful.

FWIW I spoke to some folks from AWS Auth and the only service they know of using the header is S3 (interesting that you found a code sample using IAM). They suggested that the 15 minute expiration for STS presigned URLs would not be changing.

另一位前 AWS 员工进一步注意到:

I was able to reproduce this behavior both on the AWS SDKs for Go and PHP with presigned URLs for EC2, IAM, STS, and Route 53. The only service I observed that invalidated a presigned URL after the time specified in the "x-amz-expires" header (instead of the default 15 minutes) was S3.

因此,我想不可能增加 API 网关访问的预签名 link 有效期。我认为 AWS 没有设计签名算法来支持您的用例。我认为 S3 预签名 URL 操作是 AWS 允许延长有效期的罕见例外之一。

在查看他们按原样创建签名算法背后的动机时,我注意到他们试图最小化重放攻击的攻击面:

Protect against potential replay attacks
In most cases, a request must reach AWS within five minutes of the time stamp in the request. Otherwise, AWS denies the request. [10]

还有一些资源 [11][12] 得出的结论是,让客户选择过长的到期值会破坏该参数的原始安全目的

我认为没有通用的方法可以为 AWS 服务的 REST API 创建预签名 URL 并在未来很长一段时间内执行它。

如果我处在您的位置,我会使用 JWT 和 API Gateway Lambda 授权方实施自定义身份验证策略。 [13] 这样你就可以自己控制签名算法,特别是它的到期时间。我想补充一点,JWT 是 URL-安全的,就像 AWS 签名查询字符串参数一样。 [14]

[1] https://docs.aws.amazon.com/AmazonS3/latest/API/sigv4-query-string-auth.html
[2] https://docs.aws.amazon.com/general/latest/gr/sigv4-add-signature-to-request.html
[3] https://github.com/aws/aws-sdk-go/issues/2304#issuecomment-441755864
[4] https://github.com/aws/aws-sdk-go/issues/2304#issuecomment-441758599
[5] https://github.com/aws/aws-sdk-go/blob/6212dfa8032336d438c526c086918c8d2ceb6432/aws/request/request.go#L310
[6] https://github.com/mhart/aws4/blob/master/aws4.js#L130
[7] https://docs.aws.amazon.com/general/latest/gr/sigv4-signed-request-examples.html
[8] https://docs.aws.amazon.com/general/latest/gr/sigv4-add-signature-to-request.html
[9] https://github.com/aws/aws-sdk-go/issues/2167#issuecomment-428764319
[10] https://docs.aws.amazon.com/general/latest/gr/signing_aws_api_requests.html
[11] https://aws.amazon.com/de/articles/making-secure-requests-to-amazon-web-services/?nc1=h_ls(“重放攻击”部分)
[12]
[13] https://docs.aws.amazon.com/apigateway/latest/developerguide/apigateway-use-lambda-authorizer.html
[14]

对于其中一个用例,我一直在为同样的问题而苦苦挣扎。这就像用户想要在有限的时间内访问 S3 中的 CSV 文件。

我最终构建了以下架构:

  • 一个 lambda 函数来处理“用户访问请求”。它位于 API 网关后面,负责将 CSV 文件放入 S3(您可能不需要这部分)。 Lambda 正在生成一个 URL 并在适当的位置触发另一个 lambda (#2) API GW.
  • 我的第二个 lambda 也落后于 API GW,并且可能由第一个 lambda 生成的 URL 触发。这是用户出现的地方,因为来自第一个 lambda 的 URL 被提供给实际用户点击它。第二个 lambda 所做的很简单。它为 S3 对象即时生成一个 6 小时长的预签名 URL,并发回带有预签名 URL 的 HTTP 302,因此用户的浏览器会立即下载 CSV。

这样,几乎可以与任何人共享 URL 生成的 lambda,并且 URL 会一直工作直到 S3 对象存在。

如果您需要某种策略来让 URL 可访问 30 天或更长时间,您可以设置存储桶策略以在之后删除该对象。