Grow your CSS skills. Land your dream job.

Last updated on:

Generate Expiring Amazon S3 Link

You don't have to make files on Amazon S3 public (they aren't by default). But you can generate special keys to allow access to private files. These keys are passed through the URL and can be made to expire.

<?php 

  if(!function_exists('el_crypto_hmacSHA1')){
    /**
    * Calculate the HMAC SHA1 hash of a string.
    *
    * @param string $key The key to hash against
    * @param string $data The data to hash
    * @param int $blocksize Optional blocksize
    * @return string HMAC SHA1
    */
    function el_crypto_hmacSHA1($key, $data, $blocksize = 64) {
        if (strlen($key) > $blocksize) $key = pack('H*', sha1($key));
        $key = str_pad($key, $blocksize, chr(0x00));
        $ipad = str_repeat(chr(0x36), $blocksize);
        $opad = str_repeat(chr(0x5c), $blocksize);
        $hmac = pack( 'H*', sha1(
        ($key ^ $opad) . pack( 'H*', sha1(
          ($key ^ $ipad) . $data
        ))
      ));
        return base64_encode($hmac);
    }
  }

  if(!function_exists('el_s3_getTemporaryLink')){
    /**
    * Create temporary URLs to your protected Amazon S3 files.
    *
    * @param string $accessKey Your Amazon S3 access key
    * @param string $secretKey Your Amazon S3 secret key
    * @param string $bucket The bucket (bucket.s3.amazonaws.com)
    * @param string $path The target file path
    * @param int $expires In minutes
    * @return string Temporary Amazon S3 URL
    * @see http://awsdocs.s3.amazonaws.com/S3/20060301/s3-dg-20060301.pdf
    */
    
    function el_s3_getTemporaryLink($accessKey, $secretKey, $bucket, $path, $expires = 5) {
      // Calculate expiry time
      $expires = time() + intval(floatval($expires) * 60);
      // Fix the path; encode and sanitize
      $path = str_replace('%2F', '/', rawurlencode($path = ltrim($path, '/')));
      // Path for signature starts with the bucket
      $signpath = '/'. $bucket .'/'. $path;
      // S3 friendly string to sign
      $signsz = implode("\n", $pieces = array('GET', null, null, $expires, $signpath));
      // Calculate the hash
      $signature = el_crypto_hmacSHA1($secretKey, $signsz);
      // Glue the URL ...
      $url = sprintf('http://%s.s3.amazonaws.com/%s', $bucket, $path);
      // ... to the query string ...
      $qs = http_build_query($pieces = array(
        'AWSAccessKeyId' => $accessKey,
        'Expires' => $expires,
        'Signature' => $signature,
      ));
      // ... and return the URL!
      return $url.'?'.$qs;
    }
  }

?>

Usage

<?php echo el_s3_getTemporaryLink('your-access-key', 'your-secret-key', 'bucket-name', '/path/to/file.mov'); ?>

Reference URL

Comments

  1. You should mention the real author when you paste my code here.

    Without further ado… the original source of this snippet is:
    5ubliminal’s Amazon S3 Expiring Link Builder.

    Thank you!

    • Great, thanks for noting. I always try and do that. I was looking for this function for myself, found it on Snipplr, and it wasn’t clear who the original author was, so I just credited the page I found it on.

  2. Burke
    Permalink to comment#

    What is to stop someone from refreshing the screen and getting a new URL? I thought AWS was supposed to be able to provide a secure URL, but short of learning the PHP API is there not a way to deliver content securely?

    Thanks for this script. Both of you.

    • Nothing, but then that NEW link expires after the time limit too. The point is, in order to have a useful link to the file, you have to be viewing that particular page, because otherwise the link is pretty useless.

  3. Nick
    Permalink to comment#

    Chris,

    Can you please provide some more detailed instructions for a PHP noob like me?
    Got following questions:
    a) Where do I put the first file?
    b) How do I name it?
    c) Where exactly would I put the 2nd box that you show here?
    d) Won’t the bad guy be able to see my Access KEy etc when he opens that page?

    Thanks.

  4. Permalink to comment#

    Your way of explaining everything in this article is actually good, every
    one be capable of effortlessly know it, Thanks a lot. http://shop-computer.

    info Very very first, for every single store.Try to find the product or service be
    substituted for something you normally use?

  5. This is nice tutorial….it helps me a lot.. I have checked all over but didn’t get right solution.

    Here I find the right solution without wasting a time…

    Nice tutorial.. Kepp it up…

    5***** stars

  6. tray
    Permalink to comment#

    Is there a working version of this anywhere? I saw one on wordpress but the problem was that the temporary link actually revealed the access key if you mouse over it. Another on revealed the access key & secret key when you mouse over it.

    So yeah, is there a working version? And if not could someone make this into a workable WordPress plugin? :)

  7. Permalink to comment#

    To clarify this blog post, the Amazon documentation (scroll to the last example) specifies that the string to be used to generate the signature is:

    “GET\n\n\n1378560058\n/path/to/file”

    That string is used to produce a binary sha1-hmac, which should be base64 encoded. It seems strange to do the hmac generation manually (I assume the original code was for older PHP versions) – the following is a more succinct way to do the same thing:

    function getTemporaryUrl($key, $secret, $bucket, $path, $expirySeconds = 30)
    {
        $expiry = time() + $expirySeconds;
    
        // Format the string to be signed
        $string = sprintf("GET\n\n\n%s\n/%s/%s", $expiry, $bucket, $path);
    
        // Generate an HMAC-SHA1 signature for it
        $signature = base64_encode(hash_hmac('sha1', $string, $secret, true));
    
        // Create the final URL
        return sprintf(
            "https://%s.s3.amazonaws.com/%s?%s",
            $bucket,
            $path,
            http_build_query([
                'AWSAccessKeyId' => $key,
                'Expires' => $expiry,
                'Signature' => $signature
            ])
        );
    }
    

    Which would have the same usage:

    echo getTemporaryUrl('your-access-key', 'your-secret-key', 'bucket-name', '/path/to/file.mov');
    
    • mark harris
      Permalink to comment#

      I like your snippet however would this method still work if the downloadable file is big and url expires while downloading before the file is downloaded completely.

  8. Naveen_mindfire
    Permalink to comment#

    I just have 1 doubt…what if I want the url to be valid forever i.e it should not expire or you can say infinite expiry time

    • Tristan
      Permalink to comment#

      Then you’ll be able to simply make the S3 link public. That’ll give the users unlimited access to the S3 files. The blog post was aiming to restrict that unlimited access by creating a pre-signed URL.

  9. Anuruddha
    Permalink to comment#

    Hi,
    Is there anyone who can help me to write the above two functions in c#? I have very little knowledge of php and trying to create the same on .net world.
    Thanks in advance,
    Anuruddha

  10. N

    Big Ups!

Leave a Comment

Posting Code

Markdown is supported in the comment area, so you can write inline code in backticks like `this` or multiline blocks of code in in triple backtick fences like ```this```. You don't need to escape code in backticks, Markdown does that for you.

Sadly, it's kind of broken. WordPress only accepts a subset of HTML in comments, which makes sense, because certainly some HTML can't be allowed, like <script> tags. But this stripping happens before the comment is processed by Markdown (via Jetpack). It seems to me that would be reversed, because after Markdown processes code in backticks, it's escaped, thus safe. If you think you can fix this issue, get in touch!

If you need to make sure the code (typically HTML) you post absolutely posts correctly, escape it and put it within <pre><code> tags.

Current ye@r *

*May or may not contain any actual "CSS" or "Tricks".