protect_from_forgeryに関するRailsのコメントが秀逸すぎたのでぜひみんな読んでほしい

2019年に入ってから、安全なWebアプリケーションの作り方の輪読会に参加させて頂いています。
知っているつもりで知らなかったお話も多く、また人と話すことで深掘りができて、コーディング中もヒヤリ・ハットが少しできるようになってきました。
(ただ、自分の目だけだと見落としがちなので、ツールやレビューによるチェックが1番だと思っています)

先日はCSRF(CrossSiteRequestForgery)対策についてまとめていました。
RailsCSRF対策といえば、ApplicationControllerに書いてあるprotect_from_forgery

どんなことしてるんだろう、とふと気になったのでコードを読んでいたのですが、そこで驚いたのが、コメントの秀逸さ。

rails/request_forgery_protection.rb at master · rails/rails · GitHub

約50行に渡ってコメントが記載されているのですが、CSRFからどうやってアプリを守るか、Railsの方針は何かが平易でわかりやすい言葉で書かれています。

その明確さ、方針のきれいさに読んでいて感動したので、THE・意訳をしてみました。

レンダリングされるHTMLにtokenをふくめることでControllerは守られてるよ

  • このtokenは攻撃者には知られないしランダムな文字列だよ
  • Railsはセッションに保存したtokenとリクエストのtokenがあってるか比較してるよ
  • GETリクエスト(べき等であるべきリクエスト)は保護しないよ
  • (だから大事な処理はぜったいPOSTにして(GETにしないで)ね)
  • でもJavaScriptHTMLのリクエストも含めて、セッションに基づく全リクエストはCSRFで保護されるべきだよ

GETリクエストは保護しない(だって、大事な処理はGETに書かないって決めたから)けど、 罠サイトが<script>タグをつかってあなたのアプリのJavaScriptを呼び出して、データ漏洩しちゃうかもしれない。
だから、XmlHttpRequest (XHR とか Ajaxっていうよね)だけがJavaScriptによるGETリクエストに答えられるようにしてるよ。

XMLHttpRequestとは
スキーム,ドメイン,ポート(これをまとめてオリジンと呼びます)すべてが一致したURLとしか通信できない(リクエストのこと)

そのtokenの埋め込みはどうしているのかというと、これもまた明確に記載されています。

# application.html.erb
<head>
  <%= csrf_meta_tags %>
</head>

# For AJAX requests other than GETs, extract the "csrf-token" from the meta-tag
# and send as the "X-CSRF-Token" HTTP header.
# If you are using jQuery with jquery-rails this happens automatically.

# 超意訳・再び
# GET以外のAjaxリクエストの場合はmetaタグから 'csrf-token' を抽出して
# 'X-CSRF-Token' HTTPヘッダーとして送信するよ
# jquery-rails(ライブラリ)でjQueryを使用してたら自動でやっとくよ〜😇

最後に、APICSRF対策ってどうするんだろう?やっぱりクライアント側でするんだよね…?と気になったのですが、これもまた明記されていました。

# We may want to disable CSRF protection for APIs since they are typically
# designed to be state-less. That is, the request API client will handle
# the session for you instead of Rails.

# APIのCSRFprotectionは無効にしたいよね~
# だってAPIって基本ステートレスだよね
# つまり、RailsじゃなくてリクエストAPIクライアントがセッションを処理するんだよ!

Railsの哲学に触れて感動したお話でした!