Skip to content
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

CVE 2024 34273 #107

Merged
merged 4 commits into from
Jun 11, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
# nJwt Change Log

### 2.0.1

* [#107](https://github.com/jwtk/njwt/pull/107) Freeze `prototype` of all classes to prevent prototype pollution vuln ([CVE-2024-34273](https://cve.mitre.org/cgi-bin/cvename.cgi?name=2024-34273))

### 2.0.0

* [#98](https://github.com/jwtk/njwt/pull/98) Bumps jsonwebtoken version, drop Node < 12 from engines
Expand Down
5 changes: 2 additions & 3 deletions index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -61,12 +61,11 @@ export declare class JwtBody {
toJSON(): JSONMap;
compact(): string;
}
export declare function JwtHeader(header: JwtHeaderOptions): JwtHeader;
export declare function JwtHeader(header: JwtHeaderOptions, enforceDefaultFields?: boolean): JwtHeader;
export declare class JwtHeader {
constructor(header: JwtHeaderOptions);
constructor(header: JwtHeaderOptions, enforceDefaultFields?: boolean);
typ: string;
alg: string;
reservedKeys: string[];
compact(): string;
}

Expand Down
42 changes: 31 additions & 11 deletions index.js
Original file line number Diff line number Diff line change
Expand Up @@ -113,17 +113,21 @@ JwtBody.prototype.compact = function compact(){
return base64urlEncode(JSON.stringify(this));
};

function JwtHeader(header){
var reservedHeaderKeys = ['typ','alg'];
function JwtHeader(header, enforceDefaultFields){
if(!(this instanceof JwtHeader)){
return new JwtHeader(header);
}
var self = this;
this.typ = header && header.typ || 'JWT';
this.alg = header && header.alg || 'HS256';
this.typ = header && header.typ;
this.alg = header && header.alg;
if (enforceDefaultFields !== false) {
this.typ = this.typ || 'JWT';
this.alg = this.alg || 'HS256';
}

if(header){
return Object.keys(header).reduce(function(acc,key){
if(self.reservedKeys.indexOf(key)===-1 && header.hasOwnProperty(key)){
if(reservedHeaderKeys.indexOf(key)===-1 && header.hasOwnProperty(key)){
acc[key] = header[key];
}
return acc;
Expand All @@ -132,7 +136,6 @@ function JwtHeader(header){
return this;
}
}
JwtHeader.prototype.reservedKeys = ['typ','alg'];
JwtHeader.prototype.compact = function compact(){
return base64urlEncode(JSON.stringify(this));
};
Expand Down Expand Up @@ -312,7 +315,7 @@ Parser.prototype.parse = function parse(jwtString,cb){
jwt.setSigningAlgorithm(header.alg);
jwt.signature = signature;
jwt.verificationInput = segments[0] +'.' + segments[1];
jwt.header = new JwtHeader(header);
jwt.header = new JwtHeader(header, false);
return done(null,jwt);
};

Expand Down Expand Up @@ -409,11 +412,12 @@ Verifier.prototype.verify = function verify(jwtString,cb){

var newJwt = new Jwt(body, false);

newJwt.toString = function () {
return jwtString;
};
// since prototype is now frozen, .toString can no longer be overloaded
// newJwt.toString = function () {
// return jwtString;
// };

newJwt.header = new JwtHeader(header);
newJwt.header = new JwtHeader(header, false);

if (!verified) {
return done(new JwtParseError(properties.errors.SIGNATURE_MISMTACH,jwtString,header,body));
Expand All @@ -428,6 +432,22 @@ Verifier.prototype.withKeyResolver = function withKeyResolver(keyResolver) {
return this;
};

// vuln: https://security.snyk.io/vuln/SNYK-JS-NJWT-6861582
Object.freeze(Jwt);
Object.freeze(Jwt.prototype);
Object.freeze(JwtBody);
Object.freeze(JwtBody.prototype);
Object.freeze(JwtHeader);
Object.freeze(JwtHeader.prototype);
Object.freeze(Verifier);
Object.freeze(Verifier.prototype);
Object.freeze(Parser);
Object.freeze(Parser.prototype);
Object.freeze(JwtParseError);
Object.freeze(JwtParseError.prototype);
Object.freeze(JwtError);
Object.freeze(JwtError.prototype);

var jwtLib = {
Jwt: Jwt,
JwtBody: JwtBody,
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "njwt",
"version": "2.0.0",
"version": "2.0.1",
"description": "JWT Library for Node.js",
"engines": {
"node": ">=12.0"
Expand Down
39 changes: 39 additions & 0 deletions test/exports.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
var assert = require('chai').assert;
var nJwt = require('..');

describe('njwt module exports',function () {
// https://github.com/chrisandoryan/vuln-advisory/blob/main/nJwt/CVE-2024-34273.md
describe('CVE-2024-34273', function () {
it('should export classes with frozen prototypes', function(){
assert.frozen(nJwt.Jwt);
assert.frozen(nJwt.Jwt.prototype);
assert.frozen(nJwt.JwtBody);
assert.frozen(nJwt.JwtBody.prototype);
assert.frozen(nJwt.JwtHeader);
assert.frozen(nJwt.JwtHeader.prototype);
assert.frozen(nJwt.Verifier);
assert.frozen(nJwt.Verifier.prototype);
});

it('should not allow prototype pollution', function () {

// based on: https://github.com/chrisandoryan/vuln-advisory/blob/main/nJwt/CVE-2024-34273.md#proof-of-concept-poc
var token = `ewogICJ0eXAiOiAiSldUIiwKICAiYWxnIjogIm5vbmUiLAogICJfX3Byb3RvX18iOiB7CiAgICAidHlwIjogIkpXVCIsCiAgICAiYWxnIjogIkhTMjU
2IiwKICAgICJfX3Byb3RvX18iOiB7CiAgICAgICJjb21wYWN0IjogbnVsbCwKICAgICAgInJlc2VydmVkS2V5cyI6IFsKICAgICAgICAidHlwIiwKICAgICAgICAicmF
uZG9tX2dpYmJlcmlzaCIKICAgICAgXQogICAgfQogIH0KfQ.ewogICJzdWIiOiAxLAogICJzY29wZSI6ICJ1c2VyIiwKICAianRpIjogImJhZmIxNmNlLTIwZDYtNGNk
Ny05NDgzLTY1YTA5NThhOGU2NCIsCiAgImlhdCI6IDI1Mzc0Nzg1MDYsCiAgImV4cCI6IDI1Mzc0Nzg1MDYsCiAgIl9fcHJvdG9fXyI6IHsKICAgICJjb21wYWN0Ijog
bnVsbCwKICAgICJ0b0pTT04iOiBudWxsLAogICAgInBvbGx1dGVkIjogdHJ1ZQogIH0KfQ`.replace(/\s/g, '');

assert.isOk(nJwt.JwtBody.prototype.hasOwnProperty('toJSON'))
assert.isOk(nJwt.JwtBody.prototype.hasOwnProperty('compact'))
assert.isOk(nJwt.JwtHeader.prototype.hasOwnProperty('compact'))

nJwt.verify(token);

assert.isOk(nJwt.JwtBody.prototype.hasOwnProperty('toJSON'))
assert.isOk(nJwt.JwtBody.prototype.hasOwnProperty('compact'))
assert.isOk(nJwt.JwtHeader.prototype.hasOwnProperty('compact'))
});
});

});
2 changes: 1 addition & 1 deletion test/verifier.js
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ describe('Verifier().setSigningAlgorithm() ',function(){

describe('.verify()',function(){
it('should persist the original token to the toString() invocation',function(){
var token = 'eyJhbGciOiJub25lIn0.eyJzdWIiOiIxMjMifQ.p6bizskaJLAheVyRhQEMR-60PkH_jtLVYgMy1qTjCoc';
var token = 'eyJhbGciOiJub25lIn0.eyJzdWIiOiIxMjMifQ';
assert.equal(token,nJwt.verify(token).toString());
});

Expand Down
Loading
Loading