S3 Pre-signed URLs can be used to provide a temporary 3rd party access to private objects in S3 buckets. For example non-public files on a file sharing site can only be made available to the approved users with one-off URLs that expire after 10 minutes. Here we offer a simple demo for testing the concept.
The demo consists of a number of parts:
Preparation
- Spin up Amazon Linux EC2 instance
- Assign Public IP or Elasitc IP address, e.g. IP.AD.DR.ES
- Assign EC2 IAM Role, e.g. s3sign-role
- Create S3 Bucket, e.g. s3sign-bucket
- The instance needs at least GetObject privilege on the objects in the bucket. A minimal IAM Policy for the s3sign-role should therefore be something like:
{ "Version": "2012-10-17", "Statement": [ { "Action": [ "s3:Get*" ], "Resource": [ "arn:aws:s3:::s3sign-bucket/*" ], "Effect": "Allow" } ] }
In reality you will probably want to grant some more privileges: s3:List*, s3:Get*, s3:PutObject* and s3:DeleteObject* probably.
Also don’t forget to update the bucket name in Resource block if it’s different from s3sign-bucket!
Installation
Refer to the README in the GitHub repository for details.
CloudFormation way
Alternatively instead of following the manual steps above you can use the provided CloudFormation template s3-sign-demo.json
that sets us the environment the same way. In the Outputs tab in CloudFormation console you’ll find the IP address of the instance.
Testing
Upload some files and images to s3sign-bucket with Private ACL and browse to http://IP.AD.DR.ES/index.php, where IP.AD.DR.ES is obviously the IP address of your EC2 instance. You should see a page like this:
Sure, it’s not the epitome of a contemporary web design but it works 🙂
In the web page you’ll find a table with two URLs
- Unsigned URL which is a plain HTTPS URL like https://s3-ap-southeast-2.amazonaws.com/s3sign2-bucket-hchq3nwuo8ns/s3-sign-demo.json
Most likely you’ll get an Access Denied error when clicking that URL – the file is intentionally not publicly accessible. - Signed URL which is a bit more interesting. It starts with the same URL as above with is followed by a long string with a lot of parameters: https://s3.ap-southeast-2.amazonaws.com/s3sign2-bucket-hchq3nwuo8ns/s3-sign-demo.json?X-Amz-Security-Token=FQ…&X-Amz-Credential=ASIAJF3BXG…&X-Amz-Date=20170125T044127Z&X-Amz-Expires=60&X-Amz-Signature=24db05…
Some parameters in that long string denote the signature validity (X-Amz-Date, X-Amz-Expires) while the rest provide the cryptographic signature (X-Amz-Security-Token, X-Amz-Credential, X-Amz-Signature, etc). Don’t worry about the details – there are ready made library functions that help you create the URL.
The Code
For completeness here is the code that generates the Signed URL. Check out the full PHP script in the GitHub project.
require "aws.phar"; $s3 = new Aws\S3\S3Client([ 'version' => '2006-03-01', 'region' => $region, ]); $cmd = $s3->getCommand('GetObject', [ 'Bucket' => $bucket, 'Key' => $object, ]); $request = $s3->createPresignedRequest($cmd, "+1 minute"); $signed_url = (string) $request->getUri();
That’s all 🙂
Is it possible to give ipaddress while crating signed url, so that I can access the url from particular ip only… is it possible?
If so can you please provide the sample php script for the same.
Hi, not sure if it’s possible per-URL. However you can use a Bucket Policy that restricts access to the bucket for the approved IPs.
Mike,
So nicely explained. I have a general questions. Some of my customers are in Auckland. Since AWS don’t have Region there I am using Sydney servers at the moment. Do you have any suggestions how to get better performance for the services EC2, RDS which serves requests to NZ customers
Hi Karthik, what kind of performance problems do you experience? Latency? Throughput? Sydney is only some 30ms away – I’ve never had problems with latency from New Zealand to be honest. And throughput depends on your local bandwidth. If we are talking about websites I would start with optimising caching, expiration times, etc. Feel free to share more details if you want a more detailed answer 😉
[…] See here for S3 Pre-signed URL example […]
Hi,
I need to fetch the files from S3 to website but also need that it should be access to only authenticated users how can I achieve that.
That’s exactly what this article is about, isn’t it? Please elaborate on the usecase if the article doesn’t cover it.