CSRF トークンを使って、クロスサイトリクエストフォージェリからウェブアプリを守る方法を学びます。
基本事項と効果的な実装方法を解説します。
CSRF の概要
CSRF トークンは CSRF 脆弱性を減らすために使用される重要な要素です。AppSec の専門家がこの対策の有用性を理解しやすくするため、基本概念を押さえましょう。
クロスサイトリクエストフォージェリ(CSRF)は、現代のウェブアプリが直面するサイバーリスクのひとつです。この脆弱性は、認証済みユーザーのアクセス権を悪用し、不正なリクエストを実行させる攻撃です。
攻撃者は、ソーシャルエンジニアリングの手法を使ってユーザーを騙し、不正なリクエストを実行させます。多くの場合、ユーザーのクッキーを悪用し、認証済みユーザーになりすまして攻撃を仕掛けます。
CSRF トークンの概要
CSRF トークンは、CSRF 攻撃を防ぐために使用される、サーバーが生成する一意で予測不可能な値です。
状態変更を伴うリクエスト には CSRF トークンの使用が推奨されます。
CSRF や XSS 攻撃対策 に有効です。
トークンは自動生成され、静的な値を持たず、セッション内に保存される ため、予測が困難です。
タイムスタンプの付与 により、セキュリティを向上させます。
仕組みと利用方法
CSRF トークンは二重に存在します。一方はサーバに保存され、もう一方はウェブフォームの隠しフィールドまたは HTTP リクエストのヘッダーとしてクライアントに送信されます。秘密性と固有の特徴により、トークンの複製は困難です。
クライアントが HTTP リクエストを送信すると、そのリクエストには事前に付与された CSRF トークンが含まれ、サーバはリクエスト内のトークンと保存されたトークンを比較します。
トークンが正しく存在する場合はユーザが認証され、アクセスが許可されます。一方、何らかの不一致がある場合、セッション要求は拒否されます。
CSRF トークンの生成
CSRF トークンをセキュリティ対策として導入する際は、独自の特徴を持ち、任意の値であることにより予測不可能でなければなりません。
業界では PRNG(疑似乱数生成器)の利用が推奨されています。暗号技術を用いることで、PRNG は唯一無二でタイムスタンプ付きの CSRF トークンを生成します。
さらにセキュリティを高めるため、CyberSec 専門家は PRNG の出力を若干変更して特定のトークンを生成する場合もあります。しかし、わずかな変更も正確に実施する必要があり、全体のトークン構造の信頼性の高いハッシュ値を抽出するため、高度な技術と時間が求められます。
しかし、そのセキュリティ効果は非常に高いです。加えて、CSRF トークンは利用後に廃棄することが重要です。再利用は想定されず、有効時間を短く設定することで自動的に期限切れとなります。
CSRF トークンを送信する際に HTTP GET リクエストは使用しないでください。GET リクエストでは URL にトークンが含まれるため、ハッカーに容易に抽出される可能性があります。
CSRF トークンの送信
質の高いトークンの生成と同様に、トークンの安全な送信も重要です。適切な送信方法として以下が推奨されます:
- 隠しフィールドの利用
- POST メソッドの使用
- HTML 内で CSRF トークンのフィールドを前方に配置する
- 隠しフィールド以外のフィールドより前にトークンフィールドを置く
- 送信用に URL クエリ文字列は使用しない。通常、クエリ文字列はクライアントやサーバの記録に残り、利用中のブラウザからも容易に参照され、第三者のアプリに送信される恐れがあるため
- JavaScript を用いて HTTP リクエストのヘッダーにトークンを挿入する
- トークンの検証を実施する
CSRF トークンの実装
Java、JavaScript、PHP など多様なプログラミング言語環境での CSRF 対策を見ていきます。以下に CSRF トークンの例を示します。
JavaScript による CSRF トークンの実装
JS はデフォルトで CSRF 対策をサポートしているため、主要なフレームワークでは CSRF トークンの利用が容易です。Express.js のミドルウェア csurf を用いれば、CSRF トークンの生成と検証が可能です。以下の手順を参考にしてください。
index.js ファイルに以下のコードを記述します:
app.get('/', csrfProtection, (req, res) => { res.render('index', { csrfToken: req.csrfToken() });
その後、views フォルダを開き index.ejs ファイルを追加します。以下の設定例が参考になります:
Name: Update
このコードでは、/ ルートが index.ejs テンプレート内で csrfToken 変数を隠しフィールドに埋め込んでいます。
ご覧の通り、/profile ルートがリクエストを受け取り、CSRF の検証を確認します。
Java による CSRF トークンの実装
Java は優れた言語ですが、デフォルトでの CSRF 対策は備わっていません。そのため、Java 利用時には CSRF トークンの導入が強く推奨されます。業界標準の手法として、補助クラスやフィルターを利用し、CSRF トークンの生成とリソースの検査を行います。
ジェネリックなステートレスフィルターは、ダブルサブミットクッキーパターンの迅速かつシームレスな実装が可能なため、この目的で広く利用されています。また、このフィルターは CSRF 対策のワークフローを促進します。
このフィルターを利用する前に、Java アプリの web.xml ファイルに定義を追加する必要があります。
CSRFFilter com.github.adriancitu.csrf.GenericCSRFStatelessFilter CSRFFilter /*
初期化オプションとして csrfHeadername と csrfCookieName があり、利便性や好みに応じて選択できます。
このフィルターは各 HTTP リクエストごとに専用の ExecutionContext クラスのインスタンスを提供し、リクエストの保護状態を注意深く観察します。一般的に、その状態は以下の3つに分類されます:
a. 保護の対象でない
b. 保護されるべきだがクッキーが添付されていない
c. 保護され、クッキーが添付されている
この結果に基づき、フィルターは適切な対応を実施します。例えば、状態が最初の二つの場合、TokenBuilderHook クラスを使用して CSRF トークンとその関連クッキーを生成します。
一方、最後の場合は ResourceCheckerHook を用いて現在の CSRF 保護状況を確認し、ResponseBuilderHook クラスで応答を返します。
PHP による CSRF トークンの実装
PHP は CMS 用途で最も選ばれる言語であり、数多くの動的ウェブサイトの背景にあります。ウェブサイトの守り方を学ぶには、PHP の実装方法を知ることが重要です。
この手順の基本的な流れは以下の通りです。まず、対象ウェブサイトのランディングページでフォームのフッタースクリプトを生成し、SecurityService を呼び出します。これは、PHP で CSRF トークンを生成し、PHP セッションを起動するためのクラスです。
この PHP クラスを使用すれば、リクエストの検証や隠しフィールドへのトークン埋め込みが可能な CSRF トークンを作成できます。以下は一般的な SecurityService.php の設定例です。
public function getCSRFToken() { if (empty($this->session[$this->sessionTokenLabel])) { $this->session[$this->sessionTokenLabel] = bin2hex(openssl_random_pseudo_bytes(32)); } if ($this->hmac_ip !== false) { $token = $this->hMacWithIp($this->session[$this->sessionTokenLabel]); } else { $token = $this->session[$this->sessionTokenLabel]; } return $token; }
この設定を用いれば、PHP で CSRF トークンを容易に利用できます。完了すると、問い合わせフォームのデータが取得され、メッセージ、件名、送信者情報などを記録できます。このフォームでは、csrf-token という隠しフィールドにトークンが保存される点に注意してください。
トークン送信後、アプリは JQuery によるフォーム検証を開始します。その後、フォーム内のスクリプトが既存のトークンと新たに埋め込まれたトークンを比較し、一致すればユーザセッションが認証され、不一致の場合はエラーとともにリクエストが拒否されます。
人気のWebフレームワークでの CSRF 脆弱性の対策
CSRF 脅威の事例は増加しており、これによりサイバーパンクが特定のウェブアプリに管理者並みのアクセス権を得る恐れがあります。しかし、企業としてはこの問題に直面することは望ましくありません。したがって、CSRF トークンの無効エラーや主要な脆弱性を早期かつ正確に検出・修正する必要があります。
以下に、Angular、Express、Django など主要なフレームワークでの CSRF 脆弱性対策の簡潔なガイドを示します。
1. Django における CSRF 対策
Django は Python ベースのオープンソースバックエンドフレームワークで、多くのウェブアプリに利用されています。コード再利用性や多数のプラグインサポートにより、現代のアプリ開発において高く評価されています。
Django はデフォルトでセキュリティ対策を備えているため、CSRF 攻撃防御において他のフレームワークよりも優位です。例えば、信頼性のあるミドルウェアが CSRF 脅威から守ります。csrf token django を有効にし、フォーム内に csrf_token タグを含めることで、保護が実現されます。
2. Angular における CSRF 対策
Google ブランドの Angular は非常に高度かつ実用的なオープンソースフレームワークであり、多くのプラットフォームやデータ駆動デバイスに対応する専用のユーザインターフェース要素を提供します。
CSRF 攻撃対策として、CSRF トークンを読み取り、専用ヘッダーを構築する従来の方法が採用されています。ここで使用されるカスタムヘッダーは ‘X-XESR-TOKEN’ と呼ばれます。
ただし、この対策は基本的な保護を行うだけで、Angular のデフォルトの CSRF 対策はクライアント側のみで動作するため、サーバ側リソースの保護については別途努力が必要です。
3. Express における CSRF 対策
Express は高速でカスタマイズ性に優れる Node.js 用のオープンソースバックエンドフレームワークとして世界的に有名です。基本機能のみを提供するため、組み込みの CSRF 対策はありませんが、プラガブルなミドルウェア csurf を利用することで、CSRF 脆弱性に対応できます。
csurf の優れた点は、最小限の設定で利用でき、複数のブートストラップオプションが用意されているため、容易に導入できるところにあります。
無効な CSRF トークンの修正
CSRF トークンの生成と実装に十分な対策を講じても、『無効な CSRF トークン』のエラーに直面する場合があります。以下に、その対処法をいくつか示します。
- カスタムリクエストヘッダーの利用を推奨する
CSRF トークン実装時、些細なユーザインターフェースの変更でも大きな問題を引き起こす恐れがあります。そこで、カスタムリクエストヘッダーを用いる方法が、『無効な CSRF トークン』の問題を避ける有力な手段となります。カスタムヘッダーを利用することで、同一オリジンポリシーが働き、クロスオリジンのリクエストが排除されます。
- 既存の CSRF 対策技術を活用する
多くの開発フレームワークに組み込まれているシンクロナイザートークン防御法を利用することで、このエラーは回避しやすく、アプリ全体の保護に寄与します。
- UI に注力した CSRF 対策の導入
最後に、ワンタイムトークン、CAPTCHA、再認証など、UI を利用した CSRF 対策を導入することが推奨されます。これらの方法は非常に堅牢な入力検証を実現します。
Wallarm による CSRF 対策
認証済みのログイン情報が不正利用されることを防ぐため、CSRF 攻撃が発生しないよう努める必要があります。CSRF トークンは、CSRF 攻撃の抑制に大いに役立ち、ウェブアプリに導入するだけで脅威の拡散を防止できます。
これを実現するため、組織は以下を実施する必要があります:
これらはすべて、各セキュリティ面への不断の注意が必要な手間のかかる作業です。そんな中、高度な Wallarm の WAAP と API セキュリティプラットフォーム が大きな違いを生み出します。これらのソリューションにより、セキュリティ重視の企業は完全自動化され、迅速かつ正確に、広範な CSRF 攻撃の検出と修正を実現できます。
いずれも幅広いウェブアプリやクラウド環境に対応しており、従来の RegEx 検出技術に代わり先進の手法を採用することで、誤検出や見逃しを大幅に削減します。
また、CSRF に加え、Wallarm の WAAP や API セキュリティソリューションは、クレデンシャルスタッフィング、OWASP Top 10 脅威、API 固有の脅威、DDoS 攻撃 など、多岐にわたる脅威の検出と対処が可能です。
これらの導入は手間がかからず、既存のワークフローに容易に統合でき、即時に守りを開始できます。先進の API およびアプリセキュリティソリューションを活用し、主要なデジタル資産を守りましょう。