やられアプリ BadTodo - 10.5 CSRF(対策)

前回:やられアプリ BadTodo - 10.1 CSRF(クロスサイト・リクエスト・フォージェリ) - demandosigno

安全なウェブサイトの作り方 - 1.6 CSRF(根本的解決)
 処理を実行するページをPOSTメソッドでアクセスするようにし、その「hiddenパラメータ」に秘密情報が挿入されるよう、前のページを自動生成して、実行ページではその値が正しい場合のみ処理を実行する。
 ここでは具体的な例として、「入力画面 → 確認画面 → 登録処理」のようなページ遷移を取り上げて説明します。
 まず、利用者の入力内容を確認画面として出力する際、合わせて秘密情報を「hidden パラメータ」に出力するようにします。この秘密情報は、セッション管理に使用しているセッションIDを用いる方法の他、セッションIDとは別のもうひとつのID(第2セッションID)をログイン時に生成して用いる方法等が考えられます。
 生成するIDは暗号論的擬似乱数生成器を用いて、第三者に予測困難なように生成する必要があります。次に確認画面から登録処理のリクエストを受けた際は、リクエスト内容に含まれる「hiddenパラメータ」の値と、秘密情報とを比較し、一致しない場合は登録処理を行わないようにします。このような実装であれば、攻撃者が「hiddenパラメータ」に出力された秘密情報を入手できなければ、攻撃は成立しません。
 なお、このリクエストは、POSTメソッドで行うようにします。これは、GET メソッドで行った場合、外部サイトに送信されるRefererに秘密情報が含まれてしまうためです。

トークン(秘密情報)の生成

PHPには暗号論的疑似乱数生成器が複数用意されています。

BadTodoでは badapp.php にてopenssl_random_pseudo_bytes()で生成されています。

<?php
public function get_token() {
  $token = $this->get(TOKENNAME);
  if (empty($token)) {
    $token = bin2hex(openssl_random_pseudo_bytes(TOKENLEN));
    $this->set(TOKENNAME, $token);
  }
  return $token;
}

トークンのチェック

前回確認したように、BadTodo上の更新リクエストにはCSRF用対策トークンが付与されていますが、トークンのチェックが漏れているリクエストがあります。今回は前回見つかったメールアドレスの変更リクエストを例として修正します。
changemaildo.phpの先頭付近に追記。

<?php
  require_once('./common.php');
  $app->require_loggedin();
  // ▼▼ 追記ここから
  $token = filter_input(INPUT_POST, TOKENNAME);
  if ($token != $app->get(TOKENNAME)) {
    error_exit('正規の画面から使用ください');
  }
  // ▲▲ 追記ここまで
  $id = $app->get_id();

確認

罠サイトからリクエストを送信しても、(ソースは前回を参照)

チェックが入りエラーとなります。

Refererのチェック・CookieのSameSite属性

後日

次回:BadTodo - 10.6 CSRF対策トークンの不備 - demandosigno

/* -----codeの行番号----- */