Beyond the Basics of Using AWS S3 in CFML: Uploading a File via the AWS Java SDK

Posted 8 May 2020

Our first stop in this new series on going beyond the basics of using AWS S3 in CFML is learning how to upload a file to S3 via the AWS Java SDK. CFML runtimes have long had support for uploading files to S3 using built-in CFML functions and tags. It’s important to know how to do this with the AWS Java SDK, however, because all of the advanced features of using S3 from CMFL require use of Java file objects in conjunction with requests to S3.

Additionally, and perhaps more importantly, you can use minimal IAM permissions to upload files via the AWS Java SDK. The built-in file tags and functions in Adobe ColdFusion require more than 20 separate S3-specific IAM permissions to work. Some of these permissions include bucket-level permissions. This presents a large potential attack surface should someone get ahold of your IAM credentials. AWS best practice is that you give absolutely minimal permission for an IAM user, group, or role. By using the AWS Java SDK to upload (and manipulate) files in S3, you can significantly reduce your potential attack surface and have truly secure uploading. Your IAM permissions to upload to S3 can be as simple as this:

{
    Effect: "Allow",
    Action: [
        "s3: PutObject"
    ]
}

That’s a whole lot more secure than needing 20 or more permissions for a simple file upload.

Show Me the Code!

As always, the full code for this and all of my demos can be found in my AWSPlaybox GitHub repo.

In awsplaybox/model/awsServiceFactory.cfc, I generate a reference to the S3 service itself. This is done via the com.amazonaws.services.s3.AmazonS3ClientBuilder class. Then, assuming I have a file input on a form, I can upload a file to S3 as follows:

    s3 = application.awsServiceFactory.createServiceObject('s3');
    uploadedFile = fileUpload(getTempDirectory(), "form.fileToPutOnS3", " ", "makeunique");
    fileLocation = getTempDirectory() & uploadedFile.serverFile;
    // The AWS SDK putFileRequest object requires a Java file object in binary format
    fileContent = fileReadBinary(getTempDirectory() & uploadedFile.serverFile);
    javaFileObject = CreateObject('java', 'java.io.File').init(fileLocation);
    putFileRequest = CreateObject('java', 'com.amazonaws.services.s3.model.PutObjectRequest').init(trim(form.s3BucketName, uploadedFile.serverFile, javaFileObject);
    s3.putObject(putFileRequest);

The code above is fairly straightforward, but let me point out a couple of key points:

This is absolutely more verbose than uploading a file to S3 in CFML. As you’ll see in upcoming posts, however, knowing how to do this unlocks the real power of S3 and all the additional features and functionality you have beyond “putObject.”

Categories: AWS ColdFusion