-
-
Notifications
You must be signed in to change notification settings - Fork 2.4k
BUG(isJWT): validate decoded header and payload as JSON objects #2677
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
base: master
Are you sure you want to change the base?
Changes from all commits
657328d
3233fb7
d141406
f44c09e
018dfb4
32c48e3
6853ed2
eaeaeb0
81ac039
cdea4ec
6aae9bc
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,15 +1,70 @@ | ||
| import assertString from './util/assertString'; | ||
| import isBase64 from './isBase64'; | ||
|
|
||
| function decodeBase64Url(b64) { | ||
| /* istanbul ignore else */ | ||
| if (typeof Buffer !== 'undefined') { | ||
| /* istanbul ignore else */ | ||
| if (typeof Buffer.from === 'function') { | ||
| return Buffer.from(b64, 'base64').toString('utf8'); | ||
| } | ||
| /* istanbul ignore next */ | ||
| // eslint-disable-next-line no-buffer-constructor | ||
| return new Buffer(b64, 'base64').toString('utf8'); | ||
| } | ||
| /* istanbul ignore next */ | ||
| if (typeof atob === 'function') { | ||
| const binary = atob(b64); | ||
| if (typeof TextDecoder !== 'undefined') { | ||
| const bytes = new Uint8Array(binary.length); | ||
| for (let i = 0; i < binary.length; i += 1) { | ||
| bytes[i] = binary.charCodeAt(i); | ||
| } | ||
| return new TextDecoder('utf-8').decode(bytes); | ||
| } | ||
| let encoded = ''; | ||
| for (let i = 0; i < binary.length; i += 1) { | ||
| const hex = binary.charCodeAt(i).toString(16); | ||
| const code = hex.length === 1 ? `0${hex}` : hex; | ||
| encoded += `%${code}`; | ||
| } | ||
| return decodeURIComponent(encoded); | ||
| } | ||
| /* istanbul ignore next */ | ||
| return b64; | ||
| } | ||
|
|
||
| function tryDecodeJSON(segment) { | ||
| if (!isBase64(segment, { urlSafe: true })) return false; | ||
| try { | ||
| // Normalize base64url alphabet to base64, then restore stripped padding | ||
| let b64 = segment.replace(/-/g, '+').replace(/_/g, '/'); | ||
| while (b64.length % 4) b64 += '='; | ||
Kartikeya-guthub marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| const decoded = decodeBase64Url(b64); | ||
| const parsed = JSON.parse(decoded); | ||
| if (typeof parsed !== 'object') return false; | ||
| if (parsed === null) return false; | ||
| if (Array.isArray(parsed)) return false; | ||
| return parsed; | ||
| } catch (e) { | ||
| return false; | ||
| } | ||
| } | ||
|
|
||
| export default function isJWT(str) { | ||
| assertString(str); | ||
|
|
||
| const dotSplit = str.split('.'); | ||
| const len = dotSplit.length; | ||
|
|
||
| if (len !== 3) { | ||
| return false; | ||
| } | ||
| if (dotSplit.length !== 3) return false; | ||
|
|
||
| const header = dotSplit[0]; | ||
| const payload = dotSplit[1]; | ||
| const signature = dotSplit[2]; | ||
|
|
||
| if (!tryDecodeJSON(header)) return false; | ||
| if (!tryDecodeJSON(payload)) return false; | ||
| if (!isBase64(signature, { urlSafe: true })) return false; | ||
|
|
||
| return dotSplit.reduce((acc, currElem) => acc && isBase64(currElem, { urlSafe: true }), true); | ||
| return true; | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -5542,13 +5542,27 @@ describe('Validators', () => { | |
| 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJsb3JlbSI6Imlwc3VtIn0.ymiJSsMJXR6tMSr8G9usjQ15_8hKPDv_CArLhxw28MI', | ||
| 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJkb2xvciI6InNpdCIsImFtZXQiOlsibG9yZW0iLCJpcHN1bSJdfQ.rRpe04zbWbbJjwM43VnHzAboDzszJtGrNsUxaqQ-GQ8', | ||
| 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJqb2huIjp7ImFnZSI6MjUsImhlaWdodCI6MTg1fSwiamFrZSI6eyJhZ2UiOjMwLCJoZWlnaHQiOjI3MH19.YRLPARDmhGMC3BBk_OhtwwK21PIkVCqQe8ncIRPKo-E', | ||
| 'eyJhbGciOiJub25lIn0.eyJpc3MiOiJqb2UiLCJleHAiOjEzMDA4MTkzODAsImh0dHA6Ly9leGFtcGxlLmNvbS9pc19yb290Ijp0cnVlfQ.', | ||
| ], | ||
| invalid: [ | ||
| 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9', | ||
| 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NSIsIm5hbWUiOiJKb2huIERvZSIsImlhdCI6MTUxNjIzOTAyMn0', | ||
| 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NSIsIm5hbWUiOiJKb2huIERvZSIsImlhdCI6MTYxNjY1Mzg3Mn0.eyJpc3MiOiJodHRwczovL2V4YW1wbGUuY29tIiwiaWF0IjoxNjE2NjUzODcyLCJleHAiOjE2MTY2NTM4ODJ9.a1jLRQkO5TV5y5ERcaPAiM9Xm2gBdRjKrrCpHkGr_8M', | ||
| '$Zs.ewu.su84', | ||
| 'ks64$S/9.dy$§kz.3sd73b', | ||
| 'foo.bar.', | ||
| '..', | ||
| '.t.', | ||
| 'foo.bar.baz', | ||
| 'Zm9v.YmFy.', | ||
|
Comment on lines
+5553
to
+5557
|
||
| 'eyJmb28iOiJiYXIifQ.YmFy.', | ||
| 'Zm9v.eyJiYXIiOiJiYXoifQ.', | ||
| 'W10.eyJiYXIiOiJiYXoifQ.', | ||
| 'eyJmb28iOiJiYXIifQ.W10.', | ||
| 'bnVsbA.eyJiYXIiOiJiYXoifQ.', | ||
| 'WzFd.eyJiYXIiOiJiYXoifQ.', | ||
| 'ImhlbGxvIg.eyJiYXIiOiJiYXoifQ.', | ||
| 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIn0.invalid$sig', | ||
| ], | ||
| error: [ | ||
| [], | ||
|
|
||
Uh oh!
There was an error while loading. Please reload this page.