JSONPJSON with Padding)是资料格式JSON的一种“使用模式”,可以让网页从别的网域获取资料。另一个解决这个问题的新方法是跨来源资源共享

JSONP
扩展名
.jsonp
互联网媒体类型
application/json-p
格式类型数据交换
扩展自JSONJavaScript
标准RFC 7159, RFC 4329
网站www.json-p.org
(失效链接,参见网页存档)

由于同源策略,一般来说位于server1.example.com的网页无法与 server2.example.com的伺服器沟通,而HTML<script>元素是一个例外。利用 <script>元素的这个开放策略,网页可以得到从其他来源动态产生的JSON资料,而这种使用模式就是所谓的 JSONP。用JSONP抓到的资料并不是JSON,而是任意的JavaScript,用 JavaScript解释器执行而不是用JSON解析器解析。

原理

为了理解这种模式的原理,先想像有一个回传JSON文件的URL,而JavaScript 程序可以用XMLHttpRequest跟这个URL要资料。假设我们的URL是 http://server2.example.com/RetrieveUser?UserId=xxx 。假设小明的UserId 是1823,且当浏览器透过URL传小明的UserId,也就是抓取http://server2.example.com/RetrieveUser?UserId=1823,得到:

{"Name": "小明", "Id": 1823, "Rank": 7}

这个JSON资料可能是依据传过去URL的查询参数动态产生的。

这个时候,把 <script>元素的src属性设成一个回传JSON的URL是可以想像的,这也代表从HTML页面透过script元素抓取 JSON是可能的。

然而,一份JSON文件并不是一个JavaScript程序。为了让浏览器可以在 <script>元素执行,从src里URL 回传的必须是可执行的JavaScript。在JSONP的使用模式里,该URL回传的是由函数调用包起来的动态生成JSON,这就是JSONP的“填充(padding)”或是“前辍(prefix)”的由来。

惯例上浏览器提供回调函数的名称当作送至伺服器的请求中命名查询参数的一部分,例如:

 <script type="text/javascript"
         src="http://server2.example.com/RetrieveUser?UserId=1823&jsonp=parseResponse">
 </script>

伺服器会在传给浏览器前将JSON数据填充到回调函数(parseResponse)中。浏览器得到的回应已不是单纯的资料叙述而是一个脚本,这样浏览器就可以调用该函数进行处理。在本例中,浏览器得到的是:

parseResponse({"Name": "小明", "Id": 1823, "Rank": 7});

填充

虽然这个填充(前辍)“通常”是浏览器执行背景中定义的某个回调函数,它也可以是变量赋值、if叙述或者是其他JavaScript叙述。JSONP要求(也就是使用JSONP模式的请求)的回应不是JSON也不被当作JSON解析——回传内容可以是任意的表达式,甚至不需要有任何的JSON,不过惯例上填充部分还是会触发函数调用的一小段JavaScript片段,而这个函数调用是作用在JSON格式的资料上的。

另一种说法—典型的JSONP就是把既有的JSON API用函数调用包起来以达到跨域存取的解法。

Script元素“注入”

为了要启动一个JSONP调用(或者说,使用这个模式),你需要一个script 元素。因此,浏览器必须为每一个JSONP要求加(或是重用)一个新的、有所需 src值的 <script>元素到HTML DOM里—或者说是“注入”这个元素。浏览器执行该元素,抓取src里的URL,并执行回传的 JavaScript。

也因为这样,JSONP被称作是一种“让用户利用script元素注入的方式绕开同源策略”的方法。

安全问题

使用远程网站的script标签会让远程网站得以注入任何的内容至网站里。如果远程的网站有JavaScript注入漏洞,原来的网站也会受到影响。

现在有一个正在进行项目在定义所谓的JSON-P严格安全子集,使浏览器可以对MIME类别是“application/json-p”请求做强制处理。如果回应不能被解析为严格的JSON-P,浏览器可以丢出一个错误或忽略整个回应。

跨站请求伪造

粗略的JSONP部署很容易受到跨站请求伪造(CSRF/XSRF)的攻击[1]。因为HTML <script>标签在浏览器里不遵守同源策略,恶意网页可以要求并获取属于其他网站的JSON资料。当用户正登录那个其他网站时,上述状况使得该恶意网站得以在恶意网站的环境下操作该JSON资料,可能泄漏用户的密码或是其他敏感资料。

只有在该JSON资料含有不该泄漏给第三方的隐密资料,且伺服器仅靠浏览器的同源策略阻挡不正常要求的时候这才会是问题。若伺服器自己决定要求的专有性,并只在要求正常的情况下输出资料则没有问题。只靠Cookie并不够决定要求是合法的,这很容易受到跨站请求伪造攻击。

历史

2005年夏天,乔治·詹姆提(George Jempty)建议在JSON前面选择性的加上变量赋值[2][3]。鲍勃·伊波利托(Bob Ippolito)于2005年12月提出了JSONP最原始的提案,其中填充部分已经是回调函数[4],而现在已有很多Web 2.0应用程式使用这份提案,像是Dojo Toolkit应用程式、Google Web Toolkit应用程式[5]Web服务

参考文献

  1. ^ Grossman, Jeremiah. Advanced Web Attack Techniques using GMail. 2006-01-27 [2009-07-03]. (原始内容存档于2013-01-17) (英语). 
  2. ^ eval'ing JSON. 2005-07-19 [2011-06-27]. (原始内容存档于2006-02-12) (英语). 
  3. ^ json : Message: Re: Comments. 2005-08-17 [2011-06-27]. (原始内容存档于2008-10-22) (英语). 
  4. ^ Remote JSON - JSONP. from __future__ import *. Bob.pythonmac.org. 2005-12-05 [2008-09-08]. (原始内容存档于2009-12-04) (英语). 
  5. ^ GWT Tutorial: How to Read Web Services Client-Side with JSONP. Google Web Toolkit Applications. February 6, 2008 [2009-07-03]. (原始内容存档于2013-01-17) (英语). 

外部链接