Qiita Teams that are logged in
You are not logged in to any team

Log in to Qiita Team
Community
OrganizationAdvent CalendarQiitadon (β)
Service
Qiita JobsQiita ZineQiita Blog
Help us understand the problem. What is going on with this article?

No.14 新卒未経験エンジニアがWebセキュリティの基本「SOP」を分かりやすく解説してみた〜第2章 JSONP〜

第1章の終わりにSOP(Same-Origin Policy)を回避する技術があると述べました。
具体的には今回扱う「JSONP」と次回扱う「CORS」という技術です。

先に述べておきますが、「JSONP」は今はあまり使われておりません。
CORSが実装される前にSOPの抜け道として広く利用されていましたが、セキィリティホールの温床であるため現在ではあまり使われておらず、CORSを利用するのが一般的です。

ただこういった歴史を知る事が大事だと昔の偉い人が言っていたので章分けしてまとめてみました。

目次

第1章 SOP
第2章 JSONP → 今回はココ!
第3章 CORS

前回の復習とクイズの答え

まずは前回解説した「SOP」の軽い復習とクイズの答えについて!

「SOP(Same-Origin Policy)」は日本語では「同一生成元ポリシー」と呼ばれており、異なるオリジンからのリソースへのアクセスを制限する規約です。
現在の主要なブラウザほぼ全てで実装されており、基本のセキュリティ技術となっている、という事を解説しました。

☆前回のクイズの答え☆
次のうち、同一オリジンと呼べるものはどの組み合わせでしょうか?

(A) http://example.com/test.php と http://example.com/index.html
(B) http://example.com/index.html と https://example.com
(C) http://www.example.com:80 と http://example:80
(D) http://example.com と http://example:8080

の答えですが、正解は・・・

(A)

です!!!
オリジンはスキーム+ホスト+ポートの組み合わせです。
(B) http://example.com/index.html と https://example.com → スキームが違う
(C) http://www.example.com:80 と http://example.co.jp:80 → ホストが違う
(D) http://example.com と http://example:8080 → ポートが違う

(A)は一見違うように見えますが、
スキーム:http
ホスト:example.com
ポート:80
が全て一致しています。
というわけで正解は(A)でした!

SOPの制限を回避する!?

それでは、本題に入ります!

SOPはブラウザに実装されている、セキュリティ技術です。
ブラウザに表示されるソースコードが対象になるわけですが、実は以下のHTML要素を使うとSOPの制限を回避出来ます。

・iframe要素
・image要素
・script要素

JSONPはこのうち、scriptを使った技術(裏技?)です。

JSONPって何?

JSONPはJSON with Paddingの略称です。(Padding:(本来は不要のものの)付け足しという意味)
2005年12月5日に Bob Ippolito がブログで提案したのが始まりだそうです。

JSONPは、SOPの制限を回避出来るscript要素を利用してクロスドメインでデータをやり取りする技術です。

どういうことだってばよ!!

サーバー側に非同期でデータを取得しにいく方法で有名なのはAjax(XMLHttpRequestを使った通信)だと思います。
ですが、別オリジンからの通信の場合、XMLHttpRequestはSOPに引っかかり、データの取得が出来ません。

さぁ、どうしましょう!?
というところでJSONPの出番なのです。
先ほどscript要素はSOPの制限を受けないと書きました。
具体的な方法は後で書きますが、要するに「データを取得する」のではなく「jsファイルを読みにいく」ように偽装するということです。
ブラウザは単にjsファイルを読みにいっただけだと勘違いしてSOPの制限から外れる、という仕組みです。
まさにPadding!!!

JSONとJSONPの違いは??

Paddingなのは分かったけどJSONとは何が違うかはっきりわからん!という方のために!
簡潔に言いましょう!
ズバリ!!

JSONPはスクリプトである!! (ドンッ

具体的に見てみましょう!

JSON
{ "key1" : "value1", "key2" : "value2" }

JSONP
callback({ "key1" : "value1", "key2" : "value2"})

……。
いやいや、callbak()で囲まれただけやん!!と思うかもしれません。
ですが、これこそJSONPの真骨頂なのです。
そしてこのcallback()こそがPadding足る所以です!!

詳しくは使い方を見てもらう方が早いので、実際に見てみましょう!!

JSONPはこう使う!

処理の流れ

ざっくりした処理の流れを説明します。

クライアント側でscriptのsrcで読み込むファイルを指定

読み込むファイルの中で、最終的にjavascriptとして結果を返す

クライアント側ではjavascriptのファイルが読み込まれただけと判断し、SOPの制限をすり抜けてデータ取得完了!

So Happy!!

ざっくりいうとこんな感じです。
では具体的に見ていきましょう。

クライアント側の実装

まず、クライアント側ではサーバーにデータを取得しにいく部分を実装します。

html
<script src="http://example.com/test.php?id=1&callback=callback"></script>

そして、返ってきたJSONPのデータを処理するために、srcの二個目のパラメータで指定したcallback関数の名前の関数を用意しておきます。

js
callback = function(JSONdata) {

  // 返ってきたデータをコネコネする処理

};

これでクライアント側の準備は出来ました!

サーバー側の実装

サーバー側では

test.php
// パラメータを受け取る処理
$id = $_GET['id'];

$callback = $_GET['callback'];

// 受け取ったパラメータでゴニョゴニョする処理
$result = gonyogonyo($id);

// json_encodeでJSON形式に直す処理
$json = json_encode($result);

// javascriptですよ、という宣言
header("Content-type: application/x-javascript");
echo $callback . "(" . $json . ")";

というような実装をします。
ここで最後のechoしている部分に注目してください!!
JSONをそのまま返すのではなく、JSONを引数にとった形で関数自体をjsデータとして返しています。
ここがJSONPの一番のポイントです。
JSONで返せないなら、返せる形にして返しちゃえ!
まさに郷に入っては郷に従う形式!

上記を実行!

まず、クライアント側のscriptタグが読まれ、srcで指定しているURLにデータを呼びにいきます。
URLの先で待ち構えているファイルがパラメータを受けとって処理を行い、最終的にjsとしてJSONを引数にとった形で関数を返します。
すると、、、

返ってきたcallback関数はscriptとして認識され、あらかじめ用意しておいたcallback関数が呼ばれるという仕組みです。

実際に処理の流れを追っていくと意外と単純ですが、これを思いついたBob Ippolitoさんはすごいなぁと思いました。

ちなみにscriptタグを

var script = document.createElement('script');
script.src = 'http://example.com/test.php?id=1&callback=callback';
document.head.appendChild(script);

こんな感じで、あとで要素追加出来るように動的にscriptタグを生成する形で書いておけば、クリックした時、マウスオーバーした時…etcに非同期での通信を実現出来ます!

まとめ

JSONPについて理解出来たでしょうか?
僕はJSONPから発想の転換ということを学びました笑

ただし、このJSONPはセキュリティホールの温床だけあって気をつけないといけません。
サーバー側が悪意のあるscriptタグを返してきたら途端に危険にさらされてしまいます。
信頼のおけるサービスの時にだけ利用するようにしましょう。

次章はその裏技的なJSONPを使わずにSOPをすり抜ける技術、CORSを解説します!
お楽しみに!!

kosuke_aizawa
新卒未経験エンジニアです。 2017年卒で新卒で入った会社でエンジニアとして働き始めました。 全く経験がない状態からのスタートで日々頑張っています。
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away