Code Monkey home page Code Monkey logo

amazon-cloudfront-functions's Issues

Can AWS Parameter Store variables or secrets be used in CloudFront functions?

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.

Unable to use CloudFront Function "Test" Feature with host.value

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.

Reproducible Example

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

Works in Practice

Oddly enough, the host value is evaluated correctly when CloudFront executes the function. Just seems to be an issue in the Test sandbox.

Functions are not executed when CloudFront returns an error from S3

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.

URL redirect/rewrite

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

Using cloudfron functions to remove server header

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 /:

Screen Shot 2021-12-09 at 13 30 00

With the path /login/<client> I got the following result, but it's not what I want:

Screen Shot 2021-12-09 at 13 31 15

url redirect adding multiple path

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

Use of crypto module?

I am trying to use the "crypto" module in my CF something similar to the example shown below:

return crypto.createHmac(method, key).update(input).digest('base64url');

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.

How to make a rewrite?

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 in cloudfront Function:
Screenshot 2022-11-29 at 11 33 16

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.

anti-hotlinking with referer header

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;
}

Decrypt a Cookie

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.

Cut the .html from uri but remain querystring

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?

Security header CSP: add nonce support

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.

Verify cognito jwt with cloudfront function

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.

Improving caching key

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?

Redirect naked to www example

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

Response interceptor

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;
}

redirect on 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.

  1. is there a way to create a redirect (edit location header) on the response?
  2. can lambda solve this?

Thanks.

Redirect preserve query string

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;
}

Additional "s" in the folder path in the following command

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

How can I redirect with a huge list of urls?

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?

Unable to get clientIP

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;

}
image

x-frame-options works but not x-xss-protection

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
image

and the result:
image

x-frame-options is appearing, but not x-xss-protection, and not referrer-policy

add-true-client-ip-header Runtime.HandlerNotFound

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"
]
}

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.