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

Allow unverified JWT #132

Open
FreifeldRoyi opened this issue Sep 2, 2019 · 22 comments
Open

Allow unverified JWT #132

FreifeldRoyi opened this issue Sep 2, 2019 · 22 comments
Milestone

Comments

@FreifeldRoyi
Copy link

Hey,
Is it possible to allow unverified JWTs to be injected ?

Thnx
Royi

@sberyozkin
Copy link
Contributor

@FreifeldRoyi Hi, no, I don't think so. Well it depends on what the implementation you are working with. Why would you want to do it ?

@FreifeldRoyi
Copy link
Author

Hi =)
I'm working with Quarkus.. so... SmallRye JWT I guess, which (it seems) doesn't work when no public key is specified. I would like to skip the public key verification step and just inject the JWT.
To overcome this problem I just (un)Base64 it myself but that's a piece of code I wish I could just delete at some point.

@rdebusscher
Copy link
Member

It is a highly insecure practice to trust any value supplied through the JWT token when no verifications are made.
This means that anyone can just create a JWT token and use it against your service. You can use path, request or header parameters for your use case.

@FreifeldRoyi
Copy link
Author

FreifeldRoyi commented Sep 18, 2019

@rdebusscher I agree. The JWT verification process is left for another service. The request can't "cascade" into the internal ecosystem if it is not verified by that service (that's why I ask if I can just inject the JWT with no verification)

@FreifeldRoyi
Copy link
Author

BTW, There are other jwt implementations that allow unverified access, e.g pyjwt

@sberyozkin
Copy link
Contributor

sberyozkin commented Nov 11, 2019

@FreifeldRoyi I'm not sure it can be supported at the MP-JWT level, I agree with @rdebusscher.
It is a custom verification where you'd like to replace the default MP-JWT one. Let me comment at smallrye/smallrye-jwt#144 with some smallrye-jwt specific suggestions

@benneq
Copy link

benneq commented Dec 9, 2019

I have the need for unverified JWT, too. (using Quarkus + SmallRye JWT)

Reason: We have an API Gateway, that does the verification. And if the token is valid, it will forward the request to the corresponding Quarkus Microservice. Though the Microservice shouldn't have to verify it again.

Maybe you could add some new Property like mp.jwt.verify.enable=false (default is true), to explicitly disable verification if needed.

@rdebusscher
Copy link
Member

And you are sure that the service can only be accessed from the API gateway only? That it is impossible to send a request to that service from any other location?

If not, you have a major security hole.

@pilhuhn
Copy link

pilhuhn commented Dec 12, 2019

The scenario @benneq describes is not so uncommon apparently. I am in a similar situation.

@Karm
Copy link

Karm commented Dec 12, 2019

@pilhuhn @FreifeldRoyi Why are you using JWT on that backend service then? Couldn't you pass on from the gateway only whatever data you need and proceed with that? (processing them manually in the service). No need to use JWT at all if you don't need it IMHO.

I am looking at it from my TLS termination and client cert authentication POV.

  • client ID is baked into client certificate
  • httpd verifies that the client certificate is signed (not forged), it checks OCSP/CRL whether it hasn't been revoked
  • it grabs the client ID from the certificate
  • it passes the request with added client ID on to some simple backend that is completely oblivious to any TLS or certificates and blindly trusts anything that comes from httpd

@benneq
Copy link

benneq commented Dec 12, 2019

@rdebusscher You can achieve this pretty easily using Kubernetes or Docker with their internal network for inter-microservice-communication. And the only exposed port is 80, and this is routed straight to your gateway.

Or have you ever used Elasticsearch? It has no authentication at all. By default it only accepts requests from localhost, but you can change that restriction to some specific IP or IP range or even a specific network adaptor on your machine. Then for example you could use a private VPN tunnel to communicate with other Elasticsearch clusters on the other side of the planet and restrict the network communication to the VPN tunnel network adaptor.

Here's some Blog Post from auth0: https://auth0.com/blog/apigateway-microservices-superglue/
"We can now safely remove the code handling that from our microservices and assume that, if they're receiving a request, it's both authenticated and it passed the rate-limit checks. You now have guarantees."

I understand your security concerns, but they aren't valid if you have a secure network where you can make sure that there's only a single endpoint (API Gateway) for communication with the outer world. That's why I suggested that the user has to explicitly disable the default behavior.

@Karm Sure you can. But clientId may not be enough. Often the JWT also contains some data that can't be checked by the gateway. For example "IS_ADULT" role (for /watchMovie/{movieid} request) may not be validated by the gateway, but needs to be checked by the business logic of the microservice (because the auth decision depends on the specific movie). Though you have to pass down this data as well.
Alternatively you can call your Auth Service and ask it, if clientId has "IS_ADULT" role. But in my opinion that's unnecessary if that information is already part of the validated JWT.
You could of course create another slimmed down JSON representation from your JWT and then pass it as a custom HTTP header. But what the benefit? Why not simply pass down the original JWT?
There are many options...

@rdebusscher
Copy link
Member

you can make sure that there's only a single endpoint (API Gateway) for communication with the outer world.

Inside access is as important as outside access. But if you can guarantee it is ok.

Problem is that once you have such a possibility (not do the validation), users will start to misuse it.

@benneq
Copy link

benneq commented Dec 12, 2019

Is that really a concern? There's always a way to misuse software if you want to.

I can also write an endpoint with @PermitAll that exposes my private key, because I'm too lazy to setup an OAuth server for 3rd parties.

What I wanted to say: Security in a real application is no toy for unexperienced 12-year-old "devs".

But ... if you don't want to give us an easy way to allow unverified JWT, is there at least a not-so-easy way to get it done, by providing some piece of code that will override the validation with return true?

I'd be totally fine with that, too, if it's done with a few lines of code.

@FreifeldRoyi
Copy link
Author

@Karm I can think of a few valid use cases like auditing or storing for future use along with the data (which is also important for security).
A JWT is essentially a Base64 encoded JSON, and you already provide a way to parse it (and also do some verification).. I just need to inject it to my service without validation. As @benneq said, we're totally aware of the implications, but instead of using this cool feature, I'm implementing my own JWT parsing.
Example for unverified JWT usage

@benneq
Copy link

benneq commented Jan 20, 2020

There's a new infoQ talk by Netflix about how they do auth in their microservice landscape. And why they did change their former auth system. ( https://www.youtube.com/watch?v=eEZHZ806d6o )

Short summary of the interesting parts:

  • There's an Edge Service (= Zuul API Gateway) that calls some service which is responsible for validating the auth Tokens coming from "the outside (= Browser, TV, Apps, etc.)". (Minute 13:48)
  • If the Token is valid, they will create their own internal Token (called "Passport"), which is tailored to the needs of Netflix's server infrastructure (= only data that's often used by most of their services and won't change that often). Though Passport includes "user id", but won't include "membership status". And if some service needs additional data that's not part of the Passport, this service must do a remote call to fetch the needed data.
  • They don't do additional validation of their Passport token, because it's been created based on validated information. (Minute 42:21)

In other words:
Their whole internal service infrastructure is a trust zone. External requests get validated once at the Gateway, and afterwards every service can trust this information.

@rdebusscher
Copy link
Member

How about the support for alg:none but only for injection of claims? So user from the call is still 'unauthenticated' (no valid SecurityContext.getUserPrincipal(), @RolesAllowed and isUserInRole() return always false, ...)

@benneq
Copy link

benneq commented Jan 21, 2020

@rdebusscher I think that's a bad idea.
If I made sure that the token is valid beforehand, then the user is authenticated.
The whole purpose behind this strategy is to access the claims and also use @RolesAllowed and any other authorization technique - but without re-validating a token that is already known to be valid.

@radcortez
Copy link
Contributor

I think having a config to control if the JWT has to be validate or not is acceptable. By default is always on.

@sberyozkin
Copy link
Contributor

sberyozkin commented Feb 18, 2020

IMHO it can be optionally supported if Principal is not null, then if the property allowing for the non-validation is set, the token is parsed and it is asserted JsonWebToken.getName() returns the same name as that of Principal.

There must be some way at the low level spec level to enforce it. It is easy to document at the individual OIDC provider documentation page because it guarantees to its users the token will be validated. At the spec level like MP JWT we need to assert the verification has happened.

@wfrank2509
Copy link

wfrank2509 commented Feb 26, 2020

Having a related topic here for a project.

The information where to fetch the public key for verification is part of the JWT token.
It also contains other information (i.e. iss + some custom header fields) that are checked to verify the provided/computed url to fetch the public key is valid.

So the process needs to be 2 step when the token needs to be verified:

  1. read unverified JWT token and compute the public key url and fetch it (and cache it)
  2. verify the token using the public key (which is cached now for following calls having a JWT token with the corresponding meta info)

So I think having an optional way to register/provide a PublicKeyFactory that has access to the complete http request (and token) and that is called prior to the "real" verification would be very beneficial.

Also having a way to extend the Principal with further Information from the request/jwt would also be very useful in many cases.

as a side note: e.g. Using Kotlin extension methods makes it very intuitive to just get all additional information you would expect from the Principal.

I did similar things with other JWT implementations (mainly NodeJS).

@sberyozkin sberyozkin added this to the JWT-2.0 milestone Feb 27, 2020
@sberyozkin
Copy link
Contributor

@wfrank2509 I've added it to the JWT 2.0 milestone for us to review what can realistically be done...thanks

@GregJohnStewart
Copy link

GregJohnStewart commented Feb 3, 2024

👋 Hey guys, how's this going? I have a usecase related to testing; it's far too complex in the setup I have to have my JWT provider use the same certs as my JWT-loving Quarkus app (For testing I just don't mind it being unverified)

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

9 participants