Skip to content

fix: reject + and / in the no-native-fromBase64 decode fallback#879

Open
spokodev wants to merge 1 commit into
panva:mainfrom
spokodev:fix/base64url-fallback-reject-plus-slash
Open

fix: reject + and / in the no-native-fromBase64 decode fallback#879
spokodev wants to merge 1 commit into
panva:mainfrom
spokodev:fix/base64url-fallback-reject-plus-slash

Conversation

@spokodev

Copy link
Copy Markdown

Problem

base64url.decode() decodes the same input differently across runtimes when it contains + or /.

The native path rejects them, since they are not in the Base64URL alphabet:

Uint8Array.fromBase64(input, { alphabet: 'base64url' }) // throws on + or /

The fallback (runtimes without Uint8Array.fromBase64) translates -_+/ and decodes via atob, which accepts standard-Base64 + and /:

encoded = encoded.replace(/-/g, '+').replace(/_/g, '/')
return decodeBase64(encoded) // atob accepts + and /

So the same JWS/JWE/JWT segment is accepted on a runtime without the native method and rejected on one with it:

delete Uint8Array.fromBase64
decode('+/+/') // fallback: Uint8Array(3) [251, 255, 191];  native: throws

This is a portability inconsistency rather than a security issue (a forged signature still fails verification), but a given segment should decode the same way on every runtime, and the fallback should match the alphabet the native path already enforces.

Fix

Reject + and / in the fallback before the -_+/ translation, so it behaves like the native path. Only those two characters change; the padding and whitespace that atob tolerates are untouched.

Tests

Added a unit test exercising both paths (native, and the fallback forced by removing Uint8Array.fromBase64): +// are rejected and valid base64url still decodes. The fallback case fails on main and passes with the fix.

When `Uint8Array.fromBase64` is unavailable, base64url `decode()` falls
back to translating `-_` to `+/` and decoding via `atob`, which accepts
the standard-Base64 characters `+` and `/`. The native path
(`Uint8Array.fromBase64(input, { alphabet: 'base64url' })`) rejects them,
so the same input is accepted on runtimes without the native method and
rejected on runtimes with it.

Reject `+` and `/` in the fallback before the `-_` -> `+/` translation,
matching the native path. Only those two characters change behaviour;
padding and whitespace handled by `atob` are untouched.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant