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

CSRF Token is refreshed on every request #38225

Closed
ia3andy opened this issue Jan 16, 2024 · 23 comments · Fixed by #38229
Closed

CSRF Token is refreshed on every request #38225

ia3andy opened this issue Jan 16, 2024 · 23 comments · Fixed by #38229
Assignees
Labels
area/security kind/bug Something isn't working
Milestone

Comments

@ia3andy
Copy link
Contributor

ia3andy commented Jan 16, 2024

Describe the bug

As far as I know the token expiry should be extended but not the token itself shouldn't be changed on new request. Currently this makes requests with htmx fails as after the first request the token is wrong.

Expected behavior

The token expiry should be increased but the token should be the same

Actual behavior

it changes the token

How to Reproduce?

clone https://github.com/ia3andy/renotes/
start with quarkus dev
load the page and refresh many times, keep a look on the body tag, you will see the token getting updated

Output of uname -a or ver

No response

Output of java -version

No response

Quarkus version or git rev

3.6.5

Build tool (ie. output of mvnw --version or gradlew --version)

No response

Additional information

No response

@ia3andy ia3andy added the kind/bug Something isn't working label Jan 16, 2024
Copy link

quarkus-bot bot commented Jan 16, 2024

/cc @pedroigor (bearer-token), @sberyozkin (bearer-token,jwt,security)

@ia3andy
Copy link
Contributor Author

ia3andy commented Jan 16, 2024

This is a big issue AFAIK

@maxandersen
Copy link
Member

did this ever work or is it a long standing issue?

@ia3andy
Copy link
Contributor Author

ia3andy commented Jan 16, 2024

Yes it's working in 3.6.3, but there were other issues, it seems to have been broken in #37725

@maxandersen
Copy link
Member

@FroMage @sberyozkin I notice #37725 got in without testing - not sure if we could have caught it then but now we know about this issue is there some testing we can add to avoid breaking in future?

@FroMage
Copy link
Member

FroMage commented Jan 16, 2024

The original idea of #37725 was to make sure that AJAX request would extend the life-time of CSRF tokens, which so far only get renewed by regular HTTP calls, leading to AJAX apps failing due to token non-refreshment.

The problem is that we should have extended their life-time without changing their value. Just renewed the cookie with a further expiry. Changing the CSRF token value itself invalidates other parts of the HTML forms that have the original token, if they're not also refreshed.

We might be able to mitigate this by returning the new token value in a header, unencrypted, so that ajax calls can update it.

Note that none of these ajax issues would have happened if we'd gone with a fixed CSRF header with no value, as OWASP recommends. The value of the CSRF token is only advised for HTML forms, not for AJAX requests, where any special CSRF header with any random or fixed value is enough: https://cheatsheetseries.owasp.org/cheatsheets/Cross-Site_Request_Forgery_Prevention_Cheat_Sheet.html#employing-custom-request-headers-for-ajaxapi

@sberyozkin
Copy link
Member

Changing the CSRF token value itself invalidates other parts of the HTML forms that have the original token, if they're not also refreshed.

This is what I missed - it should not be a problem with simple forms, pages. The unit tests are there, but from now on, indeed, the practitioners like Steph and Andy should verify the CSRF PRs before there are merged since I'm not practicing CSRF related setups with HTMX etc, it is hard to get it right with some basic unit tests.

@FroMage Steph, FYI, the double cookie submit pattern is sound, specifically we also implement an optional signing feature.

OK, let me give it a try and I'll ask yourself and Andy to verify it, thanks

@ia3andy
Copy link
Contributor Author

ia3andy commented Jan 16, 2024

I will implement an integration test in Renarde ecosystem-ci with Playwright to make sure CRSF and htmx works properly with Renarde. It won't detect errors in the PR, but it will check the main branch before it gets released...

@ia3andy
Copy link
Contributor Author

ia3andy commented Jan 16, 2024

Also we will be able to run it locally with the PR if needed

@maxandersen
Copy link
Member

@ia3andy does it really need a full renarde setup to verify? just curious as seems like something we should have test for closer to the code.

@ia3andy
Copy link
Contributor Author

ia3andy commented Jan 16, 2024

here is my take on this:

  1. I implement some test using playwright in Renarde
  2. @sberyozkin have a look at what is happening on the network and add some equivalent unit test in Quarkus core

I am saying this because the use cases are strongly tied to the sequence it is used in the app and it's always something new breaking. So better have the full integration test to make sure it actually works in the browser...

@sberyozkin
Copy link
Member

@ia3andy even with HtmlUnit test it is hard to verify at the Quarkus level it really works with HTMX containing multiple forms, so like you said having some integration tests in Renarde would complement it.

@maxandersen
Copy link
Member

yeah, i 'm not saying don't do it in renarde - just wondering if can get it closer as renarde test runs aren't run all the time.

@ia3andy
Copy link
Contributor Author

ia3andy commented Jan 16, 2024

Yeah for example in this case, it works in the first form and breaks on the second...

@ia3andy
Copy link
Contributor Author

ia3andy commented Jan 16, 2024

ecosystem-ci is run everyday AFAIK

@FroMage
Copy link
Member

FroMage commented Jan 16, 2024

@FroMage Steph, FYI, the double cookie submit pattern is sound, specifically we also implement an optional signing feature.

Sure, but read the OWASP article I linked. Double cookie submit pattern is for regular HTML forms. For AJAX, a single special header with a fixed value is enough.

@FroMage
Copy link
Member

FroMage commented Jan 16, 2024

@BarDweller Hey, how're your CSRF skills? :)

@sberyozkin
Copy link
Member

sberyozkin commented Jan 16, 2024

@FroMage

For AJAX, a single special header with a fixed value is enough.

Possibly, it feels to me though that cookie comparison adds an extra safety dimension even in this case. I don't know how we can prove for certain that a single header only without having to compare it with anything is sufficient in various setups. I think we should take even OWASP published content as not necessarily 100% correct, possibly applicable to certain deployments only.

@sberyozkin
Copy link
Member

@FroMage @ia3andy @BarDweller In any case, I think in this case only a minor tweak is needed to resolve it, if the cookie is already available then reuse its value when refreshing a cookie, instead of always generating a new value - IMHO it is a technical bug which does not loosen the security cover, only breaks the complex UIs unfortunately

@BarDweller
Copy link
Contributor

BarDweller commented Jan 16, 2024 via email

@quarkus-bot quarkus-bot bot added this to the 3.7 - main milestone Jan 17, 2024
@FroMage
Copy link
Member

FroMage commented Jan 17, 2024

Possibly, it feels to me though that cookie comparison adds an extra safety dimension even in this case. I don't know how we can prove for certain that a single header only without having to compare it with anything is sufficient in various setups. I think we should take even OWASP published content as not necessarily 100% correct, possibly applicable to certain deployments only.

Well, my understanding is that if there's proof that a user could set a custom header (any header, of any value) then it can't be a CSRF attack in the browser.

I am, of course, skeptical of my understanding, but I'm also skeptical about your assertion that OWASP would get things wrong more than we would. I am also skeptical of any security benefit of requiring more checks than necessary, as this introduces usability issues inevitably.

@ia3andy
Copy link
Contributor Author

ia3andy commented Jan 17, 2024

As announced, here is the integration test in Renarde: quarkiverse/quarkus-renarde#191 and it fails with 3.6.5

@takesson
Copy link

I ran into this issue a couple of days ago when using HTMX. I doubted my code/template for a while before finding this issue.

I can confirm that 3.6.7 resolved the issue for my use-case.

Not sure I am qualified but I made a couple of comments in #38229 related to the concerns raised by @FroMage.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
area/security kind/bug Something isn't working
Projects
None yet
Development

Successfully merging a pull request may close this issue.

7 participants