この文書は2016年以降更新されていません

クロスサイトリクエストフォージェリ (CSRF) の防止策に関するチートシート

OWASP 作成
ジャンプ先: 移動検索
Cheatsheets-header.jpg

最終改訂日 (yy/mm/dd):2015/12/08

はじめに

クロスサイトリクエストフォージェリ (CSRF) は攻撃の一種であり、悪意のある Web サイト、電子メール、ブログ、インスタントメッセージ、またはプログラムを使用して、ユーザーが現在認証されている信頼されたサイト上で、ユーザーの Web ブラウザーに意図しないアクションを実行させます。クロスサイトリクエストフォージェリ攻撃が成功した場合、影響を受けるのは脆弱性のあるアプリケーションによって露出される機能に限られています。たとえば、この攻撃により、送金処理、パスワード変更、ユーザーコンテキストを使用した物品の購入などが行われることがあります。実際、攻撃者は CSRF 攻撃を使用して、ターゲットのシステムにターゲットのブラウザー経由で機能 (送金処理、フォーム送信など) を実行させますが、少なくとも、承認されていない機能の実行が完了するまで、標的にされたユーザーは事態に気づきません。

CSRF 攻撃が成功した場合の影響は、被害者の役割によって大きく異なります。通常のユーザーをターゲットにした場合、CSRF 攻撃が成功すると、エンドユーザーのデータと関連する機能に危害を与える可能性があります。ターゲットにされたエンドユーザーが管理者アカウントの場合、CSRF 攻撃は Web アプリケーション全体に危害を与える可能性があります。攻撃される可能性が高いサイトは、コミュニティ Web サイト (ソーシャルネットワーキング、電子メール) や、高額のアカウントが関連するサイト (銀行、株式売買、請求書支払いサービス) です。強力な暗号化 (HTTPS) を使用している Web サイトに ユーザーがログインした場合でも、この攻撃が起きる可能性があります。ソーシャルエンジニアリングを利用して、攻撃者は悪意のある HTML または JavaScript コードを電子メールまたは Web サイトに埋め込み、「特定のタスクを実行させる URL」 にアクセスさせます。その結果、ユーザーの認識の有無に関係なく、直接またはクロスサイトスクリプティングの脆弱性を利用して(Samy MySpace Worm など)、タスクが実行されます。

CSRF の詳細については、「Cross-Site Request Forgery (CSRF)」ページを参照してください。

クロスサイトスクリプティング (XSS) 脆弱性の防止

クロスサイトスクリプティングがなくても、CSRF は機能します。ただし、クロスサイトスクリプティング脆弱性が使用されると、トークン、Cookie の二重送信、Referer ヘッダおよびOrigin ヘッダにもとづくCSRF の防御が無効になる可能性があります。これは、XSS ペイロードが XMLHttpRequest を使用して簡単にサイト上のページを読み込み、生成されたトークンを応答から取得して、そのトークンを偽造したリクエストに含めることができるからです。この技術は、2005 年に MySpace (Samy) ワームが MySpace のCSRF 対策機能をかいくぐってワームを増殖させた方法そのものです。XSS は、チャレンジレスポンスの防御 (Captcha、再認証、ワンタイムパスワードなど) を無効にすることができません。CSRF 防御が回避されないようにするには、XSS 脆弱性が存在しないことが必須になります。XSS 脆弱性を防止する方法の詳細なガイダンスについては、「XSS Prevention Cheat Sheet」を参照してください。

機能しない防止法

シークレットクッキーの使用

すべての Cookie は (たとえシークレットのものでも)、あらゆるリクエストで送信されることに注意してください。エンドユーザーがだまされてリクエストを送信したのかどうかに関係なく、すべての認証トークンが送信されます。さらに、アプリケーションコンテナーは単純にセッション識別子を使用して、リクエストを特定のセッションオブジェクトに関連付けます。セッション識別子の有無を調べるだけでは、エンドユーザーがリクエストの送信を意図していたかどうかを検証できません。

POST リクエストのみの受け入れ

アプリケーションは、ビジネスロジックを実行するための POST リクエストのみを受け入れるように開発できます。攻撃者が悪意のあるリンクを作成できなければ CSRF 攻撃は実行できない、というのは誤った認識です。残念ながら、この考え方は正しくありません。攻撃者が被害者をだまして、偽造された POST リクエストを送信させる方法は数多くあります(例えば、攻撃者の Web サイトに、非表示の値を含む単純なフォームを置いてアクセスさせるなど)。しかも、このフォームは JavaScript を使用して自動的にトリガさせることも、フォームが何か別のことをすると思っている被害者にトリガさせることもできます。

マルチステップトランザクション

マルチステップトランザクションでは、CSRF を十分に防ぐことはできません。完了したトランザクションの各ステップについて予測や推測ができれば、CSRF は可能になります。

URL の書き換え

攻撃者が被害者のセッション ID を推測できないため、これは有効な CSRF 対策技法だと思われるかもしれません。しかし、ユーザーの資格情報が URL で露出します。

一般的な推奨事項:同期トークンパターン

"透過的でわかりやすい" CSRF ソリューションを促進するために、開発者は 同期トークンパターン (http://www.corej2eepatterns.com/Design/PresoDesign.htm) を使用することをお勧めします。同期トークンパターンは、ユーザーの現在のセッションと関連するランダムな "チャレンジ" トークンを生成するよう要求します。これらのチャレンジトークンは、機密に関わるサーバー側の操作に関連する、HTML のフォームとリンクに挿入されます。ユーザーがこれらの機密に関わる操作を実行しようとする場合、このチャレンジトークンが HTTP リクエストに含まれている必要があります。すると、サーバーアプリケーションは、このトークンの存在と正確性を検証する責任を負います。各リクエストにチャレンジトークンを含めることによって、開発者は、ユーザーが実際に希望するリクエストを送信するつもりなのかを検証するための、強力なコントロールを持ちます。要求されたセキュリティトークンを機密に関わるビジネス機能と関連する HTTP リクエストに含めることは、CSRF 攻撃の軽減に役立ちます。攻略行為を成功させるには、攻撃者がターゲット被害者のセッションでランダムに生成されるトークンを知っていることが前提だからです。すなわち、攻撃者はターゲット被害者のセッション識別子を推測できる必要があります。次に、リクエストにチャレンジトークンを組み込むための一般的な方法について概説します。

Web アプリケーションは、リクエストを作成 (ユーザーが送信またはクリックするとリクエストを発生させるリンクまたはフォームを生成) するときに "CSRFToken" などの一般的な名前の付いた非表示の入力パラメーターを加えておきます。このトークンの値は、攻撃者が推測できないように、ランダムに生成される必要があります。Java アプリケーションの場合には、十分な長さのランダムなトークンを生成するために java.security.SecureRandom クラスを利用することを検討してください。または、256 ビットのハッシュ値を BASE64 でエンコードする方法もあります 。ただしこの方法を使う場合は、ランダムなトークンを生成するためにハッシュされるデータを確実にランダムで一意のものにする必要があります。

  <form action="/transfer.do" method="post">
  <input type="hidden" name="CSRFToken" 
  value="OWY4NmQwODE4ODRjN2Q2NTlhMmZlYWE...
  wYzU1YWQwMTVhM2JmNGYxYjJiMGI4MjJjZDE1ZDZ...
  MGYwMGEwOA==">
  …
  </form>

通常、開発者は現在のセッションに対して、このトークンを 1 回だけ生成する必要があります。このトークンを最初に生成したら、値をセッションに保存します。この値はセッションが期限切れになるまで、後続の各リクエストに利用されます。エンドユーザーがリクエストを送信すると、サーバー側のコンポーネントはセッション内に保存されているトークンと比較して、リクエスト内のトークンの存在と有効性を検証する必要があります。リクエスト内にトークンが見当たらない場合や、提供される値がセッションに保存されている値と一致しない場合は、リクエストの処理を中止し、トークンをリセットして CSRF 攻撃進行中の可能性ありというイベントを記録する必要があります。

ここで提案した設計のセキュリティを強化するために、CSRF トークンのパラメーター名と各リクエストの値をランダム化することを検討してください。このアプローチを実装すると、セッション単位のトークンではなく、リクエスト単位のトークンが生成されます。ただし、これは使い勝手の問題につながる可能性がある点に注意してください。たとえば、ブラウザーの [戻る] ボタンの機能は頻繁に機能しなくなります。これは前のページに含まれているトークンが、すでに無効になっている場合があるためです。前のページとの対話により、サーバーでは CSRF 攻撃を受けたという誤ったセキュリティイベントが引き起こされます。どのような アプローチを採用するにしても、 認証された後のセッション識別子を保護する場合と同じ方法で (TLS の使用など)、CSRF トークンを保護することをお勧めします。

URL でのトークンの開示

このコントロールの実装の多くでは、GET (URL) リクエストと POST リクエストにチャレンジトークンを含めます。ページなどの一般的なデザインパターンに埋め込まれたリンクから機密に関わるサーバー側の操作が呼び出されたときに、これを実装するのが一般的です。これらデザインパターンは、CSRF の知識も CSRF 対策のデザイン戦略の理解もないまま実装されることがよくあります。このコントロールは CSRF 攻撃のリスクを軽減するために役立ちますが、一意のセッション単位トークンが GET リクエストで露呈します。GET リクエストの CSRF のトークンは、ブラウザーの履歴、HTTP ログファイル、必ず HTTP リクエストの最初の行を記録するネットワークアプライアンス、および保護されたサイトが外部サイトにリンクする場合の Referer ヘッダーなど、いくつかの場所でリークされる可能性があります。

後者の場合 (リンクされたサイトで Referer ヘッダーが解析されることによる CSRF トークンのリーク)、リンクされたサイトはいとも簡単に保護されたサイトに CSRF 攻撃を開始できます。Referer ヘッダーからサイトと CSRF トークンが分かる ので、攻撃者はきわめて効果的に攻撃を仕掛けることができます。攻撃は JavaScript だけで 実行できます。サイトの HTML にスクリプトタグを追加するだけで (攻撃者が用意した 悪意のあるサイト上に追加するか、ハッキングされたサイト上に追加することで)、攻撃を起動できます。さらに、HTTPS コンテキストからの HTTPS リクエストは (HTTPS からの HTTP リクエストとは対照的に) Referer ヘッダーを削除しないため 、HTTPS アプリケーションでは、Referer を経由した CSRF トークンのリークが依然として発生する可能性があります。

理想的なソリューションは、POST リクエストにのみ CSRF トークンが含まれるようにし、サーバー側で状態変化を起こすようなアクションは POST リクエストでのみ可能とするよう修正することです。実は、これは RFC 2616 が GET リクエストに要求していることです。機密に関わるサーバー側のアクションが POST リクエストへの応答以外にないことが保証されれば、トークンを GET リクエストに含める必要はありません。

しかし、ほとんどの JavaEE Web アプリケーションでは、リクエストから HTTP パラメーターを取得する際に HTTP メソッドのスコーピングを使用することは滅多にありません。"HttpServletRequest.getParameter" の呼び出しは、リクエストが GET メソッドと POST メソッドどちらを使ったものであっても、パラメーター値を返します。これは、HTTP メソッドのスコーピングを実施できないというわけではありません。開発者が HttpServlet クラスの doPost() を明示的にオーバーライドしたり、Spring の AbstractFormController クラスのようなフレームワーク固有の機能を活用することで実施できます。

これらの場合、既存アプリケーションをこのパターンに合わせて改修 しようとすると、開発に膨大な時間とコストが必要になり、一時的措置として CSRF トークンを URL に含めて渡す方がましかもしれません。アプリケーションが HTTP GET リクエストと POST リクエスト に適切に応答するように修正したら、GET リクエストの CSRF トークンはオフにする必要があります。

ViewState (ASP.NET)

ASP.NET には、ViewState と呼ばれる機能があります。ViewState はサーバーに送信されたときのページの状態を示します。状態は、

コントロールで各ページに配置される非表示フィールドを使用して定義されます。攻撃者が有効な ViewState を偽造することは難しいため、ViewState は CSRF 対策として使用できます。攻撃者がパラメーター値を取得したり、推測することは可能なので、有効な ViewState の偽造は不可能ではありません。しかし、現在のセッション ID を ViewState に追加すると、それぞれの ViewState は一意になるため、CSRF の影響を受けません。

ViewStateUserKey プロパティを ViewState で使用して、なりすましのポストバックを阻止するには、Page 派生クラスの OnInit 仮想メソッドに以下を追加します (このプロパティは Page.Init イベントで設定される必要があります)

  protected override OnInit(EventArgs e) {
     base.OnInit(e); 
     if (User.Identity.IsAuthenticated)
        ViewStateUserKey = Session.SessionID; }

以下は、任意に選択した一意の値を使用して、ViewState に個人の鍵を設定します。

   (Page.ViewStateUserKey)

鍵は ViewState の読み込み前に提供する必要があるため、Page_Init で適用されている必要があります。このオプションは、ASP.NET 1.1 以上で使用可能になりました。

しかし、このメカニズムには制限があります。たとえば、ViewState の MAC はポストバック時にのみチェックされるため、ポストバックを使用しない他のアプリケーションリクエストは CSRF を許容してしまいます。

Cookie の二重送信

Cookie の二重送信は、Cookie およびリクエストパラメーターの双方でランダムな値を送信し、サーバー側で Cookie の値とリクエストの値が等しいかどうか検証する手法です。

ユーザーがサイトにログイン するとき、サイトは暗号強度の高い疑似ランダム値を生成し、その値を Cookie としてユーザーのマシンに、セッション ID とは別に送ります 。どんな形であれ、サイトはこの値を保存しておく必要はありません。次にサイトは、機密に関わる送信にはすべてこのランダム値が非表示のフォーム値 (または他のリクエストパラメーター) および Cookie の値として含まれていることを確認します。同一生成元ポリシーにより、攻撃者はサーバーから送信されるどんなデータも読み取ることができません。また、Cookie の値を変更することもできません。攻撃者は、任意の値を悪意のある CSRF リクエストに添付して送信できますが、Cookie に保存されている値は、変更することも、読み取ることもできません。Cookie の値と、リクエストパラメーターまたはフォームの値は同じにする必要があるので、攻撃者はランダムの CSRF 値を推測できない限り、フォームを正常に送信できません。

Direct Web Remoting (DWR) の Java ライブラリバージョン 2.0 には、CSRF 対策として、透過的に Cookieの二重送信を行う機能が組み込まれています。

暗号化トークンパターン

概要

暗号化トークンパターンは、トークン検証のメソッドに比較ではなく暗号化を活用します。認証が正常に行われると、サーバーはサーバー上でのみ使用可能な一意の鍵を使用して、ユーザーの ID、タイムスタンプの値、および nonce で構成される一意のトークンを生成します。このトークンはクライアントに返され、非表示フィールドに埋め込まれます。後続の AJAX リクエストは二重送信パターンと同じように、このトークンをリクエストヘッダーに含めます。非 AJAX のフォームベースのリクエストは、トークンを暗黙的に非表示フィールドに保持します。このリクエストを受信すると、サーバーはトークンの作成に使用したキーと同じキーを使用して、トークンの値を読み取り、復号化します。正しく復号化できない場合は、侵入の試行があることを暗示しています。一度復号化すると、トークン内に含まれる UserId とタイムスタンプは、有効性を保証するために検証されます。UserId と現在ログインしているユーザーが比較され、タイムスタンプと現在の時間が比較されます。

検証

トークンの復号化に成功すると、サーバーは解析された値にアクセスします (理想的には クレーム (claims)の形で)。これらのクレームは、UserId 要求と、保存された UserId (すでにサイトに認証方法が含まれている場合は、Cookie またはセッション変数内に) を比較することにより処理されます。タイムスタンプは現在の時間と比較検証され、再生攻撃を防止します。 また CSRF 攻撃の場合、サーバーは侵害されたトークンを復号化できず、攻撃をブロックして記録することができます。

このパターンは主として、開発者や設計担当者がセッション情報に依存せずに CSRF を阻止できるようにするためのものです。また、Cookie のサブドメイン問題と HTTPONLY の問題を回避してデータを Cookie に保存する必要性など、他のステートレスアプローチの欠陥にも対処します。

同期トークンを使用しない CSRF の防止

CSRF は数多くの方法で防ぐことができます。同期トークンの使用も 1 つの方法です。この方法では、リクエストを認証するためにシークレットトークンを維持することで、アプリケーションは同一生成元ポリシーに依拠して CSRF を防ぐことができます。このセクションでは、アプリケーションが CSRF 攻撃で絶対に破られないようなルールに依拠することにより CSRF を防ぐ、その他の方法について説明します。

Referer ヘッダーのチェック

ブラウザーが送信する Referer ヘッダーをユーザー自身が改変するのは簡単ですが 、CSRF 攻撃でこれを行うのは不可能です。組み込み ネットワーク機器で CSRF 対策として一般に使用される手法は Referer チェックです。この手法はユーザー単位の状態を必要としないため、メモリ容量が少ない場合に有効な CSRF 対策手法になります。また、この手法は、認証が行われる前のリクエストに対しても一般的に使われます。認証が行われていなければ、同期トークンの追跡に必要な セッション状態が確立されて いないからです。

しかし、Referer チェックは CSRF 対策としては弱い方法と見なされています。たとえば、オープンリダイレクト脆弱性を使用すれば、Referer チェックで保護されている GET ベースのリクエストを悪用することができます。また、一部の組織やブラウザーツールでは、データ保護の一環として Referer ヘッダーを削除しています。Referer チェックには、一般的な実装のミスもあります。たとえば、CSRF 攻撃が HTTPS ドメインから始まると、Referer は省略されます。この場合、リクエストが状態の変更を実行するものであるなら、Referer の欠如は攻撃と見なす必要があります。また、攻撃者が Referer に対して限定的ながら影響力を持っている点にも注意が必要です。たとえば、被害者のドメインが "site.com" であれば、攻撃者はドメイン "site.com.attacker.com" から攻撃することにより、壊れた Referer チェックの実装を欺く可能性があります。また、XSS を使用すれば Referer チェックを迂回できます。

つまり、Referer チェックは CSRF の侵入を発見および防止するための合理的な方法ではありますが、完璧な対策にはなりません。Referer チェックはある程度攻撃を検出できますが、すべての攻撃を阻止できるわけではありません。たとえば、HTTP Referrer が別のドメインからのもので、ユーザーは自分のドメインからのリクエストだけを想定している場合、ユーザーはそのリクエストを安全にブロックすることができます。

Origin ヘッダーのチェック

Origin HTTP ヘッダー標準は、CSRF や他のクロスドメイン攻撃に対する防御メソッドとして導入されました。Referer ヘッダーとは異なり、Origin ヘッダーは HTTPS の URL から発生する HTTP リクエストにも含まれます。

Origin ヘッダーが存在する場合は、その一貫性をチェックする必要があります。

チャレンジレスポンス

チャレンジレスポンスは、CSRF のもう 1 つの防御オプションです。チャレンジレスポンスオプションの例を次に示します。

  • CAPTCHA
  • 再認証 (パスワード)
  • ワンタイムトークン

チャレンジレスポンスは (適切に実装されている場合) CSRF に対する非常に強力な防御となりますが、ユーザーが行うべき操作に影響を与えます。高度なセキュリティが必要なアプリケーションにおいては、リスクの高い機能の実行時にはトークン (透過的) とチャレンジレスポンスを使用すべきでしょう。

クライアントとユーザーの予防策

CSRF 脆弱性は広範囲に 報告されています。ベストプラクティスに従ってリスクを軽減することをお勧めします。例えば、

  • Web アプリケーションの使用後はすぐにログオフする。
  • ブラウザーにユーザー名やパスワードを保存しない。サイトにログインを "記憶" させない。
  • 機密に関わるアプリケーションへのアクセスと、インターネットの気ままなサーフィンに同じブラウザーを使用しない。(複数のタブを開いて異なるサイトを同時にブラウズするような使い方をしない。)
  • No-Script などのプラグインを使用して POST ベースの CSRF 脆弱性の悪用を難しくする 。これは、ページが 読み込まれた途端にフォームを自動的に送信させる、というような攻撃がJavascript を使って行われるからです。JavaScript が働かない場合、攻撃者はユーザーが手動でフォームを送信するよう仕向ける必要があります。

統合された HTML 対応のメール / ブラウザーや、ニュースリーダー / ブラウザーの環境は、さらなる危険をもたらします。メールメッセージやニュースメッセージをただ表示するだけで、攻撃が実行される可能性があるからです。

これを移動

Authors and Primary Editors

Paul Petefish - paulpetefish[at]solutionary.com
Eric Sheridan - eric.sheridan[at]owasp.org
Dave Wichers - dave.wichers[at]owasp.org

Other Cheatsheets

Developer Cheat Sheets (Builder)

Assessment Cheat Sheets (Breaker)

Mobile Cheat Sheets

OpSec Cheat Sheets (Defender)

Draft Cheat Sheets