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

option to disable CSRF for list of endpoints #6784

Open
ghostebony opened this issue Sep 13, 2022 · 7 comments
Open

option to disable CSRF for list of endpoints #6784

ghostebony opened this issue Sep 13, 2022 · 7 comments
Labels
documentation Improvements or additions to documentation
Milestone

Comments

@ghostebony
Copy link

ghostebony commented Sep 13, 2022

Describe the problem

With the new csrf protection I can't receive webhooks without disabling csrf on all routes

Describe the proposed solution

In the csrf config, it would be nice to have a "exclude" setting to disable csrf protection in some routes.

// svelte.config.js

const config = {
    kit: {
        csrf: {
            checkOrigin: true,
            // exclude: [ "/webhooks/*" ],
            // AND/OR
            // exclude [ "/webhooks/1", "/webhooks/2" ]
        },
    },
};

export default config;

and/or

// src/routes/+(page.)server.js

export const csrf = false;

Alternatives considered

No response

Importance

would make my life easier

Additional Information

No response

@Conduitry
Copy link
Member

This is fairly easy to do now simply be reproducing SvelteKit's current CSRF check logic (it's pretty simple) in your handle hook, but wrapped in whatever additional check with event.routeId you want to include/exclude certain endpoints.

If we do add more customization here, it will likely be something more along the lines of code rather than configuration. That is, a new function exported by SvelteKit exposing its default CSRF handling that you can conditionally call in your handle hook or in a +page.server.js.

@benmccann benmccann added the documentation Improvements or additions to documentation label Sep 14, 2022
@Rich-Harris Rich-Harris added this to the whenever milestone Sep 19, 2022
@dangreaves
Copy link

Adding an additional use-case here.

My app uses an OpenID integration, where on success, the OpenID provider POST's back to the app.

This doesn't work without a way to exclude certain endpoints from CSRF protection.

@sidharthramesh

This comment was marked as spam.

@Bluebie
Copy link

Bluebie commented Nov 25, 2022

My blog app uses Webmention, which works by having remote server's POST urlencoded form data to a special endpoint. I wish sveltekit let me export something in the +server.ts file that disabled csrf form submission protection selectively for just that. Or alternatively, disable it for server endpoints but not for form actions.

@kyuuaria
Copy link

This would make things so much easier

@jamesbirtles
Copy link

As conduitry hinted at this can be done manually with hooks, so until something is implemented I am doing this in hooks.server.ts:

import { error, json, text, type Handle } from '@sveltejs/kit';

/**
 * CSRF protection copied from sveltekit but with the ability to turn it off for specific routes.
 */
const csrf =
	(allowedPaths: string[]): Handle =>
	async ({ event, resolve }) => {
		const forbidden =
			event.request.method === 'POST' &&
			event.request.headers.get('origin') !== event.url.origin &&
			isFormContentType(event.request) &&
			!allowedPaths.includes(event.url.pathname);

		if (forbidden) {
			const csrfError = error(
				403,
				`Cross-site ${event.request.method} form submissions are forbidden`,
			);
			if (event.request.headers.get('accept') === 'application/json') {
				return json(csrfError.body, { status: csrfError.status });
			}
			return text(csrfError.body.message, { status: csrfError.status });
		}

		return resolve(event);
	};
function isContentType(request: Request, ...types: string[]) {
	const type = request.headers.get('content-type')?.split(';', 1)[0].trim() ?? '';
	return types.includes(type);
}
function isFormContentType(request: Request) {
	return isContentType(request, 'application/x-www-form-urlencoded', 'multipart/form-data');
}

export handle = csrf(['/auth/callback/apple']);

this basically reimplements the csrf handling built in to sveltekit, but allows a list of pathnames that can bypass the protection, so you will need to disable the built in behaviour in your svelte.config.js

csrf: {
	checkOrigin: false,
},

@hjaber
Copy link

hjaber commented Jun 7, 2023

As conduitry hinted at this can be done manually with hooks, so until something is implemented I am doing this in hooks.server.ts

Found this while trying to whitelist apple oauth to POST to a specific api/auth/callback. I added validation to the origin so only a specific origin (appleid.apple.com) could post to my callback url

const csrf =
	(allowedPaths: string[]): Handle =>
	async ({ event, resolve }) => {
		const origin = event.request.headers.get('origin') || ''
		const referer = new URL(event.request.headers.get('referer')).hostname;
		const forbidden =
			event.request.method === 'POST' &&
			origin !== event.url.origin &&
			!((origin === 'https://appleid.apple.com' || referer === 'appleid.apple.com') && allowedPaths.includes(event.url.pathname)) &&
			isFormContentType(event.request);
			
			/* rest of code */

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
documentation Improvements or additions to documentation
Projects
None yet
Development

No branches or pull requests

10 participants