Baroshem / nuxt-security

🛡 Automatically configure your app to follow OWASP security patterns and principles by using HTTP Headers and Middleware
https://nuxt-security.vercel.app/
MIT License
808 stars 56 forks source link

Nonce attributes values are erased during application boot - SSR mode #427

Closed BnitoBzh closed 2 months ago

BnitoBzh commented 5 months ago

Version

nuxt-security: 1.3.2 nuxt: 3.11.2

Reproduction Link

Nothing

Steps to reproduce

Nothing

What is Expected?

Nuxt must render the page in SSR mode with all nonce attributes set on each script tag.

What is actually happening?

When Nuxt render the page in SSR mode, all nonce attributes are set. i am using the browser utlity "show the page source", this is OK ! But when the page is fully loaded and the Nuxt app is running, the developer tools shows that all nonce attribute values are empty ... This cause multiple issue with CSP ...

Here is my nuxt config file :

// https://nuxt.com/docs/api/configuration/nuxt-config

export default defineNuxtConfig({
    devtools: {enabled: process.env.NODE_ENV !== 'production'},
    ssr: process.env.NODE_ENV === 'production',

    runtimeConfig: {
        public: {
            contentfulSpaceId: process.env.CONTENTFUL_SPACE_ID,
            contentfulAccessToken: process.env.CONTENTFUL_ACCESS_TOKEN,
            contentfulHomePageId: process.env.CONTENTFUL_HOME_PAGE_ID,

            gtm: {
                id: process.env.GTM_ID!,
                /*queryParams: {
                    gtm_auth: '',
                    gtm_preview: '',
                    gtm_cookies_win: '',
                },*/
                defer: false,
                compatibility: false,
                // nonce: '2726c7f26c',
                enabled: process.env.NODE_ENV === 'production',
                debug: process.env.NODE_ENV !== 'production',
                loadScript: false,
                enableRouterSync: true,
                ignoredViews: [],
                trackOnNextTick: false,
                devtools: true,
            }
        },
    },

    modules: [
        '@zadigetvoltaire/nuxt-gtm',
        '@nuxtjs/tailwindcss',
        'nuxt-security',
    ],

    tailwindcss: {
        exposeConfig: true,
        viewer: true,
    },

    app: {
        head: {
            charset: 'utf-8',
            viewport: 'width=device-width, initial-scale=1',
            script: [
                ...(process.env.NODE_ENV === 'production' ? [
                    // GTM
                    {
                        type: 'text/javascript',
                        innerHTML: "window.dataLayer = window.dataLayer || [];\r\nwindow.dataLayer.push({'gtm.start': new Date().getTime(), event: 'gtm.js'});"
                    },
                    {
                        src: `https://www.googletagmanager.com/gtm.js?id=${process.env.GTM_ID}`,
                        type: 'text/javascript',
                        async: true,
                    },
                    // Onetrust
                    {
                        src: `https://cdn.cookielaw.org/consent/${process.env.ONETRUST_ID}/otSDKStub.js`,
                        type: 'text/javascript',
                        'data-domain-script': process.env.ONETRUST_ID,
                    },
                    {
                        type: 'text/javascript',
                        innerHTML: 'function OptanonWrapper() { }'
                    }
                ] : [])
            ],
        }
    },

    security: {
        enabled: process.env.NODE_ENV === 'production',
        ssg: {
            meta: true, // Enables CSP as a meta tag in SSG mode<
            hashScripts: true, // Enables CSP hash support for scripts in SSG mode
            hashStyles: false // Disables CSP hash support for styles in SSG mode (recommended)
        },
        sri: true,
        nonce: true,
        headers: {
            crossOriginResourcePolicy: false,
            crossOriginOpenerPolicy: false,
            crossOriginEmbedderPolicy: false,// process.env.NODE_ENV === 'development' ? 'unsafe-none' : 'require-corp',
            contentSecurityPolicy: {
                'default-src': [
                    "'self'",
                    "'strict-dynamic'",
                    "'unsafe-inline'",
                    "'nonce-{{nonce}}'",
                    "'unsafe-eval'",
                    'https://*.ctfassets.net',
                    'https://*.contentful.com',
                    'use.typekit.net',
                    'https://*.googletagmanager.com',
                    'https://tagmanager.google.com',
                    'https://*.google-analytics.com',
                    'https://*.analytics.google.com',
                    'https://ssl.gstatic.com',
                    'https://www.gstatic.com',
                    'https://fonts.googleapis.com',
                    'https://cdn.cookielaw.org',
                    'https://geolocation.onetrust.com',
                ],
                'script-src-elem': [
                    "'self'",
                    "'strict-dynamic'",
                    "'nonce-{{nonce}}'",
                    'https://*.ctfassets.net',
                    'https://*.contentful.com',
                    'https://*.googletagmanager.com',
                ],
                'frame-ancestors': [
                    "'self'",
                    'https://app.contentful.com'
                ],
                'font-src': [
                    "'self'",
                    "'nonce-{{nonce}}'",
                    'data://*',
                    'use.typekit.net',
                    'fonts.gstatic.com',
                    'https://fonts.googleapis.com',
                ],
                'upgrade-insecure-requests': true,
                'img-src': false,
                'script-src': false,
            }
        },
    },
})
vejja commented 5 months ago

Hi @BnitoBzh

There are many small things that you should double-check in your configuration. For instance font-src is not nonceable, setting CSP via script-src-elem instead of script-src is likely to block some script-src-attr, and setting open values to default-src is generally not recommended. Also you are loading the GTM script both via modules and via app.head unless I'm mistaken. There might be good reasons for all of these but just wanted to flag these first.

Aside from these, and in order to keep issues separate, can you do the following:

BnitoBzh commented 5 months ago

@vejja, thanks for the CSP config issue I will make changes and test. For the GTM script it must be loaded by the app.head in order to load it in the <head> section and not at the end of the <body> section. That is why the gtm.loadScript option is set to false, the nuxt-gtm module doesn't manage the script load. In Chrome and Firefox everything works fine (although the empty nonce issue is still there), it doesn't just work on Safari.

vejja commented 5 months ago

Could you let me know what you mean by 'empty nonce' ? If your scripts are inserted by the client-side this is absolutely normal because only the server-side can insert nonces. But it shouldn't prevent your scripts from loading if you use strict-dynamic.

Please provide screenshots of the Safari complaints. Maybe the issue is due to how you manage the onload event handler, if required. Because your GTM script is inserted twice, it's hard to understand where the issue is coming from. I would first try to disable the module entry if it doesn't manage anything.

I can see that you are using Nuxt 3.11.2 : you could use the new useScript composable, which deals with all the hassle of loading external scripts.

BnitoBzh commented 5 months ago

Here are two examples : From the server response (with nonce) from-server From the dev toolbar, after load : from-developer-tools

vejja commented 5 months ago

The dev toolbar in the 'Elements' section does not display the nonce for security reasons You can only see them in the raw server response in the Networks' Response tab

BnitoBzh commented 5 months ago

Hum ok .. So do you have any idea why my CSP are ignored in Safari ?

vejja commented 5 months ago

I can try to help if you send me a screenshot of the Console and Network Headers tabs of the Safari devtools

Baroshem commented 5 months ago

@BnitoBzh any details from your side? :)

BnitoBzh commented 5 months ago

I have removed all nonce configurations and I use the forced declarations instead.

vejja commented 5 months ago

@BnitoBzh I think this might actually be related to #432 I can see that you have { 'script-src': false, 'img-src': false } and another user reported that setting boolean values to CSP directives erased the nonces from the headers. If you don't use boolean values in contentSecurityPolicy, would it fix the problem ?

vejja commented 5 months ago

Hi @BnitoBzh , we fixed #432 in today's release. Would you be able to upgrade to 1.4.2 to check if it solves your issue also ?

BnitoBzh commented 5 months ago

Sorry, not enough time to test it now, i will test it in the next week.

Baroshem commented 2 months ago

Closing the ticket as the issue was resolved. If there is a need to reopen, please let me know :)