16

If I have a generated Presigned URL that expired, should I be doing get_headers() (in PHP) to see if a 403 Forbidden error is thrown, otherwise use that same URL? Or is that a bad idea because it's an unnecessary GET request? Should I always just regenerate a new Presigned URL every time? I'm a little confused because there doesn't seem to be much information about this.

helloV
  • 50,176
  • 7
  • 137
  • 145
D-Marc
  • 2,937
  • 5
  • 20
  • 28

4 Answers4

31

The URL has the time it expires at.

Signature Version 2

htt ps://bucket.s3.amazonaws.com/foo.txt?AWSAccessKeyId=AKIAABCDEFGHIJK&Expires=1508608760&Signature=xxxxxxxxxxx

Expires gives the time in Unix timestamp (in seconds) and Coordinated Universal Time (UTC) .

$ date -d @1508608760
Sat Oct 21 17:59:20 UTC 2017

You can extract the value and compare it with the current time in UTC [time()], then decide to regenerate or not.


Signature Version 4

htt ps://s3.amazonaws.com/bucket/foo.txt?X-Amz-Algorithm=AWS4-HMAC-SHA256& X-Amz-Expires=3600&X-Amz-Credential=AKIAJRZXXXXXXXXus-east-1%2Fs3%2Faws4_request&X-Amz-SignedHeaders=host&X-Amz-Date=20171021T190750Z&X-Amz-Signature=8b84ae9b59e9f8a8d7066ecc39e797c8dc29848abcdef61717

X-Amz-Date gives the UTC time in ISO 8601 format.

You can extract the value, convert it to epoch/UTC and compare it with the current time in UTC [time()], then decide to regenerate or not.

iwaduarte
  • 1,600
  • 17
  • 23
helloV
  • 50,176
  • 7
  • 137
  • 145
  • 1
    Awesome solution, don't know why I hadn't thought of that. However, for me, `Amz-Expires` is the expiration time in seconds, while `X-Amz-Date` is the the timestamp – D-Marc Oct 21 '17 at 18:12
  • 2
    @D-Marc `X-Amz-*` is the newer format, called Signature Version 4. Regions where AWS services were rolled out before about 2014 support those plus the older style, called Signature Version 2. – Michael - sqlbot Oct 21 '17 at 19:24
  • 5
    This is not a complete answer - you have to add the expires duration to the `X-Amz-Date` value to get the expiry time – Mike Miller Apr 07 '20 at 14:14
  • Doesn't this rely on implementation details from Amazon's signature process? Is there a more robust way to fetch the details without having to explicitly parse the values from query string and interpreting them? – julealgon Jan 28 '21 at 22:09
  • We also ran into the situation where the Expires unix timetamp looks ok, i.e. sometimes in the future, but the link still presented as access denied after a short period of time, still debugging but it's most likely related to https://aws.amazon.com/premiumsupport/knowledge-center/presigned-url-s3-bucket-expiration/ – James Z. Jun 25 '21 at 03:53
  • @JamesZ. found any solution for this? – radix May 12 '22 at 10:08
5

The accepted answer isn't quite right for Signature Version 4.

Read on if you landed here looking for a way to find the expiry in js.

import { parseISO, addSeconds } from 'date-fns';

// get the query params: https://stackoverflow.com/a/901144/9362404
const params = new Proxy(new URLSearchParams(signedUrl), {
    get: (searchParams, prop) => searchParams.get(prop),
});
const creationDate = parseISO(params['X-Amz-Date']);
const expiresInSecs = Number(params['X-Amz-Expires']);

const expiryDate = addSeconds(creationDate, expiresInSecs);
const isExpired = expiryDate < new Date();

It's pretty obvious what's going on once you list out the query params:

// https://bucketname.s3.ap-southeast-2.amazonaws.com/file-name.pdf
// X-Amz-Algorithm=     AWS4-HMAC-SHA256
// X-Amz-Credential=    <aws-access-key>/20220722/ap-southeast-2/s3/aws4_request
// X-Amz-Date=          20220722T101759Z
// X-Amz-Expires=       259200
// X-Amz-Signature=     gobbeldygook-signature
// X-Amz-SignedHeaders= host
Gus Bus
  • 369
  • 3
  • 7
3

On macOS, use date -r 1535416265.

mr. fixit
  • 1,404
  • 11
  • 19
0

If you use Memcached (or similar), you have the option to push the presigned url in Memcached with the same expiration time. Something like this (pseudo-php code):

$mc->set($your_key, $url, $expiration);

So, you can get the url using

$url = $mc->get($your_key);

If $mc->get returns false, you have to re-generate the presigned url.