0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

CSRF対策:SPAとMPAでの対策方法について

Last updated at Posted at 2024-11-14

CSRF対策とは?

CSRF(Cross-Site Request Forgery)対策は、悪意あるサイトやスクリプトがユーザーになりすまして不正なリクエストをサーバーに送る攻撃を防ぐための仕組みです。簡単に言うと、「ユーザー本人しかできない操作」と「不正なリクエスト」を区別する仕組みを導入することです。

CSRF攻撃が発生する仕組み

  1. ユーザーがログイン中の状態で、悪意のあるサイトやメールに誘導される。
  2. 悪意のあるサイトに埋め込まれたスクリプトが、ログイン状態のセッションを利用してサーバーに不正なリクエストを送信する。
  3. サーバーはリクエストが正当かどうかを確認せず、そのリクエストに従ってしまう。
    • 例えば、ユーザー本人が意図していないお金の送金や情報の変更など。

CSRF対策の基本的な仕組み

CSRF対策として、CSRFトークンを使うのが一般的です。CSRFトークンとは、「このリクエストはユーザー本人から来ていることを確認するための特別なパスワードのようなもの」です。

CSRFトークンの流れ

  1. サーバーがページを表示する際、CSRFトークンを生成し、そのページ内のフォームやリクエストと一緒にトークンを含めます。
  2. ユーザーが操作を行うとき、そのリクエストにはCSRFトークンが含まれます。
  3. サーバーはリクエストを受け取ったときに、送信されたCSRFトークンが正しいか(サーバーが発行したものと一致しているか)を確認します。
  4. トークンが一致しなければ、「このリクエストは正当なユーザーからのものではない」と判断して、リクエストを拒否します。

CSRFトークンを使う理由

悪意のあるサイトからのリクエストには、正しいCSRFトークンが含まれていないため、サーバーは不正なリクエストと判断できるわけです。これによって、ユーザーの意図しない操作がサーバーで実行されるのを防ぎます。

簡単な例

  1. トークンなし:家の玄関に鍵がなく、誰でも自由に開け閉めできる。
  2. トークンあり:家の玄関に鍵があり、家の人だけが持っている鍵でしか開けられない。

つまり、CSRF対策のCSRFトークンは、「家に入っていいのは家の人だけ」という認証の役割を果たし、悪意のある第三者が操作することを防ぐものです。

RailsでのMPA(Multi-Page Application)のCSRF対策方法は?

RailsでのMPA(Multi-Page Application)の場合、CSRF対策は基本的にフレームワークが自動的に処理してくれますが、具体的な対策の仕組みについて解説します。

1. CSRFトークンの自動生成と埋め込み

Railsでは、CSRF対策がデフォルトで有効になっています。コントローラに protect_from_forgery が設定されており、Railsが自動的にリクエストの中にCSRFトークンを埋め込む仕組みが組み込まれています。

CSRFトークンの仕組み

  • Railsは、サーバー側でCSRFトークンを生成し、それをビューに埋め込みます。
  • HTMLフォームを生成する際、自動的にCSRFトークンがフォームに含まれるようにします。

例えば、次のようにフォームを作成すると、Railsが自動的にCSRFトークンをフォームに追加します。

<%= form_with(url: '/submit', method: :post) do |form| %>
  <%= form.label :name %>
  <%= form.text_field :name %>
  <%= form.submit %>
<% end %>

Railsは、上記のフォーム内に次のような隠しフィールドを自動で追加します。

<input type="hidden" name="authenticity_token" value="CSRFトークンの値">

この authenticity_token がCSRFトークンで、サーバー側で生成したトークンと一致するかを確認するために使われます。

2. リクエスト時のトークン確認

  • ユーザーがフォームを送信すると、CSRFトークンも一緒に送信されます。
  • サーバーは、リクエストに含まれるCSRFトークンがセッション内のトークンと一致するかどうかを確認します。
  • 一致していればリクエストを受け入れ、不一致の場合は「リクエストが正当でない」と判断し、エラーを返してリクエストを拒否します。

3. 非同期リクエスト(AJAX)でのCSRFトークン利用

MPAであっても、非同期リクエストを使う場合には、CSRFトークンをJavaScriptから利用できるようにする必要があります。

CSRFトークンをJavaScriptから取得する方法

Railsは、JavaScriptでもCSRFトークンを取得しやすいように、metaタグを使ってCSRFトークンを埋め込んでくれます。例えば、application.html.erb<head> 部分に次のような記述があります。

<meta name="csrf-token" content="<%= csrf_meta_tags %>">

これにより、JavaScriptでCSRFトークンを取得し、AJAXリクエストのヘッダーに追加できます。

JavaScriptでのCSRFトークンの設定例

document.addEventListener('DOMContentLoaded', function() {
  const token = document.querySelector('meta[name="csrf-token"]').getAttribute('content');

  fetch('/your_endpoint', {
    method: 'POST',
    headers: {
      'X-CSRF-Token': token,
      'Content-Type': 'application/json'
    },
    body: JSON.stringify({ your_data: 'value' })
  })
});

4. Railsでの標準設定を維持すること

Railsはデフォルトで protect_from_forgery を有効にしているため、基本的に設定をそのままにしておけば、CSRF対策が機能します。特にカスタマイズが必要でない限り、Railsのデフォルトのセキュリティ設定を維持することが推奨されます。

まとめ

RailsのMPAでのCSRF対策は、主に以下の仕組みによって行われます。

  • Railsが自動生成するCSRFトークンをフォームに埋め込み、リクエスト時にサーバーが確認。
  • 非同期リクエストで必要な場合、JavaScript経由でトークンを取得しリクエストヘッダーに設定。

これにより、ユーザー以外からの不正リクエストを防ぐことができます。

以下の見出しに従い、Rails APIとReact SPA構成でのCSRF対策に関する記事をまとめます。


rails apiモードとreactの場合のSPA構成でのCSRF対策

RailsをAPIモードで使用し、フロントエンドにReactを使うSPA構成の場合、Rails側のCSRF対策を通常のMPA(マルチページアプリケーション)とは少し異なる方法で設定する必要があります。SPA構成では、主にAPIリクエストを通じてデータをやり取りするため、APIリクエストにCSRFトークンを正しく含めることでCSRF攻撃を防ぐことが重要です。

Rails APIモードとReact SPAでのCSRF対策

  1. CSRFトークンの発行と保存

    • Rails APIサーバーは、ログイン成功時や初回リクエストでCSRFトークンを発行します。
    • 発行されたCSRFトークンはクライアント側(Reactアプリ)で保存します。一般的には、localStoragesessionStorage、あるいはHTTP-onlyなクッキーで保存します。
    • HTTP-onlyなクッキーにトークンを保存することで、JavaScriptからのアクセスを防ぎ、セキュリティを強化できます。
  2. リクエスト時のCSRFトークン送信

    • 保存したCSRFトークンを、すべてのAPIリクエストのヘッダーに付加します。例えば、リクエストヘッダーに X-CSRF-Token というキーでトークンを含めます。
    • Rails API側で、このヘッダーのトークンを検証し、不正なリクエストをブロックします。

    以下のように、Reactのコードでリクエストにトークンを追加できます。

    const token = localStorage.getItem('csrfToken');
    
    fetch('/api/your_endpoint', {
      method: 'POST',
      headers: {
        'X-CSRF-Token': token,
        'Content-Type': 'application/json'
      },
      body: JSON.stringify({ data: 'your_data' })
    })
    
  3. Rails側の設定

    • RailsのAPIモードでは、デフォルトで protect_from_forgery が無効になっていますが、CSRFを必要とする場合は、以下のように protect_from_forgery with: :exception を追加し、CSRFトークンがヘッダーに含まれているかを検証します。
    class ApplicationController < ActionController::API
      include ActionController::RequestForgeryProtection
      protect_from_forgery with: :null_session
    
      before_action :verify_authenticity_token, unless: -> { request.format.json? }
    end
    
  4. クッキーを使ったトークン管理

    • RailsでCSRFトークンをクッキーとして発行し、クライアントがこれを利用できるようにする方法もあります。Railsは、cookies["X-CSRF-Token"] のようにクッキーにトークンを保存し、Reactアプリがそれをリクエストヘッダーにセットすることで、APIリクエストの際に自動的にCSRFトークンが含まれるようになります。
  5. セキュリティヘッダーの設定

    • APIとSPA構成では、SameSite=LaxまたはSameSite=Strictクッキー属性も設定すると、セッションやトークンが第三者によって不正に使用されるのを防げます。

まとめ

  • Rails API は、セッションやクッキーでCSRFトークンをクライアントに発行し、リクエストヘッダーにトークンを含めることで不正なアクセスをブロックします。
  • Reactアプリ側 は、発行されたトークンをヘッダーに付与してAPIリクエストを行い、CSRF攻撃を防止します。
  • Rails APIのCSRFトークンとHTTP-onlyクッキー設定を活用すると、よりセキュアなCSRF対策が実現できます。

このようにして、Rails APIとReact SPA構成でのCSRF対策を実施できます。

CSRFトークンがJavaScriptからアクセスできてしまう場合の対処法

ReactとRails APIの構成では、CSRFトークンがJavaScriptからアクセスできる状態が、悪意あるスクリプトによる不正利用を招くリスクを含んでいます。ここでは、そのリスクを最小限にするための対処法を紹介し、各設定方法を詳しく解説します。


対処法1: HTTP-onlyなクッキーを使ってトークンを保存する

CSRFトークンをHTTP-only属性を持つクッキーに保存することで、JavaScriptから直接アクセスできないようにし、セキュリティを強化します。この「ダブルサブミッションクッキー」方式は、クライアントとサーバー間でトークンの一致を確認するための重要な対策です。

  1. Rails側でCSRFトークンの生成とクッキーへのセット

    • Rails API側で、CSRFトークンを生成し、HTTP-onlyかつSameSite=Strict属性を持たせたクッキーとしてユーザーに送信します。
    class SessionsController < ApplicationController
      def create
        # ログイン成功時にCSRFトークンをHTTP-onlyクッキーにセット
        cookies['csrf_token'] = {
          value: form_authenticity_token,
          httponly: true,
          same_site: :strict
        }
        # 他のログイン処理
      end
    end
    
  2. クライアントからのリクエストでCSRFトークンを送信

    • クライアントがリクエストを送信する際、クッキーに格納されたCSRFトークンがサーバー側に自動的に送信されるため、JavaScriptから直接トークンを設定する必要はありません。
  3. サーバーでのトークン検証

    • リクエストを受け取ると、サーバー側でX-CSRF-Token ヘッダーのトークンとHTTP-onlyクッキーのトークンを比較して検証します。トークンが一致しない場合は、不正なリクエストとして処理を中止します。

対処法2: Content Security Policy(CSP)の設定

CSPヘッダーを活用して、外部の悪意あるスクリプトからのトークンアクセスを制限することで、CSRFトークンがJavaScriptにアクセスされるリスクを減らします。特に、ReactとRails API構成でのCSP設定が有効です。

  1. CSP設定のGemインストールと初期設定

    • Rails API側で、secure_headersrack-csp などのGemを利用して、CSPヘッダーを設定します。まずは、Gemをインストールします。

      # Gemfile
      gem 'secure_headers'
      
  2. Railsの初期設定でCSPを設定する

    • 設定ファイル(例: config/initializers/secure_headers.rb)を作成し、Reactアプリで必要なリソースのみ許可するCSPポリシーを記述します。

      # config/initializers/secure_headers.rb
      SecureHeaders::Configuration.default do |config|
        config.csp = {
          default_src: ["'self'"],
          script_src: ["'self'"],
          style_src: ["'self'", "'unsafe-inline'"],
          img_src: ["'self'", "data:"],
          connect_src: ["'self'", "https://your-react-app-domain.com"],
          font_src: ["'self'", "https://fonts.googleapis.com"],
          frame_src: ["'none'"]
        }
      end
      
    • これにより、信頼されたドメイン以外からのスクリプトの読み込みを防ぎ、悪意のあるスクリプトがトークンにアクセスすることを防止します。


対処法3: トークンの頻繁な再発行と短い有効期限

トークンを頻繁に再発行し、有効期限を短く設定することで、トークンが漏洩しても短時間しか使えないようにする対策です。

  1. Railsでトークンの再発行と短い有効期限の設定

    • Railsでは、リクエストごとに新しいトークンを発行することでセキュリティを強化できます。force_sslを使用してトークンがHTTPS経由でのみ送信されるように設定するのも有効です。
  2. 期限切れのトークンを検出してエラーを返す

    • クッキーに設定したトークンが期限切れである場合、Rails側でエラーを返し、React側で再認証やリフレッシュを行う処理を実装します。

対処法4: SameSite属性によるクロスサイトリクエスト制限

SameSite クッキー属性を活用して、クロスサイトからのリクエスト時にCSRFトークンが送信されないよう制限します。

  1. SameSite属性をStrictまたはLaxに設定

    • Railsのクッキー設定でSameSite=Strictを設定することで、異なるサイトからのリクエストが発生した場合、クッキーが送信されないようにします。
    cookies['csrf_token'] = {
      value: form_authenticity_token,
      same_site: :strict
    }
    
  2. クロスサイトのセキュリティ強化

    • Strictが厳しすぎる場合は、Laxに設定し、同一サイト内でのリクエストにはクッキーが送信されるようにすることで、柔軟性を確保しつつセキュリティを強化します。

まとめ

以上の対策を組み合わせることで、CSRFトークンがJavaScriptからアクセスされるリスクを最小限に抑えることができます。Rails APIとReact SPA構成でのセキュリティをさらに向上させるために、各対策を環境に合わせて適切に設定しましょう。

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?