简体   繁体   中英

AWS upload file from client to S3

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 :/)

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.

直接文件上传到S3序列图

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.

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

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.

(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.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM