3秒でわかる
ログイン中の利用者に、本人が意図しない操作を裏側で実行させる攻撃。送信元を確かめる仕組みが無いサイトで成立し、退会や送金が勝手に走ります。
30秒図解
もう少し詳しく
どういうものか
CSRF はクロスサイトリクエストフォージェリの略で、ログイン状態の利用者に、本人の意図しない要求をサーバーへ送らせる攻撃です。攻撃者が用意した別サイトに、標的サイトへ自動送信されるフォームや画像タグを仕込んでおき、利用者がそのページを開いた瞬間に要求が飛びます。
成立の鍵はブラウザの仕組みにあります。クッキーは、要求を出したページがどのサイトであっても、宛先のドメインに対して自動で付きます。サーバーから見ると正しいセッションクッキー付きの要求なので、本人の操作と区別が付きません。
なぜ必要か
対策が無いと、パスワード変更、退会、送金、設定変更といった副作用のある操作を、利用者の意思と無関係に実行されます。攻撃者は応答を読めないため情報を盗むのには向きませんが、状態を書き換える操作はすべて成立します。掲示板の書き込みで犯行予告が送信された事件のように、被害者が加害者に見えてしまう点も厄介です。
具体例
罠側は数行で作れます。
<form action="https://example.com/account/delete" method="POST" id="f">
<input type="hidden" name="confirm" value="yes">
</form>
<script>document.getElementById("f").submit();</script>防ぐ側は、推測できないトークンをフォームに埋め、送られてきた値をセッション側の値と突き合わせます。
app.post("/account/delete", (req, res) => {
if (req.body.csrfToken !== req.session.csrfToken) {
return res.status(403).send("forbidden");
}
// 削除処理
});クッキーに SameSite 属性の Lax か Strict を付けておくと、別サイトからの送信でクッキー自体が付かなくなり、多くの経路が塞がります。
つまずきやすいところ
ログイン必須にすれば防げると考えるのが典型的な誤解です。CSRF はログインしている状態を利用する攻撃なので、認証の有無は関係ありません。
トークンの置き場所も間違えやすい点です。URL のクエリに入れると履歴や外部サイトへの参照元情報から漏れます。フォームの隠しフィールドか専用のヘッダに入れます。GET で状態を変える設計になっていると、画像タグ一つで攻撃が成立するため、副作用のある操作は POST 以降に限る設計が前提になります。
似た用語との違い
XSS は攻撃者のスクリプトを被害者のブラウザで動かす攻撃、CSRF はスクリプトを動かさず要求だけを送らせる攻撃です。XSS が成立するサイトでは CSRF 対策のトークンも読み取られてしまうため、先に XSS を塞ぐ必要があります。
覚え方
「本人のブラウザに、本人の知らない命令を出させる」。信用しているのは利用者ではなくクッキーだ、と気付けば対策の方向が見えます。