feat(middleware): add CSRF protection examples - #1379
Open
yoozzeek wants to merge 1 commit into
Open
Conversation
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Add CSRF examples for the double submit cookie and synchronizer token patterns
The repository has no CSRF example. Actix Web ships no CSRF middleware and has not since the request was first filed (actix/actix-web#147, actix/actix-web#1185). This adds two
middleware/members, one per pattern documented in the OWASP CSRF Prevention Cheat Sheet, both built onactix-csrf-middleware 0.9. No existing example, workspace dependency, or CI job is touched. The crate is the only new package inCargo.lock; all 17 of its dependencies already resolve in the workspace tree at HEAD.Disclosure: I maintain
actix-csrf-middleware.Changes
middleware/csrf-double-submit: stateless, no session storeFiles:
middleware/csrf-double-submit/{Cargo.toml,README.md,src/main.rs}The token lives in a cookie and is mirrored into a hidden
csrf_tokenform field. The middleware compares the two in constant time and rejects any mutating request whose copies disagree. The cookie value is an HMAC over the session (or pre-session) identifier rather than a bare random string, which lets the server verify provenance and keeps an anonymous token from being accepted on an authenticated endpoint. Four routes:GET /renders the form,POST /loginsets a session id cookie and callsrotate_csrf_after_login,POST /logoutcallsrotate_csrf_after_logout, andPOST /messageis the protected endpoint. Every mutating route is protected, login and logout included. Before sign-in the client carries apre-sessioncookie and aCSRF-ANONtoken, covering registration and sign-in forms before a session exists. Cookie flags are the crate defaults (SameSite=Strict); onlywith_secure(false)is overridden, for plain-HTTP localhost.middleware/csrf-synchronizer: stateful, backed byactix-sessionFiles:
middleware/csrf-synchronizer/{Cargo.toml,README.md,src/main.rs}The token is held server-side in the session and is never readable by client scripts, at the cost of requiring a session store. The example uses
CookieSessionStoreto stay self-contained. Routes match the double submit example. Two ordering constraints the example exists to demonstrate:SessionMiddlewareis wrapped afterCsrfMiddleware, making it the outer layer, which populates the session before the CSRF middleware runs; andKey::generate()is called once outsideHttpServer::new, giving every worker one key. The session cookie is renamed tosession, sinceactix-sessionand the middleware both default toidand, left colliding, classify every request authorized from the first response onward.Workspace registration
Files:
Cargo.toml,Cargo.lockAdds the two members. The lock gains three
[[package]]blocks (the two examples andactix-csrf-middleware 0.9.0) and nothing else.Compatibility
hmac 0.13,sha2 0.11,subtle,zeroize,rand 0.10,url,hex,base64 0.22, and the actix crates) all already resolve inCargo.lockat HEAD; the lock diff introduces no new transitive version.rust-version = "1.98").Testing
cargo check -p middleware-csrf-double-submit -p middleware-csrf-synchronizer: clean.cargo clippy -p middleware-csrf-double-submit -p middleware-csrf-synchronizer --all-features --all-targets -- -D warnings: clean.cargo +nightly fmt -p middleware-csrf-double-submit -p middleware-csrf-synchronizer -- --check: clean.curlagainst both binaries, 18 assertions each, 36/36 passing. AnonymousGET /mints a token and setspre-session.POST /messagewith no token returns400 {"error":"csrf_token_missing"}; a forged token returns 400.POST /loginreturns 303, sets the session id cookie, and rotates the token. The stale anonymous token is rejected against the now-authenticated session. The authorized token returns200 accepted: hellowithContent-Type: text/plain; charset=utf-8, and the same token is accepted in anX-CSRF-Tokenheader.POST /logoutreturns 303, the nextGET /is anonymous again, and the pre-logout token no longer validates.