クロスサイトリクエストフォージェリ(Cross Site Request Forgery, CSRF)
クロスサイトリクエストフォージェリ(以下CSRF)とは、悪意を持ったページを利用して正規のフォームを通さずにデータの書き込みなど本来フォームからでなければ実行できない処理が行う攻撃方法です。
CSRFを体験する
下記のファイルを作成し、htmlディレクトリに配置しましょう。
csrf.html
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>CSRF</title>
<script src="//code.jquery.com/jquery-3.3.1.slim.min.js" integrity="sha384-q8i/X+965DzO0rT7abK41JStQIAqVgRVzpbzo5smXKp4YfRvH+8abtTE1Pi6jizo" crossorigin="anonymous"></script>
</head>
<body>
<h1>csrf体験</h1>
<p>
CSRFを体験してみましょう。
勝手にカートに商品が追加されます。
<a href="cart.php">カートへ移動</a>
</p>
<iframe
id="csrf"
src="index.php">
</iframe>
<script>
setTimeout(function(){
$('#csrf').contents().find('form').each(function(){
$(this).submit();
});
alert('カートに商品を登録しました。');
}, 3000);
</script>
</body>
</html>
任意のユーザーでログイン後、csrf.htmlにアクセスすることでcsrfによりカートに商品が追加されます。
CSRFを利用した典型的な攻撃方法
上記のCSRF脆弱性を利用した攻撃方法の典型が「成りすまし投稿」です。本来自分しか投稿できないはずのSNSのタイムライン等にCSRFを通じて商品の紹介文などを投稿させ、友人などそのユーザーに対して信頼を持っている相手を詐欺サイトなどに誘導します。
そのほかにも、「POST投稿を通じて行うことができる操作」の全てがCSRFの対象になり得ます。
CSRFへの対策
CSRFへの対策に用いられるのが「トークン」と呼ばれるランダムな文字列を用いる方法です。
1. トークンの生成・埋め込み
POST投稿を行うフォームに対して、直前に推測されないランダムな文字列を埋め込んでおきます。この文字列はフォームを表示する直前に新しく生成することになります。
この、ランダムな文字列はフォームに埋め込むだけでなく、セッションに保存しておくことによって後ほどPOST送信を受け取った際に照合できるようにしておきます。
2. トークンの照合
続いて、POST投稿を受け取って操作を行う際には、直前に生成したランダムな文字列が正しく埋め込まれているのかどうかをチェックします。文字列が埋め込まれていなかったり、セッションに保存されている文字列と異なる場合には不正なリクエストとしてその後の処理を行わないことになります。
3. トークンの破棄・再生成
最後に重要なのが、POST投稿を受け取って操作を行ったらすぐに現在のトークンを破棄し、新たなトークンを生成することです。一度利用したトークンがそのまま利用されてしまうと、なんらかの方法で該当のトークン文字列が盗まれ、再利用されてしまう可能性があります。
トークンの生成とチェックに利用する関数の例
// トークンの生成
function get_csrf_token(){
// get_random_string()はユーザー定義関数。
$token = get_random_string(30);
// set_session()はユーザー定義関数。
set_session('csrf_token', $token);
return $token;
}
// トークンのチェック
function is_valid_csrf_token($token){
if($token === '') {
return false;
}
// get_session()はユーザー定義関数
return $token === get_session('csrf_token');
}
例えば上記のような関数を利用することで、CSRFトークンの生成とチェックが可能です。トークン利用の流れと上記の関数を踏まえて、CSRF対策を実装してみましょう。
トークンの盗難を防ぐ
iframeという仕組みを使うことで、CSRFトークンをいわば盗みだすことも可能です。最初に紹介したcsrf.htmlの例では、サイトに埋め込まれているCSRFトークンをそのまま利用しているため、CSRFトークンの導入だけではCSRFを防ぐことができません。
この場合、トークンの盗難を防ぐためにはiframeによるサイトの読み込みを禁止する必要があります。
X-Frame-Options という項目をPHPスクリプト、またはWebサーバーソフトウェア(apacheなど)の設定ファイルに設定することで、iframeの読み込みを禁止することが可能です。
apache設定ファイルの修正による方法については今回のカリキュラムの範囲には含まれませんが、PHPスクリプトからの設定方法については、一度方法を調べてみると良いでしょう。