Skip to content

Commit 3f49a36

Browse files
authored
feat(react-router/server-runtime): treat cookie signatures with invalid encodings as invalid (#13847)
1 parent 509dd49 commit 3f49a36

File tree

3 files changed

+30
-4
lines changed

3 files changed

+30
-4
lines changed

.changeset/bright-cougars-buy.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"react-router": patch
3+
---
4+
5+
Handle `InvalidCharacterError` when validating cookie signature

packages/react-router/__tests__/server-runtime/cookies-test.ts

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,20 @@ describe("cookies", () => {
7777
expect(value).toBe(null);
7878
});
7979

80+
it("fails to parse signed string values with invalid signature encoding", async () => {
81+
let cookie = createCookie("my-cookie", {
82+
secrets: ["secret1"],
83+
});
84+
let setCookie = await cookie.serialize("hello michael");
85+
let cookie2 = createCookie("my-cookie", {
86+
secrets: ["secret2"],
87+
});
88+
// use characters that are invalid for base64 encoding
89+
let value = await cookie2.parse(getCookieFromSetCookie(setCookie) + "%^&");
90+
91+
expect(value).toBe(null);
92+
});
93+
8094
it("parses/serializes signed object values", async () => {
8195
let cookie = createCookie("my-cookie", {
8296
secrets: ["secret1"],

packages/react-router/lib/server-runtime/crypto.ts

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -23,10 +23,17 @@ export const unsign = async (
2323
let data = encoder.encode(value);
2424

2525
let key = await createKey(secret, ["verify"]);
26-
let signature = byteStringToUint8Array(atob(hash));
27-
let valid = await crypto.subtle.verify("HMAC", key, signature, data);
28-
29-
return valid ? value : false;
26+
try {
27+
let signature = byteStringToUint8Array(atob(hash));
28+
let valid = await crypto.subtle.verify("HMAC", key, signature, data);
29+
30+
return valid ? value : false;
31+
} catch (error: unknown) {
32+
// atob will throw a DOMException with name === 'InvalidCharacterError'
33+
// if the signature contains a non-base64 character, which should just
34+
// be treated as an invalid signature.
35+
return false;
36+
}
3037
};
3138

3239
const createKey = async (

0 commit comments

Comments
 (0)