PHPセキュリティ対策:文字列入力とバリデーション

(1/1)
数値入力とバリデーション」に続き、今回は、入力データが文字列である場合、PHPで入力バリデーションを行うプログラムを作ってみることにする。

(2022年6月27日)FastCGIで正常動作しない不具合を修正
(2022年5月18日)大幅改訂,pahooInputData.php分離,PHP8対応

目次

サンプル・プログラムの実行例

PHPセキュリティ対策:文字列入力とバリデーション

サンプル・プログラム

圧縮ファイルの内容
getValidString1.phpサンプル・プログラム本体。
pahooInputData.phpデータ入力に関わる関数群。
使い方は「数値入力とバリデーション」「文字入力とバリデーション」などを参照。include_path が通ったディレクトリに配置すること。

解説:入力要件など

  40: //表示幅(ピクセル)
  41: define('WIDTH', 600);
  42: 
  43: //入力要件
  44: $InputRequirements = array(
  45:     //オブジェクト名, ラベル名, 最小長, 最大長, 排除パターンかどうか, マッチングパターン
  46:     array('id',   'ID',       1, 10, FALSE, array('/^[0-9]+$/')),
  47:     array('kana', 'カナ氏名', 1, 30, FALSE, array('/^[ァ-ヶ]+$/ui')),
  48:     array('name', '氏名',     1, 20, FALSE, array('/^[A-Zぁ-んァ-ヶー一-龠]+$/ui')),
  49: );
  50: 
  51: /**

文字列入力の要件として4つのパラメータを用意した。
  1. 最小長‥‥入力を許容する文字列の最小長。マルチバイト文字も1文字と数える。
  2. 最大長‥‥入力を許容する文字列の最大長。マルチバイト文字も1文字と数える。
  3. 排除パターンかどうか‥‥つづくマッチングパターン配列が、そのパターンにマッチする文字列を排除したいときはTRUEを、受容したいときはFALSEを指定する。
  4. マッチングパターン配列


サンプル・プログラムでは、ID、カナ氏名、氏名の3つの入力項目を用意し、それぞれに入力要件を設定した。これを配列 $InputRequirements に定義してある。
入力要件は次の通り。
  • ID‥‥1文字以上、10文字以下、数字のみ
  • カナ氏名‥‥1文字以上、30文字以下、全角カタカナのみ
  • 氏名‥‥1文字以上、20文字以下、全角英字・ひらがな・カタカナ・漢字のみ
冒頭の実行例では、カナ氏名に全角ひらがなが含まれるため、エラーメッセージを表示している。
なお、データ入力に関わる関数群は別ファイル "pahooInputData.php" に分離しており、include_path が通ったディレクトリに配置すること。

解説:文字列バリデーション

 355: /**
 356:  * 文字列バリデーションを行う.
 357:  * filter_input()関数および $argv を参照する.
 358:  * 文字列の最小長,最大長,排除または受容するパターンを指定する.
 359:  * @参考URL https://www.pahoo.org/e-soul/webtech/phpsec/phpsec-10-01.shtm
 360:  * @param   string $data      バリデーションしたい文字列
 361:  * @param   string $errmsg    エラーメッセージを格納する
 362:  * @param   int    $minlen    入力可能な最小長(省略時=1)
 363:  * @param   int    $maxlen    入力可能な最大長(省略時=99)
 364:  * @param   bool   $exclusion $patternsはTRUE:受容/FALSE:排除(省略時=TRUE)
 365:  * @param   array  $patterns  受容/排除するパターンマッチ配列(省略時=NULL)
 366:  * @return  bool TRUE:成功/FALSE:失敗
 367: */
 368: function validString($data, &$errmsg, $minlen=1, $maxlen=99, $exclusion=TRUE, $patterns=NULL) {
 369:     $res = TRUE;
 370:     $errsg = '';
 371: 
 372:     //引数チェック
 373:     if ($minlen > $maxlen) {
 374:         $errmsg = "最小長({$minlen})が最大長({$maxlen})より大きい";
 375:         $res = FALSE;
 376: 
 377:     //文字列バリデーション
 378:     } else {
 379:         //値の存否
 380:         if ($data == '' || $data == NULL) {
 381:             $errmsg = '値がありません';
 382:             $res = FALSE;
 383:         //最小長チェック
 384:         } else if (mb_strlen($data< $minlen) {
 385:             $errmsg = sprintf("最小長(%d)より短い", $minlen);
 386:             $res = FALSE;
 387:         //最大長チェック
 388:         } else if (mb_strlen($data> $maxlen) {
 389:             $errmsg = sprintf("最大長(%d)より長い", $maxlen);
 390:             $res = FALSE;
 391:         //受容パターンのチェック
 392:         } else if ($exclusion && ($patterns !NULL)) {
 393:             foreach ($patterns as $pat) {
 394:                 if (preg_match($pat, $data) == 0) {
 395:                     $errmsg = '受容できない文字が含まれています';
 396:                     $res = FALSE;
 397:                     break;
 398:                 }
 399:             }
 400:         //排除パターンのチェック
 401:         } else if (!$exclusion && ($patterns !NULL)) {
 402:             foreach ($patterns as $pat) {
 403:                 if (preg_match($pat, $data> 0) {
 404:                     $errmsg = '受容できない文字が含まれています';
 405:                     $res = FALSE;
 406:                     break;
 407:                 }
 408:             }
 409:         }
 410:     }
 411:     return $res;
 412: }

ユーザー関数 validString は、与えられた文字列 $data が前述の入力要件を満たすかどうかバリデーションを行い、問題があった場合にはエラーメッセージ $errmag を戻す。

まず、引数 $minlen$maxlen の大小関係に矛盾がないかどうかをチェックする。
次に文字列バリデーションを行うが、下記の順序でバリデーションを行う。
  1. 値は存在するか(空文字ではないか)
  2. 最小長と最大長の範囲にあるか
  3. $exclusionが受容なら、配列 $patterns の要素1つずつにマッチングさせ、受容できない文字が含まれているかどうかチェックする
  4. $exclusionが排除なら、配列 $patterns の要素1つずつにマッチングさせ、受容できない文字が含まれているかどうかチェックする

解説:バリデーション付き文字列取得

 414: /**
 415:  * HTML FORMで指定したINPUTの内容を文字列として取り出す(バリデーション付き)
 416:  * filter_input()関数および $argv を参照する.
 417:  * @参考URL https://www.pahoo.org/e-soul/webtech/phpsec/phpsec-10-01.shtm
 418:  * @param   string $key       パラメータ名(省略不可)
 419:  * @param   string $errmsg    エラーメッセージを格納する(省略不可)
 420:  * @param   mixed  $def       初期値(省略時:空文字)
 421:  * @param   int    $minlen    入力可能な最小長(省略時=1)
 422:  * @param   int    $maxlen    入力可能な最大長(省略時=99)
 423:  * @param   bool   $exclusion $patternsはTRUE:受容/FALSE:排除(省略時=TRUE)
 424:  * @param   array  $patterns  受容/排除するパターンマッチ配列(省略時=NULL)
 425:  * @return  string 入力値
 426: */
 427: function getValidString($key, &$errmsg, $def='', $minlen=1, $maxlen=99, $exclusion=TRUE, $patterns=NULL) {
 428:     $data = (string)getParam($key, TRUE, $def);     //URLパラメータを取り出す
 429:     $str = trim($data);                             //先頭・末尾の空白文字を除く
 430:     $str = htmlspecialchars($str);                  //XSS対策
 431:     //文字列バリデーション
 432:     if ($str !'') {
 433:         $res = validString($data, $errmsg, $minlen, $maxlen, $exclusion, $patterns);
 434:     }
 435: 
 436:     return $str;
 437: }

ユーザー関数 getValidString は、HTML FORMに入力された文字列を取り出し、バリデーションを実施する。
HTML FORMからデータを取り出すユーザー関数 getParam については、「PHPでGET/POSTでフォームから値を受け取る」を参照いただきたい。
次に、取り出したデータの先頭、末尾の空白文字を組み込み関数  trim  によって取り除く。
さらに、組み込み関数  htmlspecialchars  を使って特殊文字をエスケープしておく。これはクロスサイトスクリプティング(XSS)対策のためである。
最後に、ユーザー関数 validString を使ってバリデーションを行う。
バリデーションの成否にかかわらず、入力データは、先頭・末尾の空白を取り除き、特殊文字をエスケープした状態で、戻り値とする。バリデーションで問題が発生した場合は $errmsg にエラーメッセージが入る仕組みである。

解説:メインプログラム

 194: // メイン・プログラム ======================================================
 195: //パラメータ
 196: $errmsg = '';
 197: $num = getValidNumber('num', $errmsg, '', CLASS_INT, NUM_MIN, NUM_MAX);
 198: 
 199: //リセット
 200: if (isButton('reset')) {
 201:     $num = '';
 202: }
 203: 
 204: //表示HTML作成
 205: $HtmlBody = makeCommonBody($num, $errmsg);
 206: 
 207: //表示
 208: echo $HtmlHeader;
 209: echo $HtmlBody;
 210: echo $HtmlFooter;

メインプログラムは簡素なものになった。
入力項目の各々について、ユーザー関数 getValidString を使って、バリデーション付きで数値取り出しを行い、それを画面に表示する。

参考サイト

(この項おわり)
header