プライバシーポリシー

2018年11月12日月曜日

解答:間違ったCSRF対策~初級編~

この記事は、先日の記事「問題:間違ったCSRF対策~初級編~」に対する解答編です。まだ問題を見ていない方は、先に問題を読んで(できれば自分で解答を考えて)からこの記事をお読みいただくとよいと思います。

それでは、解答を説明します。

はじめに

CSRF対策の不備として、ありがちなパターンは以下のとおりです。
  1. トークンが予測可能(ユーザIDのハッシュ値をトークンとして用いている等)
  2. 他人のトークンが利用できてしまう(参考記事
  3. トークンのチェック方法に不備がある。
問題のコードは、暗号論的に安全な乱数生成器(PHPのrandom_bytes関数)を用いてトークンを生成し、それをセッション変数に記憶しているので、上記1 と 2 は問題ないと考えられます。したがって、3 が該当しそうだと当たりをつけます。そのためには、攻撃者は以下のトークンチェック(chgmail.php内)を回避する必要があります。

if ($_POST['token'] !== $_SESSION['token']) { // ワンタイムトークン確認
  die('正規の画面からご使用ください');
}
$_POST['token']は攻撃者が自由に設定できますが、正規のトークンの値は乱数なので予想できません。そこで、$_SESSION['token']がどのような値をとるか調べてみましょう。ログイン直後(mypage.php)にはこの変数は設定されていません。そして、PHPの場合、設定されていない変数を参照するとNULLが返ります(警告が表示され処理は継続される)。つまり、$_SESSION['token']の見かけの初期値はNULLということになります。そして、入力フォーム(chgmailform.php)に遷移するとトークンが設定され、メールアドレス変更(chgmail.php)の際にNULL(未定義)に戻ります。この様子を状態遷移図として下図に示します。
$_SESSION['token']に通常NULLが入っていることを使ってCSRF攻撃ができます。$_POST['token']がNULLであれば、上記のif文はNULL同士の比較になるので、チェックを回避できます。具体的には、POSTパラメータtokenを消してしまうことによって、$_POST['token']がNULLとなり、CSRFチェックを回避できます。

スクリプトを動かして確認してみる

ここで、Burp Suite(無償のCommunity Editionを想定)を用いて、上記を確認してみましょう。プロキシとしてBurp Suiteを設定した状態で、サンプルプログラムのメールアドレス変更を実行します。そして、chgmail.phpへのPOSTリクエストを選択し、コンテキストメニューを表示(Windowsの場合は右マウスクリック)して、「Send To Repeater」をクリックします。


下図のようにRepeaterタブが赤色表示に変わるので、このRepeaterタブをクリックします。

以下のように、HTTPリクエストの内容が表示されます。


ここで、リクエストボディ(上記赤枠の中)からtoken=以下をまるごと削除し、mail=も適当なメールアドレスに変更します(下図)。


この状態でRepeaterの「Go」ボタンを押すと、下図右半分のように、「aliceさんのメールアドレスをevil@wasbook.orgに変更しました」と表示されます。


すなわち、tokenパラメータを削除しても、更新処理が正常に完了することがわかりました。

PoCの作成

それでは、PoC(概念実証コード)を作成しましょぅ。Burp SuiteのProfessional版やOWASP ZAPにはCSRFのPoC生成ツールが利用できますが、このケースはPoCも単純なので、手で作ってしまいましょう。

要件としては、http://example.jp/chgmail.phpに対して、POSTリクエストでmail=... というパラメータを送信するだけです。そのようなHTMLを作成します。手動でサブミットでもいいのですが、以下はフォームが表示されてから5秒後に自動的にサブミットするようにしています。
<body onload="setTimeout('document.forms[0].submit()', 6000)">
<form action="http://example.jp/chgmail.php" method="post">
<input name="mail" value="evil@example.com">
<input type="submit">
<form>
<body>
これを動かしてみましょう。あなたは現在被害者の立場です。mypage.phpを閲覧して対象スクリプトにログインします。


この状態で、あなた(被害者)は、CSRFの罠をうっかり閲覧してしまいました。


罠の中のJavaScript(body要素のonloadイベント)により上記FormがPOSTされ、以下のように無事(?)メールアドレスが変更されました。


参考文献

平成29年春季の情報処理安全確保支援士試験 午後Ⅰ問2(問題解答講評)にこの知識を問う問題が出題されました。たまたまこの試験を私も受けていましたw
拙著「体系的に学ぶ 安全なWebアプリケーションの作り方 第2版」のP194には、この内容についての解説があります。

以上で、初級問題の解答編は終わりです。よろしければ「中級編」にもチャレンジしてみてください。

0 件のコメント:

コメントを投稿