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

Using Keycloak/OKTA with jwt instead of sessions #9120

Closed
1 task done
yelhouti opened this issue Jan 26, 2019 · 75 comments
Closed
1 task done

Using Keycloak/OKTA with jwt instead of sessions #9120

yelhouti opened this issue Jan 26, 2019 · 75 comments

Comments

@yelhouti
Copy link
Contributor

Overview of the feature request

I would like to be able to use jwt instead of sessions when working keycloak.

Motivation for or Use Case

Using jwt instead of sessions has many advantages including having stateless micro-services. I don't think you guys need more convincing, since UAA is staeless.

There is a security problem with how OAuth2 is implemented today (as discussed here: #6941 (comment)), and while fixing it I would prefer to do things the right way.

Related issues or PR

It have been discussed in some comments on other issues, but I prefer having a new issue that either fixes the concern, or explain the choices to other users.

  • Checking this box is mandatory (this is just to show you read everything)
@PierreBesson
Copy link
Contributor

We don't plan to do this as there was specific reasons why it was decided to do stateful Oauth2 instead of stateless, see this twitter thread: https://twitter.com/avdev4j/status/1087652807505780736

As for the sign out problem you talk about, it should be fixed in the last 5.8 release : #8757

@yelhouti
Copy link
Contributor Author

@PierreBesson I'm talking about security problems, not the logout ones. Also, if someone manages to access the token localStorage (we can also use sessionStoarge by the way) he could also access the sessionCookie, so no difference there. Any other reason?

@PierreBesson
Copy link
Contributor

It seems you know what you are talking about so we should discuss this more. I'm reopening the issue. ping @mraible.

@mraible
Copy link
Contributor

mraible commented Jan 26, 2019 via email

@yelhouti
Copy link
Contributor Author

@mraible even if httpOnly flag is used the attacker could send any request through my browser when using sessions, which opens to attacks like XCRF.
Also I'm not suggesting using implicite flow, but hybrid flow (which doesn't require a client secret, and allows for refresh tokens on the front end), implicit flow is such a hassle since you spend time redirecting to retrieve a new access token.

@mraible
Copy link
Contributor

mraible commented Jan 26, 2019

@yelhouti If you want to create a pull request, I'll be happy to review the code.

@yelhouti
Copy link
Contributor Author

yelhouti commented Jan 26, 2019

@mraible can I first create project, we agree on what is generated, then merge?
Also, I didn't mention that PKCE used to be a necessity for iOS mobile apps, since older iOS versions allow for multiple apps using the same URL (scheme) now (since iOS 10 I think) you can use https with domain verification, so not a problem anymore.

@mraible
Copy link
Contributor

mraible commented Jan 26, 2019

Here's the process I typically use when modifying JHipster:

  1. Generate a new app, make the necessary changes, get it all working. Check into source control.
  2. For the generator-jhipster project, make changes to templates, and iterate until I can generate the same project as the final result of step 1.
  3. Create pull request with necessary changes. Fix any test failures caught by CI.

My co-worker @aaronpk submitted a new draft for OAuth 2.0 browser-based apps that recommends authorization code flow + PKCE.

We don't support CORS on our /authorize endpoint just yet, so it's not possible to do PKCE in a browser with Okta today. We hope to fix that in the next couple of months. I'm not sure about Keycloak.

@yelhouti
Copy link
Contributor Author

yelhouti commented Jan 27, 2019

Keycloak support CORS, they also have valid redirect URIs which might be required.
What I will do is step one, validate with you, then do the others steps.

@avdev4j
Copy link
Contributor

avdev4j commented Jan 28, 2019

I am very interested in this feature @yelhouti
Feel free to fork the generator and open a pull request (even with WIP status). We could probably help you if it's necessary.
👍

@yelhouti
Copy link
Contributor Author

First I will create the POC with a monolith project (it's almost done) then I will contact you for wokring on the generator. I'll share my code with you @avdev4j very soon. feel free to talk to me on gitter so we can better organise.

@VinodAnandan
Copy link
Contributor

Hi,

I agree with the fact that a long lived insecure session has more security implications compared to the short lived tokens.

As HTTP is stateless, the protocol session was mainly used to identify the request. The OpenID connect enables the Identity, Authentication, Authorization. If OIDC is already utilized for IAM, then there is no need of using additional session handling. In-fact it may result in a more complicated architecture as well as possible security and operational issues. Session is not a good friend for the microservice architecture.

To implement session management securely - https://www.owasp.org/index.php/Session_Management_Cheat_Sheet

There are several implementations of SPA with PKCE

https://damienbod.com/2019/01/09/securing-angular-applications-using-the-openid-connect-code-flow-with-pkce/

https://github.com/damienbod/angular-auth-oidc-client

https://github.com/IdentityModel/oidc-client-js

@yelhouti
Copy link
Contributor Author

Here my version with angular and keycloak: https://github.com/elhoutico/jhipster-keycloak-stateless
There are still few things todo (check the TODOs) and help is appreciated

@yelhouti
Copy link
Contributor Author

@VinodAnandan @mraible or anyone, can you tell me if PKCE is needed in any other case than the one I mentionned (with iOS) to see how important it is to implement. Thanks

@mraible
Copy link
Contributor

mraible commented Jan 30, 2019

@yelhouti Can you provide a brief summary of what you did to get things working? I see you're using keycloak-js. Does this store the access token in local or session storage? If so, I'm not in favor of this change as it reduces security.

@yelhouti
Copy link
Contributor Author

yelhouti commented Jan 30, 2019

@mraible keylcoak-js doesn't use storage for storing the tokens, also I'm planning to use session storage for my implementation that replaces keycloak-js to make it work with Okta and solve other problems. can you tell me how an attacker that manages to get access to my machine/browser and steal the token from storage, not able to use the cookie (I can show how the opposite is true meaning cookies are less secure).

What I did is:

  • get/refresh the token in the front end and send it with each request
  • verify the tokens signature and audience using the private key instead of and endpoint using the new OIdC support by spring security.
  • some other minor stuff

@mraible
Copy link
Contributor

mraible commented Jan 30, 2019

My colleague, @rdegges, wrote a post about what's wrong with using local storage, which I believe applies to session storage as well. The most common way that attackers can get access to your local storage is via 3rd party scripts. Of course, a developer probably won't add 3rd party scripts, but as soon as marketing gets involved, some might be added.

For cookies, you can use an httpOnly flag to prevent JavaScript from having access to them.

I think it's OK to add what you're implementing to JHipster, but I don't think it should be the default option. I think we should prompt the user to choose between authorization code flow (the current setup) and implicit flow. Then they can make the security decision themselves, and we can warn them that implicit flow is less secure.

If we do add support for implicit flow, we should also enable a CSP by default that says "only scripts from this app" are allowed.

@yelhouti
Copy link
Contributor Author

yelhouti commented Jan 30, 2019

@mraible, With all due respect to your colleague, if some javascript can be used to access storage, it can be used to send any request using the cookie without retrieving it, it might be less convenient for the attacker but for me it's the same. Also, using cookies opens you to more attacks like XSRF, where a script can execute GET requests using your cookie without even injecting malicious code, getting access to your browser or attacking the server, but only abusing the trust the server has in your browser (in his own website, the attacker can send GET requests to yours if both are open inside the same browser, and use your cookies in the process).
Also open to discuss the matter with your colleague if his interested.
By the way I read the article, I agree with most point except using sessionStorage for JWT, which for fairness he doesn't say to avoid at all cost.
Here is OWASP take on the subject: https://www.owasp.org/index.php/JSON_Web_Token_(JWT)_Cheat_Sheet_for_Java#Token_storage_on_client_side
And is exactly what I'm planning to do.

@mraible
Copy link
Contributor

mraible commented Jan 30, 2019

That's good info @yelhouti, thanks for the link!

Another thought I had today is that this is already implemented in Ionic for JHipster. When you create an Ionic project, it sets up a resource server on the JHipster side and uses implicit flow. In the next version, I'll upgrade to Ionic 4 and try adding PKCE support. Since you can use Ionic for PWAs, and it supports Angular, React, and Vue in v4, that could be an option.

@ruddell
Copy link
Member

ruddell commented Jan 30, 2019

@yelhouti I think you are forgetting about the CORS and XSRF protection in JHipster

  • CORS prevents an attacker from making a request to your backend unless you specifically add their domain to the authorized origins.
  • The XSRF token is extracted from a cookie value and added as request header to any PUT/POST requests. This also prevents an outside site/script from making a valid request that changes data (other domains don't have direct access to your domain's cookies, so can't extract the value).

In the link you posted, it addresses this in the section notes:

Note:
It's also possible to implements the authentication service in a way that the token is issued within a hardened cookie, but in this case, a protection against Cross-Site Request Forgery attack must be implemented.

Note that I'm not trained in security so others will probably know better.

@VinodAnandan
Copy link
Contributor

Hi,

In the implicit flow, the token exchange is happening via GET requests and it will result into the token getting logged in different servers, client side, etc. An abuser who gets access to this token can easily impersonate the user.

Using PKCE, the client app only receives the authorization code via the GET request and it's exchanged via a POST request with the token endpoint to obtain tokens. Even if the abuser gets the authorization code, it will be protected with PKCE (Proof Key for Code Exchange ).

@yelhouti
Copy link
Contributor Author

yelhouti commented Jan 31, 2019

First of all guys, thank you all for your comments, I really appreciate it.
@mraible I will definitely check the cods and see if we can reuse/improve it. Also, since Ionic 4, a simple blueprint might help generate the Ionic project if stateless OIdC support is added.
@ruddell I didn't forget about the protections, these are complimentary:
If you follow the linked you mentioned, about using cookies it says on protecting against XSRF: The following list assumes that you are not violating RFC2616, section 9.1.1, by using GET requests for state changing operations. Which I can tell you many people use (example: mark message as seen when user X GET's it) to summarize XSRF tokens protect only POTS/PUT and XHR requests when implemented correctly, GET request can't be protected that way since (your second point) they can circumvent CORS protection. If you want more information on how or to discuss the matter more in dept please DM me on gitter it would be a pleasure. (Edit: experience is often more valuable that training, and we are all human who forget things, so thanks for helping :) ).
@VinodAnandan , as I tried to explain I'm planning to use hybrid flow instead implicit flow which doesn't not have the problem of GET params. for PKCE, the RFC (https://tools.ietf.org/html/rfc7636#section-1) says it's used to avoid attacks "within a communication path not protected by Transport Layer Security (TLS), such as inter-application communication within the client's operating system" this is not possible with browser and the only thing I can think of it resembles to is "scheme squatting" for iOS which is not a problem anymore if you use universal links/deeplinks (https://developer.apple.com/ios/universal-links/) available since iOS 9. Am I missing a point or there is no need for PKCE anymore in our use case.

@mraible
Copy link
Contributor

mraible commented Jan 31, 2019

@yelhouti My colleague, @aaronpk, proposed a new spec for OAuth 2.0 that recommends PKCE for browser-based apps. See OAuth 2.0 for Browser-Based Apps for more information. From its overview section:

For authorizing users within a browser-based application, the best current practice is to

o Use the OAuth 2.0 authorization code flow with the PKCE extension
o Require the OAuth 2.0 state parameter
o Recommend exact matching of redirect URIs, and require the hostname of the redirect URI match the hostname of the URL the app was served from
o Do not return access tokens in the front channel

Previously it was recommended that browser-based applications use the OAuth 2.0 Implicit flow. That approach has several drawbacks, including the fact that access tokens are returned in the front-channel via the fragment part of the redirect URI, and as such are vulnerable to a variety of attacks where the access token can be intercepted or stolen. See Section 7.8 for a deeper analysis of these attacks and the drawbacks of using the Implicit flow in browsers, many of which are described by [oauth-security-topics].

Instead, browser-based apps can perform the OAuth 2.0 authorization code flow and make a POST request to the token endpoint to exchange an authorization code for an access token, just like other OAuth clients. This ensures that access tokens are not sent via the less secure front-channel, and are only returned over an HTTPS connection initiated from the application. Combined with PKCE, this enables the authorization server to ensure that authorization codes are useless even if intercepted in transport.

@yelhouti
Copy link
Contributor Author

yelhouti commented Jan 31, 2019

EDIT: this comment doesn't serve any purpose anymore :)

@VinodAnandan
Copy link
Contributor

Hi,

I will explain the other situations when the authorization code can be leaked and abused. Between a public client and an Identity Provider Server, there can be many other servers and devices ( Load balancer, Firewalls, WebServers, app servers, etc.). Most of these will log (e.g: access_log) the URL parameters including the public client (browser history).If an abuser gets the authorization_code from any of these sources, he can send it to the Identity Provider and obtain the tokens to impersonate the user.

For a PKCE flow, along with the initial request, the public client will send a code_challenge/hash of the random string. And when the public client will request the tokens with an authorization code, it must provide the code_verifier (random string) otherwise the tokens won't be issued.

Authorization code and refresh code is really sensitive and it needs more security. Silent renewal is a secure replacement of using refresh token SPA.

All the information mentioned above can be implemented in an Angular SPA and it's explained in the following link.

https://damienbod.com/2019/01/09/securing-angular-applications-using-the-openid-connect-code-flow-with-pkce/

If you need further details, please let me know.

@yelhouti
Copy link
Contributor Author

@VinodAnandan authorization_code can't be used twice, and if you have SSL from your keycloak server the browser/app there is no way it can be retreived. I understand exactly how PKCE works, what I don't see is the need for it when you have HTTPS. (event query parameters are intercepted when you use HTTPS). Any way, If you guys insiste on adding PKCE, even if I don't agree I will oblige, it's 10 more lines of code, I will live :) .
But thanks for confirming my suspicions.
Also I found this: https://github.com/Hitachi/contributions/wiki/Description-of-RFC7636-for-keycloak that says OIdC protects against the same attacks using nonce...

@mraible
Copy link
Contributor

mraible commented Feb 7, 2019 via email

@yelhouti
Copy link
Contributor Author

yelhouti commented Feb 7, 2019

@mraible I'm using PKCE so no problem with that. How do you suggest we move forward then please?
from my stand point:

  • We should keep sessions even if I don't like then for backward compatibility (but use the private key to check the token instead of user endpoint for obvious performance reason + use OIdC instead of OAuth2 and add the Audience validator).
  • We should be able to have stateless clients without using JHipster UAA
    @deepu105 is having 4 choices that bad? :p

@pascalgrimaud
Copy link
Member

@yelhouti : the problem is not having 4 choices, the main problem is to maintain the option once it is in the main generator-jhipster.
We have too much tickets opened right now, and I'd prefer to try closing / solving them, rather than adding new option.

That's the reason I proposed to implement your solution inside a blueprint:

  • you can propose your solution to the community
  • you don't have to wait for review, pull request
  • you'll have your own CI
  • we can easily see if the option is popular or not
  • etc

That's why I love Vue.js blueprint and it's the perfect example what it is possible to do.

So as Deepu, I think the current options are enough: JWT, session, OAuth2, UAA

@PierreBesson
Copy link
Contributor

Personally I'm against a blueprint for authentication. IMO for structuring options like this one it adds more maintenance burden (always keeping up with the main generator) and a discoverability issue (people don't know about it). Also knowing the security code, it is actually only a handful of files and I think it's totally possible to maintain an additional choice.

For me this Stateless OIDC option is good to have and probably will have a larger audience than the UAA solution as you can use any IdP. It would be a shame to pass on the offer of @yelhouti's contribution.
So +1 for me.

@deepu105
Copy link
Member

deepu105 commented Feb 8, 2019

@PierreBesson I'm definitely against adding it as an option as I already said. We have to think of our end users and the majority of them are not security experts and would be confused by seeing 2 options for OAuth. Many of them trust us to provide the right defaults and most of them believe that if we provide an OAuth option it's a good one. So I'll be more considerate of those users rather than a minority who are security experts and will know what to choose. So I stand by my decision as it will only add more maintenance(it's not just a few files, you need to take the Angular, React and now Vue clients also into account), more confusion to end users and will benefit may be few people. So for me, the benefit doesn't outweigh the complexity. Whereas building it as a blueprint or module means the maintenance burden is only on @yelhouti and not on the entire community and core team.

@PierreBesson
Copy link
Contributor

@deepu105, Very good points !

I have a last idea, @yelhouti do you think it might be possible for your option to "merge" with the UAA option, in my understanding they do similar thing, it's just that in case of UAA we provide our own JHipster based IdP vs using Keycloak/Okta.

@yelhouti
Copy link
Contributor Author

yelhouti commented Feb 9, 2019

@PierreBesson Unfortunately, they will be too much useless code generated in the frontend and backend, for user/role management, what I could do on the other hand, is make the UAA code easier to switch to another Idp and have a script/blueprint/module that removes/changes all the unusable code.

@PierreBesson
Copy link
Contributor

@yelhouti This sounds like a plan as long as @xetys is OK with it. Note that we do already have a --skip-user-management flag but I'm not sure if it's applicable in this case.

@yelhouti
Copy link
Contributor Author

yelhouti commented Feb 12, 2019

@PierreBesson thanks for the info, didn't know about that, I will try it. @xetys are you ok with this approach?
Thank you guys for your help.

@rdegges
Copy link

rdegges commented Feb 12, 2019

Heyo! I saw my name mentioned in this thread, in regards to a suggestion I made to not use JWTs for web authentication.

I've covered this extensively in talks I've given, but I just wanted to chime in with one aspect that I didn't see discussed in this thread, but one which I think is probably the most important: if you're using JWTs for web auth, you aren't going to get any stateless benefits.

Here's what I mean.

Let's say that you modify JHipster to allow for users to authenticate and store a token in the browser using localStorage or sessionStorage (both of which OWASP says not to do, btw: https://www.owasp.org/index.php/HTML5_Security_Cheat_Sheet#Local_Storage). In this scenario, you're now opening yourself up to XSS, which is probably the most difficult type of web attack to prevent today.

But ignore that for a moment, because attackers can XSS you even if you're authenticated using a sever-side session cookie, and can just make requests to your backend to accomplish stuff (albeit, it's much more difficult for them, since they can't make requests impersonating a user remotely unless the victim's browser is active).

So anyway: you've got your JWT as your session token now.

The problem is this: your JWT will have a timeout on it. That timeout is a variable that a developer will set to some value. For "stateless" benefits, most developers set this value to some amount of time > a few seconds, otherwise, they'd have the same old issues as typical server-side session cookies: they would need to retrieve the user's data out of some central db/cache.

So you've got this JWT now, and let's say it has a 10 minute timeout. Cool. If an average user makes 10 page requests per minute, you've essentially saved yourself 100 database/cache lookups, right? Yey!

But going back to the way this works, what happens if you want to revoke or change a user's permissions? Or what happens if the user updates their information? Or what happens if the user overruns their account balance and doesn't have enough money in their account to fulfill some operation?

Do you have any way to log this user out? To stop them from accessing your service?

The answer is "no", and that's the problem.

JWTs are stateless by design, but web authentication isn't meant to be stateless. The entire point of web authentication (and web security, in general) is to guarantee consistency of sensitive data like user information.

If a user is logged into your app as an admin, but then their permissions are revoked, if your server-side code is only locally validating that JWT then you're now in a compromised state. You have opened up the ability for users to do things they should not be able to.

The solution, of course, is to build a centralized revocation list. Some sort of DB or cache that keeps track of all issued tokens, so that every request a user makes to your site is then validated against this central DB to make sure the token hasn't been revoked, or the user's data hasn't changed since the token was issued.

But if you do this, you're now right back to square 1: everything is centralized, and your app is not stateless. Plus, you're now making the client send a larger token over the network to identify itself on every single request (because JWTs have user data embedded inside of them for statelessness), which means network requests are slower from the client to your server.

All in all, I'm not a huge fan of using JWTs for web authentication. I don't see the benefit. The risk of using them is:

  • Added complexity
  • Larger network reqeusts for clients
  • More security risk

Hope that makes sense.

@yelhouti
Copy link
Contributor Author

@rdegges I see we have a fervent defender of cookies here :) . So here is why I disagree and I beleive you get benefits when using JWT:

  • In the thread OWASP link you mentioned: "Use the object sessionStorage instead of localStorage if persistent storage is not needed. sessionStorage object is available only to that window/tab until the window is closed."
  • As you said, you don't protect yourself from XSS when you use cookies, indeed the user can't get cookie and send it to the attacker, but he doesn't need to, the script can send requests directly and the browser will add the cookie for authentication making it easier to automate attacks. further more, if the attacker can execute code on your browser he can steal your username password, and now you are in worst shape since other services can use similar passwords...
    To summarize XSS risks are much higher if you don't use an IdP...

If your access token are short lived, and you work with microservices, for each request a user does, there might multiple requests to the IdP to check the token, and revoking a refresh token is centralized any way on the IdP (with the microservices still being stateless). So my point it, yes web authentication is not really stateless (the IdP by the way has a session to allow SSO...) but client server communication should be to allow scalability... and the seconds the access token is valid is (and I think you would agree) in most cases not a problem.
To take your point one by one:

  • if the user updates his data, at most seconds later the data is updated, and how often do someone changes there name...
  • For the users balance, double spending problems are not solved by cookies anyway there is a centralized database for account balances and usually only one microservices (used by all the others) keeps the info cached and checks the requests...
  • If I manually remove a permission for a user, 10 seconds would almost never make a difference IMHO.
  • The way you block a user from accessing a service is by revoking the refresh token that is centralized any way and is part of OIdC spec by I think you already know that.

So, no, we can agree that the solution is not to build a centralized system, therefore all the points you make after that I don't agree with.

Now how about the benefits: well I mentioned them in my previous comments and you can find them all over the web.

Also, I sincerely thank you for taking them time and giving you feedback since it allows the team to get a global view of all the pros and cons of each solution.

Now this part is mainly for the Core team @deepu105 @mraible @PierreBesson @ruddell @jdubois (and sorry if I forget anyone interested):

  • UAA already uses JWTs (even though there are some fixes that still needs to be implemented)
  • I will try to have the same code for UAA and Keycloak/Okta and have the unecessary code removed using the flag mentionned --skip-user-management by @PierreBesson
  • End use will still have the possibilty choose and will see what the trend is to set the default option.

@VinodAnandan
Copy link
Contributor

Hi @rdegges,

Thank you for your comments. You are not comparing the correct things, please don't isolate the JWT from the OpenID Connect context.

Also, please consider a highly scalable application with microservices and multiple technology stacks, they need a common language to authenticate and authorize the user request.

XSS has far more abuse cases than just stealing session ID, Tokens, etc. Happy to discuss more about XSS if needed.

Also, OpenID connect delivers more security and privacy controls. I think your company (Okta) is a great believer and support for OpenID Connect.

Thanks and Regards,

Vinod

@rdegges
Copy link

rdegges commented Feb 12, 2019

Heyo!

Thanks for all the responses.

@yelhouti:

  • if the user updates his data, at most seconds later the data is updated, and how often do someone changes there name...

This scenario was an example. This is more problematic when it relates to authorization (like what is common in OAuth/OIDC with scopes). It's more of a problem when a user authenticates and gets a token that allows them to perform some action, but then that permission is changed or removed.

Then you have a security issue on your hands for the remaining duration of the token.

  • For the users balance, double spending problems are not solved by cookies anyway there is a centralized database for account balances and usually only one microservices (used by all the others) keeps the info cached and checks the requests...

In this case, you are saying the same thing as me, e.g. there must be some central service that checks the user account/profile data/whatever related user info there is to validate that there is enough $$$ (or whatever data is a precious quantity) to complete a request.

In all of these cases, you will have centralization and a local validation will be insufficient.

  • If I manually remove a permission for a user, 10 seconds would almost never make a difference IMHO.

In your apps this might be true, but this is literally the exact opposite or what security means =)

Authentication and authorization, by definition, will be "broken" if you cannot guarantee the consistency of your requests.

To be clear: I don't think there's anything wrong with using JWTs the way you're explaining here (particularly in the context of OIDC), so long as the developers building the systems know that they are making an explicit speed vs security tradeoff.

When you choose to use local validation + JWTs (like done in OIDC) you are basically shifting the burden of security correctness onto the end developer, and expect them to know/understand these complex tradeoffs in their codebases.

I've worked with hundreds of companies who've built products this way and they often times have absolutely no idea that because they're using 6-hour tokens they've essentially crippled their application security model.

That's why I even both mentioning this stuff: very few developers know enough (or care enough) about security to really understand these tradeoffs. I don't think it's fair to shift from a secure authn/authz system to an insecure one and put the burden on the end developer to understand :(

  • The way you block a user from accessing a service is by revoking the refresh token that is centralized any way and is part of OIdC spec by I think you already know that.

Noooooooooooo. As a matter of fact, refresh tokens shouldn't be used in almost any circumstance.

Your IdP should be maintaining sessions instead, and you should be doing transparent redirects and token refreshes that way, vs. leaving a refresh token around which increases risk.

Doing things this way still leaves you with the same problems I mentioned in my initial post: you cannot revoke user permissions, log users out, etc., because you are only relying on local validation. If your app needs the ability to do anything where permissions are being changed or removed, etc., you have to have centralization, and there are more efficient ways to do that than via JWTs.

Here's the way I generally position this stuff (high level);

If you don't care much about security, and want to use JWTs, use OIDC's Auth Code flow w/ PKCE. If you're building a server-to-server API, use OAuth Client Credentials. Never use refresh tokens.

If you care about security, just use a traditional session cookie approach. Nothing wrong with that. It's been around forever, is well vetted, and security professionals recognize it as the best way to handle web sessions.

@VinodAnandan:

Thank you for your comments. You are not comparing the correct things, please don't isolate the JWT from the OpenID Connect context.

I wasn't separating them. I was just using those examples to simplify the context, but all of that remains true for OIDC as well. OIDC with local validation is NOT a good solution if you care about security. If you decide to use token introspection on every request, however, along with the auth code flow, then sure, do whatever you want. Ya, it is less efficient, etc., but go for it.

Also, please consider a highly scalable application with microservices and multiple technology stacks, they need a common language to authenticate and authorize the user request.

If you're talking about server-to-server APIs here (which most microservices would fall into), OIDC will not help you (neither will JWTs) since the spec doesn't address that. The only solution you have is:

  • HTTP Basic Auth
  • OAuth Client Credentials (which doesn't require JWTs, and is just one layer on top of basic auth)

In both of these cases, those standards are well defined, and you can use them to handle your microservice auth in a simple way.

If you're building high-scale services in private networks (so your services aren't publicly exposed), I don't personally see anything wrong with going the client credentials route, and knowing that you're making a security tradeoff since you will be locally validating your tokens.

XSS has far more abuse cases than just stealing session ID, Tokens, etc. Happy to discuss more about XSS if needed.

Yes! I was just highlighting the obvious things here, but I'm pretty experienced with XSS =)

Also, OpenID connect delivers more security and privacy controls. I think your company (Okta) is a great believer and support for OpenID Connect.

Nooooo. OIDC does not deliver more security or privacy controls for first party apps. The entire reason that OAuth/OIDC was created in the first place was to handle delegated authn/authz, which is an entirely different problem.

If the website you're building will be exposing user data to third parties, then by all means, use OIDC auth code flow wherever you can to make your app easier for other services to interconnect with. But if you're talking purely about building a website that handles authn/authz, there are far more secure ways to handle things.

@yelhouti
Copy link
Contributor Author

@rdegges I think we can agree that storing the $$$ or anything of that nature in a token is just plain stupid plus this often than by an ACID transaction in db...
Furthermore when you retrieve an access token with a refresh token you permissions are updated, at each refresh = each 10 secs.
Tokens are by default are 10 Secs and revoking a refresh token blocks the IdP from generating a access token using it so your Noooooo is a bit of a stretch, also refresh tokens have normaly the same lifespan as a session (10 min) and if not refreshed you are logged out.
And, as I mentionned in earlier comments, redirects don't work with native mobile apps, and you need the refresh token for these case. I also can agree that the implicit flow (but with iframe) would solve them problem.
OIdC doesn't only help with third party auth, it always in a company to have one place where users authenticate one time and have only one password to access everything + SSO...
We are talking here about applications with microservices, the a simple website to showcase a company, I'm not suggestion to add OIdC to a wordpress website, it's JHipster...

@VinodAnandan
Copy link
Contributor

VinodAnandan commented Feb 12, 2019

Hi @rdegges

I think we have a different point of view on many things. In some cases, I may be wrong, if you could answer my following questions, it would be helpful.

  • Do you think localized Identity & Access Management is more secure than Federated/Centralized Identity & Access Management? if yes, why?
  • Do you think maintaining multiple individual identity stores without a proper security protection and monitoring controls will have less chance to get hacked than a centralized Identity store with more protection and monitoring?
  • Do you think SAML is a secure alternative to OIDC for federation? why? Or do you have any other preference?
  • What are the problems related to validating a short-lived token with a digital signature? What is the problem if a highly sensitive application is utilizing token introspection? what can be the problems related to classifying application in groups based on their security sensitivity and deciding token expiration based on it?
  • We are in a cloud era. If people start to rely on their network security, don't you think a cloud IAM provider is a very insecure option? You recommend basic auth and client credentials, but do you agree that if one mircoservice gets hacked or if its credentials get leaked, the attacker can steal all users data from all other microservices?
  • If a End-User can have visibility of all his authorized client's active sessions and have a privilege to revoke the access - is that a bad privacy design?
  • Do you think the option to obtain End-User Consent in OpenID connect is a bad privacy practice?

@rdegges
Copy link

rdegges commented Feb 15, 2019

Hi @VinodAnandan. I'll answer your questions inline.

Do you think localized Identity & Access Management is more secure than Federated/Centralized Identity & Access Management? if yes, why?

No. There's nothing wrong with centralizing or federating identity/access management. The only insecure parts are the ones I've mentioned, e.g., using local validation only. You can do federated IAM without local validation.

Do you think maintaining multiple individual identity stores without a proper security protection and monitoring controls will have less chance to get hacked than a centralized Identity store with more protection and monitoring?

I'm not really sure what you mean here by individual identity stores without security protection... Obviously, if whatever you are doing doesn't have adequate security then it will be worse than another option that is secure.

To that end, centralization doesn't really matter. If the centralized solution is better, I say go with that.

Do you think SAML is a secure alternative to OIDC for federation? why? Or do you have any other preference?

No. SAML is a bad choice. There are countless ways to mess up SAML implementations purely do the XML-based nature of the standard. As a matter-of-fact, I just did a write-up on this not too long ago: https://developer.okta.com/blog/2018/02/27/a-breakdown-of-the-new-saml-authentication-bypass-vulnerability This sort of thing is pervasive over years and years.

What are the problems related to validating a short-lived token with a digital signature? What is the problem if a highly sensitive application is utilizing token introspection? what can be the problems related to classifying application in groups based on their security sensitivity and deciding token expiration based on it?

The problem with short-lived tokens + digital signatures are:

  • It is insecure to trust local validation because the user authn/authz/record may have changed. That is anti-security.
  • If you want to trade security for speed, you can use local validation and short-lived tokens, but then if you want to have revocation functionality you have to re-centralize again anyway, so....

The problem with highly sensitive apps using token introspection is purely spec related. There's nothing high-level wrong with doing token introspection on every request, except that it is less efficient than normal session cookie authentication since the end-user is going to be passing larger tokens over the network on every request, vs a small signed session ID only. This impacts user experience in low-bandwidth scenarios.

If you want to know more about the spec issues/risk, you can read this excellent article: https://paragonie.com/blog/2017/03/jwt-json-web-tokens-is-bad-standard-that-everyone-should-avoid

The problem with using something like JWTs in JHipster by default is that you're pushing more security burden onto end developers, and are expecting them to know a lot of new things in order to build a reasonably secure app.

Whereas with session cookies a developer doesn't need to know anything to have a secure app, the same is not true of token-based authentication. Developers now need to know:

  • What endpoints they have, and what security guarantees they need for each endpoint?
  • What is an acceptable token lifetime/duration for their app, given their security needs?
  • How to handle token introspection/local validation, and what each of those things means, and what performance considerations they have?

I'm just not a huge fan of giving end-developers that much extra work to do, especially with it relates to something that could have a very real security impact on an application. Developers are trusting y'all to do the right thing for them =)

We are in a cloud era. If people start to rely on their network security, don't you think a cloud IAM provider is a very insecure option? You recommend basic auth and client credentials, but do you agree that if one mircoservice gets hacked or if its credentials get leaked, the attacker can steal all users data from all other microservices?

First part: do I think that cloud IAM providers are an insecure option? I don't necessarily think so. It's probably at least as secure as doing it yourself.

Let's say that you are deploying an enterprise app on AWS. Well, if you don't use a cloud-hosted IAM provider (like Okta) for IAM, and you roll it yourself, where do your users go? Into RDS? In that case, they're still sitting right there on AWS, except now you, as the developer, have a lot more work to do.

In regards to my recommendation of using basic auth or client credentials: these are basically the only options for machine-to-machine authentication today. What other standards are there? You could potentially use PKI, but that is going to be a one-off and not easily understandable by most people.

Regardless of whether you're using OAuth or Basic Auth for server-to-server API security, if you leak your credentials you are screwed no matter what :)

If a End-User can have visibility of all his authorized client's active sessions and have a privilege to revoke the access - is that a bad privacy design?

Not at all! But that functionality is ONLY POSSIBLE if you centralize your session management anyway! That is true of session cookies, JWTs, or any form of token auth. So in that scenario, the best option is to use session cookie auth regardless, since it is more secure and provides less risk.

Do you think the option to obtain End-User Consent in OpenID connect is a bad privacy practice?

Not at all. None of the things I said above have anything against that.

@yelhouti
Copy link
Contributor Author

@rdegges this is the second time you mention this point:

  • If you want to trade security for speed, you can use local validation and short-lived tokens, but then if you want to have revocation functionality you have to re-centralize again anyway, so....

And I really don't understand why you are trying to make it, obviously you must know how revocation works, and it being centralized with OIdC doesn't impact performance. When a refresh token is revoked it can't be used again to retrieve an access token and in the same time, you remove all communication between the IdP and all the SPs. So what are you talking about when you are mentioning re centralizing revocation like it's a bad/un-optimized thing.

Another point you are trying to make is:

Whereas with session cookies a developer doesn't need to know anything to have a secure app, the same is not true of token-based authentication. Developers now need to know:

  • What endpoints they have, and what security guarantees they need for each endpoint?
  • What is an acceptable token lifetime/duration for their app, given their security needs?
  • How to handle token introspection/local validation, and what each of those things means, and what performance considerations they have?

Well, as matter of fact, unless you use microservices (Monolith which is the encouraged and the default) you need to manually say that you don't want cookies and in that case you are making the choice to handling your security.
Also even if you use token validation through endpoints, you need to configure the validation endpoint the same way you would with the IdP public key/certificate (by the way you use the same configuration file/URL to configure both: .well-known/openid-configuration). for token duration and introspection we provide the defaults, if you decide to change them to a bad value that is still your choice...
So suggesting the team would be encouraging the use of JWT's by default for small apps even after our modifications would not be factual.

Sorry for the spam guys (@deepu105 @mraible @PierreBesson @ruddell @jdubois) : but what do you think of my last proposition (keeping both after fixing the security issues)?

@mraible
Copy link
Contributor

mraible commented Feb 16, 2019

@yelhouti I would prefer to see this as a module first. That way, you can just support the features you’d like to without needing to make it work for React and Vue too.

If your module becomes popular, we can merge it into the main generator. This also allows you to develop and release improvements at your own pace.

@VinodAnandan
Copy link
Contributor

Hi @rdegges ,

I really appreciate your reply. I am definitely interested to continue the conversation. If you prefer a private communication, you may contact me at vinod@owasp.org

Do you agree that we use the session as a way to identify a user as HTTP/s is a stateless protocol?

Is there any centralized session management technology which takes care of integration with multiple technologies in a microservice world, which also can be configured with different session security properties ( Idle Timeout, Absolute Timeout, Renewal Timeout, Simultaneous Session Logons, Session Revocation, etc. ) based on the security sensitivity of the application?

In a microservice model, if you provide access to the data based on user identity, even if a user's access token gets compromised the attacker can only take particular user data from other microservices. A microservice doesn't have to trust any other microservices just because they are in the same network or they know some secret. When an Identity Provider issues an access token for a user, the main application/SPA/client application can carry forward the user's access token to all other microservices.

To implement an effective central Identity and Access Management system, the system should have an end to end coverage. Consider a scenario where a user authenticated to an enterprise application via central IAM solution. But the application was integrated in a way that it only utilizes the central IAM solution for the initial identity establishment (login) and after that application is handling its own local session management to identify the user requests. If an attacker managed to steal the local session ID, it will be very difficult for the security team monitor the central IAM as well as the end-user to identify it and revoke the access.

I believe that the developer must know how his application authentication and authorization work at least at a high level. It doesn't mean that they need to learn complete spec or implement the spec by them self. Lack of understanding may result in insecure apps with weak authentication and authorization.

In order to simplify this, the OpenID foundation already provides different OpenID Connect Implementations designs for use cases and technologies ( Server side application, Single Page Application, Mobile App, etc.). OpenID foundation also provides certified and recommend implementations ( https://openid.net/developers/certified/ ). Developers just need to utilize it.

@mraible , please check this library: https://github.com/IdentityModel/oidc-client-js/wiki

@rdegges
Copy link

rdegges commented Feb 19, 2019

Hi @yelhouti:

And I really don't understand why you are trying to make it, obviously you must know how revocation works, and it being centralized with OIdC doesn't impact performance. When a refresh token is revoked it can't be used again to retrieve an access token and in the same time, you remove all communication between the IdP and all the SPs. So what are you talking about when you are mentioning re centralizing revocation like it's a bad/un-optimized thing.

It definitely does impact performance. If you need to guarantee the integrity of the requestor, then you need to make a request to a centralized IdP at some point. There is no way to do that without centralization (unless you decide to broadcast your revocation list with cache invalidation to your nodes or something, which is a whole other discussion).

You're talking about refresh tokens here being revoked. But again: that doesn't matter, because the problem is with the access tokens that can't be trusted for the duration of their lifetimes.

If you want to configure your services such that on every request the make a token introspection request to the IdP to validate the user's identity/accuracy, then sure! Go ahead and do it =) You will not be trading speed for security in this case. But the question is: why do that? Now you've just increased the complexity and network burden of your clients for no apparent gain?

Well, as matter of fact, unless you use microservices (Monolith which is the encouraged and the default) you need to manually say that you don't want cookies and in that case you are making the choice to handling your security.

I'm not a Java developer and do not have any working knowledge of how this implementation would work. I was pulled into the thread for the security POV only, sorry.

Also even if you use token validation through endpoints, you need to configure the validation endpoint the same way you would with the IdP public key/certificate (by the way you use the same configuration file/URL to configure both: .well-known/openid-configuration). for token duration and introspection we provide the defaults, if you decide to change them to a bad value that is still your choice...

If you are providing default values for token introspection (any value) and are not doing introspection on every request by default, then you are potentially opening up holes in your appsec model for reasons I outlined previously.

@rdegges
Copy link

rdegges commented Feb 19, 2019

Hi @VinodAnandan, I'll definitely shoot you an email separately. Thanks!

@yelhouti
Copy link
Contributor Author

Hi @rdegges, thanks for your answers, I really appreciate it.
Indeed I'm talking about refresh token invalidation and not access tokens, so I want enter in the details of your first paragraph. I'm not planning to send a request to the IdP on each request this clearly goes against the performance I'm trying to gain.
So what we are discussing is: is it worth it to allow an attacker 10 more seconds with an access token (compared to cookie where the victim is "protected" once they close their browser) or should we try to defend against that.
If your data and actions are so sensitive that you can't afford that, having cookie won't protect you as the attacker can send request through the victims browser and you have no way to find out, these kind of actions should in all cases be protected by something like a second factor... so am i trading security for speed, not really.
For the implementation details, I was just answering you point about the defaults and how other developers trust to us to make the right choices, and I'm saying: these are not the defaults...

@Tcharl
Copy link
Contributor

Tcharl commented Mar 2, 2019

Another good stuff I see to not using sessions is that Kubernetes support will then be reachable ;-)

@deepu105
Copy link
Member

deepu105 commented Mar 9, 2019

Ok guys, this is not going anywhere so I'm gonna make an executive decision, as one of the project lead

I'm in favour of not adding any new implementation to core and to keep status quo, you can ofcourse create a module/blueprint and demonstrate this so that people who are for JWT option can use it and as Matt said if it becomes popular we can reconsider additing to core. And hence I'm closing the issue but ofcourse feel free to continue the discussion as the security insights are valuable to the community.

My decision is based on below facts.

  1. Current implementation works and there has not been any vulnerability or big issue found in it
  2. Clearly there are 2 camps and both have valid arguments and unfortunately I don't think there will be a consensus here
  3. As I said before to an average JHipster user this is not a transparent thing so I'll keep it simple

Let's revisit if it becomes an issue

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests