Skip to content

Commit 88da3aa

Browse files
comments
1 parent 73cb975 commit 88da3aa

File tree

2 files changed

+25
-27
lines changed

2 files changed

+25
-27
lines changed

src/utils/api.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -244,7 +244,7 @@ function invalidCsrfResponse(res) {
244244
export async function validateAddress(req, res, appObj = app) {
245245
const tokenObject = appObj["tokenObject"];
246246
const parsedTokenFromRequestBody = parseTokenFromPostRequestCookies(req);
247-
const csrfTokenValid = validateCsrfToken(req, parsedTokenFromRequestBody);
247+
const csrfTokenValid = validateCsrfToken(req);
248248
if (!parsedTokenFromRequestBody) {
249249
const newToken = generateNewToken();
250250
setCsrfTokenCookie(res, newToken);

src/utils/utils.ts

+24-26
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,18 @@ const generateSecret = () => {
9292
);
9393
};
9494

95+
// Creates a cookie like 'next-auth.csrf-token' with the value 'token|hash',
96+
// where 'token' is the CSRF token and 'hash' is a hash made of the token and
97+
// the secret, and the two values are joined by a pipe '|'. By storing the
98+
// value and the hash of the value (with the secret used as a salt) we can
99+
// verify the cookie was set by the server and not by a malicous attacker.
100+
const generateNewCookieToken = (csrfToken) => {
101+
return `${csrfToken}|${createHash("sha256")
102+
.update(`${csrfToken}${generateSecret()}`)
103+
.digest("hex")}`;
104+
};
105+
106+
// Parses token and hash generated by generateNewCookieToken.
95107
const parseTokenFromPostRequestCookies = (req) => {
96108
let value, hash;
97109
if (req.cookies[cookie.metadata().csrfToken.name]) {
@@ -102,35 +114,28 @@ const parseTokenFromPostRequestCookies = (req) => {
102114

103115
/**
104116
* validateCsrfToken
105-
* Function to verify a csrf token based on code from `next-auth`
106-
* and modified for our purposes:
117+
* Function to verify a csrf token once upon a time based on code from
118+
* `next-auth` and modified for our purposes, now heavily refacotred:
107119
* https://github.com/nextauthjs/next-auth/blob/main/src/server/index.js
108-
* Most comments are kept in place but some were added for clarity.
120+
*
121+
* For more details, see the following OWASP links:
122+
* https://cheatsheetseries.owasp.org/cheatsheets/Cross-Site_Request_Forgery_Prevention_Cheat_Sheet.html#double-submit-cookie
123+
* https://owasp.org/www-chapter-london/assets/slides/David_Johansson-Double_Defeat_of_Double-Submit_Cookie.pdf
109124
*/
110125
const validateCsrfToken = (req) => {
111-
const tokenValueFromPostRequestBody = req.body?.csrfToken;
126+
const tokenFromRequestBody = req.body?.csrfToken;
112127
const tokenFromRequestCookie = parseTokenFromPostRequestCookies(req);
113-
// Ensure CSRF Token cookie is set for any subsequent requests.
114-
// Used as part of the strategy for mitigation for CSRF tokens.
115-
//
116-
// Creates a cookie like 'next-auth.csrf-token' with the value 'token|hash',
117-
// where 'token' is the CSRF token and 'hash' is a hash made of the token and
118-
// the secret, and the two values are joined by a pipe '|'. By storing the
119-
// value and the hash of the value (with the secret used as a salt) we can
120-
// verify the cookie was set by the server and not by a malicous attacker.
121-
//
122-
// For more details, see the following OWASP links:
123-
// https://cheatsheetseries.owasp.org/cheatsheets/Cross-Site_Request_Forgery_Prevention_Cheat_Sheet.html#double-submit-cookie
124-
// https://owasp.org/www-chapter-london/assets/slides/David_Johansson-Double_Defeat_of_Double-Submit_Cookie.pdf
125128

126129
if (postRequestHashMatchesServerHash(tokenFromRequestCookie)) {
127130
return (
128131
req.method === "POST" &&
129-
tokenFromRequestCookie.value === tokenValueFromPostRequestBody
132+
tokenFromRequestCookie.value === tokenFromRequestBody
130133
);
131134
} else return false;
132135
};
133136

137+
// Validate that the token value from the request cookies generates what the
138+
// server knows to be a valid hash
134139
const postRequestHashMatchesServerHash = (tokenFromRequestCookie) => {
135140
return (
136141
tokenFromRequestCookie.hash ===
@@ -140,6 +145,8 @@ const postRequestHashMatchesServerHash = (tokenFromRequestCookie) => {
140145
);
141146
};
142147

148+
// Ensure CSRF Token cookie is set for any subsequent requests.
149+
// Used as part of the strategy for mitigation for CSRF tokens.
143150
const setCsrfTokenCookie = (res, csrfToken) => {
144151
const newCsrfTokenCookie = generateNewCookieToken(csrfToken);
145152
cookie.set(
@@ -154,15 +161,6 @@ const generateNewToken = () => {
154161
return randomBytes(32).toString("hex");
155162
};
156163

157-
const generateNewCookieToken = (csrfToken) => {
158-
// If there is no csrfToken because it's not been set yet or because
159-
// the hash doesn't match (e.g. because it's been modified or because the
160-
// secret has changed), then create a new token and create a cookie.
161-
return `${csrfToken}|${createHash("sha256")
162-
.update(`${csrfToken}${generateSecret()}`)
163-
.digest("hex")}`;
164-
};
165-
166164
export {
167165
redirectIfUserHasRegistered,
168166
homePageRedirect,

0 commit comments

Comments
 (0)