Close

January 31, 2014

Generating Pre-Signed URLs for Amazon S3 with ColdFusion

We’ve recently been trying to get one of our applications to play nicely with Amazon S3 to deliver output rather than storing it on our production servers. Now ColdFusion 9.0.1 plays nicely with S3 in terms of uploading content to S3 but it doesn’t have an answer when the content you want is in a private bucket and you therefore need a signature with an expiry for an authorised user to download.

Barney Boisvert has written his own CFC here which seemed to work a treat… until it didn’t. In testing it proved to be inconsistent as to whether AWS would let you in or not; in addition different browsers (e.g. Chrome/IE) seemed to deal with the encoded URLs differently and would tell you your signature was invalid.

So the answer lay in the actual Amazon AWS SDK for Java. Download it and add it to your WEB-INF/cfusion/lib folder. You will also need to acquire the following third party jar files to add into the same place as the SDK is missing them for ColdFusion:

  • httpclient-4.1.1.jar
  • httpcore-4.1.jar
  • jackson-core-asl-1.4.3.jar
  • mail-1.4.3.jar
  • stax-api-1.0.1.jar
  • stax-1.2.0.jar

Restart your CF server for these libraries to be loaded.

So for your code, you’ll need a function like this:

<cffunction name=”javaS3Url”>
<cfargument name=”bucket” type=”string” required=”true” />
<cfargument name=”objectKey” type=”string” required=”true” />

<cfscript>

var url = ”;
var accessKey=<your access key>;
var secretKey=<your secret key>;
var thisDate = dateAdd(“d”,1,now());
var clientConfig = createObject(‘java’,    ‘com.amazonaws.ClientConfiguration’).init();
var thisProtocol = createObject(‘java’, ‘com.amazonaws.Protocol’);

clientConfig.setProtocol(thisProtocol.HTTP);

var awsCred = createobject(‘java’,
‘com.amazonaws.auth.BasicAWSCredentials’).init(
accessKey,
secretKey
);
var s3 = CreateObject(    ‘java’,
‘com.amazonaws.services.s3.AmazonS3Client’
).init(awsCred, clientConfig);

url = s3.generatePresignedUrl(bucket, objectKey, thisDate );

return url;
</cfscript>

</cffunction>

The above code takes your object key, creates an expiry date of 24 hours, configures the AWS config to use HTTP not the default HTTPS, creates an S3 client object with your credentials (called “s3”) and then calls the method generatePresignedUrl on the object with your bucket and object key. The function returns a properly signed URL you can now display and use.

The reason for using HTTP not the default HTTPS is that with S3 buckets named with dot notation (e.g. sword.sci-ware.com) the certificate will not match *.s3.amazonaws.com. So unless you have a simple bucket name (e.g. swordsciwarecom) browsers will throw security exceptions at you. Your content is already secured via a signature so you don’t really need both.

This last point however took ages to get right due to difficulties in making use of a Java enum (in this case being able to set the protocol to be HTTP). In order to set the protocol to be HTTP you need to create a Protocol object (but not use init()) and pass that object with the suffix .HTTP (which is the enum) to the method on the config object.

Leave a Reply

Your email address will not be published. Required fields are marked *