はじめに
この記事は、RailsのAPIモードで「protect_from_forgery」エラーが発生した場合の原因と対処法を解説します。
この記事でわかる・できること
- エラー発生の仕組み(protect_from_forgeryがNoMethodErrorになる理由)
- CSRFとXSSとAPI認証の関係
- APIモードでの安全な運用方法
- 解決方法
この記事の対象者
- Rails API(--api)モードを使う開発者
- SPA/モバイルアプリのバックエンドAPI開発者
- RailsのCSRFやXSS対策に疑問がある人
動作環境・使用するツールや言語
- OS バージョン: macOS Sequoia 15.5
- ツール: VSCode 1.102.1
- 言語: Ruby 3.4.4 / Rails 8.0.2
- 認証ツール: Firebase Auth
- フレームワーク: Rails APIモードなど
エラーの原因とWeb/APIの違い
エラーの内容
NoMethodError (undefined method 'protect_from_forgery' for class Api::SuggestionsController)
原因
APIモードのRailsアプリは、protect_from_forgeryを持つActionController::Baseではなく、ActionController::APIを継承しています。
そのため、CSRF保護機能自体が存在しませんというエラーが発生します。
このエラーの詳細
通常のWebアプリはCookieベースのセッション認証+CSRF対策が必須。
RailsのAPIモードは「トークン認証(JWT等)」が基本です。
私は、Firebase AuthのJWTを使用していました。
トークン認証だとCookieを自動送信しないためCSRFリスクがほとんどないと言われています。
解決方法
① 該当行を削除する
APIコントローラから下記を完全に削除してOKです。
protect_from_forgery with: :null_session
② どうしてもprotect_from_forgeryを使いたいとき
Webコントローラ(ActionController::Base継承)へ切り替えて運用します。
しかし通常のAPI連携には不要なので、SPA+API+トークン認証なら削除してOKです。
トークン認証方式でCSRF対策が不要な理由
CSRF(クロスサイトリクエストフォージェリ)とは?
「Cookieベースの認証セッション」+「リクエストを外部サイトから自動送信」できることにより成立する攻撃です。
APIはトークン認証が前提
JWT・BearerトークンなどはCookie経由では送信せず、明示的にヘッダーなどでトークンを付与します。
ブラウザが外部サイトからAPIに勝手にトークンを送ることができないので、CSRFはほとんど発生しません。
詳しくは以下のサイトをご覧ください。
APIモードにおけるXSS対策は必要か?
XSS(クロスサイトスクリプティング)とは?
「Webページの入力フィールドやデータ表示領域に悪意のあるJavaScript等のコードを埋め込むことで、ユーザーのブラウザ上で不正な動作を実行させる攻撃手法」です。
ユーザーがコメント欄などにJavaScriptコードを投稿したとき、サイト側がサニタイズせずそのまま表示した場合、コメント欄を見た他ユーザーのブラウザでそのコードが実行されます。
一般的に、JWTなどのトークンはクライアント側に一時保存されて、UXの向上を図っています。
このトークンの保管場所でXSSリスクが変わると言われてます。
トークンの代表的な保存場所とXSSリスク
1. localStorage
JavaScriptで簡単に読み書きできるキー・バリュー型ストレージで中身のデータはブラウザを閉じても消えません。XSS攻撃でJSから簡単に盗まれるリスクがあります。
2. sessionStorage
ローカルストレージと似ていますが“タブ/セッションごと”に管理されるので、ブラウザを閉じるとデータが消えます。ただし、localStorage同様XSS耐性は小さいと言われています。
3. Cookie
サーバーとの通信時に自動で送信でき、httpOnly属性を付ければJSからの盗難を防ぎやすいです。
secure属性でHTTPS限定送信にできる点も魅力的。
そのため、httpOnly属性ならXSS耐性が強く、サーバ側で自動的・確実に取得できます。
ただし、Cookieで送信すると「CSRF攻撃の可能性」が再び生じます(CSRF対策必須)。
4. IndexedDB
より高度なクライアントサイドデータベースで、localStorageよりもXSS耐性が強いです。
IndexedDB は Javascript から読み取れるが、バイナリ構造でXSS攻撃の難易度が比較的高いと言われています。
Firebase Auth の JWT は IndexedDB に保存されています。
どこが推奨?
最も安全なのは、CookieのhttpOnly属性付き保存(ただしCSRF対策必須)です。
CSRFトークンやSameSite属性(Strict, Laxなど)などを使って、CSRF対策も行いましょう。
ただし、SPAで簡易運用ならXSS対策を万全にして、LocalStorageやIndexedDBなどに保存することも可能です。
その場合は、以下の3点を行いましょう。
- エスケープ処理やサニタイジング(React なら JSX がデフォルトで防御)
- 外部データを直接 HTML に入れない
- CSP(Content Security Policy)
まとめ表
| 保存場所 | XSS耐性 | CSRF耐性 | サーバ自動送信 | 備考 |
|---|---|---|---|---|
| localStorage | 弱 | 強 | なし | JSから奪われやすい |
| sessionStorage | 弱 | 強 | なし | 一時的(JSアクセス可) |
| Cookie | 強(※) | 弱 | あり | httpOnly属性でJSアクセス不可、CSRFリスク残る |
| IndexedDB | 弱 | 強 | なし | JSでアクセス可(バイナリ構造でXSSリスクが相対的に低い) |
※CookieのXSS耐性はhttpOnly属性を使った時のみ
参考資料
おわりに・まとめ
- APIモードのRailsではCSRF機能が省略され、protect_from_forgeryは不要
- 基本的にはAPIはトークン認証を使い、CSRFリスクは非常に低い
- 例外的にCookie認証を使うAPIではCSRF対策が必要となることもある