結論
クロスサイトリクエストフォージェリの略で、あるWebサイトに対して、別の悪意のあるWebサイトがユーザーのcookieを利用して攻撃する手法のことです。
前提知識
cookieやセッションについて知っておく必要があります。
以下の記事でまとめているので、よければ先にご覧になってください。
CSRFとは
CSRFの仕組みについて解説します。
1. 通常のWebサイトにログインする
例えば、送金をしようと銀行のWebサイトにアクセスするとします。
おさらいになりますが、この時ブラウザは、サーバー側から発行されるセッションIDの情報をcookieとして保存します。
2. 不正なWebサイトにアクセスする
そのWebサイトにログイン中の状態で、別のWebサイトにアクセスします。
不正なWebサイトへのアクセス経由ですが、例えばスパム(迷惑)メールに添付されているURLを誤ってクリックしてしまうなどがあげられます。
3. 通常のWebサイトに対して不正なリクエストが送信される
URLを開てしまうだけで、不正なリクエストが通常のWebサイトに対して送信されてしまうことがあります。
どのようにするのかの例を作成してみました。
<!-- ユーザーの目には見えない隠しフォーム -->
<form id="csrf-form" action="https://example-bank.com/transfer" method="POST">
<input type="hidden" name="to_account" value="悪意のある人の口座番号" />
<input type="hidden" name="amount" value="100,000" />
</form>
<script>
// ページが開かれた瞬間に、上のフォームを勝手に送信する
document.getElementById('csrf-form').submit();
</script>
これは、画面を構成するためのHTMLという言語です。
※もし、HTMLについて詳しくない場合は、軽く流してもらって構いません。
なんとなく、ある銀行口座に10万円を振り込むということをするイメージを掴んでもらえればOKです。
4. cookie情報により、通常のWebサイトがリクエストが本人によるものだと誤解
ここで問題となるのが、cookieの仕様です。
すなわち、こちらの記事でも述べた通り、ユーザーはリクエストのたびにIDとパスワードを毎回入力して送信する必要がありません。
これは、cookieが自動でセッションIDを送信してくれるからでした。
しかしなら、これのせいでサーバー側は同じセッションIDなら本人の意思によるリクエストだとみなしてしまうという問題が発生します。
通常なら問題ないのですが、先ほどの例のように不正なWebサイト経由でリクエストを送信するときも、cookieの特性上、セッションIDを一緒に送信してしまうのです。
まさに本人の意思によるリクエストであるかのように 偽造(Forgeries) するわけです。
対処方法
では、どう対処するのかと言いますと、一番有名な方法はCSRFトークンというものを利用する方法です。
1. セッション内にCSRFトークンを生成して保存。
サーバー側でセッションIDとは別にCSRFトークンと呼ばれる、いわば合言葉を生成してセッション内に保持しておきます。
CSRFトークンは、使うたびに生成して使ったら破棄するという使い捨ての合言葉です。
2. 画面側にそれを埋め込む
セッション内に保存したトークンを画面(HTML)に埋め込んでクライアントに送信します。
これは、画面上からは見えない隠し要素として埋め込んでおきます。
ユーザーがその画面からリクエストを送ると自動的にそのCSRFトークンも一緒に送信されるようにします。
3. サーバー側で送信されたトークンを照合
送信されたCSRFトークンを見てセッションに保存したものと同じか確認します。
もし、CSRFトークンがない、または異なる場合は不正なリクエストとみなして処理をストップさせます。
このようにセッションIDだけでなく、CSRFトークンという使い捨ての合言葉も照合することで、本人が送信しているということだけでなく、正しいWebサイトからのリクエストであること、すなわち本人の意思によるリクエストであることを確認するわけです。
以下のようなイメージです。
<?php
// 1. セッションを開始する
session_start();
// 2. もしセッションにトークンがなければ、ランダムな文字列(合言葉)を作って保存
if (empty($_SESSION['csrf_token'])) {
// 安全なランダム文字列生成のために random_bytes() を使う
$_SESSION['csrf_token'] = bin2hex(random_bytes(32));
}
// 3. フォームが送信されてきたときの処理(答え合わせ)
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
$posted_token = $_POST['csrf_token'] ?? '';
// セッションの合言葉と、送られてきた合言葉が一致するかチェック
if (hash_equals($_SESSION['csrf_token'], $posted_token)) {
// 一致した!=本物の画面から送られたリクエスト
echo "安全なリクエストです。処理を実行します。";
// 【重要】処理が終わったらトークンを使い捨てるために削除・再生成するのがベスト
unset($_SESSION['csrf_token']);
} else {
// 一致しない!=罠サイトからの不正なリクエストの可能性大
http_response_code(403);
echo "不正なリクエスト(CSRFの疑い)です。";
exit;
}
}
?>
<!-- 4. HTMLフォーム(画面の表示) -->
<!DOCTYPE html>
<html lang="ja">
<head><title>CSRF対策のテスト</title></head>
<body>
<form action="" method="POST">
<p>メッセージ:<input type="text" name="message"></p>
<!-- 画面には見えない「隠しパーツ」として合言葉を埋め込む -->
<input type="hidden" name="csrf_token" value="<?php echo htmlspecialchars($_SESSION['csrf_token'], ENT_QUOTES, 'UTF-8'); ?>">
<button type="submit">送信</button>
</form>
</body>
</html>
PHPというプログラム言語で書かれています。
※PHPに詳しくない方は、流してもらって構いません。
※これは、あくまでイメージですので、これをコピペしてもあなたのサイトのセキュリティが保証されるものではありません。ご了承ください。
現代のWeb開発における対策
現代では、強力なWeb開発用フレームワークがたくさんあるので、CSRFについて強く意識する必要がなくなっています。
フレームワークとは、Webアプリケーションを作るための基本となるプログラムの枠組みがあらかじめ作られているものです。
いくつか例を紹介しますが、プログラミングに詳しくない方は、気にしなくてもOKです。
Laravel
PHPのフレームワークであるLaravelでは、フォームに @scrf と書くだけでOKだったりします。
Next.js
TypeScriptのフレームワークであるNext.jsでは、ServerActionsという機能がページのURLの情報をリクエストの内容に自動で付与してくれる仕組みがあります。
ブラウザの進化
ブラウザ自体も進化しています。
特にSameSite属性が登場したことにより、何でもかんでもcookieを自動付与するという仕組みが見直されました。
SameSite属性については、別の記事にまとめてみたいと思います。
まとめ
- CSRFは。クロスサイトリクエストフォージェリの略で、あるWebサイトに対して、別の悪意のあるWebサイトがユーザーのcookieを利用して攻撃する手法のこと
- cookieが自動でセッションIDをリクエストに付与する性質を利用
- それにより、ユーザー本人の意思によるリクエストだと偽造する
- 対策としては、CSRFトークンを用いる方法がある
- 最近は、技術向上により開発者が対策を一から実装する必要がなくなりつつある




