Create a gist now

Instantly share code, notes, and snippets.

What would you like to do?
AWS S3 bucket policy to make all files public (+CORS)
{
"Statement": [
{
"Sid": "AllowPublicRead",
"Effect": "Allow",
"Principal": {
"AWS": "*"
},
"Action": "s3:GetObject",
"Resource": "arn:aws:s3:::bucket_name_here/*"
}
]
}
// https://github.com/elasticsales/s3upload-coffee-javascript
var s3upload = new S3Upload({
file_dom_selector: '#files', // an <input type="file"> element
s3_sign_put_url: '/sign_s3_put',
onProgress: function(percent, message, publicUrl, file) { // Use this for live upload progress bars
console.log('Upload progress: ', percent, message);
},
onFinishS3Put: function(public_url, file) { // Get the URL of the uploaded file
console.log('Upload finished: ', public_url);
},
onError: function(status, file) {
console.log('Upload error: ', status);
}
});
<?xml version="1.0" encoding="UTF-8"?>
<CORSConfiguration xmlns="http://s3.amazonaws.com/doc/2006-03-01/">
<CORSRule>
<AllowedOrigin>*</AllowedOrigin>
<AllowedMethod>GET</AllowedMethod>
<MaxAgeSeconds>3000</MaxAgeSeconds>
<AllowedHeader>Authorization</AllowedHeader>
</CORSRule>
<CORSRule>
<AllowedOrigin>*</AllowedOrigin>
<AllowedMethod>PUT</AllowedMethod>
<MaxAgeSeconds>3000</MaxAgeSeconds>
<AllowedHeader>Content-Type</AllowedHeader>
<AllowedHeader>x-amz-acl</AllowedHeader>
<AllowedHeader>origin</AllowedHeader>
</CORSRule>
</CORSConfiguration>
{
"Statement": [{
"Action": [
"s3:GetObject",
"s3:PutObject",
"s3:PutObjectAcl"
],
"Effect": "Allow",
"Resource": [
"arn:aws:s3:::bucket_name_here/*"
]
}]
}
import time, os, json, base64, urllib, hmac, sha
@app.route('/sign_s3_put/')
@login_required
def sign_s3_put():
"""
Provide a temporary signature so that users can upload files directly from their
browsers to our AWS S3 bucket.
The authorization portion is taken from Example 3 on
http://s3.amazonaws.com/doc/s3-developer-guide/RESTAuthentication.html
"""
# don't give user full control over filename - avoid ability to overwrite files
random = base64.urlsafe_b64encode(os.urandom(2))
object_name = random+request.args.get('s3_object_name')
object_name = urllib.quote_plus(object_name) # make sure it works for filenames with spaces, etc.
mime_type = request.args.get('s3_object_type')
expires = int(time.time()+300) # PUT request to S3 must start within X seconds
amz_headers = "x-amz-acl:public-read" # set the public read permission on the uploaded file
resource = '%s/%s' % (app.config['AWS_EMAIL_ATTACHMENTS_BUCKET_NAME'], object_name)
str_to_sign = "PUT\n\n{mime_type}\n{expires}\n{amz_headers}\n/{resource}".format(
mime_type=mime_type,
expires=expires,
amz_headers=amz_headers,
resource=resource
)
sig = urllib.quote_plus(base64.encodestring(hmac.new(app.config['AWS_EMAIL_ATTACHMENTS_SECRET_ACCESS_KEY'], str_to_sign, sha).digest()).strip())
url = 'https://%s.s3.amazonaws.com/%s' % (app.config['AWS_EMAIL_ATTACHMENTS_BUCKET_NAME'], object_name)
return json.dumps({
'signed_request': '{url}?AWSAccessKeyId={access_key}&Expires={expires}&Signature={sig}'.format(
url=url,
access_key=app.config['AWS_EMAIL_ATTACHMENTS_ACCESS_KEY_ID'],
expires=expires,
sig=sig
),
'url': url
})
@ghost

ghost commented Jul 5, 2013

Can you confirm this still works? My OPTIONS request to s3 just freezes and nothing ever happens.

@ghost

ghost commented Jul 15, 2013

Nope?

chosak commented Feb 3, 2015

@philfreo thanks for the helpful blog post and code examples.

@dangerfarms I just implemented this and had to make some minor changes. Specifically, Chrome was reporting a net::ERR_INSECURE_RESPONSE error on the OPTIONS call, because the certificate for https://<your bucket name>.s3.amazonaws.com isn't trusted.

To modify the code above, you could do something like

url = 'https://s3.amazonaws.com/%s/%s' % (
    app.config['AWS_EMAIL_ATTACHMENTS_BUCKET_NAME',
    object_name
)

This has the same effect -- use this URL both as the returned value for 'url' as well as in your signed request.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment