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

Parse without validating signature. #205

Closed
laurencenoton opened this issue Feb 12, 2017 · 16 comments
Closed

Parse without validating signature. #205

laurencenoton opened this issue Feb 12, 2017 · 16 comments

Comments

@laurencenoton
Copy link

laurencenoton commented Feb 12, 2017

I want to use this library on both front-end client and back-end server, as my design uses JWT.

The server will use the JWT as a way of knowing who is making API calls. IT will validate the JWT signature etc.

However my front-ends are a desktop client and also a website. They have no need to worry about the signature..but I do want them to be able to pull the contents out from the JWT so it can render the front end based on the users Claims. I want to be able to display their name and use the Claims to show and hide functionality.

I have no requirement for the the signature to be validated by the client, as the client is inherently insecure, and my server APIs are what do the work and are secured. I cannot give the desktop or browser application the key obviously. I'm not convinced an asymmetric answer is the complete answer.

Can you make verifying the signature optional?

@laurencenoton
Copy link
Author

Ok I've seen #175 and issue #86 .

Really want this to happen ASAP! Can I help, or are you guys just in the process of getting it merged and signed off?

@bhowell2
Copy link

@laurencenoton, You CAN read the body if you want. You just need to parse it yourself. So, you need to parse the token in Base64 - realize that the body and header are just stored in plain text (so long as you're not encrypting the JWT and just signing it). I think the Les really wanted to keep stuff like this out of the library, though; because it could lead people to security issues by bypassing the signature verification and just taking the body - this doesn't mean you cant do it yourself though.

@lhazlewood
Copy link
Contributor

You don't need to parse it yourself. Use a SigningKeyResolver and ignore any resulting signature exception. :) That way your code will be self-documenting that you are explicitly ignoring the security model of JWT based on your specific use case (i.e. your actions were intentional and documented in code as such).

@lhazlewood
Copy link
Contributor

FWIW @laurencenoton, asymmetric keys are exactly the solution for your client use case. You can embed the public key in your client and use it to verify the signature that was created by the private key in the server.

Again, if you want to avoid that (which is less than ideal from the JWT spec committee's opinion - it's why asymmetric keys were invented after all), you can do so by using a SigningKeyResolver and ignoring any SignatureException.

@lhazlewood
Copy link
Contributor

lhazlewood commented Feb 14, 2017

I'll say it again, like I've said elsewhere: JWTs and the keys used when signing them are largely influenced by the audience of the JWT. You shouldn't expect that a shared private key JWT would be usable everywhere in your infrastructure or application stack. If you want to send it (and have it readable) by a client (a different audience), create a new JWT that is signed asymmetrically and send that one to the client.

I don't know where this idea that once a JWT is created it has to be used everywhere. They shouldn't. JWTs weren't designed with that (limited) idea in mind, and in fact, that practice significantly reduces your security posture because it creates a larger exposure/attack area.

Security is all about context - context drives the security choices, not the other way around.

HTH!

@dogeared
Copy link
Contributor

dogeared commented Feb 14, 2017 via email

@bpappin
Copy link

bpappin commented Mar 23, 2017

I was able to work around the signature error to parse the claims, by stripping the signature off after the second period.

@dogeared
Copy link
Contributor

@bpappin - That's an insecure way to parse a JWS.

If the JWT is signed, and is therefore a JWS, the specification makes it incumbent upon the JWT library to verify that signature. This is laid out in:

"However, in all cases, at least one JWS Signature value MUST successfully validate, 
or the JWS MUST be considered invalid."

@bpappin
Copy link

bpappin commented Mar 23, 2017

@dogeared unfortunately that's not always a reasonable specification for the use of this kind of token, in the use-case of client/server.

In the case where the server issues the token to authenticate the client, the client doesn't actually need to validate the token. And in fact should not be able to do so. The moment the client can sign the token, the server can no longer trust it.

When dealing with mobile clients, the situation is compounded by the update cycle, and the likelihood of someone poking around in the binary. It essentially makes the signature worthless if the client ever knows what it is.

So, although I have heard the arguments for requiring the signing token at both ends, and even understand the argument, the scenario does not work in the real world.
It is long past time that this particular issue was considered.

@dogeared
Copy link
Contributor

I don't think I mentioned having an untrusted client (like a mobile app) sign JWTs. The solution (and one that is spec compliant), is simply to use asymmetric keys (as @lhazlewood has addressed previously). It's perfectly safe to embed a public key in a mobile app. The mobile app is then empowered to definitively verify that the incoming JWT has not been tampered as its been signed with the server's private key.

The other easy alternative (also mentioned previously by @lhazlewood) is to use the SigningKeyResolver and catch a thrown exception. This makes your code self-documenting and easily updated if the spec (and then the JJWT) are revised.

@bpappin
Copy link

bpappin commented Mar 23, 2017

We have tried using the SigningKeyResolver, but we have to use roundabout wiring to get access to the claims. At least as far as we understood it.

A key pair is certainly something we considered, and we may implement in the future if it's needed, but it's really overkill for this instance.
There is also still the problem of the update cycle, and permanent keys, which are obviously pretty useless.

@dogeared
Copy link
Contributor

Here's a SigningKeyResolver example. You have access to the header and the claims in the method. Hope it helps!

SigningKeyResolver signingKeyResolver = new SigningKeyResolverAdapter() {
    @Override
    public Key resolveSigningKey(JwsHeader header, Claims claims) {
        // Examine header and claims
        return null; // will throw exception, can be caught in caller
    }
};

try {
    Jwts.parser()
        .setSigningKeyResolver(signingKeyResolver)
        .parseClaimsJws(token)
        .getBody();
} catch (Exception e) {
  // no signing key on client. We trust that this JWT came from the server and has been verified there
}

@bpappin
Copy link

bpappin commented Mar 23, 2017

Thanks for making that effort @dogeared, i will review it shortly.

@jong64
Copy link

jong64 commented Mar 24, 2017

The recommended approach of using SigningKeyResolver seems to work fine, although having to write verbose anonymous inner class and deal with the slight overhead of (unnecessary) exception is less than elegant in my opinion.

I tested it even with an expired token, and it worked fine, because apparently the resolveSigningKey() method (or any other overloaded flavor) still gets invoked even if the token is invalid from the point of view of expiration time.

My point here is that, I'm willing to live with the SigningKeyResolver approach, AS LONG AS the library author can guarantee us that the parsed (but untrusted) header and claims will "always" be made available to the application code from within the anonymous class. For instance, the parser should NEVER throw ExpiredJwtException (or any other similar exception reporting on constraint violation) until after the resolveSigningKey() method is invoked.

By the way, I appreciate the time and effort the authors put in to develop and maintain this library.

@zbstof
Copy link

zbstof commented Jun 24, 2017

Found a use-case for this. Using SigningKeyResolver for successful case is fine.
But what about the error case?
I want to populate an audit event with the issuer claim in the authorization detail:

public class JwtAuthenticationProvider extends AbstractUserDetailsAuthenticationProvider {
    @Autowired private UserDetailsService userDetailsService;
...
    @Override
    protected UserDetails retrieveUser(String username, UsernamePasswordAuthenticationToken authentication) throws AuthenticationException {
        String rawToken = ((JwtAuthenticationToken) authentication).getToken();
        try {
            SigningKeyResolverAdapter signingKeyResolver = new SigningKeyResolverAdapter() {
                @Override
                public byte[] resolveSigningKeyBytes(JwsHeader header, Claims claims) {
                    String username = claims.getIssuer(); // <- how do I set this as a authorization detail?
                    UserDetails userDetails = userDetailsService.loadUserByUsername(claims.getIssuer()); 
                    return userDetails.getPassword().getBytes();
                }
            };
            String username = Jwts.parser().setSigningKeyResolver(signingKeyResolver)
                    .parseClaimsJws(token).getBody().getIssuer();
            return userDetailsService.loadUserByUsername(username);
        } catch (JwtException exception) {
            authentication.setDetails(username); // <-this always results in NONE_PROVIDED later down the line
            throw new BadCredentialsException("Invalid JWT: " + rawToken, exception);
        }
    }
}

Also note that I have to call userDetailsService.loadUserByUsername 2 times.
Maybe I'm missing an obvious way to do it, what do you think?

@laurencenoton
Copy link
Author

Got it. Thanks. I've continued to permit the client to get claims without the requirement to verify signature, but I'll take lhazlewoods input into consideration next time round.

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

No branches or pull requests

7 participants