ふたつめ

$_POSTをいきなりエスケープ処理していいの?

『詳細 PHP7+MySQL入門ノート』p.272に、「フォームの入力データのチェック」として、以下のようなコードが載っている。

入力フォームを表示する (nameCheckForm.php)

<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="utf-8">
<title>フォーム入力</title>
<link href="../../css/style.css" rel="stylesheet">
</head>
<body>
<div>
  <form method="POST" action="nameCheck.php">
    <ul>
      <li><label>名前:<input type="text" name="name"></label></li>
      <li><input type="submit" value="送信する"></li>
    </ul>
  </form>
</div>
</body>
</html>

そして、次に nameCheck.php が載っている。

nameCheck.php

<!DOCTYPE html>
<html lang="ja">
<head>
  <meta charset="utf=8">
  <title>フォーム入力チェック</title>
  <link href="../../css/style.css" rel="stylesheet">
</head>
<body>
<div>

<?php
  require_once("../../lib/util.php");      <== <1>
  // 文字エンコードの検証
  if (!cken($_POST)) {                     <== <2>
    $encoding = mb_internal_encoding();
    $err = "Encoding Error! The expected encoding is " . $encoding;
    // エラーメッセージを出して、以下のコードをすべてキャンセルする
    exit($err);
  }
  // HTMLエスケープ (XSS対策)
  $_POST = es($_POST);                     <== <3>
?>

<?php
  // エラーフラグ
  $isError = false;
  // 名前を取り出す
  if (isset($_POST['name'])) {
    $name = trim($_POST['name']);
    if ($name === "") {
      // 空白のときエラー
      $isError = true;
    }
  } else {
    // 未設定のときエラー
    $isError = true;
  }
?>

<?php if ($isError): ?>
  <!-- エラーがあったとき -->
  <span class="error">名前を入力してください。</span>
  <form method="POST" action="nameCheckForm.php">
    <input type="submit" value="戻る">
  </form>
<?php else: ?>
  <!-- エラーがなかったとき -->
  <span>
  こんにちは、<?php echo $name; ?>さん。
  </span>
<?php endif; ?>

</div>
</body>
</html>

このコードでは、<1>で util.php を読み込み、<2>で cken関数を、
<3>で es関数を呼び出している。

util.php

<?php
// (p.267)
// XSS対策のための HTMLエスケープ
function es($data, $charset = 'UTF-8') {
  // $data が配列のとき
  if (is_array($data)) {                <== <4>
    // 再帰呼び出し
    return array_map(__METHOD__, $data);
  } else {
    // HTMLエスケープを行う
    return htmlspecialchars($data, ENT_QUOTES, $charset);
  }
}

// (p.269)
// 配列の文字エンコードのチェックを行う
function cken(array $data) {
  $result = true;
  foreach ($data as $key => $value) {
    if (is_array($value)) {
      // 含まれている値が配列のとき文字列に連結する
      $value = implode("", $value);
    }
    if  (!mb_check_encoding($value)) {
      // 文字エンコードが一致しないとき
      $result = false;
      // foreachでの走査をブレイクする
      break;
    }
  }
  return $result;
}
// ?>

疑問に思うのは、<2> と <3> のところである。

両方とも $_POST 全部を一度でチェックしている。
だから、es関数も、cken関数も、配列にも対応している。

しかし、このやりかたはおかしい。

文字エンコードのチェックはまだいいとしても、HTMLエスケープをチェックしている <3> のところは危険だと思う。

$_POST をHTMLエスケープをして、しかも、$_POST に上書きしている!!!!

こんなこと、絶対やったらあかんことやろ。

データベースに保存するときどうするねん?

es関数の中で、<4>のところで配列に対してエスケープ処理している。
こういう処理を初めて見た。
$_POST をいっぺんでエスケープ処理したいから、こういうことをしているんやな。

ふつう、エスケープ処理は HTMLに表示する間際におこなうから、文字列に対しておこなうことになる。

配列をそのまま表示することはないからなあ。

この本の著者は、初心者を意識しているだろうから、初心者には難しいことは言わず、とりあえず $_POST をエスケープさせようということだろうか。

しかし、これは初心者に対してエスケープ処理についての間違った考え方を教えることになる。

エスケープ処理はHTMLに表示する間際でおこなうことに注意させるべきだろう。

この本はけっこういい本だと思うんだけど、最初の php.ini の設定のところとか、この部分とか、著者の我流で書かれているところがあるので、気をつけて読まなくてはならないなあ。

『詳細! PHP7+MySQL入門ノート』

大重美幸・著
ソーテック社
2019年5月31日 初版第4刷