Webサイトへのセキュリティ攻撃の1つにCSRF(クロスサイトリクエストフォージェリ)というものがあります。
この記事では、
- CSRFの攻撃の流れ
- 攻撃による影響・被害
- PHPでWebアプリを制作する際の対策方法
について解説をしていきます。
- CSRF(クロスサイトリクエストフォージェリ)とは何か?
- CSRF攻撃実行までの流れ
- CSRFによる被害・影響範囲
- PHPアプリ制作におけるCSRF対策
Webアプリ開発においてセキュリティ対策は必須項目です。対策するべきセキュリティ攻撃はいくつかありますが、この記事ではCSRF(クロスサイトリクエストフォージェリ)を取り上げて解説をしていきます。
Webアプリのセキュリティ対策についてしっかり学ばれたい方にはこちらの書籍がおすすめです。
(通称:徳丸本と呼ばれる「Webアプリ開発者必読」とまで言われている書籍です)
体系的に学ぶ 安全なWebアプリケーションの作り方 第2版 脆弱性が生まれる原理と対策の実践
徳丸 浩/著 SBクリエイティブ/出版│Amazon
CSRF(クロスサイトリクエストフォージェリ)とは?
一般ユーザが攻撃用に用意されたWebサイトへアクセスすることで、攻撃対象のサーバーへ不正なリクエストが送信・実行されてしまう攻撃手法
CSRF攻撃により、攻撃対象のサーバー、例えば掲示板サイトなどで、ユーザが意図しない投稿が行われてしまう、あるいは銀行口座から不正送金が行われてしまうといった被害が生じます。
CSRF攻撃の流れ
初めてCSRFという言葉を聞く方にとってはなかなかピンと来ないかもしれません。
そこで、実際にどういった流れで、どういった攻撃が実行されるのかを具体例を交えてお伝えしようと思います。
- 悪い人(攻撃者)が「トラップ用のWebサイト」を用意する
- 一般ユーザが普段から利用しているサイトへログインをし、その後ログアウトをしないままサイト(=攻撃対象のサイト)を閉じる
- 一般ユーザが1で用意された「トラップ用のWebサイト」へ、(罠と気が付かずに)アクセスする
- 「トラップ用のWebサイト」から「攻撃対象のサイト」へ一般ユーザが意図していないリクエスト(コメント投稿など)が送られる
上記を、以下のような具体的な人物やサイトに置き換えて、再度攻撃の流れをお見せします。
一般ユーザ ⇒ Aさん
攻撃対象のサイト ⇒ 銀行サイト
- 悪い人(攻撃者)が「トラップ用のWebサイト」を用意する
- Aさんが「銀行サイト」へログインをする。そして、ログアウトせずに「銀行サイト」を閉じる。
- 悪い人からAさん宛てに偽物のメールが届く(いわゆるフィッシング詐欺)
- Aさんが、偽物のメールに記載されていたリンクをクリックし、1で用意された「トラップ用のWebサイト」へアクセスする
- すると「トラップ用のWebサイト」から「銀行サイト」へ【Aさんの口座から悪い人の口座へ10万円を送金する】というリクエストが送られる
- 悪い人は10万円をゲットする
このように、Aさん(一般ユーザ)が気が付かない内に、不正操作を行わせてしまうのがCSRF攻撃です。
気づかれない内に不正な操作が実行されてしまうというのが恐ろしいですね……。
CSRFの影響・被害
具体例でCSRFがどのような攻撃なのかというイメージを掴めたところで、CSRFによる被害や影響範囲についてお伝えをしたいと思います。
前提として、本記事では
どこまで影響が及ぶのか、またそれによってどのような被害が生じるか
という観点から整理をさせて頂きます。
影響範囲
- Webアプリ
- サーバー
まずWebアプリについてですが、前述した例では銀行サイトのログイン状況が関連していましたが、ログイン機能の有無は実は関係ありません。
例えば、ログイン機能が必要のない掲示板サイトなどに対しても、同様の流れで「不正投稿」が可能となります。
また、Webアプリと言っていますが、もう少し初学者の方向けにかみ砕いて言うと、
PHPなどのサーバーサイド言語などによって、ユーザの操作に応じて処理を行うサイト
そのすべてについて、CSRFの影響が及ぶ可能性があると言えます。
悪い人たちは、一般ユーザがそのときまさに使用しているブラウザを勝手に拝借し、Webアプリが持つ動的な処理部分を利用して悪事を働いているというわけです。
次のサーバーについては、別のセキュリティ攻撃であるDos攻撃という、言うなれば連続でサーバーに命令を出して疲れさせる攻撃の影響があります。
具体的には、CSRFによって掲示板サイトへ大量の不正な書き込みをすることにより、サーバーを落とすということが可能です。
DoS攻撃に関してですが、金曜ロードショーで某ジブリ映画が放映されるとき、某SNSのエンジニアの方々がサーバーが落ちないよう準備をしているそうです。(一般ユーザが合言葉を一斉に投稿するため)
DoS攻撃ではありませんが、つまりは大量の投稿が一気に発生するとサーバーが疲れて(落ちて)しまうということなんですね。
被害
- 意図せず不正リクエストを送信させられたユーザに対して、「不正リクエストを送信した攻撃者」という誤解が生じる
- CSRFを利用したDoS攻撃によってサーバーが落ちる(=サービス停止)
- 不正送金など一般ユーザへ実質的な損害が発生する
後者2つはおおよそイメージが掴みやすいかと思いますが、1つ目については補足をさせて頂くと、
不正リクエストを送信するのは、あくまでも攻撃者のブラウザからではなく、一般ユーザのブラウザからとなります。
そのため、Webアプリ側のログには、一般ユーザ側の情報が残ってしまい、結果として一般ユーザが「攻撃者」だと認識されてしまうのです。
全く身に覚えがないのに、気が付けば悪者扱いされてしまうかもしれないのです。
しかし、Webアプリを開発する側でしっかりと対策を施すことでCSRFを防げる確率は大きく向上します。
一般ユーザの方々に安全にWebアプリを使用してもらうためにも、Webアプリ開発をする方はしっかりと知識を身に付けて対策をしましょう。
CSRF攻撃の対策(PHP)
トークンを利用
PHPでアプリ開発をする際、CSRF攻撃への最も一般的な対策として知られているのが、トークンを利用する方法です。
トークンとは、ランダムな文字の組み合わせによって生成された文字列のことです。
簡単にご説明すると、ユーザからのリクエストを受け取る際に、トークンをチェックすることで、そのリクエストが本当に正しい(=不正ではない)リクエストなのかを確認し、CSRFを防ぐことが可能です。
具体的には以下のような流れでトークンの生成とトークンのチェックを行います。
- ユーザ操作によるリクエストの送信時にトークン(合言葉)をランダム生成する
- リクエストと共に、トークンをセッションに格納かつPOST送信する
- 受取り側で、受け取ったトークンとセッションに格納されているトークンを照合する
- 一致していれば正規リクエストとして正常処理し、
- 不一致なら不正リクエストとして拒否する
これを具体的なコードに書き起こすと以下のようになります。
// トークンを生成
$token = bin2hex(random_bytes(32));
// トークンをセッションに格納
$_SESSION["token"] = $token;
<form action="宛先パス" method="post">
// 隠したinputタグにトークンを持たせ、POST送信時に他の入力値と共に送信する
<input type="hidden" name="token" value="<?php echo $_SESSION["token"]; ?>">
// ---------------------------------
// --- 入力フォーム関係のタグなど ---
// ---------------------------------
<input type="submit" name="back" value="戻る" />
<input type="submit" name="send" value="送信" />
</form>
解説
まず、ここでランダムな文字列であるトークンを生成しています。
// トークンを生成
$token = bin2hex(random_bytes(32));
random_bytes()はランダムなバイト列を生成する関数で、暗号学的に安全なため、生成されたデータは第三者に推測されない性質を持ちます。
bin2hex()は引数で渡した文字列(バイナリデータ)を16進表現に変換する関数で、上記ではrandom_bytes()によって生成したランダムな文字列を、bin2hex()によって16進数に変換しています。
この2つの関数の組み合わせによって、ランダムな文字列を生成するという流れです。
続いて、生成したトークンをセッションに格納します。
// トークンをセッションに格納
$_SESSION["token"] = $token;
最後に、POST送信をする際にトークンを一緒に送信するため、HTMLのformタグ内に、トークンを持たせたinputタグを配置します。
<form action="宛先パス" method="post">
// 隠したinputタグにトークンを持たせ、POST送信時に他の入力値と共に送信する
<input type="hidden" name="token" value="<?php echo $_SESSION["token"]; ?>">
// ---------------------------------
// --- 入力フォーム関係のタグなど ---
// ---------------------------------
<input type="submit" name="back" value="戻る" />
<input type="submit" name="send" value="送信" />
</form>
このようにトークンを利用することで
となります。
トークンとは言うなれば合言葉と同じです。
合言葉を知っている人=正規のリクエストしか受け付けないようにすることで、不正リクエストを送信してくるCSRF攻撃の対策となるのです。
リファラーをチェックする
次に、HTTPヘッダの情報の1つであるリファラを利用して、不正リクエストを弾く方法をご説明します。
理由は後述しますが、この方法はあくまで保険的な位置づけとなるため、確実にCSRFを防げるというものではありません。前述したトークンでリクエストをチェックする方法と組み合わせることで、防げる確率が上がるという風にお考え下さい。
そもそもHTTPヘッダとは、
Webサイトを表示する際にやり取りされる情報の1つで、この中に表示するページのコンテンツタイプであったり、Cookieに含まれる情報などが記載されている、言うなれば説明書的なものです。
この中にリファラーという、直前までいたWebサイトのURLが記載されている情報が含まれています。
つまり、リファラーを確認することで、リクエストを送信してきた人(ブラウザ)が、本来通るべきページをきちんと通ってそのリクエストを送信しているかどうかが判別可能となります。
ただし、ここで注意点があります。
リファラーは、必ずしも毎回記載されているわけではなく、さらにやろうと思えばリファラー自体を変更することも可能です。つまりリファラーには、その情報が正しいという保証があるわけではなく、安心して信用していいというものではないということです。
この節の冒頭でお伝えした注意書きは、こうした理由によるものです。
実際に使用することでCSRFを防げる確率は高まりますが、確実性は無いため
トークンを利用した対策と併用することが望ましい
です。
それではここでリファラーを利用したリクエストチェック方法についてコードを解説していきます。
// リファラー=直前にいたWebページの情報を取得
$referer = $_SERVER['HTTP_REFERER'];
// リファラーからホスト名(ドメイン名)を取得
$referer_host = parse_url($referer)['host'];
// 自分のサイトのドメインを設定
$host_url = 'example.com';
if(stristr($referer_host, $host_url)){
// 正常なアクセスだった際の処理
} else {
// 不正アクセスだった場合の処理
// (例)エラーページを返すなど
}
解説
まず、この部分でリファラーを取得しています。
// リファラー=直前にいたWebページの情報を取得
$referer = $_SERVER['HTTP_REFERER'];
ここで登場している$_SERVER[‘HTTP_REFERER’]には、PHPのスーパーグローバル変数の1つでリファラが格納されています。
次に、取得したリファラーからドメイン部分を取得します。
// リファラからホスト名(ドメイン名)を取得
$referer_host = parse_url($referer)['host'];
parse_url()は引数で渡したURLを解析し、連想配列形式で返す関数です。上記の例では配列名に[’host’]を指定することで、リファラー(URL)からホスト名=ドメイン名を取得しています。
次に自分のサイトのドメインを設定し、リファラーから取得したホスト名と一致するかどうかを検証、正常ならば正規の処理を行い、不一致ならば不正リクエスト用の処理を実行します。
// 自分のサイトのドメインを設定
$host_url = 'example.com';
if(stristr($referer_host, $host_url)){
// 正常なアクセスだった際の処理
} else {
// 不正アクセスだった場合の処理
// (例)エラーページを返すなど
}
stristr()は、大文字と小文字を区別せずに、指定文字列内においてキーワードが現れる場所を返す関数です。上記のコードではstristr()を利用して、リファラーのホスト名部分に自分のサイトのドメインが含まれているかを検証しています。
※ちなみに関数名が似ているものにstrstr()がありますが、こちらは大文字小文字を区別する仕様になっているため、使用時には注意して下さい。
リファラーを上手く利用することでCSRF攻撃を防げる確率を高めることが可能です。
トークンを利用した対策と組み合わせることでより安全性の高いWebアプリケーションを開発しましょう。
まとめ
CSRF攻撃の流れや被害・影響、そしてPHPでアプリ開発する際の対策方法についてお伝えをしてきました。
今回はお伝えしませんでしたが、CSRF対策についてはこの他にも
- 二要素認証を導入する
- リクエストを処理する直前にユーザにパスワード入力を促す
などの方法もあり、これらを導入することでより安全性の高いWebアプリ開発が可能となります。
今回取り上げたCSRF以外にも考慮すべき攻撃手法がいくつも存在します。
それらすべてにセキュリティ対策を講じていくことは大きな労力を伴いますが、ユーザに安心して利用してもらうためにも1つ1つ実践していくことが大切です。
フレームワークを利用すれば効率的なセキュリティ対策も可能となります!
他のセキュリティ攻撃の解説記事も作成していくため、ご参考にしていただければと思います。
最後に、僕がセキュリティ対策を学ぶ際に利用した書籍をご紹介させていただきます。
体系的に学ぶ 安全なWebアプリケーションの作り方 第2版 脆弱性が生まれる原理と対策の実践
徳丸 浩/著 SBクリエイティブ/出版│Amazon
こちらの書籍は通称徳丸本と呼ばれており、Webセキュリティの第一人者である徳丸浩さんが手がけられている書籍です。Webアプリを開発する方やWebアプリケーションのセキュリティ対策を徹底的に学びたい方にとてもオススメできる一冊です。
Webアプリケーション開発において、ある意味セキュリティ対策は技術力以上に重要です。
こちらの書籍を読むことでWebアプリケーションにおいて必要なセキュリティ要件も明確となるため、Webアプリ開発に携わる方は必ず目を通しておきたい一冊です。
それでは最後までお付き合いいただきありがとうございました!
また別の記事でお会いできれば光栄です!