Missing configuration in AWS amplify custom headers - amazon-web-services

Having a hard time with setting custom headers up to prevent cross side scripting on an Amplify App.
This is the configuration:
customHeaders:
- pattern: '**/*'
headers:
- key: Strict-Transport-Security
value: max-age=31536000; includeSubDomains
- key: X-Frame-Options
value: deny
- key: X-XSS-Protection
value: 1; mode=block
- key: X-Content-Type-Options
value: nosniff
- key: Content-Security-Policy
value: frame-ancestors 'none'
When loading the site in an iframe like so:
<html>
<head></head>
<body>
<h1>IFRAME blocked</h1>
<iframe width=100% height=100% src="https://foo.bar/"></iframe>
</body>
</html>
Then the iFrame is successfully blocked.
But when loading the iframe like this:
<html>
<head></head>
<body>
<h1>IFRAME Not blocked</h1>
<iframe width=100% height=100% src="https://foo.bar/login"></iframe>
</body>
</html>
Then the iFrame is rendering the page.
Any ideas how to extend the custom header configuration to also include any additional "path" of the url?

Please try this
X-Frame-Options: ALLOW-FROM URL
Details
X-Frame-Options: DENY It completely denies to be loaded in
frame/iframe.
X-Frame-Options: SAMEORIGIN It allows only if the site which wants
to load has a same origin.
X-Frame-Options: ALLOW-FROM URL It grants a specific URL to load
itself in a iframe. However please pay attention to that, not all
browsers support this.

Related

Add X-Frame-Options header to all URLs using CloudFront functions

I have a single page application and I'm trying to prevent clickjacking by adding X-Frame-Options header to the HTML responses. My website is hosted on S3 through CloudFront.
The CloudFront distribution is configured to send index.html by default:
Default root object
index.html
In the Error Pages section I configured 404 page to also point to the index.html. This way all URLs that are not in S3 return the default HTML, i.e. /login, /admin etc.
Update The 403 status code is also configured:
Then I have created a CloudFront function as described here and assigned it to the Viewer response:
function handler(event) {
var response = event.response;
var headers = response.headers;
headers['x-frame-options'] = {value: 'DENY'};
return response;
}
This works, but only for /:
curl -v https://<MYSITE.com>
....
< x-frame-options: DENY
For other URLs it doesn't work - the x-frame-options header is missing:
curl -v https://<MYSITE.com>/login
....
< x-cache: Error from cloudfront
My question is - why my cloudfront function does not append a header in the error response, and what can I do to add it?
I understand that your questions are:
Q1: Why does the CloudFront function work for /?
Q2: Why doesn't the CloudFront function work for other url path?
Please refer to the responses below:
A1: Since you might specify a Default Root Object [1] (e.g.index.html) which returning the object when a user requests the root URL. When CloudFront returns the object with 200 ok, the CloudFront Function will be invoked on the viewer response event.
A2: You might not give the s3:ListBucket permissions in your S3 bucket policy(e.g. OAI). As the result, you will get Access Denied(403) errors for missing objects instead of 404 Not Found errors. Namely, the Error Pages you have configured isn't applied to this case, and the CloudFront Function won't be invoked because the HTTP status code is higher than 399[2].
[Updated] Suggestion:
Since CloudFront does not invoke edge functions for viewer response events when the origin returns HTTP status code 400 or higher. However, Lambda#Edge functions for origin response events are invoked for all origin responses. In this senario, I'll suggest that we should use Lambda#Edge instead of CloudFront Functions.
For your convenience, please refer to the sample code of l#e:
exports.handler = async (event, context) => {
const response = event.Records[0].cf.response;
const headers = response.headers;
headers['x-frame-options'] = [{
key: 'X-Frame-Options',
value: 'DENY',
}];
return response;
};
FYI. Here is my curl test result:
# PATH: `/`
$ curl -sSL -D - https://dxxxxxxx.cloudfront.net/
HTTP/1.1 200 OK
Content-Type: text/html
Content-Length: 12
Connection: keep-alive
ETag: "e59ff97941044f85df5297e1c302d260"
___snipped___
Server: AmazonS3
X-Frame-Options: DENY
___snipped___
# PATH: `/login`
$ curl -sSL -D - https://dxxxxxxx.cloudfront.net/login
HTTP/1.1 200 OK
Content-Type: text/html
Content-Length: 12
Connection: keep-alive
ETag: "e59ff97941044f85df5297e1c302d260"
___snipped___
Server: AmazonS3
X-Frame-Options: DENY
___snipped___

AWS Cloudfront redirects TOO_MANY times

I folowed Redirecting Internet Traffic to Another Domain and Redirecting HTTP Requests to HTTPS.
This is my status.
s3 : 3 buckets for web hosting
1) example.com(index.html in it, has policy),
2) www.example.com(for request redirection, no policy, redirect to example.com)
3) bucket-for-redirection(for cloudfront, no policy, redirect to example.com, https protocol)
cloudfront: 1 CloudFront
CNAMEs: example.com, www.example.com
Origin Domain Name and Path: bucket-for-redirection.s3-website.ap-northeast-2.amazonaws.com
Origin ID: S3-Website-bucket-for-redirection.s3-website.ap-northeast-2.amazonaws.com
Route 53
Type A for 2 domain
1) example.com: Alias target is CloudFront
2) www.example.com: Alias target is s3
But my site returns ERR_TOO_MANY_REDIRECTS. Is there something i missed?
solution
I removed all the buckets except one(bucket-for-redirection).
Put the resources(ex. index.html) in there.
create the bucket policy.
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "AddPerm",
"Effect": "Allow",
"Principal": "*",
"Action": "s3:GetObject",
"Resource": "arn:aws:s3:::your-bucket-name/*"
}
]
}
Then, set its properties with "Static Web Hosting", select first option, and type 'index.html' or else.
Make sure the Alias's target is CloudFront in Route 53.
(If you're Korean, please refer to my blog.)
This problem is caused by too many redirects. This means that the user is going to one URL, then the user is sent to another URL and then sent to another URL, ... The web browser detects multiple redirects and displays an error the user. Otherwise it is possible for the user to get into a loop constantly moving from one URL to another and never displaying the desired web page.
There is not enough information in your question about how you have configured S3 and CloudFront, so I will explain how to determine the exact problem.
To debug this problem use curl.
Let's say that your goal is that all users go to https://www.example.com. Now verify that this url does not redirect. Note: some webservers will redirect a DNS name to a DNS name + the home page url. If this is the case test with the home page url (second command).
curl -i https://www.example.com > data.txt
OR (replace with your home page url):
curl -i https://www.example.com/index.html > data.txt
Now open the file data.txt in an editor. The first line should be HTTP/1.1 200 or HTTP/2 200. The key is the 200 (anything between 200 and 299). If instead the number is 301 (Moved Permanently) or 307 (Temporary Redirect), then you are redirecting the user. See my example below. This is most likely the problem. The key is to figure out why your desired DNS name is redirecting and to what it is redirecting to. Then find the configuration file / service that is redirecting incorrectly.
If the previous command is working correct, then test the other supported DNS names and see if they are redirecting correctly to your desire DNS name (https://www.example.com). A common problem is that the redirects are going to the wrong desired page which then loops back and forth redirecting.
Your goal is that the webserver returns the following (which includes both the HTTP headers and HTML body). The important items are the status code (301 or 307) and the redirect location (5th line below). The HTML body is ignored for redirects.
Example correct redirect for everything but the desired DNS name:
HTTP/2 301
date: Fri, 08 Mar 2019 04:17:18 GMT
server: Apache
x-frame-options: SAMEORIGIN
location: https://www.example.com/
content-length: 232
content-type: text/html; charset=iso-8859-1
via: 1.1 google
alt-svc: quic=":443"; ma=2592000; v="46,44,43,39"
<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML 2.0//EN">
<html><head>
<title>301 Moved Permanently</title>
</head><body>
<h1>Moved Permanently</h1>
<p>The document has moved here.</p>
</body></html>
Use curl and test all supported possibilities:
curl -i http://www.example.com
This should redirect to https://www.example.com
curl -i http://example.com
This should redirect to https://www.examle.com
curl -i https://example.com
This should redirect to https://www.examle.com
Repeat the above tests using your home page url and a few sub pages.
A common problem that I see even on correctly running websites is that the user is redirected more than once. Properly designed redirects should send the user to the correct location in one step, not in multiple steps. Multiple redirects slow down getting to the correct page.

AWS GET request with body rejected by CloudFront

I'm build an API using AWS SAM (Lambda & API Gateway) whose contract is defined by a 3rd party.
The 3rd party calls my API with a GET request that contains JSON in the body. However, when a request is sent to the API with a body it gets rejected by CloudFront.
This is the request:
curl -X GET -H "Content-Type: application/json" --data '{"hello":"world"}' https://my-api.execute-api.us-east-2.amazonaws.com/Prod/my-api
This is the response:
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<HTML>
<HEAD>
<META HTTP-EQUIV="Content-Type" CONTENT="text/html; charset=iso-8859-1">
<TITLE>ERROR: The request could not be satisfied</TITLE>
</HEAD>
<BODY>
<H1>403 ERROR</H1>
<H2>The request could not be satisfied.</H2>
<HR noshade size="1px">
Bad request.
<BR clear="all">
<HR noshade size="1px">
<PRE>
Generated by cloudfront (CloudFront)
Request ID: 1p0St_-e3noQL-2uMxeB_2I6lkMr1mg5afvxJRmVpCdnG67Vgnhj9w==
</PRE>
<ADDRESS></ADDRESS>
</BODY>
</HTML>
Checking the logs, the request never hits API Gateway or the Lambda function. However, if I remove the body from the request, then it hits the Lambda function and I get the appropriate error message from the API (telling the caller that the expected body is missing.)
curl -X GET -H "Content-Type: application/json" https://my-api.execute-api.us-east-2.amazonaws.com/Prod/my-api
I'm using basic configuration of API Gateway via a SAM template. This is the relevant section:
MyApiFunction:
Type: AWS::Serverless::Function
Properties:
CodeUri: bin/main.zip
Handler: main
Runtime: go1.x
Tracing: Active
Role: !Sub ${MyApiLambdaExecutorRole.Arn}
Events:
CatchAll:
Type: Api
Properties:
Path: /my-api
Method: GET
GET requests cannot contain a request body on CloudFront. Try using POST instead.
If you want to send limited data in a GET request, you can use query parameters.
You can see in the AWS Docs that this isn't possible here:
https://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/RequestAndResponseBehaviorCustomOrigin.html#RequestCustom-get-body
If a viewer GET request includes a body, CloudFront returns an HTTP status code 403 (Forbidden) to the viewer.
You could perhaps use an EC2 instance or other service that doesn't use API Gateway to handle the request.

Can GET but not POST to a Lambda via API Gateway. Why?

Below is the request the client makes. The GET request is a success, but the POST request gives an error.
fetch('api/public/libraries/sign-out-discourse', {
method: 'GET', // or 'POST'
headers: new Headers([
['Accept', 'application/json'],
['Content-Type', 'application/json'],
['Authorization', jwtToken],
]),
})
Here is the error from the POST request:
HTTP/1.1 403 Forbidden
Server: CloudFront
Date: Fri, 05 Oct 2018 08:50:14 GMT
Content-Type: text/html
Content-Length: 694
Connection: keep-alive
X-Cache: Error from cloudfront
Via: redacted
X-Amz-Cf-Id: redacted
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<HTML><HEAD><META HTTP-EQUIV="Content-Type" CONTENT="text/html; charset=iso-8859-1">
<TITLE>ERROR: The request could not be satisfied</TITLE>
</HEAD><BODY>
<H1>403 ERROR</H1>
<H2>The request could not be satisfied.</H2>
<HR noshade size="1px">
This distribution is not configured to allow the HTTP request method that was used for this request. The distribution supports only cachable requests.
<BR clear="all">
<HR noshade size="1px">
<PRE>
Generated by cloudfront (CloudFront)
Request ID: redacted
</PRE>
<ADDRESS>
</ADDRESS>
</BODY></HTML>
What I have tried
In CloudFront I have chosen behaviors -> edit -> Allowed HTTP Methods: GET, HEAD, OPTIONS, PUT, POST, PATCH, DELETE. I have invalidated caches since making this chance in the unlikely case that AWS stores settings in the CloudFront cache somehow.
In API Gateway graphical, I have enabled CORS for the resource. I have redeployed the API since making this change.
Other debugging
curl -X POST API_ENDPOINT returns the same error as calling the endpoint with POST from my application.
If you are using Lambda Proxy integration, you need to provide the CORS header in the response, API Gateway will not do that for you for a POST request.
var response = {
statusCode: 200,
headers: {
"Access-Control-Allow-Origin" : "*",
"Access-Control-Allow-Credentials" : true
},
body: JSON.stringify({"message":"Success"})
}
callback(null, response);

AWS CloudFront: Font from origin has been blocked from loading by Cross-Origin Resource Sharing policy

I'm receiving the following error on a couple of Chrome browsers but not all. Not sure entirely what the issue is at this point.
Font from origin https://ABCDEFG.cloudfront.net has been blocked from loading by Cross-Origin Resource Sharing policy: No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin https://sub.domain.example is therefore not allowed access.
I have the following CORS Configuration on S3
<CORSConfiguration>
<CORSRule>
<AllowedOrigin>*</AllowedOrigin>
<AllowedHeader>*</AllowedHeader>
<AllowedMethod>GET</AllowedMethod>
</CORSRule>
</CORSConfiguration>
The request
Remote Address:1.2.3.4:443
Request URL:https://abcdefg.cloudfront.net/folder/path/icons-f10eba064933db447695cf85b06f7df3.woff
Request Method:GET
Status Code:200 OK
Request Headers
Accept:*/*
Accept-Encoding:gzip,deflate
Accept-Language:en-US,en;q=0.8
Cache-Control:no-cache
Connection:keep-alive
Host:abcdefg.cloudfront.net
Origin:https://sub.domain.example
Pragma:no-cache
Referer:https://abcdefg.cloudfront.net/folder/path/icons-e283e9c896b17f5fb5717f7c9f6b05eb.css
User-Agent:Mozilla/5.0 (Macintosh; Intel Mac OS X 10_9_4) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/37.0.2062.94 Safari/537.36
All other requests from Cloudfront/S3 work properly, including JS files.
Add this rule to your .htaccess
Header add Access-Control-Allow-Origin "*"
even better, as suggested by #david thomas, you can use a specific domain value, e.g.
Header add Access-Control-Allow-Origin "your-domain.example"
Chrome since ~Sep/Oct 2014 makes fonts subject to the same CORS checks as Firefox has done https://code.google.com/p/chromium/issues/detail?id=286681. There is a discussion on this in https://groups.google.com/a/chromium.org/forum/?fromgroups=#!topic/blink-dev/TT9D5-Zfnzw
Given that for fonts the browser may do a preflight check, then your S3 policy needs the cors request header as well. You can check your page in say Safari (which at present doesn't do CORS checking for fonts) and Firefox (that does) to double check this is the problem described.
See Stack overflow answer on Amazon S3 CORS (Cross-Origin Resource Sharing) and Firefox cross-domain font loading for the Amazon S3 CORS details.
NB in general because this used to apply to Firefox only, so it may help to search for Firefox rather than Chrome.
I was able to solve the problem by simply adding <AllowedMethod>HEAD</AllowedMethod> to the CORS policy of the S3 Bucket.
Example:
<?xml version="1.0" encoding="UTF-8"?>
<CORSConfiguration xmlns="http://s3.amazonaws.com/doc/2006-03-01/">
<CORSRule>
<AllowedOrigin>*</AllowedOrigin>
<AllowedMethod>GET</AllowedMethod>
<AllowedMethod>HEAD</AllowedMethod>
<MaxAgeSeconds>3000</MaxAgeSeconds>
<AllowedHeader>Authorization</AllowedHeader>
</CORSRule>
</CORSConfiguration>
Nginx:
location ~* \.(eot|ttf|woff)$ {
add_header Access-Control-Allow-Origin '*';
}
AWS S3:
Select your bucket
Click properties on the right top
Permisions => Edit Cors Configuration => Save
Save
http://schock.net/articles/2013/07/03/hosting-web-fonts-on-a-cdn-youre-going-to-need-some-cors/
On June 26, 2014 AWS released proper Vary: Origin behavior on CloudFront so now you just
Set a CORS Configuration for your S3 bucket:
<AllowedOrigin>*</AllowedOrigin>
In CloudFront -> Distribution -> Behaviors for this origin, use the Forward Headers: Whitelist option and whitelist the 'Origin' header.
Wait for ~20 minutes while CloudFront propagates the new rule
Now your CloudFront distribution should cache different responses (with proper CORS headers) for different client Origin headers.
The only thing that has worked for me (probably because I had inconsistencies with www. usage):
Paste this in to your .htaccess file:
<IfModule mod_headers.c>
<FilesMatch "\.(eot|font.css|otf|ttc|ttf|woff)$">
Header set Access-Control-Allow-Origin "*"
</FilesMatch>
</IfModule>
<IfModule mod_mime.c>
# Web fonts
AddType application/font-woff woff
AddType application/vnd.ms-fontobject eot
# Browsers usually ignore the font MIME types and sniff the content,
# however, Chrome shows a warning if other MIME types are used for the
# following fonts.
AddType application/x-font-ttf ttc ttf
AddType font/opentype otf
# Make SVGZ fonts work on iPad:
# https://twitter.com/FontSquirrel/status/14855840545
AddType image/svg+xml svg svgz
AddEncoding gzip svgz
</IfModule>
# rewrite www.example.com → example.com
<IfModule mod_rewrite.c>
RewriteCond %{HTTPS} !=on
RewriteCond %{HTTP_HOST} ^www\.(.+)$ [NC]
RewriteRule ^ http://%1%{REQUEST_URI} [R=301,L]
</IfModule>
http://ce3wiki.theturninggate.net/doku.php?id=cross-domain_issues_broken_web_fonts
I had this same problem and this link provided the solution for me:
http://www.holovaty.com/writing/cors-ie-cloudfront/
The short version of it is:
Edit S3 CORS config (my code sample didn't display properly)
Note: This is already done in the original question
Note: the code provided is not very secure, more info in the linked page.
Go to the "Behaviors" tab of your distribution and click to edit
Change "Forward Headers" from “None (Improves Caching)” to “Whitelist.”
Add “Origin” to the "Whitelist Headers" list
Save the changes
Your cloudfront distribution will update, which takes about 10 minutes. After that, all should be well, you can verify by checking that the CORS related error messages are gone from the browser.
For those using Microsoft products with a web.config file:
Merge this with your web.config.
To allow on any domain replace value="domain" with value="*"
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<system.webserver>
<httpprotocol>
<customheaders>
<add name="Access-Control-Allow-Origin" value="domain" />
</customheaders>
</httpprotocol>
</system.webserver>
</configuration>
If you don't have permission to edit web.config, then add this line in your server-side code.
Response.AppendHeader("Access-Control-Allow-Origin", "domain");
For AWS S3, setting the Cross-origin resource sharing (CORS) to the following worked for me:
[
{
"AllowedHeaders": [
"Authorization"
],
"AllowedMethods": [
"GET",
"HEAD"
],
"AllowedOrigins": [
"*"
],
"ExposeHeaders": []
}
]
There is a nice writeup here.
Configuring this in nginx/apache is a mistake.
If you are using a hosting company you can't configure the edge.
If you are using Docker, the app should be self contained.
Note that some examples use connectHandlers but this only sets headers on the doc. Using rawConnectHandlers applies to all assets served (fonts/css/etc).
// HSTS only the document - don't function over http.
// Make sure you want this as it won't go away for 30 days.
WebApp.connectHandlers.use(function(req, res, next) {
res.setHeader('Strict-Transport-Security', 'max-age=2592000; includeSubDomains'); // 2592000s / 30 days
next();
});
// CORS all assets served (fonts/etc)
WebApp.rawConnectHandlers.use(function(req, res, next) {
res.setHeader('Access-Control-Allow-Origin', '*');
return next();
});
This would be a good time to look at browser policy like framing, etc.
Late to the party, but I just ran into this problem and solved it with the following settings in my AWS bucket config (Permission tab). The requested format is not XML anymore but JSON:
[
{
"AllowedHeaders": [
"Content-*"
],
"AllowedMethods": [
"GET",
"HEAD"
],
"AllowedOrigins": [
"https://www.yourdomain.example",
"https://yourdomain.example"
],
"ExposeHeaders": []
}
]
Just add use of origin in your if you use node.js as server...
like this
app.use((req, res, next) => {
res.header('Access-Control-Allow-Origin', '*');
next();
});
We Need response for origin
If you want to allow all the fonts from a folder for a specific domain then you can use this:
<location path="assets/font">
<system.webServer>
<httpProtocol>
<customHeaders>
<add name="Access-Control-Allow-Origin" value="http://localhost:3000" />
</customHeaders>
</httpProtocol>
</system.webServer>
</location>
where assets/font is the location where all fonts are and http://localhost:3000 is the location which you want to allow.
Add this to your .htaccess file. This solved my problem.
<FilesMatch ".(eot|otf|ttf|woff|woff2)">
Header always set Access-Control-Allow-Origin "*"
</FilesMatch>
Working solution for heroku is here http://kennethjiang.blogspot.com/2014/07/set-up-cors-in-cloudfront-for-custom.html
(quotes follow):
Below is exactly what you can do if you are running your Rails app in Heroku and using Cloudfront as your CDN. It was tested on Ruby 2.1 + Rails 4, Heroku Cedar stack.
Add CORS HTTP headers (Access-Control-*) to font assets
Add gem font_assets to Gemfile .
bundle install
Add config.font_assets.origin = '*' to config/application.rb . If you want more granular control, you can add different origin values to different environment, e.g., config/config/environments/production.rb
curl -I http://localhost:3000/assets/your-custom-font.ttf
Push code to Heroku.
Configure Cloudfront to forward CORS HTTP headers
In Cloudfront, select your distribution, under "behavior" tab, select and edit the entry that controls your fonts delivery (for most simple Rails app you only have 1 entry here). Change Forward Headers from "None" to "Whilelist". And add the following headers to whitelist:
Access-Control-Allow-Origin
Access-Control-Allow-Methods
Access-Control-Allow-Headers
Access-Control-Max-Age
Save it and that's it!
Caveat: I found that sometimes Firefox wouldn't not refresh the fonts even if CORS error is gone. In this case keep refreshing the page a few times to convince Firefox that you are really determined.