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

Default CSRF tokens to the root path "/" #1248

Merged
merged 1 commit into from
Aug 16, 2016
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions yesod-core/ChangeLog.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,8 @@
## 1.4.23.1

* Don't allow sending multiple cookies with the same name to the client, in accordance with [RFC 6265](https://tools.ietf.org/html/rfc6265). This fixes an issue where multiple CSRF tokens were sent to the client. [#1258](https://github.com/yesodweb/yesod/pull/1258)
* Default CSRF tokens to the root path "/", fixing an issue where multiple tokens were stored in cookies, and using the wrong one led to CSRF errors [#1248](https://github.com/yesodweb/yesod/pull/1248)

## 1.4.23

* urlParamRenderOverride method for Yesod class [#1257](https://github.com/yesodweb/yesod/pull/1257)
Expand Down
6 changes: 5 additions & 1 deletion yesod-core/Yesod/Core/Class/Yesod.hs
Original file line number Diff line number Diff line change
Expand Up @@ -491,14 +491,18 @@ csrfCheckMiddleware handler shouldCheckFn headerName paramName = do

-- | Calls 'csrfSetCookieMiddleware' with the 'defaultCsrfCookieName'.
--
-- The cookie's path is set to @/@, making it valid for your whole website.
--
-- Since 1.4.14
defaultCsrfSetCookieMiddleware :: Yesod site => HandlerT site IO res -> HandlerT site IO res
defaultCsrfSetCookieMiddleware handler = csrfSetCookieMiddleware handler (def { setCookieName = defaultCsrfCookieName })
defaultCsrfSetCookieMiddleware handler = setCsrfCookie >> handler

-- | Takes a 'SetCookie' and overrides its value with a CSRF token, then sets the cookie. See 'setCsrfCookieWithCookie'.
--
-- For details, see the "AJAX CSRF protection" section of "Yesod.Core.Handler".
--
-- Make sure to set the 'setCookiePath' to the root path of your application, otherwise you'll generate a new CSRF token for every path of your app. If your app is run from from e.g. www.example.com\/app1, use @app1@. The vast majority of sites will just use @/@.
--
-- Since 1.4.14
csrfSetCookieMiddleware :: Yesod site => HandlerT site IO res -> SetCookie -> HandlerT site IO res
csrfSetCookieMiddleware handler cookie = setCsrfCookieWithCookie cookie >> handler
Expand Down
8 changes: 6 additions & 2 deletions yesod-core/Yesod/Core/Handler.hs
Original file line number Diff line number Diff line change
Expand Up @@ -1358,7 +1358,7 @@ stripHandlerT (HandlerT f) getSub toMaster newRoute = HandlerT $ \hd -> do
--
-- The form-based approach has the advantage of working for users with Javascript disabled, while adding the token to the headers with Javascript allows things like submitting JSON or binary data in AJAX requests. Yesod supports checking for a CSRF token in either the POST parameters of the form ('checkCsrfParamNamed'), the headers ('checkCsrfHeaderNamed'), or both options ('checkCsrfHeaderOrParam').
--
-- The easiest way to check both sources is to add the 'defaultCsrfMiddleware' to your Yesod Middleware.
-- The easiest way to check both sources is to add the 'Yesod.Core.defaultCsrfMiddleware' to your Yesod Middleware.

-- | The default cookie name for the CSRF token ("XSRF-TOKEN").
--
Expand All @@ -1368,12 +1368,16 @@ defaultCsrfCookieName = "XSRF-TOKEN"

-- | Sets a cookie with a CSRF token, using 'defaultCsrfCookieName' for the cookie name.
--
-- The cookie's path is set to @/@, making it valid for your whole website.
--
-- Since 1.4.14
setCsrfCookie :: MonadHandler m => m ()
setCsrfCookie = setCsrfCookieWithCookie def { setCookieName = defaultCsrfCookieName }
setCsrfCookie = setCsrfCookieWithCookie def { setCookieName = defaultCsrfCookieName, setCookiePath = Just "/" }

-- | Takes a 'SetCookie' and overrides its value with a CSRF token, then sets the cookie.
--
-- Make sure to set the 'setCookiePath' to the root path of your application, otherwise you'll generate a new CSRF token for every path of your app. If your app is run from from e.g. www.example.com\/app1, use @app1@. The vast majority of sites will just use @/@.
--
-- Since 1.4.14
setCsrfCookieWithCookie :: MonadHandler m => SetCookie -> m ()
setCsrfCookieWithCookie cookie = do
Expand Down
6 changes: 6 additions & 0 deletions yesod-core/test/YesodCoreTest/Csrf.hs
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,12 @@ csrfSpec = describe "A Yesod application with the defaultCsrfMiddleware" $ do
assertStatus 200 res
assertClientCookieExists "Should have an XSRF-TOKEN cookie" defaultCsrfCookieName

it "uses / as the path of the cookie" $ runner $ do -- https://github.com/yesodweb/yesod/issues/1247
res <- request defaultRequest
assertStatus 200 res
cookiePath <- fmap setCookiePath requireCsrfCookie
liftIO $ cookiePath `shouldBe` Just "/"

it "200s write requests with the correct CSRF header, but no param" $ runner $ do
getRes <- request defaultRequest
assertStatus 200 getRes
Expand Down