Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Setting S3 Bucket Policies

I'm building a website where users can upload files directly to amazon s3. I temporarily sign a url w/ my secret key that grants the user temporary PUT access to my site. This is possible through the CORS configuration.

Now, once the files are up there I want no one to read them but me. The current bucket policy I have disallows everyone from reading objects including me, the admin. Which is not good. What bucket policy will help me here?

Here is my working CORS.

<?xml version="1.0" encoding="UTF-8"?>
<CORSConfiguration xmlns="http://s3.amazonaws.com/doc/2006-03-01/">
    <CORSRule>
        <AllowedOrigin>http://url.com</AllowedOrigin>
        <AllowedMethod>PUT</AllowedMethod>
        <AllowedHeader>*</AllowedHeader>
    </CORSRule>
</CORSConfiguration>

UPDATE: Works with this policy:

 {
        "Version": "2008-10-17",
        "Statement": [
            {
                "Sid": "AllowPublicRead",
                "Effect": "Deny",
                             "NotPrincipal": {
                                "AWS":"arn:aws:iam::123456789012:user/Bob"
                             }
                "Action": "s3:GetObject",
                "Resource": "arn:aws:s3:::<bucket>/*"
            }
        ]
    }
like image 312
The Internet Avatar asked Nov 16 '13 10:11

The Internet


1 Answers

Ok we created an IAM user with full permission to S3, we use this user to sign the temporary policies to allow the upload. The User policy for the user is (You could further restrict this user to allow access to only one bucket, or even further to allow the user only put permissions on the bucket):

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Action": "s3:*",
      "Resource": "*"
    }
  ]
}

We then create a temporary policy to allow an upload into our useruploads bucket

{ "expiration": "2013-11-20T12:00:00.000Z",
  "conditions": [
    {"bucket": "useruploadtest"},
    ["starts-with", "$key", "russ"], 
    {"acl": "public-read"}, 
    {"success_action_redirect": "http://localhost/successful_upload.html"},
    ["starts-with", "$Content-Type", "image/"],
  ]
}

We then sign the base64 encoded policy with our secret key (this code below is a sample of creating the signature with cryptojs never put your secret key in client side code)

  <script src="http://crypto-js.googlecode.com/svn/tags/3.1.2/build/rollups/hmac-sha1.js">    </script>
  <script src="http://crypto-js.googlecode.com/svn/tags/3.1.2/build/components/enc-base64-min.js"></script>
  <script>
    var hash = CryptoJS.HmacSHA1("eyAiZXhwaXJhdGlvbiI6ICIyMDE1LTEyLTAxVDEyOjAwOjAwLjAwMFoiLA0KICAiY29uZGl0aW9ucyI6IFsNCiAgICB7ImJ1Y2tldCI6ICJ1c2VydXBsb2FkdGVzdCJ9LA0KICAgIFsic3RhcnRzLXdpdGgiLCAiJGtleSIsICJydXNzIl0sIA0KICAgIHsiYWNsIjogInB1YmxpYy1yZWFkIn0sIA0KICAgIHsic3VjY2Vzc19hY3Rpb25fcmVkaXJlY3QiOiAiaHR0cDovL2xvY2FsaG9zdC9zdWNjZXNzZnVsX3VwbG9hZC5odG1sIn0sDQogICAgWyJzdGFydHMtd2l0aCIsICIkQ29udGVudC1UeXBlIiwgImltYWdlLyJdLA0KICBdDQp9", "YourSecretKeyHere");
    var base64 = CryptoJS.enc.Base64.stringify(hash);
    console.log(base64);
  </script>

Then you can use the signed policy to allow a temporary upload

<form action="http://useruploadtest.s3.amazonaws.com" method="post" enctype="multipart/form-data">
    <input type="hidden" name="acl" value="public-read" />
    <input type="hidden" name="success_action_redirect" value="http://localhost/successful_upload.html" />
    <input type="hidden" name="AWSAccessKeyId" value="AKIAJM3U7AMGF6J6YYWQ" />
    <input type="hidden" name="Policy" value="eyAiZXhwaXJhdGlvbiI6ICIyMDE1LTEyLTAxVDEyOjAwOjAwLjAwMFoiLA0KICAiY29uZGl0aW9ucyI6IFsNCiAgICB7ImJ1Y2tldCI6ICJ1c2VydXBsb2FkdGVzdCJ9LA0KICAgIFsic3RhcnRzLXdpdGgiLCAiJGtleSIsICJydXNzIl0sIA0KICAgIHsiYWNsIjogInB1YmxpYy1yZWFkIn0sIA0KICAgIHsic3VjY2Vzc19hY3Rpb25fcmVkaXJlY3QiOiAiaHR0cDovL2xvY2FsaG9zdC9zdWNjZXNzZnVsX3VwbG9hZC5odG1sIn0sDQogICAgWyJzdGFydHMtd2l0aCIsICIkQ29udGVudC1UeXBlIiwgImltYWdlLyJdLA0KICBdDQp9" />
    <input type="hidden" name="Signature" value="Db4K65tz/WJtjAUUCWRn5MXdAJ4=" />


    Key to upload: <input type="input" name="key" value="test.jpg" /><br />
    Content-Type: <input type="input" name="Content-Type" value="image/jpeg" /><br />
    File: <input type="file" name="file" /> <br />

    <input type="submit" name="submit" value="Upload to Amazon S3" />
  </form>

So long as the bucket in our case "useruploadtest" allows read access from another IAM user which we use in our application to read the files and process them it all works.

Hope this helps

like image 154
rabs Avatar answered Oct 07 '22 23:10

rabs