Rubyで安全なWebアプリを作るためのメモ (2)
前回の記事のはてブのコメントでrack-protectionについての言及がないというご指摘をいただきました。恥ずかしながらrack-protectionについては知らなかったので、少し調べてみました。
rack-protectionは、CSRFやXSSをはじめとした攻撃に対処するためのRack middlewareです。アプリケーションを起動する前にuse Rack::Protectionを呼び出して組み込むことですべての機能を簡単に利用できます。
ただ、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を防ぎます。
Rack::Protection::FormToken (option)
Rack::Protection::AuthenticityTokenのチェックに加えて、X-Requested-Withヘッダを確認してXMLHttpRequestによるアクセスだった場合のみ許可するようにします。これにより、攻撃者が用意したリンクを踏んでページを開かされても、リクエストが処理されることはありません。
Rack::Protection::JsonCsrf
同一ホストのリファラが付いていないJSON (application/json) へのリクエストに対して、エラーを返すことでJSONPを無効化します。これによってCSRFやJSONハイジャッキングを防ぐことができます。多くの場合、JSONPで外部にデータを提供する必要はないと思うのでこれで十分でしょう。しかし、JSONPで外部サイトに対してデータを提供する場合には、リクエストの検証を行うか、そもそも非公開のデータをJSONに含めないようにするなどの対策が必要です。
さらに、リファラをチェックする場合、ブラウザの設定でリファラを送信しないようにしているとエラーになってしまうという問題があります。これを防ぐために、デフォルトではリファラが空の場合は強制的にチェックを通過させるようになっています。一見丸く収まったかと思ってしまいますが、要するにリファラを送信していない人に対しては一切のCSRF対策をしていないということになるので、Rack::Protection::JsonCsrfを使うだけではすべての利用者に対して十分な対策を提供できているとは言えません。実際にはリファラを有効にしている人がほとんどでしょうから、ある程度は意味がありますし、別の方法でリクエストを検証している場合の補助的な対策としては有効でしょう。
Rack::Protection::RemoteReferrer (option)
POSTなどGET・HEAD・OPTIONS・TRACE以外のリクエスト時にリファラをチェックし、同一ホストからでない場合はエラーを返します。こちらもRack::Protection::JsonCsrfと同様にリファラを無効にしている環境では意味がないという問題があるため、後述するRack::Protection::RemoteTokenを利用したほうがよいでしょう。
Rack::Protection::RemoteToken
Rack::Protection::AuthenticityTokenとRack::Protection::RemoteReferrerの組み合わせです。tokenとリファラの両方をチェックし、どちらか一方でも満たされない場合にはエラーになります。
XSS対策
Rack::Protection::EscapedParams
Rack::Request#paramsをエスケープします。ドキュメントには、Railsの場合は二重エスケープを避けるためにhtml_safeを呼ぶと記述されているのですが、私はRailsを使ったことがないのでよく分かりません。一応Sinatraで試してみましたが、案の定二重エスケープになってしまいました。
Rack::Protection::XssHeader
前回の記事ではnginxやApacheで行っていたX-XSS-Protectionヘッダの追加をRack middlewareとして実現したものです。どちらでやってもいいのですが、rack-protectionを使うなら任せてしまうのが手っ取り早いかもしれないですね。
クリックジャッキング対策
Rack::Protection::FrameOptions
これもRack::Protection::XssHeaderと同様にX-Frame-Optionsヘッダの追加を行います。
ディレクトリトラバーサル対策
Rack::Protection::PathTraversal
PATH_INFOに含まれる/と.を展開してくれます。例えば、/foo/../bar は /bar に、 /foo/../../bar も /bar に変換されます。これにより、PATH_IFNOをそのままファイルパスに使ってファイルを開いたとしても、想定したディレクトリより上の階層を読み書きさせられることがなくなります。しかし、対象はPATH_INFOだけなのでQUERY_STRINGで渡されたパラメータに対しては全く意味がありません。そもそも外部からファイル名を直接指定するような実装をしないことで、根本的に問題を防いだほうがよいでしょう。
セッションハイジャッキング対策
Rack::Protection::SessionHijacking
初回アクセス時にUser-Agent・Accept-Encoding・Accept-Languageヘッダの内容をsession内に記憶しておき、一致しなくなった場合にセッションハイジャッキングが行われたと判断します。セッションハイジャッキングツールのFiresheepによる攻撃を防ぐのに有効なようです。しかし、ドキュメントにも書いてあるようにすべての攻撃を防げるものではないので、一般的なセッションハイジャッキング対策とあわせて利用すべきです。
IPスプーフィング対策
Rack::Protection::IPSpoofing
Client-IPヘッダやX-Real-IPヘッダがX-Forwarded-Forヘッダと矛盾しないことをチェックします。これでIPスプーフィング攻撃を一部防ぐことができるようです。