monolithic kernel

Rubyで安全なWebアプリを作るためのメモ (2)

February 21, 2012

    前回の記事はてブのコメントrack-protectionについての言及がないというご指摘をいただきました。恥ずかしながらrack-protectionについては知らなかったので、少し調べてみました。

    rack-protectionは、CSRFやXSSをはじめとした攻撃に対処するためのRack middlewareです。アプリケーションを起動する前にuse Rack::Protectionを呼び出して組み込むことですべての機能を簡単に利用できます。

    require 'rack/protection'
    use Rack::Protection
    run App

    ただ、rack-protectionが何をしているのか把握していないとはまりそうな部分もいくつかあるので、導入前に各機能の概要を理解しておいたほうがよいでしょう。

    rack-protectionが実現している機能は以下の通りです。

    • CSRF対策
    • XSS対策
    • クリックジャッキング対策
    • ディレクトリトラバーサル対策
    • セッションハイジャッキング対策
    • IPスプーフィング対策

    ここでは、各機能の概要を紹介します。機能ごとにそれぞれRack middlewareになっているので、use Rack::Protectionの代わりに個別にuseして利用することもできます。(option) と表記したmiddlewareはデフォルトでは組み込まれません。

    CSRF対策

    Rack::Protection::AuthenticityToken (option)

    前回紹介したrack-csrfと同様に、POSTなどGET・HEAD・OPTIONS・TRACE以外のリクエスト時にsessionに格納しておいたtokenとリクエストのtokenを比較することでCSRFを防ぎます。

    use Rack::Protection::AuthenticityToken

    Rack::Protection::FormToken (option)

    Rack::Protection::AuthenticityTokenのチェックに加えて、X-Requested-Withヘッダを確認してXMLHttpRequestによるアクセスだった場合のみ許可するようにします。これにより、攻撃者が用意したリンクを踏んでページを開かされても、リクエストが処理されることはありません。

    use Rack::Protection::FormToken

    Rack::Protection::JsonCsrf

    同一ホストのリファラが付いていないJSON (application/json) へのリクエストに対して、エラーを返すことでJSONPを無効化します。これによってCSRFやJSONハイジャッキングを防ぐことができます。多くの場合、JSONPで外部にデータを提供する必要はないと思うのでこれで十分でしょう。しかし、JSONPで外部サイトに対してデータを提供する場合には、リクエストの検証を行うか、そもそも非公開のデータをJSONに含めないようにするなどの対策が必要です。

    さらに、リファラをチェックする場合、ブラウザの設定でリファラを送信しないようにしているとエラーになってしまうという問題があります。これを防ぐために、デフォルトではリファラが空の場合は強制的にチェックを通過させるようになっています。一見丸く収まったかと思ってしまいますが、要するにリファラを送信していない人に対しては一切のCSRF対策をしていないということになるので、Rack::Protection::JsonCsrfを使うだけではすべての利用者に対して十分な対策を提供できているとは言えません。実際にはリファラを有効にしている人がほとんどでしょうから、ある程度は意味がありますし、別の方法でリクエストを検証している場合の補助的な対策としては有効でしょう。

    use Rack::Protection::JsonCsrf

    Rack::Protection::RemoteReferrer (option)

    POSTなどGET・HEAD・OPTIONS・TRACE以外のリクエスト時にリファラをチェックし、同一ホストからでない場合はエラーを返します。こちらもRack::Protection::JsonCsrfと同様にリファラを無効にしている環境では意味がないという問題があるため、後述するRack::Protection::RemoteTokenを利用したほうがよいでしょう。

    use Rack::Protection::RemoteReferrer

    Rack::Protection::RemoteToken

    Rack::Protection::AuthenticityTokenとRack::Protection::RemoteReferrerの組み合わせです。tokenとリファラの両方をチェックし、どちらか一方でも満たされない場合にはエラーになります。

    use Rack::Protection::RemoteToken

    XSS対策

    Rack::Protection::EscapedParams

    Rack::Request#paramsをエスケープします。ドキュメントには、Railsの場合は二重エスケープを避けるためにhtml_safeを呼ぶと記述されているのですが、私はRailsを使ったことがないのでよく分かりません。一応Sinatraで試してみましたが、案の定二重エスケープになってしまいました。

    use Rack::Protection::EscapedParams

    Rack::Protection::XssHeader

    前回の記事ではnginxやApacheで行っていたX-XSS-Protectionヘッダの追加をRack middlewareとして実現したものです。どちらでやってもいいのですが、rack-protectionを使うなら任せてしまうのが手っ取り早いかもしれないですね。

    use Rack::Protection::XssHeader

    クリックジャッキング対策

    Rack::Protection::FrameOptions

    これもRack::Protection::XssHeaderと同様にX-Frame-Optionsヘッダの追加を行います。

    use Rack::Protection::FrameOptions

    ディレクトリトラバーサル対策

    Rack::Protection::PathTraversal

    PATH_INFOに含まれる/と.を展開してくれます。例えば、/foo/../bar は /bar に、 /foo/../../bar も /bar に変換されます。これにより、PATH_IFNOをそのままファイルパスに使ってファイルを開いたとしても、想定したディレクトリより上の階層を読み書きさせられることがなくなります。しかし、対象はPATH_INFOだけなのでQUERY_STRINGで渡されたパラメータに対しては全く意味がありません。そもそも外部からファイル名を直接指定するような実装をしないことで、根本的に問題を防いだほうがよいでしょう。

    use Rack::Protection::PathTraversal

    セッションハイジャッキング対策

    Rack::Protection::SessionHijacking

    初回アクセス時にUser-Agent・Accept-Encoding・Accept-Languageヘッダの内容をsession内に記憶しておき、一致しなくなった場合にセッションハイジャッキングが行われたと判断します。セッションハイジャッキングツールのFiresheepによる攻撃を防ぐのに有効なようです。しかし、ドキュメントにも書いてあるようにすべての攻撃を防げるものではないので、一般的なセッションハイジャッキング対策とあわせて利用すべきです。

    use Rack::Protection::SessionHijacking

    IPスプーフィング対策

    Rack::Protection::IPSpoofing

    Client-IPヘッダやX-Real-IPヘッダがX-Forwarded-Forヘッダと矛盾しないことをチェックします。これでIPスプーフィング攻撃を一部防ぐことができるようです。

    use Rack::Protection::IPSpoofing