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

validate jwts #118

Merged
merged 6 commits into from
Jun 4, 2024
Merged

validate jwts #118

merged 6 commits into from
Jun 4, 2024

Conversation

bengerman13
Copy link
Contributor

@bengerman13 bengerman13 commented May 14, 2024

Changes proposed in this pull request:

  • use jwcrypto for jwt decoding. this adds most/all of the validation we need
  • refactor config class hierarchy. This is mostly unrelated, but I got tired of duplicating all my changes for every test
  • refactor config tests. This is again mostly unrelated, but I wanted assertions to be clearer, and for each test to be about one thing
  • version bumps. This was a side-effect of adding jwcrypto

Things to check

  • For any logging statements, is there any chance that they could be logging sensitive data?
  • Are log statements using a logging library with a logging level set? Setting a logging level means that log statements "below" that level will not be written to the output. For example, if the logging level is set to INFO and debugging statements are written with log.debug or similar, then they won't be written to the otput, which can prevent unintentional leaks of sensitive data.

Security considerations

jwcrypto does most of the token validation we need right out of the box, so this is big step forward in security.

@bengerman13 bengerman13 requested a review from a team May 14, 2024 19:48
rcgottlieb
rcgottlieb previously approved these changes May 14, 2024
@markdboyd
Copy link
Contributor

markdboyd commented May 14, 2024

To document for the future, skipping JWT verification when TLS is in use is discussed in the OpenID spec

If the ID Token is received via direct communication between the Client and the Token Endpoint (which it is in this flow), the TLS server validation MAY be used to validate the issuer in place of checking the token signature. The Client MUST validate the signature of all other ID Tokens according to JWS [JWS] using the algorithm specified in the JWT alg Header Parameter. The Client MUST use the keys provided by the Issuer.

While I'm hesitant to argue with a spec document, I would do so for a few reasons:

  • We currently have no reason to doubt the trustworthiness of TLS connections, but I don't trust that will always be true in the future and there there is no attack vector where JWT verification would not be valuable
  • The level of effort to implement basic verification based on algorithm seems very low
  • I am comfortable with more layers of defense, even if they are possibly redundant. It seems like some redundancy may even be in line with a "defense in-depth" approach
  • This spec is the only place I have encountered the recommendation to possibly skip JWT verification of algorithms

Nevertheless, I cannot definitively say that JWT verification is absolutely necessary, so I won't hold up this PR

cf_auth_proxy/app.py Outdated Show resolved Hide resolved
- add JWK set to config
- use jwcrypto for JWT decode, since it does all the things for us
- refactor config heirarchy + tests
@bengerman13 bengerman13 requested a review from a team as a code owner May 31, 2024 20:28
@bengerman13 bengerman13 requested a review from markdboyd May 31, 2024 20:30
@bengerman13 bengerman13 changed the title update jwt comment validate jwts May 31, 2024
Copy link
Contributor

Choose a reason for hiding this comment

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

No objection per se, but is this file necessary?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

it's not necessary - it's a standard file for pytest that I was using for some fixtures while I worked through this, but they all got refactored away. I can remove this to tidy up though

Comment on lines 91 to 92
self.SESSION_REFRESH_EACH_REQUEST = False
self.PERMANENT_SESSION_LIFETIME = self.env_parser.int("SESSION_LIFETIME")
Copy link
Contributor

Choose a reason for hiding this comment

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

What are these values for? Are they related to the JWT changes?

Copy link
Contributor Author

@bengerman13 bengerman13 Jun 3, 2024

Choose a reason for hiding this comment

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

These are unrelated to the change - they just moved unchanged during the config restructure.

these control how flask tracks sessions.
session_refresh_each_request makes flask include a set cookie header on every response. I don't recall why we set it that way initially.
permanent_session_lifetime controls how long into the future to set session cookie's expiration

Copy link
Contributor

Choose a reason for hiding this comment

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

It looks like SESSION_REFRESH_EACH_REQUEST defaults to True but now will be set to False? From what you described, False seems like what we want, but just want to make sure the behavior will be what we expect

Copy link
Contributor Author

Choose a reason for hiding this comment

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

actually, looks like this is a change I introduced accidentally here. I think I set the SESSION_REFRESH_EACH_REQUEST on the local config when fighting some other issue, then assumed that because we set it True there, it must be False elsewhere.

I am not really sure whether we want this behavior or not. Setting the cookie every time seems like it could save a little annoyance if we change the session structure or something, but it also seems like it could be a meaningful amount of overhead given the number of small background requests opensearch is likely to make

Copy link
Contributor

Choose a reason for hiding this comment

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

Can we just preserve the current behavior for now? Just want to limit this PR to only the intended changes. Happy to change the behavior later as necessary

Copy link
Contributor Author

Choose a reason for hiding this comment

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

after some more thinking, I'm leaning towards leaving SESSION_REFRESH_EACH_REQUEST as the flask default.
I think either behavior will work, and the tradeoffs seem minimal at this point, so from a maintainability perspective I think it's better to have a smaller config set to think about is better, and having fewer differences between unit testing vs local testing vs production is better

@bengerman13 bengerman13 merged commit 292ecf8 into main Jun 4, 2024
@bengerman13 bengerman13 deleted the bb/check-jwts branch June 4, 2024 15:08
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