このシリーズの目的
体系的なwebコーディングの訓練ができるようになるために、PHPの初学のきっかけかつ、PHPでログインフォームやフォームを実装することができるようになるために
上記のチュートリアルを進めているのでその備忘録。
内容
今回のチュートリアル
PHP Security Tutorial: Cross-Site Request Forgery (CSRF) Protection
やったこと
CSRFとは何かを知り、その対策について基本的なコードを書きながら確認する。
成果物
<?php
// session start
session_start();
// create a key for hash hmac funciton
if(empty($_SESSION['key']))
$_SESSION['key'] = bin2hex(random_bytes(32));
// echo $_SESSION['key']; // 生成されたキーの確認。本番では記載しない。
// create CSRF token
$csrf = hash_hmac('sha256', 'this is some string: csrf.php', $_SESSION['key']);
// validate token(正常にCSRFトークンが生成されているか確認)
if(isset($_POST['submit'])) {
if(hash_equals($csrf, $_POST['csrf'])) {
echo "your name is:" .$_POST['username'];
} else
echo "CSRF Token Failed";
}
?>
<!DOCTYPE html>
<html>
<head>
<title>CSRF Tutorial by CPI</title>
</head>
<body>
<form action="csrf.php" method="POST">
<input type="text" name="username" placeholder="what is your name?">
<input type="hidden" name="csrf" value="<?php echo $csrf ?>">
<input type="submit" name="submit" value="SUBMIT">
</form>
</body>
</html>
新しい用語
CSRF(クロスサイトリクエストフォージェリ)
→Webアプリケーションの脆弱性・またそれを利用した悪意のある攻撃を指す。
*対策にはワンタイムトークンを使用する。
なので今回は不可逆性を持つハッシュ関数が利用されているということになる。
今回のコードの注釈
session_start();
新たなPHPのセッションを開始するかあるいは既存のセッションを再開する。
if(empty($_SESSION['key']))
$_SESSION['key'] = bin2hex(random_bytes(32));
emptyは引数に指定した変数が空であるか確かめる。
bin2hex(random_bytes(32));はバイナリのデータを16進表現に変換したASCII文字列にして返すコマンドで、今回は32バイトで生成したランダムバイナリを変換することになる。
つまり、if文で書いてあるのでこの場合、キー変数が空だった場合は32バイトのランダムバイナリを生成して、それを16進表現に変換したASCII文字列に変換し、それを代入するということになる。
$csrf = hash_hmac('sha256', 'this is some string: csrf.php', $_SESSION['key']);
hash_hmac()はHMAC方式を使用してハッシュ値を生成するコマンド。
引数に、使用するアルゴリズム・ハッシュするメッセージ・生成するために使用する共有の秘密鍵を指定する。
sha256とはハッシュ関数を別の値に変換するアルゴリズムの一つ。
ハッシュ関数は変換し値をもとに戻す方法が無いという不可逆性を持つ。
if(isset($_POST['submit'])) {
if(hash_equals($csrf, $_POST['csrf'])) {
echo "your name is:" .$_POST['username'];
} else
echo "CSRF Token Failed";
}
issetは変数がセットされ、かつnullじゃないことを確認するコマンド。
この場合、後述するHTMLのフォーム要素において、送信ボタンが押され、フォームの情報が取得できたか、できないかで分岐が変わる。
取得したハッシュ値が一つ前で生成されたCSRFトークンの値と等しければ、取得したname属性の情報をyour name is:の文字列に加えて出力する。
等しくなければ、エラーメッセージを出すということになる。
学習済みの知識の復習
<form action="csrf.php" method="POST">
<input type="text" name="username" placeholder="what is your name?">
<input type="hidden" name="csrf" value="<?php echo $csrf ?>">
<input type="submit" name="submit" value="SUBMIT">
</form>
</body>
</html>
送信ボタンと1行の入力欄を作る、基本的なフォームのマークアップ。
inputタグなどは必ずtype属性とname属性を指定して、役割を明確にする。
このうち、type = hiddenは画面上に表示されない隠しデータをやり取りするための属性である。
今回は送信する値にphpで生成した変数csrfをphpで出力したもので指定している。
広がった知見
CSRFとその対策について理解し、暗号キーの生成方法とハッシュを生成する方法を学習できた。
参考
サイトを安全に!PHPでcsrf対策を行う方法【初心者向け】
[PHPでSHA256を使う方法【初心者向け】]
(https://techacademy.jp/magazine/19137)
[HTTPとPOSTとGET]
(https://qiita.com/Sekky0905/items/dff3d0da059d6f5bfabf)
[GETとPOSTの使い分け方法]
(https://php-junkie.net/beginner/reserved_variables/get_post/)