4

I know there have been some questions about this. I know how to accomplish this using AWS SDK.

However, my concern is about security. My plan is to generate a signed URL so users logged on my website can upload files to S3. I generate this url on server side (based on Express framework).

The problem: anyone with access to this URL can upload files to my bucket. Only logged users can get these urls, but anyone can use them.

Is there a way to generate a "one time use" url to authenticate with S3? If not, is there an approach I can use that does not show my credentials on client side and do not upload the files to my server ?(I know, asking too much :/)

6
  • Hi Bruno, what things concern you about the use of the URLs? Would you be concerned that a user would get the URL and post it on twitter or a forum? Are you concerned about an attacker somehow obtaining a bunch of these urls and uploading files into your bucket? Commented Mar 25, 2016 at 4:58
  • That's my concern! I appreciate any suggestions :) Commented Mar 25, 2016 at 17:47
  • No worries, I'll update my answer. Commented Mar 25, 2016 at 21:23
  • Is this for an existing website that you want to add upload functionality to? If so, how do your users currently log on? Commented Mar 25, 2016 at 21:24
  • Local authentication. Commented Mar 25, 2016 at 21:40

4 Answers 4

9

Requests require authentication

Unless you make your resource publicly writeable S3 will require upload requests to contain authentication information. Authentication ties the request to an AWS account, which is then checked to see if they have permissions to carry out the request against the resource (authorisation).

See the AWS documenation on Authenticating Requests for more information.

The latest version of the algorithm used to generate the authentication information is called 'AWS Signature Version 4'. This algorithm uses the secret key of an account and some other pieces of data to produce a signature that is sent along with the request.

AWS knows the secret key and can re-calculate the signature on their end. If the signatures match the request is authenticated as coming from that account.

The AWS SDK takes care of signing requests for you with the account credentials it is given. The docs describes how the algorithm works and tells you how you could sign the requests yourself if you really wanted to.

Sign the S3 requests on the server side

Instead of keeping a secret key on the client side, so that it can sign the requests made to S3, you can generate a signed URL securely on the server on behalf of clients, and then return it for them to call S3 directly from their machine.

This scenario is one of the specific use cases mentioned in the docs:

[P]re-signed URLs are useful if you want your user/customer to be able upload a specific object to your bucket, but you don't require them to have AWS security credentials or permissions.

When you create a pre-signed URL, you must provide your security credentials, specify a bucket name an object key, an HTTP method (PUT of uploading objects), and an expiration date and time.

The pre-signed URLs are valid only for the specified duration.

Pre-Signed Request Security

A pre-signed request has the authentication signature baked right into it. As you mentioned, anyone who obtains the URL can make the request. However, I wouldn't let that fact alone dissuade me from using them; there are several things you can do to prevent an attacker from using one.

Consider the following interaction between client and server.

Direct File Upload to S3 Sequence Diagram

Request Expiry

Expiry is controlled at URL generation time with the Expires Parameter. With the flow described above the URL could expire several seconds after generation. After this time it would simply no longer work and be rejected by S3. When setting the expiry you would need to account for the latency of requests between your server and the user (steps #5-#7), and the user and S3 (step #8).

Include File Checksum in Signature

Using the Body Parameter you can instruct S3 to only allow content with a specific MD5 checksum. Similarly, but even more secure, you can require S3 to validate the payload against the SHA256 checksum of the file.

After step #3 in the flow above calculate the SHA256 checksum of the file and pass it to the server in step #4. You could then add it to the request like so:

var req = S3.putObject({ Bucket: bucket_name, Key: file_name });
req.on('build', function() {
  req.httpRequest.headers['x-amz-content-sha256'] = file_sha256;
});
var url = req.presign(url_expiry);

(References: AWS.Request class docs, AWS S3 service source code - getSignedUrl and REST Common Request Headers)

Getting an upload URL should require authentication

Only your authorised users should be able to get a pre-signed upload URL. Anonymous requests to get an upload URL (step #4) should be denied.

Furthermore, since this is a privileged call all requests should be audited (step #6). That way should a pre-signed request leak you know who generated it and can take further action if necessary.

Use HTTPS

Two main reasons, provides protection for sensitive data, like the pre-signed URL, usernames, passwords and session cookies when in transit between client and server.

It also provides assurance to the client that you are who you say your are and not a fake site set up by an attacker.

Sign up to request clarification or add additional context in comments.

2 Comments

@bruno-henrique expanded my answer, does that help?
can you talk about presigned url vs using STS to generate temporary credentials?
3

You can use PresignedUrlUploadObject to allow users to upload objects. This is an expiring URL and you can set the TTL

For NodeJS, it looks like this:

var s3 = new AWS.S3({computeChecksums: true}); // this is the default setting
var params = {Bucket: 'myBucket', Key: 'myKey', Body: 'EXPECTED CONTENTS'};
var url = s3.getSignedUrl('putObject', params);
console.log("The URL is", url);

From: AWS Docs

Comments

1

There's no way to prevent your credentials from being seen by an user if he or she knows how to find it. The best way to handle security if you're implementing a client to S3 direct upload is to use AWS's IAM system.

Instead of giving out the key and secret of your root account or any account you would use to do administrative tasks within the AWS console, register a new account using IAM and only allow this IAM account to do PUT operations to the specific bucket (or even a specific file directory within a specific bucket). This could look something like:

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Sid": "Stmt1421341195000",
            "Effect": "Allow",
            "Action": [
                "s3:PutObject",
                "s3:PutObjectAcl",
                "s3:PutObjectVersionAcl"
            ],
            "Resource": [
                "arn:aws:s3:::my-bucket",
                "arn:aws:s3:::my-bucket/*"
            ]
        }
    ]
}

Now you have an account that can only upload to your bucket. It cannot not list the items within it, it cannot update items within it, and it cannot delete items within it. On top of that, the credentials for this account cannot be used to manipulate any other AWS resources.

1 Comment

I think that this is the best approach. I don't think there is a way to make sure only users that are logged in can upload files to S3, without uploading the file to my server.
0

(This is not an answer but I cannot comment ... yet)

How does the application look like? You can create a signed URL with short TTL with IP or IP range restriction, if all uploads to S3 actually happen on your web server, I don't think anyone else can bypass the IP restriction.

1 Comment

I don't think I can use IP. The request to S3 comes from clients. To set a IP restriction I'd have to send the files to S3 from my server, which I want to avoid.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.