From 2cbe60c53d6e0955895df6fec82f040356223040 Mon Sep 17 00:00:00 2001 From: Bob Long Date: Sun, 1 May 2016 16:30:22 +0100 Subject: [PATCH 1/8] Add latest Cookie version --- stack.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/stack.yaml b/stack.yaml index 29b90e10..7266fb3c 100644 --- a/stack.yaml +++ b/stack.yaml @@ -21,3 +21,4 @@ extra-deps: - yaml-0.8.17 - nonce-1.0.2 - persistent-2.5 +- cookie-0.4.2 From a797c2e5d4a1f99b2e80e20deced86ff51cff0a6 Mon Sep 17 00:00:00 2001 From: Bob Long Date: Sun, 1 May 2016 16:31:01 +0100 Subject: [PATCH 2/8] Add laxSameSiteSessions and strictSameSiteSessions --- yesod-core/Yesod/Core.hs | 2 ++ yesod-core/Yesod/Core/Class/Yesod.hs | 18 ++++++++++++++- yesod-core/test/YesodCoreTest.hs | 3 ++- yesod-core/test/YesodCoreTest/Ssl.hs | 16 ++++++++++++- .../test/YesodCoreTest/StubLaxSameSite.hs | 23 +++++++++++++++++++ .../test/YesodCoreTest/StubStrictSameSite.hs | 23 +++++++++++++++++++ 6 files changed, 82 insertions(+), 3 deletions(-) create mode 100644 yesod-core/test/YesodCoreTest/StubLaxSameSite.hs create mode 100644 yesod-core/test/YesodCoreTest/StubStrictSameSite.hs diff --git a/yesod-core/Yesod/Core.hs b/yesod-core/Yesod/Core.hs index a63eba8b..83b1fe0e 100644 --- a/yesod-core/Yesod/Core.hs +++ b/yesod-core/Yesod/Core.hs @@ -53,6 +53,8 @@ module Yesod.Core , envClientSessionBackend , clientSessionBackend , sslOnlySessions + , laxSameSiteSessions + , strictSameSiteSessions , sslOnlyMiddleware , clientSessionDateCacher , loadClientSession diff --git a/yesod-core/Yesod/Core/Class/Yesod.hs b/yesod-core/Yesod/Core/Class/Yesod.hs index 5147fed7..f1eca3f8 100644 --- a/yesod-core/Yesod/Core/Class/Yesod.hs +++ b/yesod-core/Yesod/Core/Class/Yesod.hs @@ -49,7 +49,7 @@ import qualified Text.Blaze.Html5 as TBH import Text.Hamlet import Text.Julius import qualified Web.ClientSession as CS -import Web.Cookie (parseCookies) +import Web.Cookie (parseCookies, sameSiteLax, sameSiteStrict, SameSiteOption) import Web.Cookie (SetCookie (..)) import Yesod.Core.Types import Yesod.Core.Internal.Session @@ -366,6 +366,22 @@ sslOnlySessions = (fmap . fmap) secureSessionCookies setSecureBit cookie = cookie { setCookieSecure = True } secureSessionCookies = customizeSessionCookies setSecureBit +-- | Helps defend against CSRF attacks by setting the SameSite attribute on +-- session cookies to "Lax". +laxSameSiteSessions :: IO (Maybe SessionBackend) -> IO (Maybe SessionBackend) +laxSameSiteSessions = sameSiteSession sameSiteLax + +-- | Helps defend against CSRF attacks by setting the SameSite attribute on +-- session cookies to "Strict". +strictSameSiteSessions :: IO (Maybe SessionBackend) -> IO (Maybe SessionBackend) +strictSameSiteSessions = sameSiteSession sameSiteStrict + +sameSiteSession :: SameSiteOption -> IO (Maybe SessionBackend) -> IO (Maybe SessionBackend) +sameSiteSession s = (fmap . fmap) secureSessionCookies + where + sameSite cookie = cookie { setCookieSameSite = (pure s) } + secureSessionCookies = customizeSessionCookies sameSite + -- | Apply a Strict-Transport-Security header with the specified timeout to -- all responses so that browsers will rewrite all http links to https -- until the timeout expires. For security, the max-age of the STS header diff --git a/yesod-core/test/YesodCoreTest.hs b/yesod-core/test/YesodCoreTest.hs index 94a104db..7c0db6fa 100644 --- a/yesod-core/test/YesodCoreTest.hs +++ b/yesod-core/test/YesodCoreTest.hs @@ -1,5 +1,5 @@ {-# LANGUAGE CPP #-} -module YesodCoreTest (specs) where +module YesodCoreTest (specs) where import YesodCoreTest.CleanPath import YesodCoreTest.Exceptions @@ -48,4 +48,5 @@ specs = do LiteApp.specs Ssl.unsecSpec Ssl.sslOnlySpec + Ssl.sameSiteSpec Csrf.csrfSpec diff --git a/yesod-core/test/YesodCoreTest/Ssl.hs b/yesod-core/test/YesodCoreTest/Ssl.hs index b6162c0f..c1411b93 100644 --- a/yesod-core/test/YesodCoreTest/Ssl.hs +++ b/yesod-core/test/YesodCoreTest/Ssl.hs @@ -1,6 +1,8 @@ {-# LANGUAGE TypeFamilies, QuasiQuotes, TemplateHaskell, MultiParamTypeClasses, OverloadedStrings #-} -module YesodCoreTest.Ssl ( sslOnlySpec, unsecSpec ) where +module YesodCoreTest.Ssl ( sslOnlySpec, unsecSpec, sameSiteSpec ) where import qualified YesodCoreTest.StubSslOnly as Ssl +import qualified YesodCoreTest.StubLaxSameSite as LaxSameSite +import qualified YesodCoreTest.StubStrictSameSite as StrictSameSite import qualified YesodCoreTest.StubUnsecured as Unsecured import Yesod.Core import Test.Hspec @@ -62,3 +64,15 @@ unsecSpec = describe "A Yesod application with sslOnly off" $ do where atHome = homeFixtureFor Unsecured.App isNotSecure c = not $ Cookie.setCookieSecure c + +sameSiteSpec :: Spec +sameSiteSpec = describe "A Yesod application" $ do + it "can set a Lax SameSite option" $ + laxHome $ "_SESSION" `cookieShouldSatisfy` isLax + it "can set a Strict SameSite option" $ + strictHome $ "_SESSION" `cookieShouldSatisfy` isStrict + where + laxHome = homeFixtureFor LaxSameSite.App + strictHome = homeFixtureFor StrictSameSite.App + isLax = (== Just Cookie.sameSiteLax) . Cookie.setCookieSameSite + isStrict = (== Just Cookie.sameSiteStrict) . Cookie.setCookieSameSite diff --git a/yesod-core/test/YesodCoreTest/StubLaxSameSite.hs b/yesod-core/test/YesodCoreTest/StubLaxSameSite.hs new file mode 100644 index 00000000..365c9a07 --- /dev/null +++ b/yesod-core/test/YesodCoreTest/StubLaxSameSite.hs @@ -0,0 +1,23 @@ +{-# LANGUAGE TypeFamilies, QuasiQuotes, TemplateHaskell, MultiParamTypeClasses, OverloadedStrings #-} +module YesodCoreTest.StubLaxSameSite ( App ( App ) ) where + +import Yesod.Core +import qualified Web.ClientSession as CS + +data App = App + +mkYesod "App" [parseRoutes| +/ HomeR GET +|] + +instance Yesod App where + yesodMiddleware = defaultYesodMiddleware . (sslOnlyMiddleware 120) + makeSessionBackend _ = laxSameSiteSessions $ + fmap Just $ defaultClientSessionBackend 120 CS.defaultKeyFile + +getHomeR :: Handler Html +getHomeR = defaultLayout + [whamlet| +

+ Welcome to my test application. + |] diff --git a/yesod-core/test/YesodCoreTest/StubStrictSameSite.hs b/yesod-core/test/YesodCoreTest/StubStrictSameSite.hs new file mode 100644 index 00000000..0324178e --- /dev/null +++ b/yesod-core/test/YesodCoreTest/StubStrictSameSite.hs @@ -0,0 +1,23 @@ +{-# LANGUAGE TypeFamilies, QuasiQuotes, TemplateHaskell, MultiParamTypeClasses, OverloadedStrings #-} +module YesodCoreTest.StubStrictSameSite ( App ( App ) ) where + +import Yesod.Core +import qualified Web.ClientSession as CS + +data App = App + +mkYesod "App" [parseRoutes| +/ HomeR GET +|] + +instance Yesod App where + yesodMiddleware = defaultYesodMiddleware . (sslOnlyMiddleware 120) + makeSessionBackend _ = strictSameSiteSessions $ + fmap Just $ defaultClientSessionBackend 120 CS.defaultKeyFile + +getHomeR :: Handler Html +getHomeR = defaultLayout + [whamlet| +

+ Welcome to my test application. + |] From aed1e27cb727d2b2d78487cca055ecd7cbb989e1 Mon Sep 17 00:00:00 2001 From: Bob Long Date: Sun, 1 May 2016 17:21:33 +0100 Subject: [PATCH 3/8] Bump cookie version in cabal file --- yesod-core/yesod-core.cabal | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/yesod-core/yesod-core.cabal b/yesod-core/yesod-core.cabal index 5350ec33..cdae977b 100644 --- a/yesod-core/yesod-core.cabal +++ b/yesod-core/yesod-core.cabal @@ -42,7 +42,7 @@ library , unordered-containers >= 0.2 , monad-control >= 0.3 && < 1.1 , transformers-base >= 0.4 - , cookie >= 0.4.1 && < 0.5 + , cookie >= 0.4.2 && < 0.5 , http-types >= 0.7 , case-insensitive >= 0.2 , parsec >= 2 && < 3.2 From bc7ff2f552604e727d24b9f190b43602eb94b76d Mon Sep 17 00:00:00 2001 From: Bob Long Date: Sun, 1 May 2016 17:23:10 +0100 Subject: [PATCH 4/8] Add version information --- yesod-core/Yesod/Core/Class/Yesod.hs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/yesod-core/Yesod/Core/Class/Yesod.hs b/yesod-core/Yesod/Core/Class/Yesod.hs index f1eca3f8..4b758503 100644 --- a/yesod-core/Yesod/Core/Class/Yesod.hs +++ b/yesod-core/Yesod/Core/Class/Yesod.hs @@ -368,11 +368,15 @@ sslOnlySessions = (fmap . fmap) secureSessionCookies -- | Helps defend against CSRF attacks by setting the SameSite attribute on -- session cookies to "Lax". +-- +-- Since 1.4.21 laxSameSiteSessions :: IO (Maybe SessionBackend) -> IO (Maybe SessionBackend) laxSameSiteSessions = sameSiteSession sameSiteLax -- | Helps defend against CSRF attacks by setting the SameSite attribute on -- session cookies to "Strict". +-- +-- Since 1.4.21 strictSameSiteSessions :: IO (Maybe SessionBackend) -> IO (Maybe SessionBackend) strictSameSiteSessions = sameSiteSession sameSiteStrict From 9b0caaf2cf81834a1067704e7c999a82f677053a Mon Sep 17 00:00:00 2001 From: Bob Long Date: Tue, 3 May 2016 15:17:46 +0100 Subject: [PATCH 5/8] expand documentation on lax & strict --- yesod-core/Yesod/Core/Class/Yesod.hs | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/yesod-core/Yesod/Core/Class/Yesod.hs b/yesod-core/Yesod/Core/Class/Yesod.hs index 4b758503..45cefd0a 100644 --- a/yesod-core/Yesod/Core/Class/Yesod.hs +++ b/yesod-core/Yesod/Core/Class/Yesod.hs @@ -367,14 +367,22 @@ sslOnlySessions = (fmap . fmap) secureSessionCookies secureSessionCookies = customizeSessionCookies setSecureBit -- | Helps defend against CSRF attacks by setting the SameSite attribute on --- session cookies to "Lax". +-- session cookies to Lax. With the Lax setting, the cookie will be sent with same-site +-- requests, and with cross-site top-level navigations. +-- +-- This option is liable to change in future versions +-- of Yesod as the spec evolves. View more information . -- -- Since 1.4.21 laxSameSiteSessions :: IO (Maybe SessionBackend) -> IO (Maybe SessionBackend) laxSameSiteSessions = sameSiteSession sameSiteLax -- | Helps defend against CSRF attacks by setting the SameSite attribute on --- session cookies to "Strict". +-- session cookies to Strict. With the Strict setting, the cookie will only be +-- sent with same-site requests. +-- +-- This option is liable to change in future versions +-- of Yesod as the spec evolves. View more information . -- -- Since 1.4.21 strictSameSiteSessions :: IO (Maybe SessionBackend) -> IO (Maybe SessionBackend) From 1834d255e63299b9a3f0971c7a64137c06c30c55 Mon Sep 17 00:00:00 2001 From: Bob Long Date: Tue, 3 May 2016 15:18:04 +0100 Subject: [PATCH 6/8] replace pure with Just for backwards compat --- yesod-core/Yesod/Core/Class/Yesod.hs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/yesod-core/Yesod/Core/Class/Yesod.hs b/yesod-core/Yesod/Core/Class/Yesod.hs index 45cefd0a..2ccf2591 100644 --- a/yesod-core/Yesod/Core/Class/Yesod.hs +++ b/yesod-core/Yesod/Core/Class/Yesod.hs @@ -391,7 +391,7 @@ strictSameSiteSessions = sameSiteSession sameSiteStrict sameSiteSession :: SameSiteOption -> IO (Maybe SessionBackend) -> IO (Maybe SessionBackend) sameSiteSession s = (fmap . fmap) secureSessionCookies where - sameSite cookie = cookie { setCookieSameSite = (pure s) } + sameSite cookie = cookie { setCookieSameSite = (Just s) } secureSessionCookies = customizeSessionCookies sameSite -- | Apply a Strict-Transport-Security header with the specified timeout to From 6746c1c94f573602f399a236495970fd37f8a593 Mon Sep 17 00:00:00 2001 From: Bob Long Date: Tue, 3 May 2016 16:23:57 +0100 Subject: [PATCH 7/8] fixup whitespace in docs --- yesod-core/Yesod/Core/Class/Yesod.hs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/yesod-core/Yesod/Core/Class/Yesod.hs b/yesod-core/Yesod/Core/Class/Yesod.hs index 2ccf2591..312d3b35 100644 --- a/yesod-core/Yesod/Core/Class/Yesod.hs +++ b/yesod-core/Yesod/Core/Class/Yesod.hs @@ -370,8 +370,8 @@ sslOnlySessions = (fmap . fmap) secureSessionCookies -- session cookies to Lax. With the Lax setting, the cookie will be sent with same-site -- requests, and with cross-site top-level navigations. -- --- This option is liable to change in future versions --- of Yesod as the spec evolves. View more information . +-- This option is liable to change in future versions of Yesod as the spec evolves. +-- View more information . -- -- Since 1.4.21 laxSameSiteSessions :: IO (Maybe SessionBackend) -> IO (Maybe SessionBackend) @@ -381,8 +381,8 @@ laxSameSiteSessions = sameSiteSession sameSiteLax -- session cookies to Strict. With the Strict setting, the cookie will only be -- sent with same-site requests. -- --- This option is liable to change in future versions --- of Yesod as the spec evolves. View more information . +-- This option is liable to change in future versions of Yesod as the spec evolves. +-- View more information . -- -- Since 1.4.21 strictSameSiteSessions :: IO (Maybe SessionBackend) -> IO (Maybe SessionBackend) From 294ef285a3882fbcbb7f06b85aacf5ea84f5b2c5 Mon Sep 17 00:00:00 2001 From: Bob Long Date: Tue, 3 May 2016 16:24:12 +0100 Subject: [PATCH 8/8] remove redundant paren --- yesod-core/Yesod/Core/Class/Yesod.hs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/yesod-core/Yesod/Core/Class/Yesod.hs b/yesod-core/Yesod/Core/Class/Yesod.hs index 312d3b35..17903e83 100644 --- a/yesod-core/Yesod/Core/Class/Yesod.hs +++ b/yesod-core/Yesod/Core/Class/Yesod.hs @@ -391,7 +391,7 @@ strictSameSiteSessions = sameSiteSession sameSiteStrict sameSiteSession :: SameSiteOption -> IO (Maybe SessionBackend) -> IO (Maybe SessionBackend) sameSiteSession s = (fmap . fmap) secureSessionCookies where - sameSite cookie = cookie { setCookieSameSite = (Just s) } + sameSite cookie = cookie { setCookieSameSite = Just s } secureSessionCookies = customizeSessionCookies sameSite -- | Apply a Strict-Transport-Security header with the specified timeout to