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

Vite: Adding nonce in CSP response header and replace the nonce in index.html #751

Open
melloware opened this issue Sep 3, 2024 Discussed in #750 · 12 comments
Open

Vite: Adding nonce in CSP response header and replace the nonce in index.html #751

melloware opened this issue Sep 3, 2024 Discussed in #750 · 12 comments
Labels
enhancement New feature or request

Comments

@melloware
Copy link
Contributor

melloware commented Sep 3, 2024

Discussed in #750

Originally posted by bdevos September 3, 2024
I am currently transitioning a React frontend application to become part of the Quarkus backend, instead of using a separate Nginx container exclusively for the frontend.

The integration with Quarkus Quinoa seems really promising and it's been great for development thus far.

However, I encountered a challenge when attempting to add a nonce that is necessary for our Content Security Policy. I have implemented similar solutions in other applications and I'm conversant with the entire process when incorporating the frontend in a standalone Nginx container. Yet, I am unable to figure out how to implement this with Quinoa.

What I seek is a way to filter the request that is handling index.html. Once that's done, I should be able to insert the nonce into the CSP response header and modify the HTML content set to return in the response.

I've tried several filtering methods but none appear to give me access to the requests that Quinoa handles.

As the nonce is a requirement under my company's policy, skipping it isn't an option. I wonder if there's a simpler approach I might have overlooked. Alternatively, would it be smarter to run a development server with Quarkus Quinoa, and then build an Nginx image for the frontend during the release stage?

Vite: Its documentation for the nonce support is rather minimal, but works as expected: https://vitejs.dev/guide/features#nonce-random

In Vite you can configure a nonce, but in order for that nonce to have any effect, it needs to be randomised on every response.

When in the Vite config you set:

html: {
  cspNonce: 'NONCE_PLACEHOLDER',
},

The Vite build output in index.html will contain it in the script tag like this:

<script type="module" crossorigin src="/assets/index-DYyGYer9.js" nonce="NONCE_PLACEHOLDER"></script>

What I do now with other applications, is that I replace that NONCE_PLACEHOLDER with a random nonce on every request before sending the response to the user. To make it all work the same random nonce has to be included in the CSP response header.

@melloware melloware added the enhancement New feature or request label Sep 3, 2024
@melloware
Copy link
Contributor Author

@ia3andy there must be some clever way to add a RequestFilter on index.html to replace tokens?

We could maybe have :

quarkus.quinoa.nonce.token: NONCE_PLACEHOLDER

Which if detected would add a randomly generated NONCE UUID to the response.header as well as filter the index.html and replace that NONCE_PLACEHOLDER with the same generated UUID?

@bdevos
Copy link

bdevos commented Sep 3, 2024

@ia3andy there must be some clever way to add a RequestFilter on index.html to replace tokens?

We could maybe have :

quarkus.quinoa.nonce.token: NONCE_PLACEHOLDER

Which if detected would add a randomly generated NONCE UUID to the response.header as well as filter the index.html and replace that NONCE_PLACEHOLDER with the same generated UUID?

The solution would also need some way to replace the random generated nonce value in the CSP header itself. This header can become quite complex and the nonce will probably only be a part of the value.

So I would imagine a way to replace the nonce placeholder when the response header is set in the properties:

quarkus.http.filter.index.header.Content-Security-Policy=img-src 'self' data:; object-src 'none'; script-src 'nonce-$NONCE_PLACEHOLDER' 'report-sample'; connect-src 'self' https://browser-intake-datadoghq.eu; style-src 'nonce-$NONCE_PLACEHOLDER'

@melloware
Copy link
Contributor Author

melloware commented Sep 3, 2024

@bdevos can you update this bare bones Vite Quinoa starter to have the CSP stuff in it (of course it will fail) so we have a test case.

Minimal Vite Quinoa: vite-csp.zip

@bdevos
Copy link

bdevos commented Sep 3, 2024

I extended the bare bones starter with a vite config and the CSP response headers to test it. I hope it is OK that I made it available on Github: https://github.com/bdevos/vite-csp

The changes are, this vite config: https://github.com/bdevos/vite-csp/blob/main/src/main/webui/vite.config.js

And the CSP headers are in the properties: https://github.com/bdevos/vite-csp/blob/main/src/main/resources/application.properties

I added some comments in the application.properties because you can actually test the NONCE working when changing which CSP value is provided.

@melloware
Copy link
Contributor Author

excellent!

@melloware melloware changed the title Adding nonce in CSP response header and replace the nonce in index.html Vite: Adding nonce in CSP response header and replace the nonce in index.html Sep 3, 2024
@melloware
Copy link
Contributor Author

I don't know how to do this but it sounds like we need to:

  1. Intercept the index.html on the way out of vert.X and do a replacement on the CSP_NONCE.
  2. Inercept the response.getHeaders() and replace the CSP Policy CSP_NONCE.

@ia3andy
Copy link
Collaborator

ia3andy commented Sep 5, 2024

I think this is out of the scope of Quinoa, this is server-side-rendering.

I would suggest to give a try to the web-bundler where the index.html can actually be rendered using Qute.

If you want to stick to Quinoa, then you should be able to introduce a vertx filter in your app code and tranform the index.html content in the response.

@ia3andy
Copy link
Collaborator

ia3andy commented Sep 5, 2024

@bdevos
Copy link

bdevos commented Sep 5, 2024

Thanks! I thought I had explored all my options, but I didn't know a Vert.x filter was possible. I will give it a try and report back on whether I was successful.

@melloware
Copy link
Contributor Author

If it is successful let us know I think we should add to docs as Vite is so popular and CSP is a good thing.

@melloware
Copy link
Contributor Author

@bdevos make any progress?

@bdevos
Copy link

bdevos commented Sep 15, 2024

@melloware I haven't been able to figure out how to read the Vite-generated index.html in the route observer.

public void init(@Observes Router router) {
  router.route(HttpMethod.GET, "/").handler(ctx -> {
    // Have to access the body of index.html so I can replace the NONCE_PLACEHOLDER
    ctx.next();
  });
}

The problem is that I cannot read the body from the ctx. I could maybe delay my route observer to a point where the response is written, but that won't help because once the response is written, it is probably also committed, and I won't be able to rewrite it anymore.

I tried to read the index.html file from the Quinoa bundled resources, but I haven't been able to figure out how Quinoa stores this and if there are differences in dev vs build mode.

I do have several other options, but they all feel like hacks around the problem. For example, I could write the Quinoa bundle to a separate folder and serve everything myself, or I could use Quinoa for dev mode but build a separate frontend image for production.

The coming week, I won't be working on this problem, but I will revisit it afterward to see if I can find a way to process this file within the route observer. Any suggestions, code examples, or documentation that could help me out would be greatly appreciated.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request
Projects
None yet
Development

No branches or pull requests

3 participants