Search code examples
amazon-web-servicesaws-api-gatewaypre-signed-urlsigv4

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


I'm trying to generate a presigned link to API Gateway (that uses IAM authentication), so client may access one of my Lambda functions behind this API Gateway without authenticating request. This is mostly for client convenience, so it may use some links from response transparently, whether they points to the same authenticated API Gateway, some S3 bucket or any arbitrary URL in the Internet.

To do so, I crafting API signature v4 using query parameters (see docs and example)

So, if I try to sign following link scoped for us-west-2 region and execute-api service:

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

I will get following result (using Node.js aws4 library, but it doesn't matter here):

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

And it works! But only first 5 minutes after generation…

After five minutes passed, I will get following error in response:

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

See this response to this question for more details.

I've tried to add X-Amz-Expires query parameter mentioned in the docs with various values (both less and greater than 300 seconds), but with no luck: behavior doesn't change.

I need at least a few hours, up to allowed 6 hours for links generated by IAM instance credentials, as links are being signed by another Lambda function.

Is there any way to increase pre-signed link validity duration for API Gateway access?


Solution

  • This is a very interesting question! At first, I thought it is clearly documented in the S3 docs that X-Amz-Expires is supported by all services (including API Gateway). [1][2]

    After some more research, it turned out that it is not so clear at all if services other than S3 support the X-Amz-Expires parameter.

    There are various sources claiming that only S3 is respecting the parameter. The following is a statement by an AWS employee working on the aws-sdk for go:

    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]

    They followed up with:

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

    Then adding a note into the corresponding source code on GitHub:

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

    There are a ton of examples that show that AWS is using the parameter for the S3 service, e.g. [1][6]. However, there are also examples from AWS docs that show the use of the parameter for the IAM service, e.g. [7][8]. That is very confusing.

    There is a comment by an SDE at AWS which is dated back to 2018 in which he makes the same confusing observation [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.

    Another former AWS employee further noticed:

    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.

    Thus, I guess it is not possible to increase pre-signed link validity duration for API Gateway access. I think that AWS did not design the signature signing algorithm to support your use case. I think that the S3 presigned URL action is one of the rare exceptions for which AWS allows an extended expiry period.

    When looking at their motivation behind creating the signing algorithm the way it is, I noticed that they try to minimize the attack surface for replay attacks:

    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]

    There are some more resources [11][12] that lead to the conclusion that letting customers choose lengthy expiry values would undermine the original security purpose of that parameter.

    I think there is no generic way to create a presigned URL towards an AWS service's REST API and execute it far in the future.

    If I were in your place, I would implement a custom authentication strategy using JWTs and API Gateway Lambda authorizers. [13] That way you can control the signing algorithm and particulary its expiration time on your own. I want to add that JWTs are URL-safe in the same way AWS signature query string parameters are. [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 (section "Replay Attacks")
    [12] https://stackoverflow.com/a/12267408/10473469
    [13] https://docs.aws.amazon.com/apigateway/latest/developerguide/apigateway-use-lambda-authorizer.html
    [14] https://stackoverflow.com/a/56273952/10473469