兵どもが、夢のあとさき

JavaScript 系の言語に興味を惹かれ、まったりと更新しております

JSONあれこれ(5)

まとめ

 ここまで引っ張ってきましたが、残念ながら「お天気Webサービス」から JSON データを javascriptライブラリで取得する方法は見つかりませんでした。但しサーバ側の設定次第では、取得する方法はありますので、下記にまとめます。

 

サーバとクライアントが同一ドメインの場合

 セキュリティの観点から同一ドメイン間がベストです。その場合は、javascriptライブラリ(jQuery等)からのアクセスであることをチェックします。

・クライアント例 (HTML + jQuery)

<script src="http://code.jquery.com/jquery-2.0.3.min.js"></script>

<!-- Internet Explorer 6~8で動作させる場合はバージョン1系を使用してください -->

<!-- <script src="http://code.jquery.com/jquery-1.10.2.min.js"></script> -->

<script>

 $(function () {

  $("#load").on("click", function () {

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

    $("#food").append(data[0].item);

   })

   .fail(function(jqXHR, textStatus, errorThrown) {

    alert('getJSON request failed! ' + ': ' + textStatus + ': ' + jqXHR.responseText);

  })

 });

});

</script>

</head>

<body><div><input type="button" value="読込" id="load"><p id="food"></p></div></body>

・サーバ側の例 (PHP)

// javascriptライブラリからかチェック

if (! isset($_SERVER['HTTP_X_REQUESTED_WITH']) || strtolower($_SERVER['HTTP_X_REQUESTED_WITH']) !== 'xmlhttprequest') {

 die(json_encode(array('status' => "不正な呼び出しです")));

 }

 

// データを準備します。

$value = array(

  1 => array('item' => '台湾ラーメン', 'price' => 580, 'orders' => 113),

  2 => array('item' => '台湾ラーメン(アメリカン)', 'price' => 580, 'orders' => 72),

  3 => array('item' => 'ニンニクチャーハン', 'price' => 630, 'orders' => 87),

);

 

// Content-Typeを「application/json」に設定します。(text/json ではない)

header("Content-Type: application/json; charset=UTF-8");

// Internet ExplorerがContent-Typeヘッダーを無視しないようにします

header("X-Content-Type-Options: nosniff");

 

// 可能な限りのエスケープを行ない、JSON形式で結果を返します。

echo json_encode(

  $value,

  JSON_HEX_TAG | JSON_HEX_APOS | JSON_HEX_QUOT | JSON_HEX_AMP

);

 

 

 最初の5行を記載することで、JSON データを javascriptライブラリ以外が取得することが不可能となります。但し、他のヘッダにも言えることですが、「X-Request-With」ヘッダを改竄するスクリプトなどではアクセスできてしまいますが。

 

 

クロスドメインの場合

 ① javascriptライブラリから、直接リモートサーバの URL にアクセス

  ① のクライアント例

...

<script>

 $(function () {

  $("#load").on("click", function () {

   $.getJSON("http://127.0.0.1/php-recipe/07/11/json.php", function (data) {

    $("#food").append(data[0].item);

   })

 ...

 ①のサーバ例

  クロスドメインの場合、「X-Requested-With」ヘッダはクライアントから送信されないので、チェックは無意味です。その代わり (ではないですが) Access-Control-Allow-Origin ヘッダを送信することで、クライアントはサーバの許可を得たと認識します。

// javascriptライブラリからかチェックは不可

// if (! isset($_SERVER['HTTP_X_REQUESTED_WITH']) || strtolower($_SERVER['HTTP_X_REQUESTED_WITH']) !== 'xmlhttprequest') {

// die(json_encode(array('status' => "不正な呼び出しです")));

// }

 

// データを準備します。

$value = array(

  1 => array('item' => '台湾ラーメン', 'price' => 580, 'orders' => 113),

  2 => array('item' => '台湾ラーメン(アメリカン)', 'price' => 580, 'orders' => 72),

  3 => array('item' => 'ニンニクチャーハン', 'price' => 630, 'orders' => 87),

);

 

header("Content-Type: application/json; charset=UTF-8");

header("X-Content-Type-Options: nosniff");

// javascripライブラリのクロスドメインアクセスを許可します。* は適切に

header("Access-Control-Allow-Origin: *");

 

echo json_encode(

  $value,

  JSON_HEX_TAG | JSON_HEX_APOS | JSON_HEX_QUOT | JSON_HEX_AMP

);

 

 

 

JSONP を使用する

 HTMLの<script src="...">タグの仕様の穴をついたHack。セキュリティに十分留意が必要。また、ブラウザ側からサーバ側で設定された「コールバック名」をパラメータで渡す必要があります。

 ②のクライアント例

...

<script>

 $(function () {

  $("#load").on("click", function () {

 // コールバック名を指定する。下記では「__callback」

   $.getJSON("http://127.0.0.1/php-recipe/07/11/json.php?__callback=?", function (data) {

    $("#food").append(data[0].item);

   })

 ...

 ② のサーバ例

...

//header("Content-Type: application/json; charset=UTF-8");

header("Content-Type: text/javascript; charset=UTF-8");  // Content-Typeを「text/javascript」に設定

 

header("X-Content-Type-Options: nosniff");

// JSONPは、javascript ライブラリからのコールではないので無関係。従ってサーバ側で Access-Control-Allow-Origin ヘッダの設定は無意味

// header("Access-Control-Allow-Origin: *");

 

# 可能な限りのエスケープを行ない、JSON形式で結果を返します。

$json = json_encode(

$value, JSON_HEX_TAG | JSON_HEX_APOS | JSON_HEX_QUOT | JSON_HEX_AMP

);

 

// クライアントから、GETパラメータでコールバック名が指定された場合の処理。

 

// クライアント側で「__callback=***」パラメータを指定した場合、$jsonp_callback に *** を格納。

$jsonp_callback = isset($_GET['__callback']) ? $_GET['__callback'] : null;

// 「__callback=***」パラメータを指定した GET メソッドの場合 ***( ... )」で括った JSON データを返す

echo ($jsonp_callback ? $jsonp_callback . '(' : '') . $json . ($jsonp_callback ? ')' : '');

 

 

 

 

・サーバ側のチェック

 異なるドメインの場合、javascriptライブラリは、x-request-with ヘッダを送信しない。したがって、同一ドメインの場合の様なチェックは不可。

 

JSONデータへのアクセスの手段と、ヘッダ名 (x-request-with ⇒ ブラウザの javascript ライブラリが送信、Access-Control-Allow-Origin ⇒ サーバ側で設定) の相関

方式 ヘッダ名 送信・影響の有無
1. jQuery(同一ドメイン) x-requested-with 送信される
Access-Control-Allow-Origin 影響されない
2. jQuery(クロスドメイン) x-requested-with 送信されない
Access-Control-Allow-Origin 影響される
3. JSONP x-requested-with 送信されない
Access-Control-Allow-Origin 影響されない(scriptのため)
4. 直接(HTTP/GET)で JSON データを呼び出す x-requested-with 送信されない
Access-Control-Allow-Origin 影響されない

 サーバ側で、Access-Control-Allow-Origin を設定すると、jQueryでリモートアクセスが可能となります。しかし、これは悪意ある第三者からの攻撃を受ける可能性もあるので、ファイアウォールAccess-Control-Allow-Origin の値を設定を適切に行うことが肝要です。同時に認証トークンを使用することも良い手段です。

 それから、XMLHttpRequest は、Lvele1, 2 とあるのですが、上記の情報はすべて Level 2 となります。(Opera ではクロスドメイン通信ができないとのうわさがちらほら見られます) 

 

 参考リンク集

HTTP access control(CORS) https://developer.mozilla.org/ja/docs/HTTP_access_control

https://gist.github.com/cowboy/1200708

機密情報を含むJSONには X-Content-Type-Options: nosniff をつけるべき http://d.hatena.ne.jp/hasegawayosuke/20130517/p1