Files
node-jsonwebtoken/test/async_sign.tests.js
david-renaud-oktaandGitHub 44ce6fd923 fix!: Prevent accidental use of insecure key sizes & misconfiguration of secrets (#852)
* fix!: Disable use of weak RSA key sizes for asymmetric algorithms

Added checks to prevent invalid secrets from being used with the HS*** algorithms when signing and verifying
Added checks to prevent the use of insecure asymmetric key sizes except when explicitly overriden via options
Prevented Buffers containing malicious objects from being used as key material.

BREAKING CHANGE: Requires node 12.x or later to allow use of `KeyObject`
2022-11-29 11:58:59 -05:00

151 lines
5.7 KiB
JavaScript

var jwt = require('../index');
var expect = require('chai').expect;
var jws = require('jws');
var PS_SUPPORTED = require('../lib/psSupported');
const {generateKeyPairSync} = require("crypto");
describe('signing a token asynchronously', function() {
describe('when signing a token', function() {
var secret = 'shhhhhh';
it('should return the same result as singing synchronously', function(done) {
jwt.sign({ foo: 'bar' }, secret, { algorithm: 'HS256' }, function (err, asyncToken) {
if (err) return done(err);
var syncToken = jwt.sign({ foo: 'bar' }, secret, { algorithm: 'HS256' });
expect(asyncToken).to.be.a('string');
expect(asyncToken.split('.')).to.have.length(3);
expect(asyncToken).to.equal(syncToken);
done();
});
});
it('should work with empty options', function (done) {
jwt.sign({abc: 1}, "secret", {}, function (err) {
expect(err).to.be.null;
done();
});
});
it('should work without options object at all', function (done) {
jwt.sign({abc: 1}, "secret", function (err) {
expect(err).to.be.null;
done();
});
});
it('should work with none algorithm where secret is set', function(done) {
jwt.sign({ foo: 'bar' }, 'secret', { algorithm: 'none' }, function(err, token) {
expect(token).to.be.a('string');
expect(token.split('.')).to.have.length(3);
done();
});
});
//Known bug: https://github.com/brianloveswords/node-jws/issues/62
//If you need this use case, you need to go for the non-callback-ish code style.
it.skip('should work with none algorithm where secret is falsy', function(done) {
jwt.sign({ foo: 'bar' }, undefined, { algorithm: 'none' }, function(err, token) {
expect(token).to.be.a('string');
expect(token.split('.')).to.have.length(3);
done();
});
});
it('should return error when secret is not a cert for RS256', function(done) {
//this throw an error because the secret is not a cert and RS256 requires a cert.
jwt.sign({ foo: 'bar' }, secret, { algorithm: 'RS256' }, function (err) {
expect(err).to.be.ok;
done();
});
});
it('should not work for RS algorithms when modulus length is less than 2048 when allowInsecureKeySizes is false or not set', function(done) {
const { privateKey } = generateKeyPairSync('rsa', { modulusLength: 1024 });
jwt.sign({ foo: 'bar' }, privateKey, { algorithm: 'RS256' }, function (err) {
expect(err).to.be.ok;
done();
});
});
it('should work for RS algorithms when modulus length is less than 2048 when allowInsecureKeySizes is true', function(done) {
const { privateKey } = generateKeyPairSync('rsa', { modulusLength: 1024 });
jwt.sign({ foo: 'bar' }, privateKey, { algorithm: 'RS256', allowInsecureKeySizes: true }, done);
});
if (PS_SUPPORTED) {
it('should return error when secret is not a cert for PS256', function(done) {
//this throw an error because the secret is not a cert and PS256 requires a cert.
jwt.sign({ foo: 'bar' }, secret, { algorithm: 'PS256' }, function (err) {
expect(err).to.be.ok;
done();
});
});
}
it('should return error on wrong arguments', function(done) {
//this throw an error because the secret is not a cert and RS256 requires a cert.
jwt.sign({ foo: 'bar' }, secret, { notBefore: {} }, function (err) {
expect(err).to.be.ok;
done();
});
});
it('should return error on wrong arguments (2)', function(done) {
jwt.sign('string', 'secret', {noTimestamp: true}, function (err) {
expect(err).to.be.ok;
expect(err).to.be.instanceof(Error);
done();
});
});
it('should not stringify the payload', function (done) {
jwt.sign('string', 'secret', {}, function (err, token) {
if (err) { return done(err); }
expect(jws.decode(token).payload).to.equal('string');
done();
});
});
describe('when mutatePayload is not set', function() {
it('should not apply claims to the original payload object (mutatePayload defaults to false)', function(done) {
var originalPayload = { foo: 'bar' };
jwt.sign(originalPayload, 'secret', { notBefore: 60, expiresIn: 600 }, function (err) {
if (err) { return done(err); }
expect(originalPayload).to.not.have.property('nbf');
expect(originalPayload).to.not.have.property('exp');
done();
});
});
});
describe('when mutatePayload is set to true', function() {
it('should apply claims directly to the original payload object', function(done) {
var originalPayload = { foo: 'bar' };
jwt.sign(originalPayload, 'secret', { notBefore: 60, expiresIn: 600, mutatePayload: true }, function (err) {
if (err) { return done(err); }
expect(originalPayload).to.have.property('nbf').that.is.a('number');
expect(originalPayload).to.have.property('exp').that.is.a('number');
done();
});
});
});
describe('secret must have a value', function(){
[undefined, '', 0].forEach(function(secret){
it('should return an error if the secret is falsy and algorithm is not set to none: ' + (typeof secret === 'string' ? '(empty string)' : secret), function(done) {
// This is needed since jws will not answer for falsy secrets
jwt.sign('string', secret, {}, function(err, token) {
expect(err).to.exist;
expect(err.message).to.equal('secretOrPrivateKey must have a value');
expect(token).to.not.exist;
done();
});
});
});
});
});
});