Amplify-console: With CSP enabled vis amplify.yml results in Errors

Created on 23 Apr 2020  路  15Comments  路  Source: aws-amplify/amplify-console

App Id: xyz (sensitive)
Region: us-east-1
Step: Post-Domain Activation
Status: Activated

Note: Do not include information that is sensitive in nature such as your domain name, company etc.

Issue/question
We deployed out app.
Front end is React.
We defined the required CSP policy, but we're seeing the errors attached below:
We're creating the CSP through amplify.yml, but nonce is needed for css and js.
We also tried using a client side csp generation tool (Webpack - csp-html-webpack-plugin) - we are gettin similar errors just like the below.

We're using https://material-ui.com/ with react and amplify - without any luck to get it to work on our domain.

Error message
If there is an error message, please include it here.

Refused to load the stylesheet 'https://www.OURDOMAIN.NET/static/css/main.a4a6a0b0.chunk.css' 
because it violates the following Content Security Policy directive: "style-src self unsafe-inline unsafe-
eval https://www.OUR_DOMAIN.NET/static/css 
https://master.AMPLIFY_INSTANCE.amplifyapp.com/static/css strict-dynamic". Note that 'style-src-
elem' was not explicitly set, so 'style-src' is used as a fallback.

www.OURDOMAIN.NET/:1 Refused to load the script 
'https://www.OUR_DOMAIN.NET/static/js/runtime-main.909f4181.js' because it violates the following 
Content Security Policy directive: "script-src unsafe-inline https://www.OUR_DOMAIN.NET/static/js 
https://cognito-idp.us-east-1.amazonaws.com/ 
https://master.AMPLIFY_INSTANCE.amplifyapp.com/static/js". Note that 'script-src-elem' was not 
explicitly set, so 'script-src' is used as a fallback.

Note: Be sure to check the message for sensitive information.

Additional information
For CSP - we followed https://material-ui.com/styles/advanced/#content-security-policy-csp

feature request pending-customer-response

Most helpful comment

hi.
For future reference and for anyone's who encounters the same problem - this is the solution we've come up with:

  1. we use https://github.com/slackhq/csp-html-webpack-plugin in create-react-app in order to generate a meta tag with the relevant csp policies we need for our product.
  2. we created a postbuild script (in package.json) that extracts this csp meta tag value and creates a new amplify.yml with the correct and latest nonce id's that were created with the build. the amplify.yml.tpl includes a mustache like template where we replace the value of the csp header.

postbuild.js

const fs = require('fs');
const path = require('path');
const DomParser = require('dom-parser');
const parser = new DomParser();

try {
  const html = fs.readFileSync(path.join('./build/index.html'), 'utf8');
  const amplifyYmlTpl = fs.readFileSync(path.join('./scripts/amplify.yml.tpl'), 'utf8');
  const dom = parser.parseFromString(html);
  const cspValue = dom.getElementsByTagName('meta')[0].getAttribute('content');
  const newYml = amplifyYmlTpl.replace('{{cspValue}}', `"${cspValue}"`);

  fs.writeFileSync('./amplify.yml', newYml);
  console.log('Completed generating CSP Successfuly');

} catch (e) {
  console.error('Failed in creating new YML:', e);
}

amplify.tpl.yml

version: 0.1
frontend:
    phases:
        preBuild:
            commands: ['yarn install']
        build:
            commands: ['yarn run build']
    artifacts:
        baseDirectory: build
        files:
            - '**/*'
    cache:
        paths:
            - 'node_modules/**/*'
    customHeaders:
    - pattern: '**/*'
      headers:
      - key: 'Strict-Transport-Security'
        value: 'max-age=31536000; includeSubDomains'
      - key: 'X-Frame-Options'
        value: 'SAMEORIGIN'
      - key: 'X-XSS-Protection'
        value: '1; mode=block'
      - key: 'X-Content-Type-Options'
        value: 'nosniff'
      - key: 'Content-Security-Policy'
        value: {{cspValue}}

All 15 comments

Hi @orizens. Thanks for reaching out. From the docs you linked, it looks like you are required to generate a new nonce in every request from the server middleware. How are you managing to do that? Also, can you see the CSP Header in the response header?

hi @abhi7cr
Previously, we generated nonce during build time (with the csp webpack plugin).

this the CSP response header which we're getting the errors i mentioned:

content-security-policy: default-src self https://www.our-domain.net; script-src unsafe-eval 
unsafe-inline https://www.our-domain.net/static/js https://our-cognito.amazonaws.com/ 
https://master.our-domain.amplifyapp.com/static/js; style-src self unsafe-inline unsafe-eval 
https://www.our-domain.net/static/css https://master.our-domain.amplifyapp.com/static/css 
strict-dynamic; connect-src self https://our-cognito.amazonaws.com/; img-src self  
https://www.our-domain.net; manifest-src self https://www.our-domain.net;

what are we missing?

Hi @orizens. It looks like self, unsafe-eval, and unsafe-inline need to be surrounded by quotes.
You can check here: https://csp-evaluator.withgoogle.com/

That said, I don't think you need the unsafe values in style-src when you use a nonce and JSS adds the nonce to the inline style tags. Not sure if you are using inline script tags in which case you need the unsafe-inline for script-src.
Ref: https://developers.google.com/web/fundamentals/security/csp

is there any example to add nonce on amplify's yml?

Hi @orizens. We don't have an example specifically for nonce. We have a custom headers section example listed here that includes CSP.
I did try the nonce example with my test app and it seemed to work as expected. This is what I did:

  1. Added the customHeaders section in the build.yml as follows:
  customHeaders:
   - pattern: '**/*'
     headers:
      - key: 'Content-Security-Policy'
        value: 'script-src "nonce-EDNnf03nceIOfn39fn3e9h3sdfa"'
  1. Added a test script using the same nonce in my index.html (copied from the google link I provided above on CSP):
  <script nonce="EDNnf03nceIOfn39fn3e9h3sdfa">
     // Some inline code I can't remove yet, but need to asap.
   </script>
  1. Once deployed, I could see the CSP header for my index.html.
  2. Script loaded successfully
  3. Changed the nonce value to not match the CSP value and redeployed
  4. When I loaded the index.html, I received the following error as expected:

image

Hi Abhi,
Nonce is generated by build process; is inserted into index.html and it's different for every build. These nonce string are not available to add to amplify.yml unless build is complete but nonce can't be applied post build via amplify.yml. It seems like amplify.yml is added during build not post-build. Any thoughts?

Hi @sumeet07,

amplify.yml is used during the build. In this case, the index.html would have the nonce, but the CSP header in the yml file can鈥檛 be modified at this time. This is because we don鈥檛 support modifying the amplify yml values at runtime.

I will add this to our backlog and discuss with the team to see how we can address this. Thanks for your patience.

Sounds like no dynamic nonce going to work unless we have hard coded nonce on both index.html and amplify.yml. Is there any other solutions which could work?

@sumeet07 Unfortunately, I don鈥檛 see any other solution apart from the hard coding of nonce, until we have come up with a solution to address the dynamic nature of nonces on our end. We will get back once we have come up with a solution.

Thank you So much!

hi.
For future reference and for anyone's who encounters the same problem - this is the solution we've come up with:

  1. we use https://github.com/slackhq/csp-html-webpack-plugin in create-react-app in order to generate a meta tag with the relevant csp policies we need for our product.
  2. we created a postbuild script (in package.json) that extracts this csp meta tag value and creates a new amplify.yml with the correct and latest nonce id's that were created with the build. the amplify.yml.tpl includes a mustache like template where we replace the value of the csp header.

postbuild.js

const fs = require('fs');
const path = require('path');
const DomParser = require('dom-parser');
const parser = new DomParser();

try {
  const html = fs.readFileSync(path.join('./build/index.html'), 'utf8');
  const amplifyYmlTpl = fs.readFileSync(path.join('./scripts/amplify.yml.tpl'), 'utf8');
  const dom = parser.parseFromString(html);
  const cspValue = dom.getElementsByTagName('meta')[0].getAttribute('content');
  const newYml = amplifyYmlTpl.replace('{{cspValue}}', `"${cspValue}"`);

  fs.writeFileSync('./amplify.yml', newYml);
  console.log('Completed generating CSP Successfuly');

} catch (e) {
  console.error('Failed in creating new YML:', e);
}

amplify.tpl.yml

version: 0.1
frontend:
    phases:
        preBuild:
            commands: ['yarn install']
        build:
            commands: ['yarn run build']
    artifacts:
        baseDirectory: build
        files:
            - '**/*'
    cache:
        paths:
            - 'node_modules/**/*'
    customHeaders:
    - pattern: '**/*'
      headers:
      - key: 'Strict-Transport-Security'
        value: 'max-age=31536000; includeSubDomains'
      - key: 'X-Frame-Options'
        value: 'SAMEORIGIN'
      - key: 'X-XSS-Protection'
        value: '1; mode=block'
      - key: 'X-Content-Type-Options'
        value: 'nosniff'
      - key: 'Content-Security-Policy'
        value: {{cspValue}}

I came across this issue while researching how to create a robust CSP with Amplify and CSS-in-JS solutions like styled-components, emotion, MUI etc.

My understanding is that you should generate a new nonce for each response whereas the solution above suggests doing so at build/deploy time.

The section on use of a nonce in your CSP over on MDN has this to say:

An allow-list for specific inline scripts using a cryptographic nonce (number used once). The server must generate a unique nonce value each time it transmits a policy. It is critical to provide an unguessable nonce, as bypassing a resource鈥檚 policy is otherwise trivial. See unsafe inline script for an example. Specifying nonce makes a modern browser ignore 'unsafe-inline' which could still be set for older browsers without nonce support.

hi @jcf
we're are serverless based product - so - we dont have access to each response.
thanks for reviewing 馃憤

@orizens Amazon have the following article on adding security headers to each response using Lambda@Edge, which leads me to believe this problem can be solved in a serverless architecture:

https://aws.amazon.com/blogs/networking-and-content-delivery/adding-http-security-headers-using-lambdaedge-and-amazon-cloudfront/

All the best.

thank you so much @jcf

Was this page helpful?
0 / 5 - 0 ratings