diff --git a/CHANGELOG.md b/CHANGELOG.md index 536cd7db..5e9b8977 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,7 @@ - Drop support for the HS512256 algorithm [#650](https://github.com/jwt/ruby-jwt/pull/650) ([@anakinj](https://github.com/anakinj)) - Remove deprecated claim verification methods [#654](https://github.com/jwt/ruby-jwt/pull/654) ([@anakinj](https://github.com/anakinj)) - Remove dependency to rbnacl [#655](https://github.com/jwt/ruby-jwt/pull/655) ([@anakinj](https://github.com/anakinj)) +- Support only stricter base64 decoding (RFC 4648) [#658](https://github.com/jwt/ruby-jwt/pull/658) ([@anakinj](https://github.com/anakinj)) Take a look at the [upgrade guide](UPGRADING.md) for more details. diff --git a/lib/jwt/base64.rb b/lib/jwt/base64.rb index 14c9db28..fdf1bf95 100644 --- a/lib/jwt/base64.rb +++ b/lib/jwt/base64.rb @@ -14,22 +14,13 @@ def url_encode(str) end # Decode a string with URL-safe Base64 complying with RFC 4648. - # Deprecated support for RFC 2045 remains for now. ("All line breaks or other characters not found in Table 1 must be ignored by decoding software") # @api private def url_decode(str) ::Base64.urlsafe_decode64(str) rescue ArgumentError => e raise unless e.message == 'invalid base64' - raise Base64DecodeError, 'Invalid base64 encoding' if JWT.configuration.strict_base64_decoding - loose_urlsafe_decode64(str).tap do - Deprecations.warning('Invalid base64 input detected, could be because of invalid padding, trailing whitespaces or newline chars. Graceful handling of invalid input will be dropped in the next major version of ruby-jwt', only_if_valid: true) - end - end - - def loose_urlsafe_decode64(str) - str += '=' * (4 - str.length.modulo(4)) - ::Base64.decode64(str.tr('-_', '+/')) + raise Base64DecodeError, 'Invalid base64 encoding' end end end diff --git a/spec/jwt/jwt_spec.rb b/spec/jwt/jwt_spec.rb index 7488e438..dd919498 100644 --- a/spec/jwt/jwt_spec.rb +++ b/spec/jwt/jwt_spec.rb @@ -552,14 +552,14 @@ end context 'a token with invalid Base64 segments' do - it 'raises JWT::DecodeError' do - expect { JWT.decode('hello.there.world') }.to raise_error(JWT::DecodeError, 'Invalid segment encoding') + it 'raises JWT::Base64DecodeError' do + expect { JWT.decode('hello.there.world') }.to raise_error(JWT::Base64DecodeError, 'Invalid base64 encoding') end end context 'a token with two segments but does not require verifying' do it 'raises something else than "Not enough or too many segments"' do - expect { JWT.decode('ThisIsNotAValidJWTToken.second', nil, false) }.to raise_error(JWT::DecodeError, 'Invalid segment encoding') + expect { JWT.decode('ThisIsNotAValidJWTToken.second', nil, false) }.to raise_error(JWT::Base64DecodeError, 'Invalid base64 encoding') end end @@ -736,8 +736,8 @@ context 'when token ends with a newline char' do let(:token) { "#{JWT.encode(payload, 'secret', 'HS256')}\n" } - it 'ignores the newline and decodes the token' do - expect(JWT.decode(token, 'secret', true, algorithm: 'HS256')).to include(payload) + it 'raises an error' do + expect { JWT.decode(token, 'secret', true, algorithm: 'HS256') }.to raise_error(JWT::Base64DecodeError, 'Invalid base64 encoding') end end @@ -949,34 +949,4 @@ def verify(*) end end end - - context 'when invalid token is valid loose base64' do - it 'does not output deprecations warnings' do - expect do - JWT.decode("#{JWT.encode('a', 'b')} 9", 'b') - rescue JWT::VerificationError - nil - end.not_to output(/DEPRECATION/).to_stderr - end - end - - context 'when valid token is invalid strict base64 and decoded with the correct key' do - it 'does outputs deprecation warning' do - expect { JWT.decode("#{JWT.encode('payload', 'key')} ", 'key') }.to output(/DEPRECATION/).to_stderr - end - end - - context 'when valid token is invalid strict base64 and decoded with the incorrect key' do - it 'does not output deprecation warning, even when decoded with the correct key' do - token = JWT.encode('payload', 'key') - expect do - begin - JWT.decode("#{token} ", 'incorrect') - rescue JWT::VerificationError - nil - end - JWT.decode(token, 'key') - end.not_to output(/DEPRECATION/).to_stderr - end - end end