Search code examples
python-3.xamazon-web-servicesamazon-s3pre-signed-url

S3 presigned url expiring too early


I use boto3 to generate a presigned URL for an S3 object

S3_CLIENT = boto3.client('s3')
PRESIGNED_URL_EXPIRY_SECONDS = 7*24*3600

def generate_presigned_url(bucket_name, object_key, expiration):
"""
Generates a pre-signed URL for an S3 object.
Args:
    bucket_name (str): The name of the S3 bucket.
    object_key (str): The key of the S3 object.
    expiration (int): The expiration time of the pre-signed URL, in seconds.
Returns:
    str: The pre-signed URL for the S3 object.
Raises:
    ClientError: If there is an error generating the pre-signed URL.
"""

try:
    response = S3_CLIENT.generate_presigned_url(
        'get_object',
        Params={
            'Bucket': bucket_name,
            'Key': object_key
        },
        ExpiresIn=expiration
    )
except ClientError as e:
    print(e)
    return None

return response

and then the method is called

 media_presigned_url = generate_presigned_url('s3-bucket','s3-object-key', PRESIGNED_URL_EXPIRY_SECONDS)

The method generates a presigned url such as

https://s3-bucket.s3.amazonaws.com/lskdjfeur_2023-02-18_03-05-50.jpg?AWSAccessKeyId=ASXXXXXXXE&Signature=0n%2FYQXXXXXXXX5Re3DX%2FLNzq504%3D&x-amz-security-token=IXXXXJb3J%3D&Expires=1678160873

The Expires=1678160873 indicates that this should be expiring in about a week.

But the issue is that the presigned URL is expiring much earlier than that i.e. less than a day.

<Code>ExpiredToken</Code>
<Message>The provided token has expired.</Message>

My goal is to generate a presigned URL that would expire in a week.

What could be causing this presigned URL to expire earlier than that ?


Solution

  • A pre-signed URL is similar to a normal URL that points to an object in Amazon S3, but it includes a signature that authorizes access to the private object.

    The signature is generated from the Secret Key of a set of AWS Credentials. These credentials could be associated with an IAM User, an IAM Role or temporary credentials generated by the AWS Security Token Service (STS).

    The pre-signed URL uses the permissions of those credentials to authorize access to the object. However, if the underlying credentials do not have the necessary access, or if those credentials have expired, then access to the object will be denied.

    To provide a real-life analogy, I could write a bank check that authorises the bank to transfer my money to you. However, if an unauthorized person signs the check, then the bank is not meant to pay the money. Or, if the check was issued by an on behalf of a company and the accountant who makes the checks lost their job with the company, then the unemployed accountant should not be able to issue any more checks for the company.

    In your situation:

    • An AWS Lambda function was invoked, with an associated IAM Role
    • The AWS Lambda service generated temporary credentials with the permissions of the IAM Role
    • Your Lambda function called generate_presigned_url(), using those temporary credentials
    • The pre-signed URL was provided to end-users (presumably)
    • When the pre-signed URL was later used, the temporary credentials associated with the Role/function had expired, so access was denied

    To prevent this happening, use permanent credentials to generate the pre-signed URL (or temporary credentials that won't expire during the expiry period of the pre-signed URL). You could store these credentials in AWS Systems Manager Parameter Store and the Lambda function could retrieve the credentials before generating the pre-signed URL.