目次
- はじめに
- CSRFの対策
- ワンタイムトークンの生成
- リンク先での整合性の確認
1. はじめに
CSRFとはクロスサイトリクエストフォージェリ(cross site request forgery)の略です。
簡単に説明しますと、フォームでデータを送信する際、Webアプリケーション利用者が意図しない処理を実行してしまう脆弱性、または攻撃手法のことです。
下の記事が脆弱性について詳しいので、参考にしていただけたらと思います。
2. CSRFの対策
CSRFの対策として、現在ベストプラクティスの一つとされているのがワンタイムトークンを利用した方法になります。
流れとしては、入力フォームのあるサイトを表示する際、サーバがクライアントに対してランダムな文字列(トークン)を発行します。
データを送信した際に、このトークンがリクエストに入っていなかったり、整合性が取れなかったりすれば、処理を終了することで不正なリクエストを防ぎます。
Laravelフレームワークにおいては「@csrf」によって簡単にトークンが生成されますが、今回は仕組みを知るためにコードを一つ一つ書いていきます。
3. ワンタイムトークンの生成
フォームのあるページの最初にトークンを生成するコードを、フォーム内にトークンをhiddenで埋め込みます。
2023/6/4追記:
ランダムバイトの生成は「openssl_random_pseudo_bytes()」がPHP5.3以降、「random_bytes()」がPHP7以降で使用可能です。
「openssl_random_pseudo_bytes()」は疑似ランダムコードであり、「random_bytes()」は公式の説明で「Get cryptographically secure random bytes(暗号的に安全なランダムバイトを取得)」とあり、特別の理由がない限り、「random_bytes()」が推奨されるようです。
<?php
// セッションの開始
session_start();
// ワンタイムトークンの生成
$toke_byte = random_bytes(16);
$csrf_token = bin2hex($toke_byte);
// トークンをセッションに保存
$_SESSION['csrf_token'] = $csrf_token;
?>
<!DOCTYPE html>
<html lang="ja">
<head>
<title>フォーム</title>
</head>
<body>
<form method="POST" action="target.php">
// hiddenで生成したトークンを埋め込む
<input type="hidden" name="csrf_token" value="<?= $csrf_token; ?>">
...
</form>
<!-- 以下略 -->
4. リンク先での整合性の確認
POSTでcsrf_tokenのパラメーターが送信されていなかったり、送信されていればその値がセッションに保存された値と一致しなかったりしたときにexitで処理を中止します。
<?php
if (!isset($_POST["csrf_token"])
|| $_POST["csrf_token"] != $_SESSION['csrf_token']) {
echo "不正なリクエストです";
exit();
}
// 以下、処理続行