[5] [DFN[[[JSONP]]]] は、[[JSON]] を [[JavaScript]] の[[関数]]の呼び出しの[[引数]]として記述する
([[関数名]]と[[括弧]]で括る) ことによって異なる[[起源]]に[[要求]]を送信し、
[[応答]]を受信する技法です。

[6] [[CORS]] が実装される前に[[同一起源ポリシー]]の抜け道として広く
[[Webアプリケーション]]によって利用されていました。
[[セキュリティーホール]]の温床であることもあり、現在ではあまり使われず、
[[CORS]] を利用するのが一般的となっています。

* 代替

[34] [[JSONP]] はほぼ[[セキュリティーホール]]であり、望ましくないと考えられています。

[39] 元々公開されていて漏洩してもまったく問題のないデータならセキュリティー問題にはなりませんが、
古い時代の裏技的な手法ですから、今時敢えて使うメリットはまったくありません。
問題があるかないか微妙な場合に判断を誤るリスクを考えると、
避けておくのが無難でしょう。

[40] [[Webブラウザー]]上で異なる[[起源]]のデータを取得するには、 
[[Fetch API]] を使うことができます。
[[Fetch API]] に対応していない [[Webブラウザー]]を想定するなら、
[[XHR]] を使うことができます。

[41] [[サーバー]]で異なる[[起源]]からのデータの取得に対応させるには、
[[CORS]] を使う必要があります。

* 構文

[35] [[JavaScript]] としても解釈可能な [[JSON]] データを [[JavaScript]]
[[関数]]呼び出しの形で表現したものと理解されています (が、
正式な規定はなく、厳密な定義は不明です)。

[FIG(railroad)[
= *
== [[空白]]
= [[関数名]]
= *
== [[空白]]
= [CODE[[[(]]]]
= [[JSONテキスト]]
= [CODE[[[)]]]]
= *
== [[空白]]
]FIG]

* [CODE(URI)@en[callback]] 引数

[10] [[JavaScript]] の[[関数名]]は、 [[URL]] の [[query]] 部
([CODE(MIME)@en[[[application/x-www-form-urlencoded]]]]) の
[DFN[[CODE(URI)@en[[[callback]]]]]] [[引数]]によって指定されたものを使うことが慣例となっています。

[11] [[JSONP]] は通常は (その用途からしても) [CODE(HTTP)@en[[[GET]]]]
で使うものですが、 [CODE(HTTP)@en[[[POST]]]] でも[[要求]]を受け付けている場合、
[[要求メッセージ]]の[[メッセージ本体]]の [CODE(MIME)@en[[[application/x-www-form-urlencoded]]]]
や [CODE(MIME)@en[[[multipart/form-data]]]] の [CODE(URI)@en[[[callback]]]]
[[引数]]でも指定を受け付けることがあります。

[12] [[関数名]]として任意の文字列を受け付けると、任意の [[JavaScript]]
コードの注入を許してしまうことになり、[[脆弱性]]となります。
[[JSONP]] の生成者は[[関数名]]として使える[[文字]]を限定し、
使えない[[文字]]が含まれていると[[エラー]]の[[応答]]を返したり、
既定の[[関数名]]で返したりする必要があります。実装によっては通常の [[JSON]]
を返すかもしれません。

;; [26] その範囲は実装により異なります。 [[JavaScript]] 側のライブラリーの仕様にもよります。
[[オブジェクト]]の階層の深いところにある[[関数]]を呼び出すために [CODE[.]] を、
[[配列]]内の[[関数]]を呼び出すために [CODE[''['']] や [CODE['']'']]
を認めることもあります。

[13] [CODE(URI)@en[[[callback]]]] [[引数]]やそれに相当する指定がない場合の動作は実装により異なります。
通常の [[JSON]] で返すことやエラーの[[応答]]を返すこともあれば、既定の[[関数名]]で返すこともあります。

* JSON との互換性

[14] [[JSON]] と [[JavaScript]] の[[オブジェクトリテラル]]は、
厳密には構文的に違いがあります。
[SEE[ [[JSON]] ]]

[45] 
ですからただ単に [[JSON]] の前後に[[関数名]]と[[括弧]]を連結するだけでは、
正しい [[JavaScript]] コードとならず、構文エラーになる可能性があります。

[15] これを避けるためには、 [[JSON]] の生成時、または [[JSON]] から [[JSONP]]
への変換時にいくつかの[[文字]]を[[エスケープ]]しなければなりません。
しかしそれをしていない実装もあり、ある種の[[脆弱性]]となるかもしれません。

;; [27] 実際に [[JSONP]] を使った [[Webアプリケーション]]には特定の[[文字]]を入力として与えると誤動作するものがあります。


;; [37] >>36 は、 [[JavaScript]] の[[文字列]]と [[JSON]] の[[文字列]]で使える[[文字]]の比較です。
[REFS[
- [36] [CITE@en[Compare character sets "$es5:DoubleStringCharacter-char" and "$ecma404:string-char"]] ([TIME[2015-06-12 22:47:17 +09:00]] 版) <http://chars.suikawiki.org/set/compare?expr1=%24es5%3ADoubleStringCharacter-char&expr2=%24ecma404%3Astring-char>
]REFS]

* MIME 型

[7] [[JSONP]] は [[JavaScript]] のコードですから、 [CODE(MIME)@en[[[text/javascript]]]]
が適切と考えられます。

[8] [[JSONP]] は [[JSON]] の変種であるとして、 [CODE(MIME)@en[[[application/json]]]]
が指定されることもあります。

;; [29] この指定は必ずしも正しいとは言えませんが、 [CODE(MIME)@en[[[application/json]]]]
を表示するビューアーの類の中には [[JSONP]] に対応しているものもあり、
有用なこともあるかもしれません。

[9] また [CODE(MIME)@en[[[text/html]]]] や
[CODE(MIME)@en[[[text/plain]]]]、 [CODE(HTTP)@en[[[Content-Type:]]]]
なしなどで送信されることもあります。

;; [28] [CODE(MIME)@en[[[text/html]]]] や [CODE(MIME)@en[[[text/plain]]]]
を使うのは明らかに誤りであり、避けるべきです。

[30] いずれにせよ、 [CODE(MIME)@en[[[charset]]]] [[引数]]が指定されることもあります。

* JSONP による情報漏洩

[16] [[JSONP]] に含まれる情報が秘密の情報である場合、第三者に情報が漏洩する危険性があります。

[EG[
[17] 例えばある会員制サイトが、アクセスした[[利用者]]自身の会員登録情報を返す [[JSONP]]
を用意しているとします。
悪意のあるサイトはその [[JSONP]] を取得して内容を自サイトに送信するコードを書くことで、
同サイトを閲覧した利用者が気づかないうちに会員制サイトの登録情報を取得できます。
]EG]

* JSONP による CSRF

[18] [[JSONP]] は異なる[[起源]]に[[要求]]を送信し、特定の処理を実行してその結果を得るために用いられることがあります。
その素朴な実装は、 [[CSRF]] [[脆弱性]]そのものです。

[19] 特に、 [[JSONP]] では特定の [[URL]] の [CODE(HTTP)@en[[[GET]]]] だけで処理を実行させられますから、
[[JavaScript]] の記述や実行が認められていなくても、 [CODE(HTMLe)@en[[[img]]]]
[[要素]]の [CODE(HTMLa)@en[[[src]]]] [[属性]]に指定するなどより簡易な方法で呼び出せてしまいますので、
より深刻な問題となる可能性があります。

[EG[
[20] 例えば [[URL]] に指定した文字列を[[掲示板]]に投稿できる [[JSONP]]
[[API]] が存在するとします。また、 [[JavaScript]] は記述できないものの、
末尾が [CODE[[[.jpg]]]] の [[URL]] を貼り付けると [CODE(HTMLe)@en[[[img]]]]
[[要素]]に変換される [[SNS]] があるとします。 [[JSONP]] の [[URL]] の末尾が
[CODE[[[.jpg]]]] になるようにし、 [[SNS]] に貼り付けると、その [[SNS]]
を表示するたびに、[[利用者]]が気づかないうちに自動的に[[掲示板]]に投稿されてしまいます。
]EG]

* 歴史

[21] [[JSONP]] は、2005年12月5日に [[Bob Ippolito]] が[[ブログ]]で提案 [SRC[>>4]]
しました。呼称の由来は「JSON with Padding」[SRC[>>4]] だといいます。

[23] なお [[query引数]]の名前は [CODE[jsonp]] [SRC[>>4]] とされていました。
後に普及することになる [CODE[callback]] はまだ使われていませんでした。

[REFS[
- [4] [CITE@en[Remote JSON - JSONP]]
([[Bob Ippolito]] 著, [TIME[2013-09-18 23:18:57 +09:00]] 版)
<http://bob.ippoli.to/archives/2005/12/05/remote-json-jsonp/>
-- [22] 元の [[URL]] は <http://bob.pythonmac.org/archives/2005/12/05/remote-json-jsonp>
だったようです。
- [3] [CITE@en[JSONP: JSON With Padding - Ajaxian]]
( ([TIME[2011-12-03 15:06:37 +09:00]] 版))
<http://ajaxian.com/archives/jsonp-json-with-padding>
- [1] [CITE[TAKESAKO @ Yet another Cybozu Labs: Operaでも非同期リクエストが並列処理できる img-JSONP]] ([TIME[2008-11-26 11:37:10 +09:00]] 版) <http://labs.cybozu.co.jp/blog/takesako/2007/06/opera_img-jsonp.html>
- [2] [CITE[JSONP が Opera だと非同期処理できない - 川o・-・)<2nd life]] ([TIME[2009-02-09 18:00:12 +09:00]] 版) <http://d.hatena.ne.jp/secondlife/20060906/1157515075>
]REFS]

[24] [[JSONP]] は[[デファクト標準]]であり、仕様書と呼べるものは存在していません。

[25] 個別の [[Web API]] の実装仕様の類を除けば、 [[JSONP]]
を採用している仕様には次のものがあります。
[FIG(short list)[
- [[revokeエンドポイント]] ([[RFC 7009]])
]FIG]

[FIG(quote)[
[FIGCAPTION[
[31] [CITE@en[Draft 1: OAuth Extension for Response Data Format - Draft 1]]
([TIME[2008-09-10 03:31:09 +09:00]] 版)
<http://oauth.googlecode.com/svn/spec/ext/response_data_format/1.0/drafts/1/oauth_response_data_format_ext.html#processing>
]FIGCAPTION]

> When requesting the response data format as "JSON", the Consumers MAY pass the 'xoauth_json_callback' with the JSONP callback and the Service Provider MUST return a JSONP callback using the value from the 'xoauth_json_callback' parameter.

]FIG]

[FIG(quote)[
[FIGCAPTION[
[10] [CITE@ja[Instagram API Endpoints • Instagram Developer Documentation]]
([TIME[2015-03-05 16:50:30 +09:00]] 版)
<https://instagram.com/developer/endpoints/#jsonp>
]FIGCAPTION]

> If you're writing an AJAX application, and you'd like to wrap our response with a callback, all you have to do is specify a callback parameter with any API call:

]FIG]


[FIG(quote)[
[FIGCAPTION[
[11] [CITE[Responses & Errors]]
([TIME[2015-03-05 17:39:32 +09:00]] 版)
<https://developer.foursquare.com/overview/responses>
]FIGCAPTION]

> To use JSONP, add a callback=XXX parameter to your request and we will respond with XXX(response). In the case of JSONP, we always return 200 (except in the case of 500’s) so the brower will allow the response to be handled by your code, but the true HTTP response code can be obtained from the code in the meta response.

]FIG]


[FIG(quote)[
[FIGCAPTION[
[32] [CITE[Response Formats]]
([TIME[2012-09-26 04:52:48 +09:00]] 版)
<http://pic.photobucket.com/dev_help/WebHelpPublic/Content/Getting%20Started/ResponseFormats.htm>
]FIGCAPTION]

> To use, set "format=jsonp", and specify a callback function name in the jsonp_callback parameter. The callback can be word characters and dot, supporting OO code.

]FIG]


[FIG(quote)[
[FIGCAPTION[
[33] [CITE[WooCommerce REST API Documentation v2]]
([TIME[2015-03-11 03:10:19 +09:00]] 版)
<http://woothemes.github.io/woocommerce-rest-api-docs/#jsonp-support>
]FIGCAPTION]

> You can specify the callback using the ?_jsonp parameter for GET requests to have the response wrapped in a JSON function:

]FIG]

[FIG(quote)[
[FIGCAPTION[
[38] [CITE@en[JSON callbacks | Alfresco Documentation]]
([TIME[2015-07-14 23:37:23 +09:00]] 版)
<http://docs.alfresco.com/4.2/concepts/ws-json-callbacks.html>
]FIGCAPTION]

> A callback is invoked by adding the following URL query parameter to the web script request:
> alf_callback=<function>

]FIG]


[FIG(quote)[
[FIGCAPTION[
[42] [CITE@en[Abusing JSONP with Rosetta Flash]]
( ([[Michele Spagnuolo]]著, [TIME[2017-03-10 12:48:46 +09:00]]))
<https://miki.it/blog/2014/7/8/abusing-jsonp-with-rosetta-flash/>
]FIGCAPTION]

> To be also protected from content sniffing attacks, prepend the reflected callback with /**/. This is exactly what Google, Facebook and GitHub are currently doing.

]FIG]


[43] [CITE[JSONP Sandboxを使ったXSS - 金利0無利息キャッシング – キャッシングできます - subtech]]
([TIME[2017-04-26 01:25:54 +09:00]])
<https://subtech.g.hatena.ne.jp/mala/20140120/1390227002>

[FIG(quote)[
[FIGCAPTION[
[44] [CITE[カーセンサー | APIリファレンス | リクルートWEBサービス]]
([TIME[2017-04-28 17:40:10 +09:00]])
<https://webservice.recruit.co.jp/carsensor/reference.html>
]FIGCAPTION]

> format	レスポンス形式	レスポンスをXMLかJSONかJSONPかを指定します。JSONPの場合、さらにパラメータ callback=コールバック関数名 を指定する事により、javascript側コールバック関数の名前を指定できます。	 	初期値:xml。xml または json または jsonp。

]FIG]
