概要
以下のリソースを参考にしました:https://www.freebuf.com/articles/web/118352.html
CSRFとは何か
CSRF(Cross-Site Request Forgery)は日本語で「クロスサイトリクエストフォージェリ」と呼ばれ、XSSと混同されやすいですが、重要な違いがあります。CSRFではCookieを盗むのではなく、既存の認証情報を悪用します。つまり、攻撃者が被害者の身分を装い、本人が気づかぬうちにパスワード変更、管理者追加、送金などの危険な操作を行うことで、重大な被害をもたらします。
脆弱性の実演
DVWA(Damn Vulnerable Web Application)を使用して、実際の攻撃手法を検証します。DVWAの環境構築に興味がある方は、関連資料を参照してください。
DVWAを開きCSRFセクションを選択し、まずはLowレベルから開始します。ソースコードを確認すると:
<?php
if( isset( $_POST[ 'submit' ] ) ) {
// パラメータ取得
$new_password = $_POST[ 'new_pass' ];
$confirm_password = $_POST[ 'confirm_pass' ];
// 確認処理
if( $new_password === $confirm_password ) {
$hashed_value = ((isset($connection) && is_object($connection)) ? mysqli_real_escape_string($connection, $new_password ) : ((trigger_error("[MySQLConverterToo] mysql_escape_string()の呼び出しを修正してください!このコードは動作しません。", E_USER_ERROR)) ? "" : ""));
$hashed_value = md5( $hashed_value );
// DB更新処理
$query = "UPDATE `users` SET password = '$hashed_value' WHERE username = '" . getCurrentUser() . "';";
$execution = mysqli_query($connection, $query ) or die( '' . ((is_object($connection)) ? mysqli_error($connection) : (($error_result = mysqli_connect_error()) ? $error_result : false)) . '' );
// 結果表示
echo "パスワードが変更されました";
}
else {
// 不一致時のメッセージ
echo "パスワードが一致しませんでした";
}
((is_null($close_result = mysqli_close($connection))) ? false : $close_result);
}
?>
コードを見ると、new_passおよびconfirm_passパラメータに対して一切の保護措置が施されていないことが分かります。これにより、攻撃者はシステム構造を把握している場合、容易に悪意のあるリンクを作成できます。ユーザーがそのリンクをクリックすると、即座にCSRF攻撃が発動します。DVWAのパスワード変更リンク例:http://example.com/DVWA/vulnerabilities/csrf/?new_pass=test1234&confirm_pass=test1234&submit=Submit
認証状態で同一ブラウザでこのリンクをクリックすると、パスワードがtest1234に変更されてしまいます。しかし、このような明確なURLは誰もクリックしないため、Burp Suiteを使ってより巧妙に構築する必要があります。パスワード変更ページで通信をキャプチャし、「Generate CSRF PoC」を選択すると、簡易的なHTMLページが生成されます。
ただし、この方法でも問題があります。クリック後すぐにパスワード変更ページに遷移するため、被害者は異常に気づいてしまいます。前述の記事のアイデアに基づき、imgタグのsrc属性を隠蔽し、誤ったURLであると誤解させるエラーページを構築することで、被害者はCSRF攻撃が進行していることに気づかないままです。以下のようなコード構成になります:
<img src="http://example.com/DVWA/vulnerabilities/csrf/?new_pass=test1234&confirm_pass=test1234&submit=Submit" border="0" style="display:none;"/>
<h1>404 Not Found</h1>
<h2>リクエストされたファイルが見つかりません</h2>
MediumレベルのDVWAソースコードでは、HTTP_REFERERヘッダ(リクエスト元URL)がSERVER_NAME(ホスト名)を含むかどうかを検証しています。つまり、同一ドメイン外からのリクエストは拒否されます。
この防御を回避するには、作成したHTMLファイル名を被害者のドメイン名に合わせるだけです。例えば、www.example.com.htmlという名前にすれば、参照元チェックを通過できます。
防御対策
1)Refererフィールドの検証
HTTPヘッダにはRefererフィールドがあり、リクエストの発信元を示します。機密データへのアクセス処理では、通常Refererフィールドとリクエスト先が同一ドメインであることを確認します。しかし、前述のようにこの方法は完全ではありません。
2)トークン検証の追加(参考:Wikipedia)
CSRFの本質は、攻撃者がユーザーを自分の準備したページに誘導することにあるため、機密情報へのアクセス時に、Cookieに保存されておらず、攻撃者が偽造できない検証データを要求すれば、攻撃を防げます。これは通常フォーム内の隠しフィールドとして実装されます。サーバーがランダムな値を生成しフォームに埋め込み、クライアントがフォーム送信時に一緒に送り返します。通常のアクセスではブラウザが正しい値を取得・送信できますが、CSRFによる偽装攻撃では攻撃者が事前に値を知ることができないため、サーバーは検証失敗としてリクエストを拒否します。
3)現在のパスワード入力要求の実装
この方式では、攻撃者が現在のパスワードを知らない限り、CSRF攻撃を実行できません。