Amazon AWS S3 Query String Authentication with PHP

Okay, this one took me hours to figure out, so I figured I would put my results here for others to find.  Hopefully it will spare others the hours of frustration I went through!

If you do not know what Amazon AWS S3 is, you should read up on it.  Basically, it's super-cheap online file storage for your web site or even personal use.  You can even make it so that files are private, and can only be downloaded with a self-expiring URL (which might only be valid for, say, 1 hour).

Here is how, in PHP, to do just that.  Please note, these code snippits were pieced together from various sources online until I finally got everything working the way it is supposed to.

First things first, you will need these functions:

function gs_getStringToSign($request_type, $expires, $uri) {
   return "$request_type\n\n\n$expires\n$uri";
}

function gs_encodeSignature($s, $key) {
    $s = utf8_encode($s);
    $s = hash_hmac('sha1', $s, $key, true);
    $s = base64_encode($s);
    return urlencode($s);
}
 
function gs_prepareS3URL($file, $bucket) {
 
  $awsKeyId = "PUT ACCESS ID HERE"; // this is the non-secret key ID.
  $awsSecretKey = "PUT SECRET KEY HERE"; // this is the SECRET access key!
 
 
  $file = rawurlencode($file); 
  $file = str_replace('%2F', '/', $file);
  $path = $bucket .'/'. $file;
 
  $expires = strtotime('+1 hour');
 
  $stringToSign = gs_getStringToSign('GET', $expires, "/$path"); 
  $signature = gs_encodeSignature($stringToSign, $awsSecretKey); 

  $url = "http://$bucket.s3.amazonaws.com/$file";
  $url .= '?AWSAccessKeyId='.$awsKeyId
         .'&Expires='.$expires
         .'&Signature='.$signature;
        
  return $url;
}

Next, all you have to do is call the gs_prepareS3URL function like so:

  $file = "WordHunt.jar";
  $bucket = "bytebyte.net";
  $secure_link = gs_prepareS3URL($file, $bucket);

Isn't that easy?  Just make sure you have set the ACL's on the file in question to not be readable by anyone but you (otherwise you wouldn't need these functions at all, and you could just download the file directly).

 

Fine print: This code is considered "open source," and is released under the GNU Public License version 3 or any later version (http://www.gnu.org/copyleft/gpl.html).  In a nut shell, you can change this code however you want, and redistribute it, but ONLY if it is ALSO under the GNU Public License version 3 or later.  If you do use this software, I would appreciate credit and a link back to this site, though this is not required.

Trouble with .flv

I'm trying to apply this to media files (.flv) I have on my S3 bucket. It works, however the link does not provide a download, instead showing a text file. Any idea on why that is? Any help would be greatly appreciated!

It shows a text file? What

It shows a text file? What does it say?

Works like a charm. Many

Works like a charm. Many thanks from France.

Anyone have more code?

I know enough about PHP to be dangerous in editing scripts, but now very fluent in PHP yet.

I assume if I use the large code as whatever.PHP in the same directory as the .HTML file and use a PHP call at the top of the HTML - should it work? Is that the setup needed?

(Or, does anyone have anymore code?)

Thanks,
Dave

Well, it depends on how your

Well, it depends on how your server is set up, but most servers do not automatically understand that a .HTML file might have PHP code in it. You can always try it and see, but in general I only use .php files. Let's say you put the 3 functions into a file called aws_functions.php. Your main page's code can then look like this: [filename: welcome.php]
<?php
 
include_once("aws_functions.php");
?>

<html>
   <body>
      Hey everyone!  Download my file:
     <?php
        $file
= "myfile.zip";
       
$bucket = "my-bucket";
       
$secure_link = gs_prepareS3URL($file, $bucket);
       
       
// Output the link to the page:
       
print '<a href="' . $secure_link . '">Click here!</a>';
    
?>

   </body>
</html>

Where is the problem?

Hello,

I tried exactly what it is explained but this is what i got in Chrome. I have just changed secrets to xxx.

If somebody can help me ?:

function gs_getStringToSign($request_type, $expires, $uri) { return "$request_type\n\n\n$expires\n$uri"; } function gs_encodeSignature($s, $key) { $s = utf8_encode($s); $s = hash_hmac('sha1', $s, $key, true); $s = base64_encode($s); return urlencode($s); } function gs_prepareS3URL($file, $bucket) { $awsKeyId = "xxx"; // this is the non-secret key ID. $awsSecretKey = "xxxx"; // this is the SECRET access key! $file = rawurlencode($file); $file = str_replace('%2F', '/', $file); $path = $bucket .'/'. $file; $expires = strtotime('+1 hour'); $stringToSign = gs_getStringToSign('GET', $expires, "/$path"); $signature = gs_encodeSignature($stringToSign, $awsSecretKey); $url = "http://$bucket.s3.amazonaws.com/$file"; $url .= '?AWSAccessKeyId='.$awsKeyId .'&Expires='.$expires .'&Signature='.$signature; return $url; } Hey everyone! Download my file:
Fatal error: Call to undefined function gs_prepareS3URL() in /home/elearnin/public_html/aws/hello.php on line 10

Regards,

Well, the error says it can't

Well, the error says it can't find that function, so that must mean you don't have that function included in your source code.

Thank you so much for sharing

Thank you so much for sharing this. I find the AWS documentation quite convoluted, and I simply could not make sense of what needed to be done.

I'm going to wrap your functions in a class and share back with you when I'm done :)

Awesome-- I'm glad this post

Awesome-- I'm glad this post helped you!

Richard

Great post, really appreciate

Great post, really appreciate it. The Amazon AWS docs are so confusing sometimes.

Great Post

I've been wrestling with the Amazon S3 SDK for hours. It was adding an extra character at the end of the string which was causing the signature to fail. Your solution resolved the issue. Thank you very much.

Glad I could help!

Glad I could help!

Thanks!

Man, you rule!

wink

wow great made blog here, thanks for the article! where can i find the link?

Protect file from downloading+granting acces to elearning site

Hello,

I am not a developer but I want to use video from Amazon S3 in my elearning website without public access to the video.

The idea is for people with access to the elearning platform to watch the video and only them no matter without constraints of availability time.

Is it the correct code according to you ?

Regards,

Maxime

I don't think so. It sounds

I don't think so. It sounds like you need some kind of authentication (where they must enter a username and password), but this code isn't for that.

Hello, I have already set up

Hello,

I have already set up and authentication process to my elearning website and now the idea is to show a videao streaming securely thanks your code example.

After reading a lot opf things on the Web, I think your code do what I want: the video streaming has to be watching only from my website an I do not want people to share the link.

I tried your code without success. What I would like is to change the usual amazon s3 url to a secured one. I have an HTML editor to write my lesson content, how can I use it to put a secured url rather than an usual one?

Do I need to sign the url and how can I do that?

I purchased the S3Flowshield software hoping to use it easily but my website is not a wordpress one so it does not work...

Regards,

Maxime

Signature Doesn't Match

I was trying a similar script, then this one..and it just keeps giving me:
SignatureDoesNotMatch

The keys are correct, I don't know what's going wrong :(

After much headaches, I found

After much headaches, I found out the problem. You CAN'T have a bucket with Capital Letters in it! Amazon should really mention that when asking for the name.

Thanks for the tip! I had no

Thanks for the tip! I had no idea either.