クロスサイトリクエストフォージェリ(CSRF)
- Web サイトにログイン中のユーザのスクリプトを操ることで、Web サイトに被害を与える攻撃
- ログインしたユーザからなおリクエストが、そのユーザが意図したリクエストであるかどうかを識別する仕組みを持たないために発生する問題
- XSS はクライアント側、CSRF はサーバ上に不正な書き込みを行なう
- CSRF 対策トークン
- SameSite 属性
- 参照
- 一般的な影響
- ログインしたユーザのみが利用可能なサービスを悪用される
- ショッピングサイトなどのウェブアプリケーションへユーザが意図しないリクエストを送信させられることで、不正送金、商品購入、退会処理など、ユーザ人不利益となる被害や、不快と思われる行為が、ユーザの気がつかないうちに実行されてしまう
- ログインしたユーザのみが編集可能な情報を改ざんされる
- SNS などのウェブアプリケーションにユーザの意図しないリクエストを送信させられることで、各種設定の変更、掲示板への不適切な書き込みなど、ユーザの気がつかないうちに実行されてしまう
- ログインしたユーザのみが利用可能なサービスを悪用される
脆弱性発見と修正
基本的な検査方法
- パスワードや公開範囲などの変更、商品の購入といった重要な操作が行える画面において、下記 3 つ全てを満たす場合、CSRF の脆弱性となる
- 画面上に表示されない hidden 属性でトークンをもっていない、または推測可能である
- パスワードの再入力を求めない
- どこからのリクエストかを意味するRefererを確認していない
- この3つ全てを満たす場合に CSRF の脆弱性となります。ここでトークンとは利用者確認のために使われる秘密情報です。
- パスワードや公開範囲などの変更、商品の購入といった重要な操作が行える画面において、下記 3 つ全てを満たす場合、CSRF の脆弱性となる
一般的な修正例
- 脆弱なコード
リクエストを送信する処理
<form action="test.php" method="POST"> 新パスワード<input name="pwd" type="password"> <!-- 脆弱なコード トークンを持っていない -> <input type="submit" value="パスワード変更"> </form>
リクエストを受けサーバ側でパスワードを変更する処理の抜粋
<?php session_start(); // 脆弱なコード トークンをチェックしていない $id=$_SESSION["id"]; $pwd=$_POST["pwd"]; //以下パスワード変更処理 ?>
- 修正案
- 重要な処理を行う際には推測不可能なトークンを設定し、処理の前に確認を行うように修正する
- 自作でトークン情報を作成するのは推測される恐れがあり危険であるため、session_id()などの提供されている関数を用いる
- 修正コード
パスワード変更のリクエストを送る際に、セッション ID をトークン情報として送信
<form action="test.php" method="POST"> 新パスワード<input name="pwd" type="password"> <input type="hidden" name="token" value="<?php echo session_id()?>"> <input type="submit" value="パスワード変更"> </form>
サーバ側でパスワード変更の処理を行う前にセッション ID と送られたトークンを比較して、合致したときにパスワード変更の処理を行う
<?php session_start(); if(session_id() === $_POST["token"]{ $id=$_SESSION["id"]; $pwd=$_POST["pwd"]; //以下パスワード変更処理 } ?>
- 脆弱なコード
対策
- hidden 属性に秘密情報を挿入する
- ウェブアプリケーションは、ウェブページを出力する際に、hidden 属性に安全な疑似乱数を用いて生成した秘密情報を挿入するようし、ユーザからのリクエストを受けた際に、hidden 属性の値と秘密情報を比較し、一致しない場合は処理を行わないようにする
- ただの数字のインクリメントなどは見破られる可能性が高いので、乱数などで対応する
- CSRF対策用トークンの値にセッションIDそのものを使ってもいい時代なんて、そもそも無かった
- 確定処理の直前で再度パスワードを入力させる
- Referrer を用いてリンク元の正当性を確認する
- 重要な操作を行った後で、その内容を登録アドレスにメール送信する
- 被害は防げないが、攻撃があったことを利用者に気づかせることが出来る
- CSRF で memcached に書き込める
- CSRF で突破できる認証
- 結果の読み取りの必要がない認証方式であればパスワードを送りつけるだけなので突破可能
- チャレンジ & レスポンス形式の認証であれば突破できない
- CSRF ではリクエストの結果が読めないから