PHPプログラミング

ワークショップ

ひと言掲示板を作る(8)

投稿されたデータをサニタイズする

「ひと言掲示板」に投稿されたデータをサニタイズする機能を付けていきます。

この記事のポイント

  • サニタイズは入力データを無害化する大事な機能
  • PHPでは主にhtmlspecialchars関数を使ってサニタイズを行う

目次

不正なメッセージの投稿からシステムを守る

前回は書き込みがあった際に、「表示名」か「ひと言メッセージ」のいずれかが未入力だったらエラーメッセージを表示する機能を作りました。
今回は入力されたデータを無害化する「サニタイズ」という機能を付けていきます。

あまり馴染みがない言葉かもしれないので、サニタイズを行う理由を簡単にご紹介します。
例えば、掲示板で次のようにJavaScriptコードを書いて投稿するとします。

JavaScritpコードを入力する

ここで「書き込み」ボタンを押すと、このコードはそのまま「message.txt」に保存されます。

message.txt

'コード入力してみる','<script>alert("Hello");</script>','2019-03-25 19:46:13'

すると、次のように掲示板を読み込む度に「Hello」のアラートメッセージが表示されてしまいます。

不正なアラートが表示される例

これは掲示板を開いた全ての方がアラートメッセージを目にすることになります。
今回の例は単純なアラート文ですが、やろうと思えば外部サイトへ強制的に遷移することなどもできます。

もう1つ、よくある不正な入力例をご紹介します。
次のように、メッセージ欄に「 (シングルクォーテーション)」が混じっていたとします。

入力に「'」が含まれている例

明らかに怪しい入力ですよね。
こちらをそのまま登録すると、「message.txt」には次のように登録されます。

message.txt

'テスト太郎','不正なコード','シングルクォーテーションを打つとmessage.txtのフォーマットが崩れてしまう','2022-15-22 22:22:22'
'さらに追加で','メッセージを登録することもできる','2023-66-55 22:22:22'','2019-03-25 20:05:42'','2019-03-25 20:08:29'

フォーマットがおかしい状態になっていることが一目瞭然です。
このデータを読み込んで表示すると…

不正なデータを表示

1回の入力で2件分のデータが登録されたようになってしまいます。

前置きが長くなってしまいましたが、以上のような不正な入力を無くすための「入力データを無害化する前処理」を今回作っていきます。

「ひと言掲示板を作る」の概要については「ひと言掲示板を作る」をご覧ください。
デモはこちら

前回までに作成したコードはこちら:Github

サニタイズ機能を作る

PHPでサニタイズを行う場合はhtmlspecialchars関数を使います。
こちらの関数は記号をHTMLエンティティという形式に変換することでコードの無害化を行います。

コード例

htmlspecialchars( $data, ENT_QUOTES);

1つ目のパラメータにサニタイズしたい値、2つ目のパラメータには変換する記号を指定します。

サニタイズ用途で使用する場合は「ENT_QUOTES」を指定することが多いですが、この値は先ほど出てきた「‘ (シングルクォーテーション)」やよく登場する「 (ダブルクォーテーション)」をHTMLエンティティに変換します。

それでは掲示板にサニタイズ機能を加えていきましょう。
次の赤字のコードを追記してください。

コード例

<?php

-- 省略 --

// 変数の初期化
$now_date = null;
$data = null;
$file_handle = null;
$split_data = null;
$message = array();
$message_array = array();
$success_message = null;
$error_message = array();
$clean = array();


if( !empty($_POST['btn_submit']) ) {
	
	// 表示名の入力チェック
	if( empty($_POST['view_name']) ) {
		$error_message[] = '表示名を入力してください。';
	} else {
		$clean['view_name'] = htmlspecialchars( $_POST['view_name'], ENT_QUOTES);
	}
	
	// メッセージの入力チェック
	if( empty($_POST['message']) ) {
		$error_message[] = 'ひと言メッセージを入力してください。';
	} else {
		$clean['message'] = htmlspecialchars( $_POST['message'], ENT_QUOTES);
	}

	if( empty($error_message) ) {

		if( $file_handle = fopen( FILENAME, "a") ) {
	
		    // 書き込み日時を取得
			$now_date = date("Y-m-d H:i:s");
		
			// 書き込むデータを作成
			$data = "'".$_POST['view_name']."','".$_POST['message']."','".$now_date."'\n";
		
			// 書き込み
			fwrite( $file_handle, $data);
		
			// ファイルを閉じる
			fclose( $file_handle);
	
			$success_message = 'メッセージを書き込みました。';
		}
	}
}

-- 省略 --

$cleanにサニタイズした後の値が入るうようにしています。

サニタイズの処理自体は、バリデーションの部分で「空じゃなかった場合」に実行されるようif文にelse文を加える形で追記しました。
これで、もし未入力だった場合はエラーメッセージを作成し、入力があった場合のみサニタイズを行うというコードになります。
これを「表示名」「ひと言メッセージ」のいずれも行うように2箇所設定します。

さらに、入力されたデータの中に改行がある場合の対応をしていきます。
表示名については改行を削除し、メッセージはbr要素へ置き換えるコードをそれぞれ追記します。

コード例

<?php

-- 省略 --

if( !empty($_POST['btn_submit']) ) {
	
	// 表示名の入力チェック
	if( empty($_POST['view_name']) ) {
		$error_message[] = '表示名を入力してください。';
	} else {
		$clean['view_name'] = htmlspecialchars( $_POST['view_name'], ENT_QUOTES);
		$clean['view_name'] = preg_replace( '/\\r\\n|\\n|\\r/', '', $clean['view_name']);
	}
	
	// メッセージの入力チェック
	if( empty($_POST['message']) ) {
		$error_message[] = 'ひと言メッセージを入力してください。';
	} else {
		$clean['message'] = htmlspecialchars( $_POST['message'], ENT_QUOTES);
		$clean['message'] = preg_replace( '/\\r\\n|\\n|\\r/', '<br>', $clean['message']);
	}

	if( empty($error_message) ) {

		if( $file_handle = fopen( FILENAME, "a") ) {
	
		    // 書き込み日時を取得
			$now_date = date("Y-m-d H:i:s");
		
			// 書き込むデータを作成
			$data = "'".$clean['view_name']."','".$clean['message']."','".$now_date."'\n";
		
			// 書き込み
			fwrite( $file_handle, $data);
		
			// ファイルを閉じる
			fclose( $file_handle);
	
			$success_message = 'メッセージを書き込みました。';
		}
	}
}

-- 省略 --

改行コード「\r\n」「\n」「\r」をそれぞれ検索し、表示名の場合は空文字に置き換えて削除を行なっています。
もう1つのひと言メッセージでは「<br>」に置き換えました。

続いて、続くファイル書き込みのコードでもサニタイズした値を使用するように変更していきます。

コード例

<?php

-- 省略 --

if( !empty($_POST['btn_submit']) ) {
	
	// 表示名の入力チェック
	if( empty($_POST['view_name']) ) {
		$error_message[] = '表示名を入力してください。';
	} else {
		$clean['view_name'] = htmlspecialchars( $_POST['view_name'], ENT_QUOTES);
	}
	
	// メッセージの入力チェック
	if( empty($_POST['message']) ) {
		$error_message[] = 'ひと言メッセージを入力してください。';
	} else {
		$clean['message'] = htmlspecialchars( $_POST['message'], ENT_QUOTES);
	}

	if( empty($error_message) ) {

		if( $file_handle = fopen( FILENAME, "a") ) {
	
		    // 書き込み日時を取得
			$now_date = date("Y-m-d H:i:s");
		
			// 書き込むデータを作成
			$data = "'".$clean['view_name']."','".$clean['message']."','".$now_date."'\n";
		
			// 書き込み
			fwrite( $file_handle, $data);
		
			// ファイルを閉じる
			fclose( $file_handle);
	
			$success_message = 'メッセージを書き込みました。';
		}
	}
}

-- 省略 --

表示名の$_POST[‘view_name’]だったところを$clean[‘view_name’]に変更し、同じようにひと言メッセージの$_POST[‘message’]$clean[‘message’]へ置き換えます。
以上で、ファイルにはサニタイズされた値が書き込まれるようになりました。

サニタイズの効果を確認する

ここまで作成したサニタイズがどのような効果があるか確認していきましょう。
ページを再読み込みし、先ほどと同じJavaScriptのコードを入力して書き込んでみます。

もう一度同じJavaScriptコードを入力してみる

書き込みを行うと、次のように通常のテキストとして書き込まれました。

JavaScriptコードが通常の文字として書き込まれる

message.txt」を開いて、JavaScriptコードがどのようになっているか確認してみましょう。

message.txtに書き込まれたテキスト例

'JS太郎','&lt;script&gt;alert(&quot;Hello&quot;);&lt;/script&gt;','2019-03-25 22:12:51'

コードの記号がそのままではなく、ちゃんとHTMLエンティティに変換されて書き込まれていることが確認できました。
サニタイズの効果によってJavaScriptコードはただの文字列として登録されるようになっています。

続いて、もう1つの「’ (シングルクォーテーション)」も入力してみます。

シングルクォーテーションをもう一度入力してみる

この文字を書き込むと、次のように掲示板に表示されます。

掲示板に書き込まれた例

こちらも「message.txt」にどのように書き込まれたか確認してみましょう。

message.txtに書き込まれたテキスト例

'シングル太郎','不正なコード&#039;,&#039;シングルクォーテーションを打つとmessage.txtのフォーマットが崩れてしまう&#039;,&#039;&#039;,&#039;&#039;&#039;,&#039;&#039;&#039;,&#039;2022-15-22 22:22:22&#039;
&#039;さらに追加で&#039;,&#039;メッセージを登録することもできる&#039;,&#039;2023-66-55 22:22:22&#039;&#039;,2019-03-25 20:05:42&#039;','2019-03-25 23:08:09'

HTMLエンティティが多くごちゃごちゃしていますが、「」は「&#039;」に変換されていることがわかります。

以上、サニタイズがしっかり機能し、コードや記号が入力されても大丈夫なようになりました。

今回までは書き込まれたデータをテキストファイルに書き込んできましたが、次回からはデータベースに保存していくようにシステム改修をしていきます。

今回作成したコード:Github

こちらの記事は役に立ちましたか?

ありがとうございます。
もしよろしければ、あわせてフィードバックや要望などをご入力ください。

コメントありがとうございます!
運営の参考にさせていただきます。