-
Notifications
You must be signed in to change notification settings - Fork 10
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
Question on /csrf-token endpoint #49
Comments
Typically the recommended way to share a csrf-token with the client is by it placing it in a const resp = await fetch('/page-with-csrf-token-in-meta-tag');
const html = await resp.text();
// use regex to parse `<meta name="csrf-token" content="xxx">` here... One possible attack that they're trying to protect against is that if the CSRF protection is a deep field so I don't want to say with 100% certainty that you can disregard that comment but I can't see how the specific Incidentally, if you want to move the CSRF token to the // app/layout.tsx
import { Metadata } from 'next';
import { headers } from 'next/headers';
export async function generateMetadata(): Promise<Metadata> {
const csrfToken = headers().get('X-CSRF-Token') || 'missing';
return {
title: 'edge-csrf examples',
other: {
'csrf-token': csrfToken,
},
};
}
export default function Layout({
children,
}: {
children: React.ReactNode;
}) {
return (
<html lang="en">
<body>
{children}
</body>
</html>
);
} And the const handleSubmit = async (ev: React.FormEvent<HTMLFormElement>) => {
// prevent default form submission
ev.preventDefault();
// get form values
const data = new FormData(ev.currentTarget);
// get token from <meta> tag
const metaTag = document.querySelector('meta[name="csrf-token"]');
const csrfToken = metaTag ? metaTag.getAttribute('content') : 'missing';
// build fetch args
const fetchArgs = { method: 'POST', headers: {}, body: JSON.stringify(data) };
if (csrfToken) fetchArgs.headers = { 'X-CSRF-Token': csrfToken };
// send to backend
const response = await fetch('/form-handler', fetchArgs);
// show response
// eslint-disable-next-line no-alert
alert(response.statusText);
}; |
Thanks for your detail explanation. I was looking at their recommendation to get the token "Signed Double-Submit Cookie". With CORS disabled in your example, I don't see how an attacker can gain access to the csrf token either. Method-1:
Method-2:
Is this understanding flawed? |
This library implements the "signed double-submit cookie pattern" with a modification that instead of using a shared server-side secret, it uses a unique per-session secret (stored in a cookie). The advantage of this is that it doesn't require the developer to define an environment secret. The token is signed with the secret and both the secret (via cookie) and the token (via payload) must be present in the request and pass validation as described in the "signed double-submit cookie" pattern. Here are the relevant lines in the source code for reference: With regards to "Method-1", if CORS is disabled then a With regards to "Method-2", it sounds like you're describing a pattern that retrieves the token from the initial HTTP response. If so, then that will also be safe but you should keep in mind that typically cookies in the "double-submit cookie pattern" have Going back to your original question, the main advantage of using a |
thanks @amorey for your prompt response. |
Hi @amorey I just stumbled upon this question and was just wondering if the "signed double-submit cookie pattern"-CSRF protection with the server-secret being in stored at the client couldn't be bypassed. |
You're right to be concerned. The signed double-submit cookie pattern relies on the assumption that an attacker cannot set arbitrary cookies for the target domain. To prevent a compromised subdomain from setting a cookie for the parent domain, you should:
You can modify Edge-CSRF cookie properties here. By default Edge-CSRF leaves For reference here are the general scoping rules for And here's an overview of |
The possibility to set cookies via an XSS in a subdomain was a mere example. DOM-based cookie manipulation, HTTP Response Splitting / Header Injection or other weird coding errors might also lead to being able to set cookies. But what I would rather like to get at is that storing the server-secret at the client renders the signed double-submit cookie pattern just as secure as the naive double-submit cookie pattern which OWASP advises against using. If I'm not mistaken the library supports the CSRF-Token to be transported in the request body and in a custom header. |
Thanks, I have to think about it some more but you might be right that storing the secret client-side is effectively an implementation of the naive double-submit cookie pattern. However, I don't think that just moving the secret server-side is enough to properly implement the signed double-submit cookie pattern because that still leaves the client open to an attacker injecting a valid cookie + token obtained elsewhere. I think that to properly implement the recommended pattern, the csrf token needs to be tied to the user session. Is that correct or am I missing something? With regards to your suggestion, requiring the client to submit the token via a header (or any other method that effectively rejects simple requests) would prevent form submission via |
I totally agree. Tying the CSRF-Token-Value to the user session would be the more secure implementation! Make sense. I only had the security inspect in mind. But your suggested pattern server-side-secret + tying the value to the user session should also allow for the csrf-token to be sent in the request body. |
Hi,
I am quite new to CSRF and was using your library for NextJS 14.
Can I check with regards to this portion of code fetch("/csrf-token"):
and middleware.ts
is this endpoint /csrf-token suppose to be there in production as well? or this is just an example?
I am reading article which indicate CSRF token should not be accessed with AJAX which confuses me with your example.
Thanks
The text was updated successfully, but these errors were encountered: