aws-samples / amazon-cloudfront-functions Goto Github PK
View Code? Open in Web Editor NEWLicense: Apache License 2.0
License: Apache License 2.0
I'm trying to build CSP for a moderately large infrastructure in several environments.
I can copy/paste to hard-code the values already in the parameter store, though am curious to learn if the CloudFront functions can reference AWS Parameter Store variables, as if so it makes transferring the code between environments easier.
Doubt this is actually the correct place to report; maybe the repo owner could send this up the chain?
When using the property for the host header value in a CloudeFront Function and then testing with the "Test" tab, the test fails with the error:
The CloudFront function associated with the CloudFront distribution is invalid or could not run. TypeError: cannot get property "value" of undefined
Property used: event.request.headers.host.value
The example event in the AWS documentation for CloudFront Event Structure > Example Event shows this is a valid path and is also used in the redirect-based-on-country sample.
Create a new function with the following basic structure:
function handler(event) {
var host = event.request.headers.host.value
return request
}
Expected result: Execution result status succeeded
Actual result: Execution result status failed
Output: The CloudFront function associated with the CloudFront distribution is invalid or could not run. TypeError: cannot get property "value" of undefined
Oddly enough, the host value is evaluated correctly when CloudFront executes the function. Just seems to be an issue in the Test sandbox.
If CloudFront serves files from S3, the CloudFront function is not executed if the file doesn't exist on S3 (404). This is especially harmful in the provided example where security headers such as CSP are set. It basically eliminates all protections provided by the security headers. Any XSS vulnerability that exists on the page can easily be exploited by sending the user to a page that doesn't exist and therefore doesn't include any of the security headers.
This is a very common setup for hosting single page applications on S3 in combination with CloudFront and I assume that most developers are unaware of this behavior. There should be a huge disclaimer on this specific example that the CSP headers are only added to requests to files that exist on the S3 bucket. Any error response (even if it was rewritten to return a 200 status code) will not invoke the function and therefore not include any custom headers.
finding a way to successfully do a URL redirect/rewrite so that your base URL does not change and the query string should be rewritten with another query string. We are planning to away from my website hosted on NGINX, which is currently where these redirect/rewrites are happening.
For example, try to run www.gopetplan.com/aapo it will redirect to www.gopetplan.com/partners/AAPO by serving static webpage(which is hosted on 3rd party vendor) without changing base URL, though static webpage content getting served from another domain
I currently have a cloudfront that responds with the path /login/<client-A>
and I would like to remove the server header from the response. It's possible?
I tried this code:
function handler(event) {
var response = event.response;
var headers = response.headers;
headers['x-content-type-options'] = { value: 'nosniff'};
headers['x-frame-options'] = {value: 'DENY'};
headers['x-xss-protection'] = {value: '1; mode=block'};
headers['referrer-policy'] = {value: #'same-origin'};
headers['server'] = {value: '*'};
return response;
}
But it only worked when I accessed the cloudfront from the root path /
:
With the path /login/<client>
I got the following result, but it's not what I want:
I have this simple function to route traffic equally to two APIGW as origin. I see few requests works but after that it starts giving me 403. Looks this function is adding same literal multiple times or in some way function cache is messing it up.
function handler(event) {
var randomnumber = Math.floor(Math.random() * 100)
if (randomnumber < 50) {
var reg = 'east'
console.log({
statusCode: 302,
statusDescription: 'Found',
headers: {
"location": {
"value": `https://${event.request.headers.host.value}//${reg}${event.request.uri}`
}
}
})
return {
statusCode: 302,
statusDescription: 'Found',
headers: {
"location": {
"value": `https://${event.request.headers.host.value}//${reg}${event.request.uri}`
}
}
};
}
return event.request
}
Correct redirect url would be something like this -
https://XXXXXXXXXX.cloudfront.net//east/p1/p2/p3/p4
But when it gives 403 and i check function log, i see these as return - (essentially "east" multiple times in path, which explains why i am getting 403). But I am unable to figure out what i am doing wrong. This function is associated with distribution as "Viewer request".
https://XXXXXXXXXX.cloudfront.net//east//east/p1/p2/p3/p4
https://XXXXXXXXXX.cloudfront.net//east//east//east/p1/p2/p3/p4
I am trying to use the "crypto" module in my CF something similar to the example shown below:
but unfortunately I don't seem to get access to other functions like createDecipheriv from the crypto module. I see errors like:
TypeError: (intermediate value)["createDecipheriv"] is not a function
Based on the example above, it looks like the crypto module is this one : https://nodejs.org/api/crypto.html#crypto_crypto.
And yes, the crypto module is required at the top like the example:
var crypto = require('crypto');
Is it not the same crypto module? Could not find any documentation around this module for Cloud functions.
The docs says that is possible to use a rewrite, but there isn't any example on how to achieve that
CloudFront Functions Docs
I tried to change the request, in the test tab it works but no in the cloudfront distribution.
Code:
if (host.startsWith("www.")) {
host = host.replace("www.", "");
request.headers.host.value = host;
return request;
}
Test against distribution URL:
502 ERROR
The request could not be satisfied.
The CloudFront function tried to add, delete, or change a read-only header. We can't connect to the server for this app or website at this time. There might be too much traffic or a configuration error. Try again later, or contact the app or website owner.
If you provide content to customers through CloudFront, you can find steps to troubleshoot and help prevent this error by reviewing the CloudFront documentation.
Some users want to protect against hotlinking. A simple way is checking the value of Referer
header with a CloudFront Functions function. The request will be rejected if the referer is not in the allow list.
There is a Lambda@Edge code example on Github, but no CloudFront Functions code.
The following is my CFF code. It has been tested in my environment and has been deployed in the production of a few customers. I was wondering if it is useful for this project?
//Escape "." character in a string
RegExp.escape = function(string) {
return string.replace(/\./g, '\\.');
};
function handler(event) {
var request = event.request;
var headers = request.headers;
var referrer = headers['referer'];
var response = {
statusCode: 403,
statusDescription: 'Forbidden',
headers: {
'content-type': { 'value': 'text/plain' }
},
body: 'Invalid referrer domain'
};
// Allow requests without the Referer header
if (!referrer) return request;
// Or block requests without the Referer header
// if (!referrer) return response;
var fqdn = referrer['value'].split('/')[2];
// Input your allowed domain name here
var allowedDomains = [
'domain1.com',
'*.domain1.com',
'domain2.com',
'*.domain2.com',
'sub.domain3.com',
'*.sub.domain3.com'
];
var allowedRegexList = [];
//Convert string to regex
for (var i=0; i<allowedDomains.length; i++) {
var domainName = allowedDomains[i];
if (domainName.startsWith('*')) {
domainName = domainName.replace('*', '');
var reg = new RegExp(RegExp.escape(domainName) + '$');
} else {
reg = new RegExp('^' + RegExp.escape(domainName) + '$');
}
allowedRegexList.push(reg);
}
var matchFound = false;
for (var j=0; j<allowedRegexList.length; j++) {
if (fqdn.match(allowedRegexList[j])) {
matchFound = true;
break;
}
}
if (!matchFound) return response;
return request;
}
When will it possible to use cloudformation and/or CDK to deploy cloudfront function ?
I have a Cloudfront distribution created from aws console with a custom domain
Applications work fine except when Refreshing the route, refreshing the app goes to the index.html page
Example:
/apps/user/list route to /apps/index.html
There is a function to validate a JWT token passed in the QueryString, but is there a way to decrypt a cookie as well?
What I am attempting to do is decrypt a cookie, check the information for the user in said cookie and add a header that identifies the user as authenticated.
The function would then add a header to the request identifying the user as such. This would allow us to serve protected content cached at CloudFront as opposed to going deeper into our stack.
I have no idea how to remain querystring on the full url?
Here my now code
function handler(event) {
var request = event.request;
var uri = request.uri;
// Check whether the URI is end with .html
if (uri.includes(".html")) {
var newUri = uri.replace(".html", ""); // Delete .html from URI
var response = {
statusCode: 302,
statusDescription: "Found",
headers: { "location": { "value": newUri } }
};
return response; // Redirect user to new location without .html
}
return request; // Pass normal request to CloudFront
}
Let's say client request with
https://abc.xyz/index.html?region=ap-southeast-1
, So I want to redirect client to
https://abc.xyz/index?region=ap-southeast-1
but when I testing with real CloudFront Functions I got this result instead.
https://abc.xyz/index
This missing my query param. How to solve this?
My organization needs for compliancy a really secure CSP to be on our cloudfront distribution. Currently I host my index.html file on a API gateway instead of our s3 bucket to allow me add nonces to style an scripts blocks.
Lambda function I use on this API gate way is as follows:
const crypto = require('crypto')
const fs = require('fs')
const cheerio = require('cheerio')
exports.handler = (event, context, callback) => {
const $ = cheerio.load(fs.readFileSync('./index.html').toString('utf-8'))
const nonce = crypto.randomBytes(16).toString('base64')
$('script').attr('nonce', nonce)
$('style').attr('nonce', nonce)
$('meta[property="csp-nonce"]').attr('content', nonce)
callback(null, {
statusCode: 200,
headers: {
'Cache-Control': 'no-cache, no-store, must-revalidate',
'Content-Security-Policy': `default-src 'none'; img-src 'self'; script-src 'nonce-${nonce}' 'self'; 'nonce-${nonce}' 'self'; object-src 'none'`,
'Content-Type': 'text/html; charset=utf-8',
'Expires': '0',
'Pragma': 'no-cache',
'Referrer-Policy': 'same-origin',
'Strict-Transport-Security': 'max-age=31536001; includeSubDomains',
'X-Content-Type-Options': 'nosniff',
'X-Frame-Options': 'DENY',
'X-XSS-Protection': '1; mode=block'
},
body: $.html()
})
}
This method is made by @csarkosh you can also read his medium post about this issue he tried to solve:
https://medium.com/@csarkosh/my-experience-getting-an-a-from-mozillas-observatory-tool-on-aws-f0abf12811a1
For my stack it would be really beneficial that cloudfront support adding a nonce to CSP headers natively.
Currently Laravel functions / Lambda edge let you only change viewer requests/viewer response. What I need is to be able to change the body of the origin response.
So that I can:
Scan the HTML file I get from S3 on <script> and <style> blocks
Add via the function a attribute to these blocks ex <style nonce="test">
So what I ask is to:
Response body manipulation from within a CloudFront Function and/or Lambda@Edge function. You fetch the content from cache or the origin and I want to be able to manipulate that response body inside a Cloudfront function before the response is sent to the client.
Edit 12-5-2021:
added more clarification based on comments below.
Is it possible to verify cognito jwt (not simple jwt) with cloudfront function if we hard code public key? I know we can do cognito jwt verification with labda@edge. Built in module crypto has only 6 methods exposed.
There is no example on one of the most frequent use-cases (also one mentioned in the docs as an example usage of CloudFront Functions) of improving caching key and using queryparams.
I've managed to use it to sort values within a comma-separated param, but how do I e.g. sort the queryparams themselves?
It would be helpful for me to have an example of how to redirect naked to www (e.g. requests to domain.com should 301 to www.domain.com). When migrating from lambda@edge to cloudfront functions, some things seemed to change, like the structure of the "event" parameter, and it was a little confusing to me at first.
I'm also not sure if best practice is that it's more efficient/cheaper to use lambda@edge Origin Request for this type of an operation (because it can then be cached for a long time on subsequent requests), or cloudfront function Viewer Request.
Thanks
Can you please consider adding a feature to intercept the response HTML? Something like this:
function handler(event) {
var request = event.request;
var response = event.response;
// meta tags array
// Even better if we can fetch tags from DynamoDB in future?
const tags = [
{ name: “example1.domain.com”, value : “abc”},
{ name: “example2.domain.com”, value : “cuz”}
]
// find meta tags for custom domain
const meta = tags.find(x => x.name == request.host)
// Inject in index.html received from S3
response.content.replace(“<head><\head>”, ‘<head><meta name=“verification” content =${meta.value}<\meta><\head>’
return response;
}
hi guys, my use case is if the response is 404 from S3, to redirect to the original source to create it.
currently if I do
function handler(event) {
var response = event.response;
if (response.statusCode == '404') {
var newResponse = {
statusCode: 302,
statusDescription: 'Found',
headers:{
"location": { "value": "https://alternative-cdn-cname.com/path-to-regenerate-asset/" }
}
}
}
return newResponse;
}
on the response I get
502 ERROR
The request could not be satisfied.
The CloudFront function tried to add, delete, or change a read-only header.
Thanks.
This relates to issue #7 . I've used the code from there and made it more generic and fixed a couple of edge-cases that wouldn't have worked. Please add this to sample library as it's a very common use-case with a non-obvious solution (for those of us not js experts).
function objectToQueryString(obj) {
var str = [];
for (var param in obj)
if (obj[param].value == '')
str.push(encodeURIComponent(param));
else
str.push(encodeURIComponent(param) + "=" + encodeURIComponent(obj[param].value));
return str.join("&");
}
function handler(event) {
var request = event.request;
var uri = request.uri;
var loc = "";
var newdomain = newdomain.com;
if (Object.keys(request.querystring).length)
loc = `https://${newdomain}${uri}?${objectToQueryString(request.querystring)}`
else
loc = `https://${newdomain}${uri}`
var response = {
statusCode: 302,
statusDescription: 'Found',
headers: {
'location': { value: `${loc}` }
}
};
return response;
}
The file path results into an error, it would be great if this can be updated:
fileb://add-cache-control-headers/index.js --> fileb://add-cache-control-header/index.js
aws cloudfront create-function
--name add-cache-control-headers
--function-config Comment="Function to add cache-control headers",Runtime=cloudfront-js-1.0
--function-code fileb://add-cache-control-headers/index.js
Say I have about 2k urls that need to redirect to new locations. My idea is create json file for mapping these urls. However, it seems like cloudfront function not support file access. I also add these mapping urls to the same file with the handler but it fails because the file is so big. What can I do now?
I am using the example "Add true clientIP to request". I have followed the example and deployed a cloudfront example but I am not able to see the ip as part of the request.
function handler(event) {
var request = event.request;
var clientIP = event.viewer.ip;
console.log("test");
//Add the true-client-ip header to the incoming request
request.headers['true-client-ip'] = {value: clientIP};
return request;
Here you have my lambda edge function
'use strict';
exports.handler = (event, context, callback) => {
//Get contents of response
const response = event.Records[0].cf.response;
const headers = response.headers;
//Set new headers
headers['x-frame-options'] = [{key: 'X-Frame-Options', value: 'deny'}];
headers['x-xss-protection'] = [{key: 'X-XSS-Protection', value: '1; mode=block'}];
headers['referrer-policy'] = [{key: 'Referrer-Policy', value: 'same-origin'}];
//Return modified response
callback(null, response);
};
I set it as Viewer Response
in cloudfront
x-frame-options
is appearing, but not x-xss-protection
, and not referrer-policy
The function of add-true-client-ip-header is incomplete.
Nodejs function cannot find the index.handler, because it is not exported.
This behavior is seen in all currently supported runtimes:
Nodejs 14
Nodejs 12
Nodejs 10
Error message is below:
{
"errorType": "Runtime.HandlerNotFound",
"errorMessage": "index.handler is undefined or not exported",
"trace": [
"Runtime.HandlerNotFound: index.handler is undefined or not exported",
" at Object.module.exports.load (/var/runtime/UserFunction.js:144:11)",
" at Object. (/var/runtime/index.js:43:30)",
" at Module._compile (internal/modules/cjs/loader.js:999:30)",
" at Object.Module._extensions..js (internal/modules/cjs/loader.js:1027:10)",
" at Module.load (internal/modules/cjs/loader.js:863:32)",
" at Function.Module._load (internal/modules/cjs/loader.js:708:14)",
" at Function.executeUserEntryPoint [as runMain] (internal/modules/run_main.js:60:12)",
" at internal/main/run_main_module.js:17:47"
]
}
I'm trying to set cookies but the Date object has no toUTCString
function which is required to output the expired timestamp in the correct format for cookies. The documentation says:
All ES 5.1 Date features are supported.
Which should include this function.
See:
I was not able to find any example for modifing or adding a cookie.
We need this feature to set the Client Language via Cookie.
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.