Note

3年後の自分のために書いています

Rails の API モードで CSRF 対策機能を実装する

半年前に会社の技術ブログの下書きに雑に書いてて公開しどきを忘れたやつ、もったいないのでこちらで公開。


RailsAPI モードはデフォルトでは CSRF 対策機能が有効になってないので、クッキーによるセッション機能を使うと脆弱性を生む可能性がある。

Rails実装済みの CSRF 対策機能を API モードで使う方法を紹介します。

やること

RailsCSRF 対策機能を有効にする

  • ActionController::RequestForgeryProtection をインクルード
  • protect_from_forgery を有効にしたいコントローラに追加

クライアントに CSRF トークンを返す

セッション生成直後に { authenticity_token: form_authenticity_token } をクエリストリング or レスポンスとして渡す(#form_authenticity_token メソッドで rails が生成した CSRF トークンが取れる、ちなみにセッション内の CSRF トークンは session[:_csrf_token] で取れるが、どちらかは暗号化もしくはハッシュ化されてるので値は違うものになる)

クライアント側で POST する際に CSRF トークンを渡す

フォームから POST する場合

<input type="hidden" id="authenticity_token" name="authenticity_token"></input> を仕込んで js で getElementById とかして先ほどクエリストリング or レスポンスで渡された authenticity_token を value に突っ込む

Ajax や fetch の場合

ちなみに rails の ujs を使わずに ajax リクエスト送る時は X-CSRF-Token というヘッダに csrt-token を自前で含めないといけない

ujs では ここ でやってる。

クロスオリジンの検証を無効にする

このままだと request.base_uri と request.origin が違う(クロスオリジンになってる)と InvalidToken のエラーになってしまうので、 config.action_controller.forgery_protection_origin_check = false にする(これは Rails 4 までデフォルト false だったが Rails 5 以降は true になってる)

CSRF を有効にしたセッションのテストをする

ポイントは以下。

  • セッションを作成するエンドポイントを叩く
  • csrf の authenticity_token を params で渡す(これしないと session 取れない)InvalidToken が raise しなかったのが気になる
  • テスト環境の config.cache_store を null_store -> memory_store に(null_store は本当に cache_store を使わないことを意味する)
  • 👆 の cache の後片付けも忘れない