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

Fix CSRFTokenSigner - it needs to cope with rotated secrets too #446

Merged
merged 1 commit into from
Apr 4, 2024

Conversation

rtyley
Copy link
Member

@rtyley rtyley commented Apr 3, 2024

This fixes #445 - although play-secret-rotation has always overridden Play's RequestFactory to handle rotating the Play Application Secret, we forgot to also override the CSRFTokenSigner to teach it about rotating secrets - this PR fixes that.

Essential to coping with rotating secret keys is the ability to tolerate tokens signed with old secret keys for a transition period (for us the overlap period is 2 hours). So our RotatingKeyCSRFTokenSigner needs to always sign with the 'current' secret, but when verifying tokens, needs to try verifying the token against all applicable secret keys, to see if any of them validate it.

See also:

@rtyley rtyley changed the title Fix forgotten CSRFTokenSigner Fix forgotten CSRFTokenSigner Apr 3, 2024
@rtyley rtyley changed the title Fix forgotten CSRFTokenSigner Fix CSRFTokenSigner - it needs to cope with rotated secrets too Apr 3, 2024
@rtyley rtyley force-pushed the fix-forgotten-CSRFTokenSigner branch 6 times, most recently from 516d15c to f4d0c11 Compare April 3, 2024 21:14
Comment on lines 26 to +30
override lazy val requestFactory: RequestFactory =
RotatingSecretComponents.requestFactoryFor(secretStateSupplier, httpConfiguration)

override lazy val csrfTokenSigner: CSRFTokenSigner =
new RotatingSecretComponents.RotatingKeyCSRFTokenSigner(secretStateSupplier, systemUTC())
Copy link
Member Author

@rtyley rtyley Apr 3, 2024

Choose a reason for hiding this comment

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

This the essential bit of this fix - although RotatingSecretComponents has always overridden the RequestFactory to handle rotating the Play Application Secret, we forgot to override the CSRFTokenSigner to teach it about rotating secrets.

...as far as I can tell, there aren't any other uses of the Play Application Secret for signing/verification that I've forgotten about.

@guardian guardian deleted a comment from gu-scala-library-release bot Apr 3, 2024
@guardian guardian deleted a comment from gu-scala-library-release bot Apr 3, 2024
@rtyley rtyley force-pushed the fix-forgotten-CSRFTokenSigner branch from f4d0c11 to d57673c Compare April 4, 2024 09:43
@rtyley rtyley force-pushed the fix-forgotten-CSRFTokenSigner branch from d57673c to 6be2eca Compare April 4, 2024 09:58
@rtyley rtyley marked this pull request as ready for review April 4, 2024 10:31
@rtyley rtyley requested a review from a team as a code owner April 4, 2024 10:31
@guardian guardian deleted a comment from gu-scala-library-release bot Apr 4, 2024
@gu-scala-library-release
Copy link
Contributor

@rtyley has published a preview version of this PR with release workflow run #32, based on commit 6be2eca:

8.2.0-PREVIEW.fix-forgotten-CSRFTokenSigner.2024-04-04T1033.6be2ecaa

Want to make another preview release?

Click 'Run workflow' in the GitHub UI, specifying the fix-forgotten-CSRFTokenSigner branch, or use the GitHub CLI command:

gh workflow run release.yml --ref fix-forgotten-CSRFTokenSigner

Want to make a full release after this PR is merged?

Click 'Run workflow' in the GitHub UI, leaving the branch as the default, or use the GitHub CLI command:

gh workflow run release.yml

@@ -59,4 +66,39 @@ object RotatingSecretComponents {
val snapshotProvider: SnapshotProvider) extends FlashCookieBaker with RotatingSecretCookieCodec {
override val jwtConfiguration: JWTConfiguration = config.jwt
}

class RotatingKeyCSRFTokenSigner(
Copy link
Member Author

Choose a reason for hiding this comment

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

This class is the alternative implementation to Play's DefaultCSRFTokenSigner - our alternative implementation can cope with rotating keys.

Our implementation borrows some code as copy'n'paste from DefaultCSRFTokenSigner where it's unavoidable - specifically compareSignedTokens() - but delegates to underlying instances of DefaultCSRFTokenSigner as much as possible. If you look at the implementation of DefaultCSRFTokenSigner you can see it's doing some relatively obscure crypto things that we'd prefer delegate, rather than chase & re-implement ourselves.

Copy link

@sophie-macmillan sophie-macmillan left a comment

Choose a reason for hiding this comment

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

Nice! - LGTM 🎉

@rtyley rtyley merged commit a6cbc2f into main Apr 4, 2024
10 checks passed
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.

CSRFTokenSigner freezes its copy of the secret on startup, leads to unpredictable CSRF token rejection
2 participants