CSRF(クロスサイト・リクエスト・フォージェリ)とは
CSRFを一言で表すと『Webサイトにログインしたユーザが悪意ある人間によってあらかじめ用意された罠によって、意図しないリクエストを送信されてしまう脆弱性』です。 被害者は脆弱性のあるWebサービスにログイン状態を保持したまま他サイトの掲示板等で悪意ある人によって投稿されたURLをクリックすると被害者が意図しない形で情報やリクエストが送信されてしまいます。 具体的な被害としてはログインしているWebサービスでの設定の変更(パスワードの変更など)、掲示板への不適切な内容の書き込みが気づかないうちに実行されてしまいます。 過去にCSRFの被害者が自分のIDで掲示板に殺害予告が書き込まれ誤認逮捕されてしまったケースもあるので恐ろしい脆弱性です…CSRFの対策
CSRFが起こってしまう原因は脆弱性のあるサイト(以後脆弱性サイトと呼ぶ)のリクエストが内部からなのか外部からなのかが判別されていないからです。例えば脆弱サイトで名前を変更するために以下のようなHTMLが用意されていたとします。
<form action="change-name.php" method="get">
<label>新しい名前</label>
<input type="text" name="name" value="">
<input type="submit" value="送信">
</form>
名前の変更処理はformタグのactionにあるようにchange-name.phpで行われるとします。
getで送信しているので名前に田中と入力して送信ボタンを押すとこのようなurlが生成され送信されます。
http://zeizyaku-site/change-name.php?name=田中
この脆弱サイトのchange-name.phpではユーザがログインしていたらこのname=田中
をPOSTで受け取り名前を変更します。
しかし、問題なのはこのurlはサイトの外部からでも送信できてしまいます。
例えばある掲示板サイトに悪い人がこのようなURLを投稿したとします。
http://zeizyaku-site/change-name.php?name=バカ
このname=バカ
とあることから、仮に掲示板のこのURLをクリックしたユーザが脆弱サイトにログインしていたら勝手に名前をバカに変更されてしまいます。
これは外部からのリクエストなのにかかわらず、脆弱サイトはその見分けがつかないためこの内容で実行されてしまいます。
なので対策としてはWebサイトがリクエストを内部からなのか外部からなのかをきちんと見分けをつけれるようにすることです。
そのためにはトークンというものを使用します。
トークンというのは簡単に言うとコンピュータ側が使用するパスワードみたいなもので「ワンタイムパスワード」と言うとイメージしやすいと思います。
このトークンはPHPならばsession_id()
のような関数を使用します。自作でトークンを作成すると推測される恐れがあるので提供されている関数を使ったほうが安全です。
<?php
// トークンを生成するコード
session_start(); // セッションを開始すると自動でセッションIDが発行される
$session_id = session_id(); // 現在のセッションIDを取得
echo $session_id; // 予測不能なランダムな値が表示される。 例:k12hc94du3tfubihqhip9leupl
?>
session_id関数は引数を渡さずに呼び出すと現在設定されているセッションIDを取得できます。
トークンは予測不可能な値をサーバーサイドで生成して、それをHTMLフォームにhiddenで持たせておきます。
<form action="change-name.php" method="get">
<label>新しい名前</label>
<input type="text" name="name" value="">
<input type="hidden" name="token" value="<?php echo session_id()?>"> <!--トークンを持たせておく-->
<input type="submit" value="送信">
</form>
トークン情報を一緒に送信して、サーバーサイドで名前の変更を行う前に設定されているセッションIDと送信されたトークンが一致するかを調べます。
<?php
//change-name.phpでの処理
session_start();
// トークンのチェック
if(session_id() === $_GET["token"]){
$name = $_GET["name"];
// 以下名前の変更処理をする
}
?>
このトークンは他の人間には知られることはまず無いため、外部からのリクエストで名前を変更することは不可能になります。
仮に先程の掲示板に貼られたURLの
http://zeizyaku-site/change-name.php?name=バカ
をクリックしてもトークン情報がないためこのリクエストは処理されません。
CSRFを防止するためにも必ずトークンを使用して、内部からのリクエストからしか受け付けないようにするようにしましょう。