LoginSignup
5
2

More than 5 years have passed since last update.

特定のリソースに対してCSRFチェックを行わないようにする

Last updated at Posted at 2016-12-10

YesodはCSRF対策のサーバ側処理としてdefaultCsrfMiddleware というYesod Middlewareを提供している、ということはYesod Advent Calendar 2016|9日目に書きました。

ただ、CSRF対策はサーバ側だけでは当然ながら不充分で、クライアント側にも対処が必要です。ところが困ったことにこのクライアント側で対処がなされていない処理が見あたります。

中でも困るのがログアウト処理で、単純にdefaultCsrfMiddlewareを使うとログアウト処理(=/auth/logoutへのアクセス)でCSRFチェックでNGとなってしまい、ログアウトに失敗してしまいます。

yesod_permission_denied.png

これに対処する1つの方法として、特定パスへのアクセス時にCSRF対策を行わないようにするMiddlewareを作り、defaultCsrfMiddlewareの代わりに使うようにしてみたいと思います。

以下サンプルコード。

Middleware.hs
{-# LANGUAGE OverloadedStrings #-}
module Lib.Middleware (
    csrfMiddleware
) where

import GHC.Base
import Yesod
import Network.Wai (Request(rawPathInfo))
import Data.Textual.Encoding (decodeUtf8)
import Data.Text (Text, isSuffixOf)

csrfMiddleware :: Yesod site =>
    Route site -> HandlerT site IO res -> HandlerT site IO res
csrfMiddleware logoutRoute handler = do
    path <- getRequestPath
    renderFunc <- getUrlRender
    logoutPath <- return $ renderFunc logoutRoute
    if isSuffixOf path logoutPath then
        handler
      else
        defaultCsrfMiddleware handler

getRequestPath :: MonadHandler m => m Text
getRequestPath = getRequest
    >>= return . reqWaiRequest
    >>= return . rawPathInfo
    >>= return . decodeUtf8

次のような考え方で作っています。

  • CSRFチェックを行わないリソースを引数でもらう
  • このようなリソースへのリクエストに対してはハンドラ関数をそのまま呼び出す
  • それ以外のリソースへのリクエストに対してはYesodが提供している defaultCsrfMiddleware を介してハンドラ関数を呼び出す

このYesod Middlewareを使うには、Foundation.hsの中のdefaultCsrfMiddlewareを使っている行を、以下のように書き換えます。

Foundation.hs
yesodMiddleware = csrfMiddleware (AuthR LogoutR) . defaultYesodMiddleware

理想的にはCSRF対策が成されていないプログラムを見つけたら修正してPull Requestなりで本家に取り込んでもらうのがいいのでしょうけれども、このような方法もある、ということでご紹介しました。

5
2
2

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
5
2