PHPの配列をAjaxでJSON形式のデータとして読み取るときのコツ JSONハイジャック・XSS対策

PHP逆引きレシピ 第2版 (PROGRAMMER’S RECiPE)を読んでいると、P.618でJSON形式のデータをAjax通信で読み取るコードが2つのPHPファイルを使って紹介されています。この2つのファイルは、REST APIを理解する上で重要な事項であると感じましたので解説をいたします。なおこのブログは以下のサイト・ページを参考にしています。

合わせてこの記事では2つのPHPファイルでGitHubにアップしている、ajax.phpjson.phpに基づいて説明を進めてまいります。

目次

ajax.phpについて

JSON形式のデータが記述されているPHPファイルに直接アクセスしても、そのデータを取得することはできません。必ずXMLHttpRequestオブジェクトを用いてAjax通信で取得する必要があります。

XMLHttpRequestオブジェクトを用いてAjax通信を行うと、X-Requested-Withという特別のヘッダがついたJavaScriptファイル以外は読めなくなるため、JSONハイジャック対策に有効です。

XMLHttpRequestオブジェクトについて

したがってまずjsファイルでXMLHttpRequestオブジェクト(とそのデータ)が必要となります。ajax.phpを用いて説明すると以下のブロックです。

[php]
$.getJSON("json.php", function(data){

});
[/php]

XMLHttpRequestオブジェクトのデータを取得・表示

$.getJSONメソッドは、戻り値としてXMLHttpRequestオブジェクトを返します。第一引数は読み込むページを指定し、第二引数は通信成功時のコールバック関数となります。その関数の返り値は、

[php]
for (var i in data) {

}
[/php]

jsondata_reading_ajax_php_1_2

のブロックの内容となります。inputタグのボタンを押すと、画像のようにメニュー・価格・注文数が表示されます。

json.phpについて

json.phpはajax.phpより簡単そうに見えますが、前提となる知識がないと意味がよく分からないと思います(かく言う自分も意味が分かりませんでしたが)。

$_SERVER[‘HTTP_X_REQUESTED_WITH’]について

まず4行目の$_SERVER[‘HTTP_X_REQUESTED_WITH’]は、PHPの公式ドキュメントにおいて定義済みの変数として存在しません。存在していないため、5行目で定数の名前が’XMLHttpRequest’となることもありません。

https://e-yota.com/wp-content/uploads/2017/10/jsondata_reading_ajax_php_2.png

したがってjson.phpに直接アクセスしても、上記の画像のように”ひらがなと漢字を表す正規表現が返されプログラムの実行が停止されます。配列のデータにアクセスすることはできません。

XSS対策としてのheader()関数の意味

[php]
header("Content-Type: application/json; charset=UTF-8");
header("X-Content-Type-Options: nosniff"); // XSS対策
[/php]

1つ目のheader()関数は、引数の中に”application/json”とすることでjson形式のデータしか読みませんという宣言です。これでtextタイプのデータは読めなくなります。

2つ目のheader()関数は、引数の中に”application/json”とすることでJSONをJavaScriptで解釈不能、あるいは実行時に解釈不能とするという意味です。2つのheader関数を用いることでXSS対策となります。

解釈不能の実例

jsondata_reading_ajax_php_3

[php]echo json_encode(
$value,
JSON_HEX_TAG | JSON_HEX_APOS | JSON_HEX_QUOT | JSON_HEX_AMP
);[/php]

json_encodeのオプションでエスケープしていますが、このエスケープがなくても、配列値である$valueは正規表現で返されて読み取りがかなり困難であることが分かります。

asdora-taiga

この記事が気に入ったら
フォローしてね!

よかったらシェアしてね!
  • URLをコピーしました!
  • URLをコピーしました!
目次