Secure Uploads to AWS S3 with Ruby on Rails
--
Do not expose your secrets on the client. The following guide will outline how to upload files directly to S3 without exposing your secret tokens.
Introduction
A direct upload to the storage server (S3, GCP storage, etc) may be desired when working with large files. Since uploading through the server would consume resources with the handling of these long running requests.
A possible (yet vulnerable) approach to implementing direct uploads is pictured below. In it, a client uploads a file directly to S3 using credentials it already has (usually that allow any upload) and when the upload successfully completes the client makes a POST request to the server indicating a new file has been uploaded. However, this approach exposes the S3 secrets and would allow a bad agent to upload any file (and possibly overwrite existing files).
The addition of an extra step increases the security of the upload by not exposing secrets and allowing the server to validate the request (check authorization, etc). This new flow is shown below.
Example with Code
Pre-requisites
You will require the official aws-sdk-s3 gem to be installed and configured with your credentials and region.
Step 1: Method for Generating Signed URL
In your model (ex: models/attachment.rb) add a class method for generating a signed URL for S3 upload.
def self.generate_upload_url(file_name)
Aws::S3::Presigner.new.presigner.presigned_url(
:put_object,
bucket: ENV['MY_S3_BUCKET_NAME'],
key: file_name,
use_accelerate_endpoint: true,
expires_in: 300 # Number of seconds the URL is valid for
)
end