From a01051eaf66a263aebb3ff9e1d492b0d0c1c4429 Mon Sep 17 00:00:00 2001 From: Maximilian Tagher Date: Sun, 14 Feb 2016 17:10:07 -0800 Subject: [PATCH 01/67] Have the yesod-auth login form use a CSRF token Closes #1159 Based on reading this [StackOverflow Post](http://stackoverflow.com/questions/6412813/do-login-forms-need-tokens-against-csrf-attacks) and skimming [this paper](http://seclab.stanford.edu/websec/csrf/csrf.pdf), using CSRF protection on login forms protects against a vulnerability where an attacker submits their own username/password in the login form. Later, the user uses the real site, but doesn't realize they're logged in as the attacker. This creates vulnerabilities like: 1. If the site logs the user's activity for them (e.g. recently watched videos on YouTube, previous searches on Google), the attacker can see this information by logging in. 2. The user adds sensitive information to the account, like credit card information, the attacker can login and potentially steal that information or use it on the site. I don't think this vulnerability applies to the `Yesod.Auth.Hardcoded` plugin because the attacker couldn't create an account of their own. However: * If I understand the example in `Yesod.Auth.Hardcoded`, one use case is to share one login form that works for both the Hardcoded plugin as well as normal database-backed username/password login, in which case having a CSRF token makes sense * I don't see a downside to having the CSRF token there * It makes the Hardcoded plugin work with the CSRF middleware Does this sound like the right solution? --- yesod-auth/Yesod/Auth/Hardcoded.hs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/yesod-auth/Yesod/Auth/Hardcoded.hs b/yesod-auth/Yesod/Auth/Hardcoded.hs index 592c0cf2..bb4a24da 100644 --- a/yesod-auth/Yesod/Auth/Hardcoded.hs +++ b/yesod-auth/Yesod/Auth/Hardcoded.hs @@ -160,10 +160,14 @@ authHardcoded = where dispatch "POST" ["login"] = postLoginR >>= sendResponse dispatch _ _ = notFound - loginWidget toMaster = + loginWidget toMaster = do + request <- getRequest + let tokenKey = ("_token" :: Text) -- This value taken from yesod-form's postHelper. Not ideal that it's hard-coded in two places. [whamlet| $newline never
+ $maybe t <- reqToken request +
_{Msg.UserName} From d39ce44c21d355cf522108a973d284e568611901 Mon Sep 17 00:00:00 2001 From: Maximilian Tagher Date: Mon, 15 Feb 2016 23:59:24 -0800 Subject: [PATCH 02/67] Use `defaultCsrfParamName` instead of hard-coding its value * Up version bounds so that `defaultCsrfParamName` is available. * I didn't bump the yesod-form version. It seemed unnecessary to do a new release just for this. --- yesod-auth/ChangeLog.md | 4 ++++ yesod-auth/Yesod/Auth/Hardcoded.hs | 3 +-- yesod-auth/yesod-auth.cabal | 4 ++-- yesod-form/Yesod/Form/Functions.hs | 3 ++- yesod-form/yesod-form.cabal | 2 +- 5 files changed, 10 insertions(+), 6 deletions(-) diff --git a/yesod-auth/ChangeLog.md b/yesod-auth/ChangeLog.md index 970cd994..46640af4 100644 --- a/yesod-auth/ChangeLog.md +++ b/yesod-auth/ChangeLog.md @@ -1,3 +1,7 @@ +## 1.4.13 + +* Add a CSRF token to the login form from `Yesod.Auth.Hardcoded`, making it compatible with the CSRF middleware [#1161](https://github.com/yesodweb/yesod/pull/1161) + ## 1.4.12 * Deprecated Yesod.Auth.GoogleEmail diff --git a/yesod-auth/Yesod/Auth/Hardcoded.hs b/yesod-auth/Yesod/Auth/Hardcoded.hs index bb4a24da..0f7061ad 100644 --- a/yesod-auth/Yesod/Auth/Hardcoded.hs +++ b/yesod-auth/Yesod/Auth/Hardcoded.hs @@ -162,12 +162,11 @@ authHardcoded = dispatch _ _ = notFound loginWidget toMaster = do request <- getRequest - let tokenKey = ("_token" :: Text) -- This value taken from yesod-form's postHelper. Not ideal that it's hard-coded in two places. [whamlet| $newline never $maybe t <- reqToken request - +
_{Msg.UserName} diff --git a/yesod-auth/yesod-auth.cabal b/yesod-auth/yesod-auth.cabal index 28468eff..69c13d9d 100644 --- a/yesod-auth/yesod-auth.cabal +++ b/yesod-auth/yesod-auth.cabal @@ -1,5 +1,5 @@ name: yesod-auth -version: 1.4.12 +version: 1.4.13 license: MIT license-file: LICENSE author: Michael Snoyman, Patrick Brisbin @@ -23,7 +23,7 @@ library build-depends: base >= 4 && < 5 , authenticate >= 1.3 , bytestring >= 0.9.1.4 - , yesod-core >= 1.4 && < 1.5 + , yesod-core >= 1.4.14 && < 1.5 , wai >= 1.4 , template-haskell , base16-bytestring diff --git a/yesod-form/Yesod/Form/Functions.hs b/yesod-form/Yesod/Form/Functions.hs index 6fe69b04..fc1e9903 100644 --- a/yesod-form/Yesod/Form/Functions.hs +++ b/yesod-form/Yesod/Form/Functions.hs @@ -59,6 +59,7 @@ import Text.Blaze (Markup, toMarkup) #define Html Markup #define toHtml toMarkup import Yesod.Core +import Yesod.Core.Handler (defaultCsrfParamName) import Network.Wai (requestMethod) import Text.Hamlet (shamlet) import Data.Monoid (mempty) @@ -213,7 +214,7 @@ postHelper :: (MonadHandler m, RenderMessage (HandlerSite m) FormMessage) -> m ((FormResult a, xml), Enctype) postHelper form env = do req <- getRequest - let tokenKey = "_token" + let tokenKey = defaultCsrfParamName let token = case reqToken req of Nothing -> mempty diff --git a/yesod-form/yesod-form.cabal b/yesod-form/yesod-form.cabal index a7f715d5..ebfd6fa3 100644 --- a/yesod-form/yesod-form.cabal +++ b/yesod-form/yesod-form.cabal @@ -20,7 +20,7 @@ flag network-uri library build-depends: base >= 4 && < 5 - , yesod-core >= 1.4 && < 1.5 + , yesod-core >= 1.4.14 && < 1.5 , yesod-persistent >= 1.4 && < 1.5 , time >= 1.1.4 , shakespeare >= 2.0 From 76fc5887f96dc698295c744539678e025838502b Mon Sep 17 00:00:00 2001 From: Alex Kardos Date: Wed, 17 Feb 2016 20:39:09 -0700 Subject: [PATCH 03/67] Fixed registerHandler CSRF issue The default register handler for email authentication didn't provide a CSRF token. I provided one by using a monadic form helper. --- yesod-auth/Yesod/Auth/Email.hs | 31 ++++++++++++++++++++++++++----- 1 file changed, 26 insertions(+), 5 deletions(-) diff --git a/yesod-auth/Yesod/Auth/Email.hs b/yesod-auth/Yesod/Auth/Email.hs index 1b94c411..6a3663ff 100644 --- a/yesod-auth/Yesod/Auth/Email.hs +++ b/yesod-auth/Yesod/Auth/Email.hs @@ -107,6 +107,8 @@ data EmailCreds site = EmailCreds , emailCredsEmail :: Email } +data UserForm = UserForm { email :: Text } + class ( YesodAuth site , PathPiece (AuthEmailId site) , (RenderMessage site Msg.AuthMessage) @@ -299,18 +301,37 @@ getRegisterR = registerHandler -- Since: 1.2.6 defaultRegisterHandler :: YesodAuthEmail master => AuthHandler master Html defaultRegisterHandler = do - email <- newIdent - tp <- getRouteToParent + ((f,widget),e) <- lift $ runFormPost registrationForm + toParentRoute <- getRouteToParent lift $ authLayout $ do setTitleI Msg.RegisterLong [whamlet|

_{Msg.EnterEmail} - +

-