概要
Next(SSR)アプリケーションと、Rails APIを連携しています。
不正なリクエストを受け付けないために、CSRF
対策が必要となったのですが、Rails 標準の仕組みが利用できなかったため、対応を検討する必要がありました。
1. CSRF 対策の概要
CSRF 対策には Rails
で提供されているActionController::RequestForgeryProtection
モジュールを利用します。
[役割]
- セッションで有効なトークンを発行する
- リクエストヘッダー
X-CSRF-Token
に格納されたトークンを自動で検証する - トークンが一致しない場合は(不正なリクエスト)、
422
エラーとして処理する
Can't verify CSRF token authenticity.
Completed 422 Unprocessable Entity in 22ms (ActiveRecord: 0.0ms)
2. 全体の流れ
実装前に全体の流れを見ていきましょう。
- [Rails] CSRF トークン発行
- コントローラーで
form_authenticity_token
を使ってCSRFトークンを発行する
- コントローラーで
- [Rails] レスポンスヘッダにトークンをセット
-
X-CSRF-Token
などのヘッダ名でレスポンス
-
- [Next] フォーム送信のリクエストヘッダー
X-CSRF-Token
に受け取ったトークンをセットしてリクエストを行う - [Rails] CSRFトークンを検証
- 一致すれば処理を続行、一致しなければ
422
エラーを発生
- 一致すれば処理を続行、一致しなければ
3. 実装
全体像を理解したところで実装に移ります。
Rails
まず ApplicationController
にActionController::RequestForgeryProtection
をMIX-INしてCSRF
トークンを発行するメソッドを定義します。
class ApplicationController < ActionController::Base
include ActionController::RequestForgeryProtection
protect_from_forgery with: :exception
private
def set_csrf_token
response.set_header('X-CSRF-Token', form_authenticity_token)
end
end
次にCSRF検証を行いたいコントローラーでset_csrf_token
を呼び出します。
class PostsController < ApplicationController
before_action :set_csrf_token, only: [:new]
def new
# ...
end
end
これにより、API コール時に、レスポンスヘッダーに CSRF
トークンが含まれます。
Next.js
-
トークンを取得する API(例:
GET /posts/new
) をコールします -
レスポンスヘッダーに格納されたトークンを、
X-CSRF-Token
に付与しPOSTリクエストfetch('/posts', { method: 'POST', headers: { 'X-CSRF-Token': token, // CSRFトークン 'Content-Type': 'application/json' }, body: JSON.stringify({ title: 'Title', content: 'Content...' }) })
-
Rails で検証
create
アクション実行時に、自動でX-CSRF-Token
を検証されます。class PostsController < ApplicationController # 省略 def create # POSTリクエスト時に X-CSRF-Token が正しければ通常どおり処理される # 不正な場合は 422 Unprocessable Entity エラー end end
まとめ
CSRF対策は完了です!
他にも良いやり方があれば是非コメントをいただきたいです🫡