Skip to content
This repository has been archived by the owner on Jan 11, 2023. It is now read-only.

Make Content Security Policy (CSP) nonce available in template.html #1248

Closed
martinburger opened this issue Jun 2, 2020 · 4 comments
Closed

Comments

@martinburger
Copy link

Is your feature request related to a problem? Please describe.

I would like to use Content Security Policy (CSP) nonces in template.html. For instance:

<script nonce="rAnd0m123" src="/lib/bootstrap-4.5.0/js/bootstrap.min.js"></script>

That way, I would not have to include 'self' in script-src anymore. According to Google's CSP Evaluator, "'self' can be problematic if you host JSONP, Angular or user uploaded files."

Describe the solution you'd like

I would like to use something like %sapper.cspnonce% in my template.html file:

<script nonce="%sapper.cspnonce%" src="/lib/bootstrap-4.5.0/js/bootstrap.min.js"></script>

Sapper would replace %sapper.cspnonce% with the value of res.locals.nonce, if any.

Describe alternatives you've considered

I could not find any way to include the nonce injected via res.locals = { nonce: uuidv4() } in server.js.

How important is this feature to you?

I would like to maximize security for the users of my Sapper app.

Additional context

See Content Security Policy (CSP) in the Sapper documentation for more details on that approach.

@martinburger martinburger changed the title Make Content Security Policy (CSP) nonces available in template.html Make Content Security Policy (CSP) nonce available in template.html Jun 2, 2020
@Conduitry
Copy link
Member

This has been added in 0.28.9.

@evdama
Copy link

evdama commented Sep 26, 2020

I've upgraded and want to use it. I've two script tags in my template.html, the first one gets the nounce, but not the second one... output from chrome devtools below:
Screen Shot 2020-09-26 at 08 44 17

Here's my template html.html snippet with two times nonce="%sapper.cspnonce%"

    <script src="https://www.gstatic.com/firebasejs/7.21.1/firebase-app.js"></script>
    <script src="https://www.gstatic.com/firebasejs/7.21.1/firebase-auth.js"></script>
    <script src="https://www.gstatic.com/firebasejs/7.21.1/firebase-firestore.js"></script>
    <script src="https://www.gstatic.com/firebasejs/7.21.1/firebase-storage.js"></script>
    <script src="https://www.gstatic.com/firebasejs/7.21.1/firebase-messaging.js"></script>
    <script src="https://www.gstatic.com/firebasejs/7.21.1/firebase-performance.js"></script>
    <script src="https://www.gstatic.com/firebasejs/7.21.1/firebase-analytics.js"></script>
    <script nonce="%sapper.cspnonce%" src="./firebase-init.js"></script>

    <script src="https://cdn.jsdelivr.net/npm/gun@0.2020.520/gun.js"></script>
    <script src="https://cdn.jsdelivr.net/npm/gun@0.2020.520/nts.js"></script>
    <script src="https://cdn.jsdelivr.net/npm/gun@0.2020.520/sea.js"></script>
    <script src="https://cdn.jsdelivr.net/npm/gun@0.2020.520/lib/radix.js"></script>
    <script src="https://cdn.jsdelivr.net/npm/gun@0.2020.520/lib/radisk.js"></script>
    <script src="https://cdn.jsdelivr.net/npm/gun@0.2020.520/lib/store.js"></script>
    <script src="https://cdn.jsdelivr.net/npm/gun@0.2020.520/lib/rindexed.js"></script>
    <script src="https://cdn.jsdelivr.net/npm/gun@0.2020.520/lib/then.js"></script>
    <script src="https://cdn.jsdelivr.net/npm/gun@0.2020.520/lib/webrtc.js"></script>
    <script nonce="%sapper.cspnonce%" src="./gun-init.js"></script>

And here the snippet from my server.js creating the nounce

const helmetMiddleware = ( request, response, next ) => {
    helmet({
      contentSecurityPolicy: {
        directives: {
          defaultSrc: ["'self'"],
          connectSrc: [ 'api.stripe.com', 'hooks.stripe.com' ],
          frameSrc: [ 'js.stripe.com', 'hooks.stripe.com' ],
          imgSrc: [ '*.stripe.com' ],
          scriptSrc: [ "'self'", 'js.stripe.com', ( request, response ) => `'nonce-${ response.locals.nonce }'` ],
        },
        browserSniff: false,
      },
    })
  }

Seems the first appearance of nonce="%sapper.cspnonce%" is replaced just fine with the actual nounce but not the second one and so on?! Can anyone confirm?

@martinburger also, if I understand correctly I could get rid of the self in my scriptSrc line now no?
scriptSrc: [ 'js.stripe.com', ( request, response ) => `'nonce-${ response.locals.nonce }'` ]
instead of
scriptSrc: [ "'self'", 'js.stripe.com', ( request, response ) => `'nonce-${ response.locals.nonce }'` ]

@mcmxcdev
Copy link

@evdama I am quite sure it is only getting replaced once because String.replace() only replaces the first occurrence:

.replace('%sapper.cspnonce%', () => nonce_value);

The code should look something like this imo: .replace(/%sapper.cspnonce%/g, () => nonce_value);

Regarding the self in scriptSrc:
the files ./firebase-init.js and ./gun-init.js are part of your own domain, so you need self

@benmccann
Copy link
Member

I've filed a bug to track that issue: #1565. If anyone wants to send a PR for it I will help review it

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

No branches or pull requests

5 participants