Skip to content
Open
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
23 changes: 23 additions & 0 deletions test/issue_1046.tests.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
const jwt = require('../index');
const crypto = require('crypto');
const assert = require('chai').assert;
const expect = require('chai').expect;
const JsonWebTokenError = require('../lib/JsonWebTokenError');

describe('issue 1046', function () {
it('verifies HS256 tokens signed with a string secret', function () {
const secret = 'a-shared-secret-of-reasonable-length';
const token = jwt.sign({ sub: 'u' }, secret, { algorithm: 'HS256' });
const decoded = jwt.verify(token, secret, { algorithms: ['HS256'] });
assert.equal(decoded.sub, 'u');
});

it('still rejects HMAC verification with a PEM public key', function () {
const { publicKey } = crypto.generateKeyPairSync('rsa', { modulusLength: 2048 });
const pem = publicKey.export({ type: 'spki', format: 'pem' });
const maliciousToken = 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCIsImtpZCI6InJzYUtleUlkIn0.eyJmb28iOiJiYXIiLCJpYXQiOjE2NTk1MTA2MDh9.cOcHI1TXPbxTMlyVTfjArSWskrmezbrG8iR7uJHwtrQ';

expect(() => jwt.verify(maliciousToken, pem, { algorithms: ['RS256', 'HS256'] }))
.to.throw(JsonWebTokenError, 'must be a symmetric key');
});
});
18 changes: 16 additions & 2 deletions verify.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,15 @@ if (PS_SUPPORTED) {
RSA_KEY_ALGS.splice(RSA_KEY_ALGS.length, 0, 'PS256', 'PS384', 'PS512');
}

// PEM/SSH/JWK material must still go through createPublicKey first so an HS*
// token cannot treat an RSA public key as an HMAC secret.
function looksLikeAsymmetricKey(key) {
if (typeof key !== 'string') {
return true;
}
return /^\s*(-----BEGIN |ssh-|\{)/.test(key);
}

module.exports = function (jwtString, secretOrPublicKey, options, callback) {
if ((typeof options === 'function') && !callback) {
callback = options;
Expand Down Expand Up @@ -118,11 +127,16 @@ module.exports = function (jwtString, secretOrPublicKey, options, callback) {
}

if (secretOrPublicKey != null && !(secretOrPublicKey instanceof KeyObject)) {
const trySecretFirst = !looksLikeAsymmetricKey(secretOrPublicKey);
try {
secretOrPublicKey = createPublicKey(secretOrPublicKey);
secretOrPublicKey = trySecretFirst
? createSecretKey(typeof secretOrPublicKey === 'string' ? Buffer.from(secretOrPublicKey) : secretOrPublicKey)
: createPublicKey(secretOrPublicKey);
} catch (_) {
try {
secretOrPublicKey = createSecretKey(typeof secretOrPublicKey === 'string' ? Buffer.from(secretOrPublicKey) : secretOrPublicKey);
secretOrPublicKey = trySecretFirst
? createPublicKey(secretOrPublicKey)
: createSecretKey(typeof secretOrPublicKey === 'string' ? Buffer.from(secretOrPublicKey) : secretOrPublicKey);
} catch (_) {
return done(new JsonWebTokenError('secretOrPublicKey is not valid key material'))
}
Expand Down