sveltejs / kit

web development, streamlined
https://kit.svelte.dev
MIT License
18.52k stars 1.91k forks source link

Access to `nonce` in `<svelte:head>`? (Using Partytown with CSP enabled) #10377

Closed aradalvand closed 2 months ago

aradalvand commented 1 year ago

Describe the problem

I'm trying to use Partytown in my SvelteKit app.

The SvelteKit guide in Partytown's official documentation involves injecting the Partytown script in the root layout via a <script> tag in <svelte:head>.

This won't work if you have CSP with nonces enabled, because the <script> tag won't have the nonce. This means plugins like partytown-sveltekit also won't work.

Describe the proposed solution

Somehow provide access to the generated nonce in <svelte:head>? Like so maybe:

<svelte:head let:nonce>
    <script {nonce}>
        // Partytown script
    </script>
</svelte:head>

Or perhaps automatically add the nonce to every <script> tag including those rendered using <svelte:head> in the components?

Alternatives considered

If there are any sane workarounds for getting Partytown to work with SvelteKit while using CSP, let me know.

Importance

would make my life easier

Additional Information

No response

aradalvand commented 1 year ago

Update:

After a bit of investigation, it seems like even if SvelteKit gave us the nonce in this context, Partytown would still not necessarily be fully functional because it in turn might add <script> tags as part of its setup; which would not work due to the same problem.

I think the "right" solution is actually using strict-dynamic, which allows "trusted" scripts to load other scripts by creating <script> elements. With strict-dynamic in place, our root layout could simply render a <script> element which it then populates with the Partytown snippet on mount:

<script lang="ts">
    import { onMount } from 'svelte';
    import { partytownSnippet } from '@builder.io/partytown/integration';

    let scriptTag: HTMLScriptElement;
    onMount(() => {
        scriptTag.textContent = partytownSnippet();
    });
</script>

<svelte:head>
    <script bind:this={scriptTag}></script>
</svelte:head>

This seems to neatly solve both problems. The only thing I'm worried about is #3558. Apparently SvelteKit used to have some problems with strict-dynamic in the past? Though based on my tests everything seems to be working correctly.

aradalvand commented 1 year ago

Closed this as I don't think there's anything SvelteKIt needs to do here. strict-dynamic is tracked by #3558 of course.

aradalvand commented 1 year ago

I'm reopening this because I think having access to the nonce in <svelte:head> is generally something that you might need in many other scenarios, even if some of those can be worked around by using strict-dynamic.

yuriyostapenko commented 12 months ago

Wouldn't it be ideal if svelte automatically added nonce to such inline scripts from components? In my case, I experience this problem with a third party dependency, and wondering how to fix it best.

fnimick commented 2 months ago

This is similarly a problem with https://github.com/svecosystem/mode-watcher

The library has been updated to have the component accept a nonce prop, but there's no good way to pass that through from the CSP without manually manipulating headers in hooks and attaching a generated nonce to the locals.

dominikg commented 2 months ago

DISCLAIMER: The comment below describes a technical possibility. It is in no way a recommendation to use partytown with csp and nonces that way. You are responsible for your applications security. If you deem it important to use a csp, evaluate carefully if you want/need partytown and how to allow it to work without compromising the csp requirements you have.


You can use a server hook with transformPageChunk to inject the partytown script into a script block in app.html that you have prepared with the nonce

<script nonce="%sveltekit.nonce%">
%partytown.snippet%
</script>
export const handle = (async ({ event, resolve }) => {
    return resolve(event, {
        transformPageChunk: ({ html }) => {
            return html.replace(%partytown.snippet%', partytownSnippet());
        },
    });
}) satisfies Handle;

Access to the nonce itself has to be limited and must only be available to code provided by the sveltekit application itself. if we added it to svelte:head, a third party library could use it to render scripts with the nonce, bypassing the csp.

dominikg commented 2 months ago

As the og feature request asks for nonce access in svelte:head which would be inherently insecure, i'll close this. To improve support for partytown together with a csp, please start a discussion and/or join https://svelte.dev/chat and ask in #help-svelte-and-kit