Is there something wrong with my Amazon S3 bucket policy? - amazon-s3

I am trying to block hotlinking of my Cloudfront files from specific domains. Through a combination of online examples and Amazon's own policy generator, I have come up with this:
{
"Version": "2008-10-17",
"Id": "http referer policy",
"Statement": [{
"Sid": "Block image requests",
"Action": "s3:GetObject",
"Effect": "Deny",
"Resource": "arn:aws:s3:::mybucket/subdir/*",
"Condition": {
"StringLike": {
"aws:Referer": [
"http://example.com/*"
]
}
},
"Principal": {
"AWS": "*"
}
}]
}
I sent an invalidation request for a file in the subdirectory of mybucket, then a few minutes later tried reloading the image with the referer header still sent (verified using Chrome's dev tools). Did a hard reload with Ctrl+F5, and the response headers contained "X-Cache:Miss from cloudfront" so it's definitely getting the latest version of the image.
But the image is still displaying fine and is not blocked. The policy generator did not have an option for the "aws:Referer" key, but it's in the Amazon docs here. Have I done something wrong here?

Update 2
Revisiting your policy I wonder how you have actually allowed CloudFront access to your objects in the first place? Have you by chance followed the common advise in e.g. Start Using CloudFront with Amazon S3 that You must ensure that your object permissions are set to Make Everything Public for each object in your Amazon S3 bucket.
In this case you might have stumbled over a related pitfall due to the interaction between the meanwhile three different S3 access control mechanisms available, which can be rather confusing indeed. This is addressed e.g. in Using ACLs and Bucket Policies Together:
When you have ACLs and bucket policies assigned to buckets, Amazon S3
evaluates the existing Amazon S3 ACLs as well as the bucket policy
when determining an account’s access permissions to an Amazon S3
resource. If an account has access to resources that an ACL or policy
specifies, they are able to access the requested resource.
Consequently you would need to migrate your ACL to the bucket policy (i.e. allow CloudFront access before denying via aws:referer) and delete the overly generous ACL thereafter.
Good luck!
Update 1
Okay, now with client caching out the way, I'm afraid this is going to be non trivial (as apparent when you search for aws:referer in the AWS forums), thus might require a couple of iterations (especially given you have researched the topic yourself already):
The most common issue encountered is the leading whitespace error in the AWS documentation (which is particularly annoying, because a simple documentation fix would remedy lots of wasted time on behalf of users and AWS support staff alike)
Your policy doesn't exhibit this issue, however, given you sanitized the real domain you might have replaced the error in your production code in fact?
Also it is important to realize that the HTTP referer header is not necessarily going to be available, see e.g. Referer Hiding (thus your policy won't prevent malicious access anyway, though that's apparently not the issue)
You stated already, that you have verified it to be sent via the Chrome developer tools, so this doesn't apply either (I'm mentioning it to stress the reduced security level).
The policy looks fine at first sight - before digging further into this direction though, I'd recommend to ensure that you are actually bypassing Chrome's cache successfully, which is notoriously less straight forward than people are used to from other browsers; in particular, Ctrl + F5 simply reloads the page, but does not Bypass the cache (not reliable at least)!
As documented there as well, you could use one of the other key combinations To reload a page and bypass the cache (including the confusing 2nd Ctrl + F5 after the 1st one reloaded) , however, I recommend facilitating one of the following two alternatives instead:
Chrome's developer tools offers dedicated support for browsing without a cache - in the bottom right corner of the toolbox panel is a cog icon for settings, clicking this triggers an overlay with an options panel, amongst these you'll find the option Disable cache under section Network.
Chrome's Incognito mode (Ctrl + Shift + N) keeps Google Chrome from storing information about the websites you've visited, which as of today (might change anytime of course) seems to include cached content, cookies, DNS and the like as expected, thus is an even quicker, though less explicit option right now.

Related

Authorization Issues on S3 Hosted Static Site

I have configured cognito based authentication and everything is working on my local machine, however when I push the compiled nuxt application to S3, I get the following error after login:
<Code>AccessDenied</Code>
<Message>Access Denied</Message>
<RequestId>YJ5VBAF69BFHNTRA</RequestId>
<HostId>+ROpBRvEbtrVxTgwqSfhDvK5jwhCfbD9eoE3X6RslkFghQXDL+NwkupIqXoYW2Em9ZoBEhP31Oo=</HostId>
</Error>
This seems to be an s3 error, and I am not sure what is causing it as the site acts like normal otherwise.
Can be repeated by registering and trying to login to the site (copyswapper.com).
This is a problem serving the default document (index.html) - instructions on fixing it below.
LOCAL DEVELOPMENT
On your local machine, a development web server, eg the webpack one, serves index.html regardless of the path the user browses to within the vue.js app:
http://localhost:3000/login
AWS
I see you are deploying static files to S3, then serving them via Cloudfront. But the default document handling works differently, meaning this path does not serve an index.html file, and results in an error instead:
https://copyswapper.com/login
AWS PERMISSIONS
I have a demo Single Page App hosted the same way, which you can run from this page to compare against. The standard setup is to only allow Cloudfront to access files, via permissions like this. It results in the above errors if a file is missing though:
{
"Version": "2008-10-17",
"Id": "PolicyForPublicWebsiteContent",
"Statement": [
{
"Sid": "1",
"Effect": "Allow",
"Principal": {
"AWS": "arn:aws:iam::cloudfront:user/CloudFront Origin Access Identity H1D9C6K7CY211F"
},
"Action": "s3:GetObject",
"Resource": "arn:aws:s3:::web.authsamples.com/*"
}
]
}
CONCERN 1: DEFAULT DOCUMENT HANDLING
You need to provide a lambda edge function to serve the default document, regardless of the path of the user within your Single Page App:
Code to set the default document
Here are a couple of paths for my demo SPA. Note that the first of these could be a path within the app, so the default document handling deals with that. The second of these results in a non-existent Javascript file, and I did not try to fix that, so it results in the same error you are getting:
https://web.authsamples.com/nonexistentpath
https://web.authsamples.com/nonexistentpath.js
CONCERN 2: SECURITY HEADERS
While you are there you should also write a lambda edge function to use recommended security headers, similar to this:
Code to set security headers
If you then browse to Mozilla Observatory and type in your site name, you will get a better security rating, as for my demo app:
Demo App Security Rating
LAMBDA EDGE TESTING AND DEPLOYMENT
The lambda edge functions can be managed in a small subproject, as in my example. I use the Serverless framework, meaning the logic is expressed in a Serverless.yml file and I then run these commands during development, to test the logic:
npm install
npm run defaultDocument
npm run securityHeaders
I then deploy the code with these commands:
npm run package
npm run deploy
SUMMARY
Single Page Apps are not 100% static, and require some code to handle the two concerns mentioned above.

CloudFront origin for specific region content

I have created four S3 buckets, each with a simple index.html file and each with unique content.
I have created a CloudFront distribution and assigned it four origins, one for each of the four buckets.
Each origin has an Origin Access Identity and that OAI has been used in it's related bucket's policy, eg:
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Principal": {
"AWS": "arn:aws:iam::cloudfront:user/CloudFront Origin Access Identity 123456789ABCDE"
},
"Action": "s3:GetObject",
"Resource": "arn:aws:s3:::my-first-test-bucket/*"
}
]
}
I have also set Block all public access to true for each bucket.
When I visit the CloudFront distribution name I see the content for my region.
However, when I use a geo-browser to test the distribution from another region (one closer to one of the other buckets) I see the same content.
How can I configure my CloudFront distribution to serve the closest region-specific content? (eg: us-east-1 bucket content served through CloudFront for New York users.)
Geo-browser is not perfect for testing, you should test this with a good VPN.
to verify what I am saying, try to enter a blocked website in China. geo-browser will take you to it but it is trying to trick the server to think the connection is from China by changing IP address.
This can not Trick AWS. So test with VPN (a paid one is preferable)
More Info:
How does AWS Cloudfront CDN works:
when the first user from a specific region request a file
the file will be streamed (copied) from S3 to the closest Cloudfront server in the user region
the file will stay on this server temporary (usually 24 hours)
when a second user from the same Region request the same file he/she will get the copy from Cloudfront close server too.
if the same file changes on S3 it will be changes in very short time in the Cloudfront too (from 1 second to 5 minutes)
So, only the first request for the file will be affected by the distance of S3 bucket, which is negligible.
My recommendation is to use 1 S3 bucket only with folders specifying content depending on local (us, fr, gb, ...etc) and rely on the Cloudfront CDN to distribute content to different CDN servers for each region. I am using Cloudfront in this way and everything I wrote here is from real experiments I've done before.
Conclusion: if you use CDN then the location of storage server is not a factor for speedy delivery of content.
You can use a Route53 traffic policy. Add a Geolocation rule and then a Cloudfront distribution as an endpoint.

Access files stored on Amazon S3 through web browser

Current Situation
I have a project on GitHub that builds after every commit on Travis-CI. After each successful build Travis uploads the artifacts to an S3 bucket. Is there some way for me to easily let anyone access the files in the bucket? I know I could generate a read-only access key, but it'd be easier for the user to access the files through their web browser.
I have website hosting enabled with the root document of "." set.
However, I still get an 403 Forbidden when trying to go to the bucket's endpoint.
The Question
How can I let users easily browse and download artifacts stored on Amazon S3 from their web browser? Preferably without a third-party client.
I found this related question: Directory Listing in S3 Static Website
As it turns out, if you enable public read for the whole bucket, S3 can serve directory listings. Problem is they are in XML instead of HTML, so not very user-friendly.
There are three ways you could go for generating listings:
Generate index.html files for each directory on your own computer, upload them to s3, and update them whenever you add new files to a directory. Very low-tech. Since you're saying you're uploading build files straight from Travis, this may not be that practical since it would require doing extra work there.
Use a client-side S3 browser tool.
s3-bucket-listing by Rufus Pollock
s3-file-list-page by Adam Pritchard
Use a server-side browser tool.
s3browser (PHP)
s3index Scala. Going by the existence of a Procfile, it may be readily deployable to Heroku. Not sure since I don't have any experience with Scala.
Filestash is the perfect tool for that:
login to your bucket from https://www.filestash.app/s3-browser.html:
create a shared link:
Share it with the world
Also Filestash is open source. (Disclaimer: I am the author)
I had the same problem and I fixed it by using the
new context menu "Make Public".
Go to https://console.aws.amazon.com/s3/home,
select the bucket and then for each Folder or File (or multiple selects) right click and
"make public"
You can use a bucket policy to give anonymous users full read access to your objects. Depending on whether you need them to LIST or just perform a GET, you'll want to tweak this. (I.e. permissions for listing the contents of a bucket have the action set to "s3:ListBucket").
http://docs.aws.amazon.com/AmazonS3/latest/dev/AccessPolicyLanguage_UseCases_s3_a.html
Your policy will look something like the following. You can use the S3 console at http://aws.amazon.com/console to upload it.
{
"Version":"2008-10-17",
"Statement":[{
"Sid":"AddPerm",
"Effect":"Allow",
"Principal": {
"AWS": "*"
},
"Action":["s3:GetObject"],
"Resource":["arn:aws:s3:::bucket/*"
]
}
]
}
If you're truly opening up your objects to the world, you'll want to look into setting up CloudWatch rules on your billing so you can shut off permissions to your objects if they become too popular.
https://github.com/jupierce/aws-s3-web-browser-file-listing is a solution I developed for this use case. It leverages AWS CloudFront and Lambda#Edge functions to dynamically render and deliver file listings to a client's browser.
To use it, a simple CloudFormation template will create an S3 bucket and have your file server interface up and running in just a few minutes.
There are many viable alternatives, as already suggested by other posters, but I believe this approach has a unique range of benefits:
Completely serverless and built for web-scale.
Open source and free to use (though, of course, you must pay AWS for resource utilization -- such S3 storage costs).
Simple / static client browser content:
No Ajax or third party libraries to worry about.
No browser compatibility worries.
All backing systems are native AWS components.
You never share account credentials or rely on 3rd party services.
The S3 bucket remains private - allowing you to only expose parts of the bucket.
A custom hostname / SSL certificate can be established for your file server interface.
Some or all of the host files can be protected behind Basic Auth username/password.
An AWS WebACL can be configured to prevent abusive access to the service.

Amazon S3 Permission problem - How to set permissions for all files at once?

I have uploaded some files using the Amazon AWS management console.
I got an HTTP 403 Access denied error. I found out that I needed to set the permission to view.
How would I do that for all the files on the bucket?
I know that it is possible to set permission on each file, but it's time-consuming when having many files that need to be viewable for everyone.
I suggest that you apply a bucket policy1 to the bucket where you want to store public content. This way you don't have to set ACL for every object. Here is an example of a policy that will make all the files in the bucket mybucket public.
{
"Version": "2008-10-17",
"Id": "http better policy",
"Statement": [
{
"Sid": "readonly policy",
"Effect": "Allow",
"Principal": "*",
"Action": "s3:GetObject",
"Resource": "arn:aws:s3:::mybucket/sub/dirs/are/supported/*"
}
]
}
That * in "Resource": "arn:aws:s3:::mybucket/sub/dirs/are/supported/*" allows recursion.
1 Note that a Bucket Policy is different than an IAM Policy. (For one you will get an error if you try to include Principal in an IAM Policy.) The Bucket Policy can be edit by going the root of the bucket in your AWS web console and expanding Properties > Permissions. Subdirectories of a bucket also have Properties > Permissions, but there is no option to Edit bucket policy
You can select which directory you want it to be public.
Press on "more" and mark it as public; it will make the directory and all the files to be accessible as public.
You can only modify ACLs for a unique item (bucket or item), soy you will have to change them one by one.
Some S3 management applications allows you to apply the same ACL to all items in a bucket, but internally, it applies the ACL to each one by one.
If you upload your files programmatically, it's important to specify the ACL as you upload the file, so you don't have to modify it later. The problem of using an S3 management application (like Cloudberry, Transmit, ...) is that most of them uses the default ACL (private read only) when you upload each file.
I used Cloudberry Explorer to do the job :)
Using S3 Browser you can update permissions using the gui, also recursively. It's a useful tool and free for non-commercial use.
To make a bulk of files public, do the following:
Go to S3 web interface
Open the required bucket
Select the required files and folders by clicking the checkboxes at the left of the list
Click «More» button at the top of the list, click «Make public»
Confirm by clicking «Make public». The files won't have a public write access despite the warning says «...read this object, read and write permissions».
You could set ACL on each file using aws cli:
BUCKET_NAME=example
BUCKET_DIR=media
NEW_ACL=public-read
aws s3 ls $BUCKET_NAME/$BUCKET_DIR/ | \
awk '{$1=$2=$3=""; print $0}' | \
xargs -t -I _ \
aws s3api put-object-acl --acl $NEW_ACL --bucket $BUCKET_NAME --key "$BUCKET_DIR/_"
I had same problem while uploading the files through program (java) to s3 bucket ..
Error: No 'Access-Control-Allow-Origin' header is present on the requested resource.
Origin 'http://localhost:9000' is therefore not allowed access. The response had HTTP status code 403
I added the origin identity and changed the bucket policy and CORS configuration then everything worked fine.
Transmit 5
I wanted to add this here for potential macOS users that already have the beautifully-crafted FTP app called Transmit by Panic.
I already had Panic and it supports S3 buckets (not sure what version this came in but I think the upgrades were free). It also supports recursively updating Read and Write permissions.
You simply right click the directory you want to update and select the Read and Write permissions you want to set them to.
It doesn't seem terribly fast but you can open up the log file by going Window > Transcript so you at least know that it's doing something.
Use AWS policy generator to generate a policy which fits your need. The principal in the policy generator should be the IAM user/role which you'd be using for accessing the object(s).
Resource ARN should be arn:aws:s3:::mybucket/sub/dirs/are/supported/*
Next, click on "Add statement" and follow through. You'll finally get a JSON representing the policy. Paste this in your s3 bucket policy management section which is at "your s3 bucket page in AWS -> permissions -> bucket policy".
This worked for me on digital ocean, which allegedly has the same API as s3:
s3cmd modify s3://[BUCKETNAME]/[DIRECTORY] --recursive --acl-public
The above sets all files to public.

AWS/S3 ACLs & CloudFront

tl;dr - Is there a robust S3 ACL management tool, possibly for use with CloudFront?
I'm working on a personal private content distribution (via CloudFront) but obviously the AWS Console is severely lacking in this regard.
I know there are a handful of S3 clients out there, but none of them really do much for advanced ACL. To avoid having to use the AWS cli tools or to write wrappers for the API for everything (this is for configuring long-term systems, not for anything that would need to be done programmatically), I'm looking for one that has the best ACL support.
OR, if anyone has suggestions for managing CloudFront and custom ACLs (specifically for adding canonical user IDs/OriginAccessIdentities to buckets), I'm totally open to that too.
On a side note, the AWS docs mention the following:
Once you have a private content distribution, you must grant your CloudFront origin access identity read access to the private content. You do this by modifying the Amazon S3 ACL on each of the objects (not on the bucket).
which seems, er, exceptionally hard to maintain for a system that could potentially be used as swap (sic) storage for protected assets and modified on a regular basis (tens+ of times per day). Am I misreading that, or is it really intended to be that static and explicit?
Thanks for the suggestions, but I can't use those (Mac - didn't mention, not your fault). I ended up going with Bucket Explorer, FWIW.
Cyberduck for Mac & Windows supports ACL editing. Refer to http://trac.cyberduck.ch/wiki/help/en/howto/s3.