Skip to content

Conversation

@YoavOst
Copy link

@YoavOst YoavOst commented Jan 5, 2022

Description

This change adds a part of the state nonce to the CSRF name, making it possible to have parallel callback requests.

When having parallel ajax requests after the session cookie has expired multiple callbacks occur at the same time, creating multiple CSRF tokens with the name making it impossible for the proxy to authenticate.
It solves issue 1451 and possibly issue 817

How Has This Been Tested?

Deployed using docker-compose with OIDC (keycloak) provider and cookie session store and solve the parallel AJAX requests issue.

@YoavOst YoavOst requested a review from a team as a code owner January 5, 2022 20:27
Copy link
Member

@JoelSpeed JoelSpeed left a comment

Choose a reason for hiding this comment

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

I think this is a good improvement to the project on first glance, but it would be good to seem some tests and maybe make the code a bit easier to read if possible

cookie, err := req.Cookie(csrfCookieName(opts))

// csrfCookieName will include state to have multiple csrf in case of parallel requests
state := req.URL.Query()["state"][0][0:csrfStateLength - 1]
Copy link
Member

Choose a reason for hiding this comment

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

Is there a way we can make this a bit nicer to read and also handle the possibility of index out of bounds errors? Wondering what happens if state isn't there for example, or for some reason it ends up empty

Copy link
Contributor

Choose a reason for hiding this comment

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

I'm also curious if we can potentially manage multiple CSRF state checks in an array structure serialized in 1 CSRF struct serialized into the cookie?

Things on my mind:

  • The cookie manages the OIDC nonce across requests too. How will that handle multiple (seems fine).
  • We encrypt the cookie payload (oauth state & nonce contents). Making the state part of the cookie name will slightly weaken that posture. But I bet it's fine, the encryption was likely overkill and those would only expose a portion of the OAuth state nonce.

Copy link
Contributor

Choose a reason for hiding this comment

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

I'm also not convinced the multiple tab issue was the sole source of these intermittent CSRF failures.

At my previous company where I ran this in production, whenever users reported this error (and when it happened to me) multiple tabs logging in a once was never a symptom.

Copy link
Contributor

Choose a reason for hiding this comment

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

This long running issue has more samples of tips people have (and theories on why this happens): #817

Do we think think this will fix some of the prefetch, favicon & JS issues people have uncovered as reasons?

Copy link
Author

Choose a reason for hiding this comment

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

Hi @JoelSpeed, Added some more testing for edge cases.
@NickMeves I'm not sure about the multiple tabs, I have to say that from my experience, issues with multiple tabs can result from session management problems on the provider side.
I do suspect the parallel requests to favicon and prefetch are related to this.

func csrfCookieName(opts *options.Cookie) string {
return fmt.Sprintf("%v_csrf", opts.Name)
func csrfCookieName(opts *options.Cookie, state string) string {
return fmt.Sprintf("%v_%v_csrf", opts.Name, state)
Copy link
Contributor

Choose a reason for hiding this comment

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

NIT -- can we potentially flip these so the dynamic part of the cookie name is the suffix? (%v_csrf_%v). And it might be worth renaming the state variable to signify this is a subset of the state.

Copy link
Contributor

Choose a reason for hiding this comment

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

In the CHANGELOG we will also need to signify this is potentially breaking for any users that had the cookieName option set close to the maximum (255 characters right?) -- Although I hope no one did that 🤣

Copy link
Author

Choose a reason for hiding this comment

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

@NickMeves Sure. Fixed both

@mazenaissa
Copy link

Hello @YoavOst ,
Could you please make the suggested changes? I am facing the 403 FORBIDDEN issue due to multiple oauth2 proxy callbacks invoked from multiple browser tabs.
Thanks,

@YoavOst YoavOst changed the title Add start of state to CSRF cookie name WIP: Add start of state to CSRF cookie name Mar 28, 2022
@YoavOst YoavOst changed the title WIP: Add start of state to CSRF cookie name Add start of state to CSRF cookie name Mar 31, 2022
@BronzeDeer
Copy link

BronzeDeer commented Apr 13, 2022

@NickMeves @JoelSpeed @YoavOst Anyway other people can help get this reviewed and merged faster? At work we're also running into the parallel Ajax problem which is currently blocking

@mazenaissa
Copy link

@NickMeves @JoelSpeed @YoavOst Anyway other people can help get this reviewed and merged faster? At work we're also running into the parallel Ajax problem which is currently blocking

any updates guys?

Copy link
Member

@JoelSpeed JoelSpeed left a comment

Choose a reason for hiding this comment

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

Has anyone tested this at all? Looking at the PR there are no test changes, can we make sure there are unit tests to ensure we don't regress on this functionality in the future?

@quantumdecoherence
Copy link

I've tested the PR, and can confirm that it solves the 403 FORBIDDEN problem. Thank you for that!

However, when the oauth2 session is expired, CSRF cookies are issued for every request made. The only CSRF cookie that's cleaned up is the one leading to a successful oauth2 login. This in turn leads to large headers containing all the uncleaned cookies being sent by the browser, and eventually to requests running into max header size.

@tahoell
Copy link

tahoell commented May 17, 2022

Hello all! Since we would be very happy about this PR in a project, could someone please resolve this merge conflict?

@pfragoso
Copy link

We suffer from the #817 so happy to test this as the merge conflict is fixed.

@miguelborges99
Copy link
Contributor

miguelborges99 commented Jun 27, 2022

In face of multiple oauth2 proxy callbacks, the CSRF cookie faces several concurrency problems:

  • some requests may fail due to missing CSRF token, in case a concurrent request cleans up the cookie, or
  • some requests may fail with CSRF token mismatch, since CSRF cookie value was overriden by other concurrent request.

This fix seems to solve both issues #1451 and #817 which have more than 2 years.

Can conflits be fixed?

@github-actions
Copy link
Contributor

This pull request has been inactive for 60 days. If the pull request is still relevant please comment to re-activate the pull request. If no action is taken within 7 days, the pull request will be marked closed.

@github-actions github-actions bot added the Stale label Aug 28, 2022
JoelSpeed
JoelSpeed previously approved these changes Aug 29, 2022
@JoelSpeed
Copy link
Member

Let's give this a go, hopefully this resolves the issues. It would be nice if someone could follow up with some unit tests for this

@JoelSpeed
Copy link
Member

Ok this doesn't seem like it's working correctly as it's causing a panic within the tests, anyone got time to work out why and resolve that?

@JoelSpeed JoelSpeed self-requested a review August 29, 2022 09:36
@JoelSpeed JoelSpeed dismissed their stale review August 29, 2022 09:36

Tests aren't passing

@mazenaissa
Copy link

Hello @JoelSpeed ,
Could you check this pull request #1708
One of the twos must be accepted to fix the CSRF issue
Thanks,

@JoelSpeed JoelSpeed removed the Stale label Aug 30, 2022
@WolfgangMehner
Copy link

May I suggest the branch from @mazenaissa #1708 .
It implements some nice features, like e.g. some configuration for the CSRF cookies. In particular, you can configure a separate expiry for the CSRF-Cookies, which is very useful.
Additionally, this branch has been tested by us with a production system, and several hundred users.

@JoelSpeed
Copy link
Member

I'm going to close this in favour of #1708 as I think that's pretty much ready to go and seems to have had a bunch of testing from others

@JoelSpeed JoelSpeed closed this Aug 31, 2022
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.

10 participants