Permalink
Browse files

CSRF functionality belongs with Session not Auth

  • Loading branch information...
1 parent 4107848 commit 6c3f47ce9bc01d8626e81dfe7ee749b92e73152f @ozataman ozataman committed Apr 15, 2011
Showing with 90 additions and 80 deletions.
  1. +1 −1 snap-auth.cabal
  2. +0 −5 src/Snap/Auth.hs
  3. +0 −28 src/Snap/Auth/Handlers.hs
  4. +0 −46 src/Snap/Auth/Splices.hs
  5. +89 −0 src/Snap/Extension/Session/Helpers.hs
View
@@ -14,8 +14,8 @@ Library
exposed-modules:
Snap.Auth
Snap.Auth.Handlers
- Snap.Auth.Splices
Snap.Extension.Session.CookieSession
+ Snap.Extension.Session.Helpers
other-modules:
Snap.Auth.Password
Snap.Extension.Session
View
@@ -210,11 +210,6 @@ class (MonadSnap m, MonadSession m) => MonadAuth m where
authRememberCookieName = return "auth_remember_token"
- -- | Name of the CSRF token parameter
- authAuthenticityTokenParam :: m ByteString
- authAuthenticityTokenParam = return "authenticity_token"
-
-
-- | Remember period in seconds. Defaults to 2 weeks.
authRememberPeriod :: m Int
authRememberPeriod = return $ 60 * 60 * 24 * 14
View
@@ -13,7 +13,6 @@ module Snap.Auth.Handlers
( loginHandler
, logoutHandler
, requireUser
- , checkCSRF
) where
import Control.Applicative ( (<|>) )
@@ -70,30 +69,3 @@ requireUser :: MonadAuthUser m t => m a
-- ^ Do this if an authenticated user is present.
-> m a
requireUser bad good = authenticatedUserId >>= maybe bad (const good)
-
-
-------------------------------------------------------------------------------
--- | Handler to protect against CSRF attacks. Chain this handler at the
--- beginning of your routing table to enable.
---
--- Example:
---
--- @redirError = logError "Someone tried to bypass CSRF" >> redirect "/"
---
--- checkCSRF redirError >> route [myHandler, myHandler2, ...]
--- @
---
--- The convention is to submit an "authenticity_token" parameter with each
--- 'POST' request. This action will confirm its presence against what is safely
--- embedded in the session and execute the given action if they don't match.
--- The exact name of the parameter is defined by 'authAuthenticityTokenParam'.
-checkCSRF :: MonadAuth m => m ()
- -- ^ Do this if CSRF token does not match.
- -> m ()
-checkCSRF failAct = method POST doCheck <|> return ()
- where
- doCheck = do
- embeddedToken <- sessionCSRFToken
- param <- authAuthenticityTokenParam
- submitted <- maybe "" id `fmap` getParam param
- when (submitted /= embeddedToken) failAct
View
@@ -1,46 +0,0 @@
-{-|
-
- Convenience Splices to be used in your views. They go hand-in hand with
- handlers defined in this package to help automate some common patterns.
-
--}
-
-module Snap.Auth.Splices
- ( metaCSRFTag, hiddenCSRFTag ) where
-
-import Control.Monad.Trans.Class (lift)
-import Data.Text.Encoding as T
-
-import Snap.Auth
-import Snap.Extension.Session.CookieSession (MonadSession(..), sessionCSRFToken)
-
-import qualified Text.XmlHtml as X
-import Text.Templating.Heist
-
-
-metaCSRFTag
- :: (MonadAuth m)
- => Splice m
-metaCSRFTag = do
- embeddedToken <- lift sessionCSRFToken
- param <- lift authAuthenticityTokenParam
- let metaToken = X.Element "meta"
- [ ("name", "csrf-token")
- , ("content", T.decodeUtf8 embeddedToken) ] []
- let metaParam = X.Element "meta"
- [ ("name", "csrf-param")
- , ("content", T.decodeUtf8 param) ] []
- return $ [metaParam, metaToken]
-
-
-hiddenCSRFTag
- :: (MonadAuth m)
- => Splice m
-hiddenCSRFTag = do
- embeddedToken <- lift sessionCSRFToken
- param <- lift authAuthenticityTokenParam
- return . return $ X.Element "input"
- [ ("type", "hidden")
- , ("name", T.decodeUtf8 param)
- , ("value", T.decodeUtf8 embeddedToken)
- ] []
@@ -0,0 +1,89 @@
+{-|
+
+ Convenience Splices to be used in your views. They go hand-in hand with
+ handlers defined in this package to help automate some common patterns.
+
+-}
+
+module Snap.Extension.Session.Helpers
+ ( metaCSRFTag
+ , hiddenCSRFTag
+ , checkCSRF
+ ) where
+
+
+import Control.Applicative ( (<|>) )
+import Control.Monad (when)
+import Control.Monad.Trans.Class (lift)
+import Data.Text.Encoding as T
+
+
+import Snap.Types
+import Snap.Extension.Session (MonadSession(..), sessionCSRFToken)
+
+import qualified Text.XmlHtml as X
+import Text.Templating.Heist
+
+
+------------------------------------------------------------------------------
+-- Use this 'Splice' in your <head> section to insert a meta tag with the
+-- authenticity token.
+--
+-- Use-case similar to Rails 3; you can use unobtrusive JS bindings to extract
+-- the token from the webpage and add to your buttons/forms.
+metaCSRFTag
+ :: (MonadSession m)
+ => Splice m
+metaCSRFTag = do
+ embeddedToken <- lift sessionCSRFToken
+ let param = "authenticity_token"
+ let metaToken = X.Element "meta"
+ [ ("name", "csrf-token")
+ , ("content", T.decodeUtf8 embeddedToken) ] []
+ let metaParam = X.Element "meta"
+ [ ("name", "csrf-param")
+ , ("content", param) ] []
+ return $ [metaParam, metaToken]
+
+
+------------------------------------------------------------------------------
+-- Use in your forms to insert a hidden "authenticity_token" field.
+hiddenCSRFTag
+ :: (MonadSession m)
+ => Splice m
+hiddenCSRFTag = do
+ embeddedToken <- lift sessionCSRFToken
+ let param = "authenticity_token"
+ return . return $ X.Element "input"
+ [ ("type", "hidden")
+ , ("name", T.decodeUtf8 param)
+ , ("value", T.decodeUtf8 embeddedToken)
+ ] []
+
+
+
+------------------------------------------------------------------------------
+-- | Handler to protect against CSRF attacks. Chain this handler at the
+-- beginning of your routing table to enable.
+--
+-- Example:
+--
+-- @redirError = logError "Someone tried to bypass CSRF" >> redirect "/"
+--
+-- checkCSRF redirError >> route [myHandler, myHandler2, ...]
+-- @
+--
+-- The convention is to submit an "authenticity_token" parameter with each
+-- 'POST' request. This action will confirm its presence against what is safely
+-- embedded in the session and execute the given action if they don't match.
+-- The exact name of the parameter is defined by 'authAuthenticityTokenParam'.
+checkCSRF :: MonadSession m => m ()
+ -- ^ Do this if CSRF token does not match.
+ -> m ()
+checkCSRF failAct = method POST doCheck <|> return ()
+ where
+ doCheck = do
+ embeddedToken <- sessionCSRFToken
+ let param = "authenticity_token"
+ submitted <- maybe "" id `fmap` getParam param
+ when (submitted /= embeddedToken) failAct

0 comments on commit 6c3f47c

Please sign in to comment.