Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Nonce doesn't change per request in production build #541

Closed
brewaa opened this issue Oct 29, 2024 · 31 comments · Fixed by #547 or #532
Closed

Nonce doesn't change per request in production build #541

brewaa opened this issue Oct 29, 2024 · 31 comments · Fixed by #547 or #532
Assignees
Labels
enhancement New feature or request question Further information is requested

Comments

@brewaa
Copy link

brewaa commented Oct 29, 2024

Hi,

I've added a CSP but I'm having an issue with nonces not changing for a production deployment.

Running npm run dev the nonce changes per request.

image

Running npm run build , the nonce doesn't change. It's always this:

image

This happens in my local environment build and in production build which is Cloudflare Pages.

I tried a couple of versions both having the same result:
nuxt-security: 2.0.0-rc.9
nuxt-security: v2.0.0
nuxt: 3.12.3

The CSP I'm using is the same as listed on the FAQ page for Cloudflare config (https://nuxt-security.vercel.app/documentation/advanced/faq)

Is this a known issue and maybe I've missed a configuration?

@brewaa brewaa added the question Further information is requested label Oct 29, 2024
@vejja
Copy link
Collaborator

vejja commented Oct 29, 2024

This is strange
It looks like node:crypto is not working correctly here. There is a Cloudflare setting for this, so it could explain the issue when deployed in production.

But the very strange part is that you said this happens in your local environment build. Not normal at all.
How do you build locally?

@brewaa
Copy link
Author

brewaa commented Oct 29, 2024

Hi @vejja,

Thanks for the quick response.

I just run npm run build.

Then preview the build with npx wrangler pages dev dist/

If there is something I can do in Cloudflare to get node:crypto working then that would be great.

@vejja
Copy link
Collaborator

vejja commented Oct 29, 2024

https://developers.cloudflare.com/workers/runtime-apis/nodejs/crypto/
I think they say you need the nodejs_compat_v2 flag

@brewaa
Copy link
Author

brewaa commented Oct 29, 2024

No luck unfortunately.

I tried both nodejs_compat and nodejs_compat_v2 with a compatibility date of 2024-09-23 or later but the nonce is still the same.

@vejja
Copy link
Collaborator

vejja commented Oct 29, 2024

Locally, what if you npm run build and then node .output/server/index.mjs?
Do you have a nonce?

@brewaa
Copy link
Author

brewaa commented Oct 29, 2024

node .output/server/index.mjs gives output Listening http://[::]:3000

Do I need to configure a host IP somehow?

@vejja
Copy link
Collaborator

vejja commented Oct 29, 2024

Just open Chrome on localhost:3000

@brewaa
Copy link
Author

brewaa commented Oct 29, 2024

Yeah I tried that and strangely I'm seeing a really old version of the website that appears to not have nuxt-security at all on http://localhost:3000/.

No idea how that can happen. npm run dev / build is showing the latest version which is a relief.

@vejja
Copy link
Collaborator

vejja commented Oct 29, 2024

wow this is becoming really tough to debug 😂

  • do you have PWA?
  • do you have a zombie process listening on port :3000?
  • rm -rf node_modules
  • npm install
  • give us your wrangler.toml
  • give us your nuxt.config.ts

@brewaa
Copy link
Author

brewaa commented Oct 29, 2024

  • No PWA
  • No zombie process (restarted machine)
  • Deleted node_modules and reinstalled

nuxt.config.ts

> // https://nuxt.com/docs/api/configuration/nuxt-config
> export default defineNuxtConfig({
>   app: {
>     head: {
>       charset: 'utf-8',
>       viewport: 'width=device-width, initial-scale=1',
>     },
>   },
> 
>   debug: false,
> 
>   devtools: { 
>     enabled: false 
>   },
> 
>   gtag: {
>     id: '',
>     // loadingStrategy: 'async'
>   },
> 
>   image: {
>   },
> 
>   nitro: {
>     preset: "cloudflare-pages",
>   },
> 
>   modules: [
>     "@nuxtjs/sitemap",
>     "nitro-cloudflare-dev",
>     "@nuxtjs/robots",
>     ["nuxt-gtag", { enabled: process.env.NODE_ENV === 'production' }],
>     "@nuxt/image",
>     "nuxt-security"
>   ],
> 
>   pages: true,
> 
>   routeRules: {
>     // Don't add any /secret/** URLs to the sitemap.xml E.g
>     // '/secret/**': { robots: false },
>   },
> 
>   runtimeConfig: {
>     // The private keys which are only available within server-side
>     myPrivateKey: 42,
>     // Keys within public, will be also exposed to the client-side
>     public: {
>       gtagId: '',
>       pageSize: 20,
>     }
>   },
> 
>   security: {
>     headers: {
>       // crossOriginEmbedderPolicy: process.env.NODE_ENV === 'development' ? 'unsafe-none' : 'require-corp',
>       crossOriginEmbedderPolicy: 'unsafe-none',
>       contentSecurityPolicy: {
>         'img-src': ["'self'", 'data:'],
>         'script-src': [
>           "'self'",
>           'https:',
>           "'unsafe-inline'",
>           "'strict-dynamic'",
>           "'nonce-{{nonce}}'",
>           "'unsafe-eval'"
>         ]
>       },
>     },
>   },
> 
>   site: {
>     defaultLocale: 'en', // not needed if you have @nuxtjs/i18n installed
>   },
> 
>   sitemap: {
>     credits: false,
>     exclude: [
>       '/secret/**'
>     ],
>     sitemaps: {
>       pages: {
>         includeAppSources: true
>       }
>     }
>   },
> 
>   ssr: true,
> 
>   compatibilityDate: "2024-07-03"
> })

wrangler.toml

#:schema node_modules/wrangler/config-schema.json
name = ""
compatibility_date = "2024-09-24"
pages_build_output_dir = "./dist"
compatibility_flags = [ "nodejs_compat_v2" ]

# Bind a D1 database. D1 is Cloudflare’s native serverless SQL database.
# Docs: https://developers.cloudflare.com/pages/functions/bindings/#d1-databases
[[d1_databases]]
binding = "DB"
database_name = ""
database_id = ""

Thanks for all the help so far.

@vejja
Copy link
Collaborator

vejja commented Oct 29, 2024

ok, remove nitro: preset from nuxt.config.ts, then npm run build then node ./output/server/index.mjs
open in Chrome, can you see the nonce ?

@brewaa
Copy link
Author

brewaa commented Oct 29, 2024

Yes! That worked. I can see a nonce now.

What are the ramifications of removing the nitro preset?

Is that required for Cloudflare Pages module "nitro-cloudflare-dev"?

@vejja
Copy link
Collaborator

vejja commented Oct 29, 2024

What are the ramifications of removing the nitro preset?

I'm not 100% sure myself 😉
I'm just checking that everything works correctly in the local build, which is the case.

I'm not a Cloudflare expert, so I don't have a good knowledge of their terminology - but here is the basic stuff you need to know:

  • nonce require SSR
  • SSR requires a physical runtime (infrastructure) and an executable
  • the executable (aka Nitro) is provided by Nuxt when you do npm run build
  • Cloudflare has the infrastructure on Cloudflare Workers
  • Nitro has presets for Cloudflare Workers to be able to run the Nitro executable
  • Cloudflare Workers only support node:crypto with the special flag

I have no first-hand knowledge of how to make this whole stack run on Cloudflare, but my 2 cents are 'cloudflare_pages' probably stands for 'a CDN that can only deliver static files with no runtime' while 'cloudflare_module' probably means 'a full runtime environment that is able to run the Nitro executable'.
That's why I asked you to remove the nitro preset...

Now for better advice you should look here and here.
The first one is Nuxt's method to export its stack into Cloudflare. The second one is Cloudflare's method to import the Nuxt stack. Either one should work.

@brewaa
Copy link
Author

brewaa commented Oct 29, 2024

Thank you for everything you have done so far.

Unfortunately, the nonce still didn't work in Cloudflare production but we've clearly made some progress so that's great!

I must say, you are an absolute legend @vejja .

This is the best support I've received in the last 25 years in IT and it's from an open source project for no $$$.

I really appreciate and acknowledge your dedication and perseverance in trying to resolve this for me.

I'll look at this again tomorrow with fresh eyes on the last two links you provided.

Thanks again!

@vejja
Copy link
Collaborator

vejja commented Oct 29, 2024

Thank you for your kind words @brewaa !
I'm actually concerned that under some kind of obscure combination of modules and deployment presets, we allow a static bogus nonce value to be generated. This would in fact be a breach of the RFC.

I did have a deeper look in the meantime and I would greatly appreciate if you could try the following:

  • Reset nitro.preset to cloudflare_pages (your initial value)
  • Remove the 'nitro-cloudflare-dev' entry in modules (I think this one is messing up with something)
  • Make sure your wrangler.toml has the right compat flags (as in your example above)
  • Build with npm run build (your initial value)
  • Deploy with npx wrangler pages deploy dist/ (don't use 'dev' but 'deploy' instead)

Would be extremely interested in knowing if it works 🙏🏻

@vejja
Copy link
Collaborator

vejja commented Nov 11, 2024

Hi @brewaa
Can you let me know if you managed to make it work?

@brewaa
Copy link
Author

brewaa commented Nov 11, 2024

Hi @vejja,

Sorry I hadn't got back to this yet.

I'll create a new repo to test this now.

@brewaa
Copy link
Author

brewaa commented Nov 11, 2024

Hi @vejja,

I've created a small reproduction repo, tried all of your suggestions and deployed using npx wrangler pages deploy dist/ but I'm still seeing nonce="AAAAAAAAAAAAAAAAAAAAAA==".

The new create-cloudflare CLI (C3) creates a wrangler.toml with the compatibility_flags already added which is nice.

compatibility_date = "2024-11-06"
compatibility_flags = ["nodejs_compat"]

Unfortunately, not helpful for my situation it seems.

@brewaa
Copy link
Author

brewaa commented Nov 11, 2024

The nonces are still changing correctly in local dev environment. npm run dev

Do you want access to the repository?

@vejja
Copy link
Collaborator

vejja commented Nov 11, 2024

Yes that would be great

@vejja vejja self-assigned this Nov 11, 2024
@vejja vejja linked a pull request Nov 11, 2024 that will close this issue
6 tasks
@vejja
Copy link
Collaborator

vejja commented Nov 11, 2024

I’m proposing a change that would supposedly avoid this whole nodejs_compat issue, but some real-life testing would be great.
If you can test the branch locally and confirm whether it works that would be super helpful !
Cheers

@vejja vejja mentioned this issue Nov 12, 2024
@vejja
Copy link
Collaborator

vejja commented Nov 12, 2024

@brewaa Thanks for the repo link
I wasn't able to find the appropriate configuration for Cloudflare but I'm no expert so maybe I'm missing something.

However I used your repo to test the changes introduced by PR #547 and I can confirm it now works on Cloudflare, the nonce is there as expected.
You should be able to use it as soon as we release v2.1.0

@Baroshem: With this change, Cloudflare users don't need to think about setting a nodejs_compat flag anymore, which I think is much safer. Most people are unaware of the subtleties of API availability and compatibility issues between Node and Workers, so it's better if Nuxt-Security can work out-of-box in all deployment environments.

@brewaa
Copy link
Author

brewaa commented Nov 12, 2024

Hi @vejja ,

I'd love to be able to help test, I'm just not sure how I go about it.

Is there some way I can use the new branch and somehow get that into my test repo?

@vejja
Copy link
Collaborator

vejja commented Nov 12, 2024

Hi @vejja ,

I'd love to be able to help test, I'm just not sure how I go about it.

Is there some way I can use the new branch and somehow get that into my test repo?

Here is how I did it manually:

  1. Clone the last 2.1.0 branch of Nuxt-Security, then build it with yarn prepack. This produces a distributable under the dist/ folder
  2. Clone your repo, then npm install it
  3. Drop the 2.1.0 Nuxt Security distributable of step 1 into the node_modules/nuxt-security folder of step 2 to replace Nuxt-Security with the latest build
  4. npm run build and then deploy to Cloudflare with the wrangler command

I wish there was an easier way to do it... but you can probably use the same procedure on your end.

@Baroshem: if you think this would be useful for those of our users who want to test 2.1, I can release an RC version on npm. Let me know

@brewaa
Copy link
Author

brewaa commented Nov 12, 2024

Sorry @vejja.

I'm getting:

'nuxt-module-build' is not recognized as an internal or external command

What do I need to install?

@vejja
Copy link
Collaborator

vejja commented Nov 12, 2024

did you yarn first?
Alternatively you can also use npm install and npm run prepack if you prefer

@brewaa
Copy link
Author

brewaa commented Nov 12, 2024

Ahh yes. Thanks. No I didn't yarn first.

I've deleted everything and started again.

Yarn'd first then did yarn prepack

Receiving error:

failed to resolve "extends":"./.nuxt/tsconfig.json" in C:\Repo\nuxt-security\tsconfig.json

I can't see a .nuxt folder in the nuxt-security repo.

If this is just me, understandable if this is not worth pursuing, I don't want to take up too much of your time. I'm really happy with the effort you've put in so far and I'd be happy to wait to the 2.1.0 release. Thanks for your help.

@vejja
Copy link
Collaborator

vejja commented Nov 12, 2024

sorry it's me, I think you probably need to yarn dev:prepare before prepacking to generate the tsconfig stubs

@brewaa
Copy link
Author

brewaa commented Nov 12, 2024

YES!!!

That worked @vejja !!

You are the best!

I can confirm nonces are working in Cloudflare with chore/2.1.0.

https://nuxt-security-test.pages.dev/

@vejja vejja added the enhancement New feature or request label Nov 12, 2024
@Baroshem
Copy link
Collaborator

Awesome, let's plan to release 2.1.0 with this change then! :)

Thanks for the amazing work @vejja !

@vejja
Copy link
Collaborator

vejja commented Nov 14, 2024

@pi0 I'm flagging you here on this thread as I think we might have uncovered an edge case here - we would need your advice.

OP is deploying on Cloudflare using preset cloudflare-pages, and discovers that the crypto functions are not working.
Our module uses node:crypto under the hood, but Node APIs are not available in Cloudflare Workers, unless the nodejs_compat flag is set on Cloudflare. So we tell Cloudflare users to set that flag.
But it doesn't work...

I looked at the nitro server code generated by npm run build and it looks like all the node:crypto API calls are replaced by empty statements with a comment that the API is unsupported.
In other words, it looks like the nitro bundler detects that Node is not supported in Workers, and discards the Node API calls. Which prevents us from using legit Node API calls within Workers where the Node API was enabled manually.

I might be completely wrong, but from the surface of it, it looks like an unjs/unbuild issue. Leaving it up to you here.
FYI we are circumventing the problem by moving our codebase to never use the Node APIs in nitro, which solves the issue on our end from a functional perspective.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request question Further information is requested
Projects
None yet
3 participants