nonce -> token (#214)

This commit is contained in:
Michael Snoyman 2012-03-16 06:39:30 +02:00
parent f11656f73a
commit ec62f6f68c
12 changed files with 56 additions and 56 deletions

View File

@ -25,7 +25,7 @@ module Yesod.Internal
, toUnique , toUnique
-- * Names -- * Names
, sessionName , sessionName
, nonceKey , tokenKey
) where ) where
import Text.Hamlet (HtmlUrl, hamlet, Html) import Text.Hamlet (HtmlUrl, hamlet, Html)
@ -95,8 +95,8 @@ newtype Head url = Head (HtmlUrl url)
newtype Body url = Body (HtmlUrl url) newtype Body url = Body (HtmlUrl url)
deriving Monoid deriving Monoid
nonceKey :: IsString a => a tokenKey :: IsString a => a
nonceKey = "_NONCE" tokenKey = "_TOKEN"
sessionName :: IsString a => a sessionName :: IsString a => a
sessionName = "_SESSION" sessionName = "_SESSION"

View File

@ -416,19 +416,19 @@ defaultYesodRunner handler master sub murl toMasterRoute msb req = do
redirect url' redirect url'
Unauthorized s' -> permissionDenied s' Unauthorized s' -> permissionDenied s'
handler handler
let sessionMap = Map.fromList . filter ((/=) nonceKey . fst) $ session let sessionMap = Map.fromList . filter ((/=) tokenKey . fst) $ session
let ra = resolveApproot master req let ra = resolveApproot master req
yar <- handlerToYAR master sub toMasterRoute yar <- handlerToYAR master sub toMasterRoute
(yesodRender master ra) errorHandler rr murl sessionMap h (yesodRender master ra) errorHandler rr murl sessionMap h
extraHeaders <- case yar of extraHeaders <- case yar of
(YARPlain _ _ ct _ newSess) -> do (YARPlain _ _ ct _ newSess) -> do
let nsNonce = Map.toList $ maybe let nsToken = Map.toList $ maybe
newSess newSess
(\n -> Map.insert nonceKey (TE.encodeUtf8 n) newSess) (\n -> Map.insert tokenKey (TE.encodeUtf8 n) newSess)
(reqNonce rr) (reqToken rr)
sessionHeaders <- liftIO $ maybe sessionHeaders <- liftIO $ maybe
(return []) (return [])
(\sb -> sbSaveSession sb master req now session nsNonce) (\sb -> sbSaveSession sb master req now session nsToken)
msb msb
return $ ("Content-Type", ct) : map headerToPair sessionHeaders return $ ("Content-Type", ct) : map headerToPair sessionHeaders
_ -> return [] _ -> return []

View File

@ -36,16 +36,16 @@ data Request = Request
, reqWaiRequest :: W.Request , reqWaiRequest :: W.Request
-- | Languages which the client supports. -- | Languages which the client supports.
, reqLangs :: [Text] , reqLangs :: [Text]
-- | A random, session-specific nonce used to prevent CSRF attacks. -- | A random, session-specific token used to prevent CSRF attacks.
, reqNonce :: Maybe Text , reqToken :: Maybe Text
} }
parseWaiRequest :: W.Request parseWaiRequest :: W.Request
-> [(Text, ByteString)] -- ^ session -> [(Text, ByteString)] -- ^ session
-> Bool -> Bool
-> IO Request -> IO Request
parseWaiRequest env session' useNonce = parseWaiRequest env session' useToken =
parseWaiRequest' env session' useNonce <$> newStdGen parseWaiRequest' env session' useToken <$> newStdGen
parseWaiRequest' :: RandomGen g parseWaiRequest' :: RandomGen g
=> W.Request => W.Request
@ -53,8 +53,8 @@ parseWaiRequest' :: RandomGen g
-> Bool -> Bool
-> g -> g
-> Request -> Request
parseWaiRequest' env session' useNonce gen = parseWaiRequest' env session' useToken gen =
Request gets'' cookies' env langs'' nonce Request gets'' cookies' env langs'' token
where where
gets' = queryToQueryText $ W.queryString env gets' = queryToQueryText $ W.queryString env
gets'' = map (second $ fromMaybe "") gets' gets'' = map (second $ fromMaybe "") gets'
@ -75,16 +75,16 @@ parseWaiRequest' env session' useNonce gen =
-- language in the list. -- language in the list.
langs'' = addTwoLetters (id, Set.empty) langs' langs'' = addTwoLetters (id, Set.empty) langs'
-- If sessions are disabled nonces should not be used (any -- If sessions are disabled tokens should not be used (any
-- nonceKey present in the session is ignored). If sessions -- tokenKey present in the session is ignored). If sessions
-- are enabled and a session has no nonceKey a new one is -- are enabled and a session has no tokenKey a new one is
-- generated. -- generated.
nonce = if not useNonce token = if not useToken
then Nothing then Nothing
else Just $ maybe else Just $ maybe
(pack $ randomString 10 gen) (pack $ randomString 10 gen)
(decodeUtf8With lenientDecode) (decodeUtf8With lenientDecode)
(lookup nonceKey session') (lookup tokenKey session')
addTwoLetters :: ([Text] -> [Text], Set.Set Text) -> [Text] -> [Text] addTwoLetters :: ([Text] -> [Text], Set.Set Text) -> [Text] -> [Text]
addTwoLetters (toAdd, exist) [] = addTwoLetters (toAdd, exist) [] =

View File

@ -27,32 +27,32 @@ noRepeat len n = length (nub $ map (randomString len . mkStdGen) [1..n]) == n
-- For convenience instead of "(undefined :: StdGen)". -- For convenience instead of "(undefined :: StdGen)".
g :: StdGen g :: StdGen
g = undefined g = error "test/YesodCoreTest/InternalRequest.g"
nonceSpecs :: [Spec] tokenSpecs :: [Spec]
nonceSpecs = describe "Yesod.Internal.Request.parseWaiRequest (reqNonce)" tokenSpecs = describe "Yesod.Internal.Request.parseWaiRequest (reqToken)"
[ it "is Nothing if sessions are disabled" noDisabledNonce [ it "is Nothing if sessions are disabled" noDisabledToken
, it "ignores pre-existing nonce if sessions are disabled" ignoreDisabledNonce , it "ignores pre-existing token if sessions are disabled" ignoreDisabledToken
, it "uses preexisting nonce in session" useOldNonce , it "uses preexisting token in session" useOldToken
, it "generates a new nonce for sessions without nonce" generateNonce , it "generates a new token for sessions without token" generateToken
] ]
noDisabledNonce :: Bool noDisabledToken :: Bool
noDisabledNonce = reqNonce r == Nothing where noDisabledToken = reqToken r == Nothing where
r = parseWaiRequest' defaultRequest [] False g r = parseWaiRequest' defaultRequest [] False g
ignoreDisabledNonce :: Bool ignoreDisabledToken :: Bool
ignoreDisabledNonce = reqNonce r == Nothing where ignoreDisabledToken = reqToken r == Nothing where
r = parseWaiRequest' defaultRequest [("_NONCE", "old")] False g r = parseWaiRequest' defaultRequest [("_TOKEN", "old")] False g
useOldNonce :: Bool useOldToken :: Bool
useOldNonce = reqNonce r == Just "old" where useOldToken = reqToken r == Just "old" where
r = parseWaiRequest' defaultRequest [("_NONCE", "old")] True g r = parseWaiRequest' defaultRequest [("_TOKEN", "old")] True g
generateNonce :: Bool generateToken :: Bool
generateNonce = reqNonce r /= Nothing where generateToken = reqToken r /= Nothing where
r = parseWaiRequest' defaultRequest [("_NONCE", "old")] True g r = parseWaiRequest' defaultRequest [("_TOKEN", "old")] True g
langSpecs :: [Spec] langSpecs :: [Spec]
@ -95,6 +95,6 @@ prioritizeLangs = reqLangs r == ["en-QUERY", "en-COOKIE", "en-SESSION", "en", "e
internalRequestTest :: [Spec] internalRequestTest :: [Spec]
internalRequestTest = descriptions [ randomStringSpecs internalRequestTest = descriptions [ randomStringSpecs
, nonceSpecs , tokenSpecs
, langSpecs , langSpecs
] ]

View File

@ -1,7 +1,7 @@
{-# LANGUAGE QuasiQuotes, TypeFamilies, TemplateHaskell, MultiParamTypeClasses #-} {-# LANGUAGE QuasiQuotes, TypeFamilies, TemplateHaskell, MultiParamTypeClasses #-}
{-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE FlexibleInstances #-} {-# LANGUAGE FlexibleInstances #-}
module YesodCoreTest.JsLoader (specs) where module YesodCoreTest.JsLoader (specs, Widget) where
import YesodCoreTest.JsLoaderSites.HeadAsync (HA(..)) import YesodCoreTest.JsLoaderSites.HeadAsync (HA(..))
import YesodCoreTest.JsLoaderSites.Bottom (B(..)) import YesodCoreTest.JsLoaderSites.Bottom (B(..))

View File

@ -1,7 +1,7 @@
{-# LANGUAGE QuasiQuotes, TypeFamilies, TemplateHaskell, MultiParamTypeClasses #-} {-# LANGUAGE QuasiQuotes, TypeFamilies, TemplateHaskell, MultiParamTypeClasses #-}
{-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE FlexibleInstances #-} {-# LANGUAGE FlexibleInstances #-}
module YesodCoreTest.JsLoaderSites.Bottom (B(..)) where module YesodCoreTest.JsLoaderSites.Bottom (B(..), Widget) where
import Yesod.Core import Yesod.Core

View File

@ -1,7 +1,7 @@
{-# LANGUAGE QuasiQuotes, TypeFamilies, TemplateHaskell, MultiParamTypeClasses #-} {-# LANGUAGE QuasiQuotes, TypeFamilies, TemplateHaskell, MultiParamTypeClasses #-}
{-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE FlexibleInstances #-} {-# LANGUAGE FlexibleInstances #-}
module YesodCoreTest.JsLoaderSites.HeadAsync (HA(..)) where module YesodCoreTest.JsLoaderSites.HeadAsync (HA(..), Widget) where
import Yesod.Core import Yesod.Core

View File

@ -18,7 +18,7 @@ mkYesod "Y" [parseRoutes|
instance Yesod Y instance Yesod Y
getRootR :: Handler RepHtml getRootR :: Handler RepHtml
getRootR = defaultLayout $ addHamlet [hamlet|<a href=@{RootR}>|] getRootR = defaultLayout $ toWidget [hamlet|<a href=@{RootR}>|]
linksTest :: [Spec] linksTest :: [Spec]
linksTest = describe "Test.Links" linksTest = describe "Test.Links"

View File

@ -27,9 +27,9 @@ instance Yesod Y where
getRootR :: Handler RepHtml getRootR :: Handler RepHtml
getRootR = defaultLayout $ do getRootR = defaultLayout $ do
addCassius [lucius|foo1{bar:baz}|] toWidget [lucius|foo1{bar:baz}|]
addCassiusMedia "screen" [lucius|foo2{bar:baz}|] addCassiusMedia "screen" [lucius|foo2{bar:baz}|]
addCassius [lucius|foo3{bar:baz}|] toWidget [lucius|foo3{bar:baz}|]
getStaticR :: Handler RepHtml getStaticR :: Handler RepHtml
getStaticR = getRootR getStaticR = getRootR

View File

@ -1,5 +1,5 @@
name: yesod-core name: yesod-core
version: 1.0.0 version: 1.0.0.20120316
license: BSD3 license: BSD3
license-file: LICENSE license-file: LICENSE
author: Michael Snoyman <michael@snoyman.com> author: Michael Snoyman <michael@snoyman.com>

View File

@ -18,7 +18,7 @@ module Yesod.Form.Functions
, aopt , aopt
-- * Run a form -- * Run a form
, runFormPost , runFormPost
, runFormPostNoNonce , runFormPostNoToken
, runFormGet , runFormGet
-- * Generate a blank form -- * Generate a blank form
, generateFormPost , generateFormPost
@ -48,7 +48,7 @@ import Text.Blaze (Html, toHtml)
import Yesod.Handler (GHandler, getRequest, runRequestBody, newIdent, getYesod) import Yesod.Handler (GHandler, getRequest, runRequestBody, newIdent, getYesod)
import Yesod.Core (RenderMessage, SomeMessage (..)) import Yesod.Core (RenderMessage, SomeMessage (..))
import Yesod.Widget (GWidget, whamlet) import Yesod.Widget (GWidget, whamlet)
import Yesod.Request (reqNonce, reqWaiRequest, reqGetParams, languages, FileInfo (..)) import Yesod.Request (reqToken, reqWaiRequest, reqGetParams, languages, FileInfo (..))
import Network.Wai (requestMethod) import Network.Wai (requestMethod)
import Text.Hamlet (shamlet) import Text.Hamlet (shamlet)
import Data.Monoid (mempty) import Data.Monoid (mempty)
@ -178,18 +178,18 @@ postHelper :: RenderMessage master FormMessage
-> GHandler sub master ((FormResult a, xml), Enctype) -> GHandler sub master ((FormResult a, xml), Enctype)
postHelper form env = do postHelper form env = do
req <- getRequest req <- getRequest
let nonceKey = "_nonce" let tokenKey = "_token"
let nonce = let token =
case reqNonce req of case reqToken req of
Nothing -> mempty Nothing -> mempty
Just n -> [shamlet|<input type=hidden name=#{nonceKey} value=#{n}>|] Just n -> [shamlet|<input type=hidden name=#{tokenKey} value=#{n}>|]
m <- getYesod m <- getYesod
langs <- languages langs <- languages
((res, xml), enctype) <- runFormGeneric (form nonce) m langs env ((res, xml), enctype) <- runFormGeneric (form token) m langs env
let res' = let res' =
case (res, env) of case (res, env) of
(FormSuccess{}, Just (params, _)) (FormSuccess{}, Just (params, _))
| Map.lookup nonceKey params /= fmap return (reqNonce req) -> | Map.lookup tokenKey params /= fmap return (reqToken req) ->
FormFailure [renderMessage m langs MsgCsrfWarning] FormFailure [renderMessage m langs MsgCsrfWarning]
_ -> res _ -> res
return ((res', xml), enctype) return ((res', xml), enctype)
@ -216,8 +216,8 @@ postEnv = do
where where
notEmpty = not . L.null . fileContent notEmpty = not . L.null . fileContent
runFormPostNoNonce :: (Html -> MForm sub master (FormResult a, xml)) -> GHandler sub master ((FormResult a, xml), Enctype) runFormPostNoToken :: (Html -> MForm sub master (FormResult a, xml)) -> GHandler sub master ((FormResult a, xml), Enctype)
runFormPostNoNonce form = do runFormPostNoToken form = do
langs <- languages langs <- languages
m <- getYesod m <- getYesod
env <- postEnv env <- postEnv

View File

@ -1,5 +1,5 @@
name: yesod-form name: yesod-form
version: 1.0.0 version: 1.0.0.20120316
license: BSD3 license: BSD3
license-file: LICENSE license-file: LICENSE
author: Michael Snoyman <michael@snoyman.com> author: Michael Snoyman <michael@snoyman.com>