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

fix(verify)!: Remove default none support verify methods, and require it to be explicitly configured #851

Merged
merged 7 commits into from
Nov 29, 2022

Conversation

jakelacey2012
Copy link
Contributor

@jakelacey2012 jakelacey2012 commented Nov 10, 2022

Description

This PR removes default support for the none algorithm verify methods, now you have to specify none before you can sign and verify. This is to fix the issue raised in #711, but still allow users to use none if they wish to do so for development/testing.

BREAKING CHANGE: Removes fallback for none algorithm for the verify method.

References

Testing

Please download this version of the package and then run the following code examples, which you can do by running [email protected]:jakelacey2012/node-jsonwebtoken.git#IPS-2481.

Simple test to get some background, should not throw... should not be effected by this change.

var jwt = require('jsonwebtoken');
const decoded = jwt.verify(jwt.sign({ foo: 'bar' }, 'sshhhhhh'), 'sshhhhhh');
// should decode and not error.

Test sign method with algorithm none option, should not error and return unsigned token.

var jwt = require('jsonwebtoken');
const sign_none = jwt.sign({ foo: 'bar' }, undefined, { algorithm: 'none' });
// return unsigned token.

Test sign method with algorithm none, and not specify none in options.

var jwt = require('jsonwebtoken');

jwt.sign({ foo: 'bar' }, undefined, { }); 
// this returns an error `secretOrPrivateKey must have a value`

jwt.sign({ foo: 'bar' }, 'secret', { }); 
// no error returned but token returned is signed with HS256

Testing verifying without none specified, should throw error please specify "none" in "algorithms" to verify unsigned tokens

const unsigned = jwt.sign({ foo: 'bar' }, 'secret', { algorithm: 'none' });
const decoded = jwt.verify(unsigned, undefined, { algorithms: [] });
// should throw error 'please specify "none" in "algorithms" to verify unsigned tokens'

Testing verifying with none specified, should not throw and return a decoded token.

const unsigned = jwt.sign({ foo: 'bar' }, 'secret', { algorithm: 'none' });
const decoded = jwt.verify(unsigned, undefined, { algorithms: ['none'] });
// should not throw and return a decoded token.

Describe how this can be tested by reviewers. Be specific about anything not tested and reasons why. If this library has unit and/or integration testing, tests should be added for new functionality and existing tests should complete without errors.

Please include any manual steps for testing end-to-end or functionality not covered by unit/integration tests.

Also include details of the environment this PR was developed in (language/platform/browser version).

  • This change adds test coverage for new/changed/fixed functionality

Checklist

  • I have added documentation for new/changed functionality in this PR or in auth0.com/docs
  • All active GitHub checks for tests, formatting, and security are passing
  • The correct base branch is being used, if not the default branch

@david-renaud-okta
Copy link
Contributor

Is this test still necessary? I think we should enable (with appropriate modifications) or remove this test as part of this change.

https://github.com/jakelacey2012/node-jsonwebtoken/blob/IPS-2481/test/async_sign.tests.js#L44-L52

@@ -34,7 +34,7 @@ describe('signing a token asynchronously', function() {
});

it('should work with none algorithm where secret is set', function(done) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Test description does not match anymore. Perhaps we should we flip this and assert an error when the none algorithm is specified.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@jakelacey2012 jakelacey2012 marked this pull request as ready for review November 15, 2022 09:22
@jakelacey2012 jakelacey2012 changed the title fix(sign&verify): Remove none support from sign and verify methods fix(sign&verify): Remove default none support from sign and verify methods, and require is explicitly. Nov 18, 2022
@jakelacey2012 jakelacey2012 changed the title fix(sign&verify): Remove default none support from sign and verify methods, and require is explicitly. fix(sign&verify): Remove default none support from sign and verify methods, and require it to be explicitly configured Nov 18, 2022
@jakelacey2012 jakelacey2012 changed the title fix(sign&verify): Remove default none support from sign and verify methods, and require it to be explicitly configured fix(sign&verify)!: Remove default none support from sign and verify methods, and require it to be explicitly configured Nov 18, 2022
…ethods, and require it to be explicitly configured

BREAKING CHANGE: Removes fallback for none algorithm for the verify method.
verify.js Outdated
Comment on lines 78 to 79


Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Intentional whitespace?

README.md Outdated
@@ -38,6 +38,7 @@ encoded private key for RSA and ECDSA. In case of a private key with passphrase
`options`:

* `algorithm` (default: `HS256`)
* * `none` MUST be configured in order to create unsigned tokens.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this comment necessary? Since algorithm must be supplied, there's no danger of someone unintentionally creating an unsigned token

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thats true, this sign method has always been explicit with the algorithm - my reasoning was mainly to make this as easily as possible for new people adopting the library.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure we want to make it easy to create none tokens :P I'd remove this personally, I think it adds confusion to someone wanting to use the library who isn't hugely experienced with best practices here - let's not call out none specifically.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah maybe if this documentation was to exist it would need to be included in a best practises section, which describes that none tokens should only be used for testing and not for production. Removing this to avoid confusing users sounds fine to me.

encoding: 'utf8'
});

jwt.verify(signed, null, {typ: 'JWT'}, function(err, p) {
expect(function () {
jwt.verify(signed, 'secret', {typ: 'JWT'});

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This case would already return an error before your changes, as you're passing in a secret and we do this check:

    if (!hasSignature && secretOrPublicKey){
      return done(new JsonWebTokenError('jwt signature is required'));
    }

We should have a test for both cases where secret is provided, or is null.

verify.js Outdated
Comment on lines 109 to 111
if (!hasSignature && !options.algorithms) {
options.algorithms = ['none'];
}

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If secretOrPublicKey and options.algorithms are both null / undefined, line 116 will error. This was previously not possible as if secretOrPublicKey is undefined then hasSignature is guaranteed to be false (per check inline 111), which means options.algorithms was always set to [none].

If alg === none then we'll have already failed, but if alg !== none then we'll get an can't read toString() of undefined error. It is correct that the token isn't valid in that case, but we should return an actual invalid token error (from passing this through to jws.verify).

IMO, we should also avoid changing the error message unnecessarily if we were previously going to be caught by the two checks above.

So what do you think about removing the check you've added on line 83, and putting the check here instead with the old condition. This will still prevent none algs being verified without being specified, as we no longer default to none in this case:

if (!hasSignature && !options.algorithms) {
    return done(new JsonWebTokenError('specify "none" in "algorithms" to verify unsigned tokens'));
}

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good catch, yeah what you've said makes sense let's move the check there.

@@ -41,16 +41,6 @@ describe('signing a token asynchronously', function() {
});
});

//Known bug: https://github.com/brianloveswords/node-jws/issues/62

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Did we mean to remove this test?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is something I changed as part of David's comment and was changed when we were removing the none functionality all together, since we're not doing that anymore I think we can just add this back in.

@@ -6,7 +6,7 @@ const util = require('util');
const testUtils = require('./test-utils');

function signWithAudience(audience, payload, callback) {
const options = {algorithm: 'none'};
const options = {algorithm: 'HS256'};

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We may be able to revert all these test changes with the latest change to sign. What do you think?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We would be able to revert but the places that we use none and verify we would need to configure that algorithm explicitly, do you see an advantage in using none in these tests or was comment more about making the PR as small as possible?

I think I prefer using HS256 for example rather than none, since its something we don't want to encourage and should be used in exceptional cases. Also means in the tests we just configure the algorithm, the secret and no options for the verify - the alternative seems verbose and repetitive IMO.

Let me know what you think :)

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yep agreed :)

@jakelacey2012 jakelacey2012 merged commit 8345030 into auth0:master Nov 29, 2022
@jakelacey2012 jakelacey2012 deleted the IPS-2481 branch November 29, 2022 14:32
@jakelacey2012 jakelacey2012 changed the title fix(sign&verify)!: Remove default none support from sign and verify methods, and require it to be explicitly configured fix(verify)!: Remove default none support verify methods, and require it to be explicitly configured Dec 1, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants