Browser-Based Uploads Using Post

Browser Based File Upload on AWS S3 using POST Request

You will have to calculate the signature from backend. Follow these details Calculating a Signature to implement at your own.

That would be something like this:

$kDate = hash_hmac('sha256', $short_date, 'AWS4' . $secret_key, true);
$kRegion = hash_hmac('sha256', $region, $kDate, true);
$kService = hash_hmac('sha256', "s3", $kRegion, true);
$kSigning = hash_hmac('sha256', "aws4_request", $kService, true);
$signature = hash_hmac('sha256', base64_encode($policy), $kSigning);

Or you can use any of the AWS SDKs of your choice.

For example using PHP SDK you would implement:

Aws\Signature\S3SignatureV4

Browser-Based Uploads Using POST

After a research and comparing the AWS post example i found out that there were some redundant fields in the form that made AWS think i'm using SHA1.

After removing the AWSAccessKeyId field from the form and renaming some other fields I managed to make the AWS4 work.

This is the updated Demo

 <form id="myForm" action="http://yourbucket.s3.amazonaws.com/" method="post" enctype="multipart/form-data">
<input type="hidden" id="key" name="key" value="uploads/${filename}"/>
<input type="hidden" id="acl" name="acl" value="YOUR_ACL_OPTION"/>
<input type="hidden" name="success_action_redirect" value="http://google.com" />

<input type="hidden" id="type" name="Content-Type" value="MIME_TYPE"/>
<input type="hidden" name="x-amz-meta-uuid" value="14365123651274" />
<input type="hidden" name="X-Amz-Credential" value="YOUR_CREDENTIALS" />
<input type="hidden" name="X-Amz-Algorithm" value="AWS4-HMAC-SHA256" />
<input type="hidden" id="date" name="X-Amz-Date" value="" />

<input type="hidden" name="x-amz-meta-tag" value="" />
<input type="hidden" id="policy" name="Policy" value="YOUR_POLICY_DOCUMENT_BASE64_ENCODED"/>
<input type="hidden" id="signature" name="X-Amz-Signature" value="YOUR_CALCULATED_SIGNATURE"/>

<input name="file" id="file" type="file"/>
<input id="btn_submit" class="btn btn-warning" type="submit" value="Upload File to S3">
</form>

AWS S3 browser upload using HTTP POST gives invalid signature

You are generating the signature using Signature Version 4, but you are constructing the form as though you were using Signature Version 2... well, sort of.

formData.append("AWSAccessKeyId", signature.accessKey);

That's V2. It shouldn't be here at all.

formData.append("x-amz-credential", signature.credentials); // <accesskey>/20161126/eu-west-1/s3/aws4_request

This is V4. Note the redundant submission of the AWS Access Key ID here and above. This one is probably correct, although the examples have capitalization like X-Amz-Credential.

formData.append("x-amz-algorithm", "AWS4-HMAC-SHA256");

That is also correct, except it may need to be X-Amz-Algorithm. (The example seems to imply that capitalization is ignored).

formData.append("Signature", signature.signature);

This one is incorrect. This should be X-Amz-Signature. V4 signatures are hex, so that is what you should have here. V2 signatures are base64.

There's a full V4 example here, which even provides you with an example aws key and secret, date, region, bucket name, etc., that you can use with your code to verify that you indeed get the same response. The form won't actually work but the important question is whether your code can generate the same form, policy, and signature.

For any given request, there is only ever exactly one correct signature; however, for any given policy, there may be more than one valid JSON encoding (due to JSON's flexibility with whitespace) -- but for any given JSON encoding there is only one possible valid base64-encoding of the policy. This means that your code, using the example data, is certified as working correctly if it generates exactly the same form and signature as shown in the example -- and it means that your code is proven invalid if it generates the same form and policy with a different signature -- but there is a third possibility: the test actually proves nothing conclusive about your code if your code generates a different base64 encoding of the policy, because that will necessarily change the signature to not match, yet might still be a valid policy.

Note that Signature V2 is only suported on older S3 regions, while Signature V4 is supported by all S3 regions, so, even though you could alternately fix this by making your entire signing process use V2, that wouldn't be recommended.

Note also that The request signature we calculated does not match the signature you provided. Check your key and signing method does not tell you anything about whether the bucket policy or any users policies allow or deny the request. This error is not a permissions error. It will be thrown prior to the permissions checks, based solely on the validity of the signature, not whether the AWS Access Key id is authorized to perform the requested operation, which is something that is only tested after the signature is validated.

Amazon AWS S3 browser-based upload using POST -

Found a solution: had to explicitly configure the s3 client to use Amazon's new signature v4. The error occurs since it defaults to an older version, causing the mismatch. Bit of a facepalm - at the time this wasn't written in boto3 docs, although folks at Amazon say it should be soon.

The method is simplified since it now returns exactly the fields required:

def s3_upload_creds(name):
BUCKET = 'mybucket'
REGION = 'us-west-1'
s3 = boto3.client('s3', region_name=REGION, config=Config(signature_version='s3v4'))
key = '${filename}'
return s3.generate_presigned_post(
Bucket = BUCKET,
Key = key
)

Which means the form can be easily generated:

<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
</head>
<body>
{{ creds }}
<form action="https://mybucket.s3.amazonaws.com" method="post" enctype="multipart/form-data">
{% for key, value in creds.fields.items() %}
<input type="hidden" name="{{ key }}" value="{{ value }}" />
{% endfor %}
File:
<input type="file" name="file" /> <br />
<input type="submit" name="submit" value="Upload to Amazon S3" />
</form>
</html>

Cheers

Max HTTP POST request size for Amazon S3 Browser-based upload?

The docs here (http://aws.amazon.com/articles/1434) say this:

Content length rule, which checks that the size of an uploaded file is between a given minimum and maximum value. If this rule is not included in a policy document, users will be able to upload files of any size up to the 5GB limit imposed by S3.

I dont know how up to date these docs are.

Amazon S3 direct file upload from client browser - private key disclosure

I think what you want is Browser-Based Uploads Using POST.

Basically, you do need server-side code, but all it does is generate signed policies. Once the client-side code has the signed policy, it can upload using POST directly to S3 without the data going through your server.

Here's the official doc links:

Diagram: http://docs.aws.amazon.com/AmazonS3/latest/dev/UsingHTTPPOST.html

Example code: http://docs.aws.amazon.com/AmazonS3/latest/dev/HTTPPOSTExamples.html

The signed policy would go in your html in a form like this:

<html>
<head>
...
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
...
</head>
<body>
...
<form action="http://johnsmith.s3.amazonaws.com/" method="post" enctype="multipart/form-data">
Key to upload: <input type="input" name="key" value="user/eric/" /><br />
<input type="hidden" name="acl" value="public-read" />
<input type="hidden" name="success_action_redirect" value="http://johnsmith.s3.amazonaws.com/successful_upload.html" />
Content-Type: <input type="input" name="Content-Type" value="image/jpeg" /><br />
<input type="hidden" name="x-amz-meta-uuid" value="14365123651274" />
Tags for File: <input type="input" name="x-amz-meta-tag" value="" /><br />
<input type="hidden" name="AWSAccessKeyId" value="AKIAIOSFODNN7EXAMPLE" />
<input type="hidden" name="Policy" value="POLICY" />
<input type="hidden" name="Signature" value="SIGNATURE" />
File: <input type="file" name="file" /> <br />
<!-- The elements after this will be ignored -->
<input type="submit" name="submit" value="Upload to Amazon S3" />
</form>
...
</html>

Notice the FORM action is sending the file directly to S3 - not via your server.

Every time one of your users wants to upload a file, you would create the POLICY and SIGNATURE on your server. You return the page to the user's browser. The user can then upload a file directly to S3 without going through your server.

When you sign the policy, you typically make the policy expire after a few minutes. This forces your users to talk to your server before uploading. This lets you monitor and limit uploads if you desire.

The only data going to or from your server is the signed URLs. Your secret keys stay secret on the server.



Related Topics



Leave a reply



Submit