クロスサイトリクエストフォージュリ(CSRF)をPHPで検証する

2 min read読了の目安(約2500字

はじめに

ちょっと難しくて理解しづらい脆弱性「クロスサイトリクエストフォージュリ(以降「CSRF」と略します)」をPHPで検証したいと思います。

CSRFはログインチェックをセッション確認のみで行っているサイト・システムに生まれる脆弱性です。攻撃者がサイト・システムを分析する必要があるため「対象が絞られた限定的な攻撃」ではあるのですが対象をピンポイントで狙う攻撃のため正しく対策されていないと被害が大きくなる傾向もあります。

CSRFがサイト・システムに存在すると、ログイン後の注文フォームに対して大量の注文が処理が行われたり、意図しないパスワード変更により不正ログインを許すことになります。

CSRFとは?

ログイン確認が必要な場合、セッションを利用する事が多いと思います。

よくあるログイン確認
1.ログイン時にセッションにログイン情報を格納する
2.ログイン情報が正しいかチェックをする

このようなチェック自体は正常な処理ですが、フォームのサブミット先の処理がログインチェックのみであるとCSRFを含む事となります。

ログインチェックのコード例

action.php
<?php
// セッションスタート
session_start();
// ログイン確認
if ( isset($_SESSION['login_id']) ) {
    $login_id = $_SESSION['login_id'];
    // ログインIDが正しいかチェックする処理...
} else {
    // ログインセッションが無かった場合の処理...
}
// ログイン後の処理に続く...

普通のページであればこれで問題は無いと思いますが、フォームのサブミット先の処理で同様のチェックのみの場合にCSRFが生まれる事になります。

「ログインパスワード更新」を例とします。

フォームから受け取った値でパスワードを更新する処理があるとします。

1.フォーム(form.php)から新しいパスワードを受け取り 更新処理(action.php)を実行する。
2.更新処理はログイン後のページであるため「ログインのチェックは行われている。」

この処理に対するCSRF攻撃パターン

1.攻撃者は更新処理(action.php)に不正な値をサブミットする処理を外部のWEBサーバに用意します。
2.被害者となるユーザーはログイン状態のブラウザで攻撃用ページを踏みます。
3.攻撃用ページから更新処理(action.php)にサブミットする事で更新処理を実行させます。

被害者となるユーザーのセッション $_SESSION['login_id'] は、すでに正しい値で存在しており、外部からサブミットを受け取っても「ログイン状態」と判断されます。その結果、パスワード変更処理が行われる事となります。

CSRF対策

1.トークンを埋め込みチェックする
2.リファラーを確認する
というような対策を実施する必要があります。実際にコードで確認したいと思います。

1.トークンを埋め込みチェックする

入力フォームにトークンを埋め込みます。

form.php
<?php
$_SESSION['token'] = bin2hex(openssl_random_pseudo_bytes(24));
?>
...
<input type="hidden" name="token" value="<?php echo htmlspecialchars($_SESSION['token']) ?>"></input>
...

入力フォームからトークンを一緒にサブミット・確認する事で、外部からのサブミットを受け付けないようにします。

action.php
if ( $_SESSION['token'] !== $_REQUEST['token'] ) {
    echo "トークン不一致エラー";
    die();
}

※ランダム文字列の生成に「bin2hex(openssl_random_pseudo_bytes(24))」を利用しています。

2.リファラーを確認する

リファラーを確認することで外部から直接の遷移を拒否するようにします。

action.php
if ( preg_match('/^https?:\/\/example.jp\//', $_SERVER['HTTP_REFERER']) !== 1){
    echo "画面遷移が不正です";
    die();
}

但し、リファラーはブラウザの設定や環境により取得できない場合があるため注意が必要です。

例にある「パスワード変更」であれば「変更を行った通知」をメールなどでユーザーに通知する事も対策として推奨のため補足します。

まとめ

CSRFは「被害者がログインしている状態(ログインセッションがある状態)」かつ「サブミット先の処理に渡す値が判明している」など成立する条件が限定されるため想定しづらく攻撃手法としても複雑と思います。また、イントラネット系のシステムであったりIP制限があり利用者が限定されたサービスなどは、対策が疎かになる傾向になります。

攻撃者はそのような開発者の緩んでいるところを突いてくるかもしれません。脆弱性は知らなかったでは済まされないため「正しく理解し対策をしていく」必要があると思います。

以上、クロスサイトリクエストフォージュリ(CSRF)をPHPで検証するでした。いかがでしたでしょうか。脆弱性は正しく理解して対策していきたいと思います。