Skip to content

JSONP 与 CSP 的探究

Published: at 08:32 AMSuggest Changes

问题探究

我在这儿了解了一下 JSONP:知乎 - JSONP 的工作原理是什么?

很简单,就是利用 <script> 标签没有跨域限制的“漏洞”(历史遗迹啊)来达到与第三方通讯的目的。 当需要通讯时,本站脚本创建一个 <script> 元素,地址指向第三方的 API 网址,形如:<script src="http://www.example.net/api?param1=1&param2=2"></script> 并提供一个回调函数来接收数据(函数名可约定,或通过地址参数传递)。 第三方产生的响应为 json 数据的包装(故称之为 jsonp,即 json padding),形如:callback({"name":"hax","gender":"Male"}) 这样浏览器会调用 callback 函数,并传递解析后 json 对象作为参数。本站脚本可在 callback 函数里处理所传入的数据。 补充:“历史遗迹”的意思就是,如果在今天重新设计的话,也许就不会允许这样简单的跨域了嘿,比如可能像 XHR 一样按照 CORS 规范要求服务器发送特定的 http 头。

但是这东西我确实没用过,为什么没用过?可能是我经历的项目场景没那么复杂吧?

找到了一个很好的示例代码

function jsonp(url, data = {}, cb = 'MusicJsonCallback') {
  data[cb] = cb;
  // 拼装参数
  const params = [];
  for (let key in data) {
    params.push(`${key}=${data[key]}`);
  }
  const script = document.createElement('script');
  script.src = url + '?' + params.join('&');
  script.defer = true;
  document.body.appendChild(script);
  return new Promise((resole, reject) => {
    try {
      window[cb] = function (data) {
        resole(data);
      };
    } catch (error) {
      reject(error);
    } finally {
      // 每次删除
      script.parentNode.removeChild(script);
    }
  });
}
jsonp('https://y.qq.com/download/download.js', { format: 'jsonp' }).then(
  (res) => {
    console.log(res);
  }
);

接下来的问题就和 jsonp 关系不大了,但是也引起了我的好奇

由此衍生的另一个疑问 - 什么是 CSP

但是,在知乎页面上我运行了代码,返回了一段错误,而在百度,QQ 页面上却能正常的运行,这让我产生了许些好奇心。

Refused to load the script 'https://y.qq.com/download/download.js?format=jsonp&MusicJsonCallback=MusicJsonCallback' because it violates the following Content Security Policy directive: "script-src 'self' blob: *.zhihu.com g.alicdn.com qzonestyle.gtimg.cn res.wx.qq.com open.mobile.qq.com 'unsafe-eval' unpkg.zhimg.com unicom.zhimg.com resource: captcha.gtimg.com captcha.guard.qcloud.com pagead2.googlesyndication.com cpro.baidustatic.com pos.baidu.com dup.baidustatic.com i.hao61.net 'nonce-33f925d3-2946-4645-9bdc-f24da7174277' hm.baidu.com zz.bdstatic.com b.bdstatic.com imgcache.qq.com vs-cdn.tencent-cloud.com gw.alipayobjects.com ssl.captcha.qq.com t.captcha.qq.com cstaticdun.126.net c.dun.163.com ac.dun.163.com/ acstatic-dun.126.net". Note that 'script-src-elem' was not explicitly set, so 'script-src' is used as a fallback.

jsonp @ VM87:12
(anonymous) @ VM87:26

这个报错很有意思,我也贴到群里进行了讨论,因为这个大多数是服务器端那边进行设置的。报错中我们可以看到关键词:Content Security Policy

查了一下 MDN:MDN - CSP

内容安全策略 (CSP) 是一个额外的安全层,用于检测并削弱某些特定类型的攻击,包括跨站脚本 (XSS (en-US)) 和数据注入攻击等。无论是数据盗取、网站内容污染还是散发恶意软件,这些攻击都是主要的手段。

CSP 被设计成完全向后兼容(除 CSP2 在向后兼容有明确提及的不一致; 更多细节查看这里 章节 1.1)。不支持 CSP 的浏览器也能与实现了 CSP 的服务器正常合作,反之亦然:不支持 CSP 的浏览器只会忽略它,如常运行,默认为网页内容使用标准的同源策略。如果网站不提供 CSP 头部,浏览器也使用标准的同源策略。

为使 CSP 可用,你需要配置你的网络服务器返回 Content-Security-Policy HTTP 头部 ( 有时你会看到一些关于 X-Content-Security-Policy 头部的提法,那是旧版本,你无须再如此指定它)。

除此之外,<meta> 元素也可以被用来配置该策略,例如

<meta
  http-equiv="Content-Security-Policy"
  content="default-src 'self'; img-src https://*; child-src 'none';"
/>

验证一下知乎的 CSP

原来如此,我猜知乎设置了CSP

打开 F12,在 html 代码里面搜了一下 Content-Security-Policy 关键词,没有找到,于是我在 network 中点开第一个请求体,看了一下 Header

如下,发现了关键词 content-security-policy 后面跟了一堆网址。y.qq.com 不在白名单中,所以说这个 Header 可能是后端加上去的。

cache-control: private, must-revalidate, no-cache, no-store, max-age=0
content-encoding: gzip
content-security-policy: default-src * blob:; img-src * data: blob: resource: t.captcha.qq.com cstaticdun.126.net necaptcha.nosdn.127.net; connect-src * wss: blob: resource:; frame-src 'self' *.zhihu.com mailto: tel: weixin: *.vzuu.com mo.m.taobao.com getpocket.com note.youdao.com safari-extension://com.evernote.safari.clipper-Q79WDW8YH9 zhihujs: captcha.guard.qcloud.com pos.baidu.com dup.baidustatic.com openapi.baidu.com wappass.baidu.com passport.baidu.com *.cme.qcloud.com vs-cdn.tencent-cloud.com t.captcha.qq.com c.dun.163.com; script-src 'self' blob: *.zhihu.com g.alicdn.com qzonestyle.gtimg.cn res.wx.qq.com open.mobile.qq.com 'unsafe-eval' unpkg.zhimg.com unicom.zhimg.com resource: captcha.gtimg.com captcha.guard.qcloud.com pagead2.googlesyndication.com cpro.baidustatic.com pos.baidu.com dup.baidustatic.com i.hao61.net 'nonce-ecc767c2-89be-4f35-8d93-68033f709c9d' hm.baidu.com zz.bdstatic.com b.bdstatic.com imgcache.qq.com vs-cdn.tencent-cloud.com gw.alipayobjects.com ssl.captcha.qq.com t.captcha.qq.com cstaticdun.126.net c.dun.163.com ac.dun.163.com/ acstatic-dun.126.net; style-src 'self' 'unsafe-inline' *.zhihu.com unicom.zhimg.com resource: captcha.gtimg.com ssl.captcha.qq.com t.captcha.qq.com cstaticdun.126.net c.dun.163.com ac.dun.163.com/ acstatic-dun.126.net
content-type: text/html; charset=utf-8

后记

其实最大的收获可能不是了解了 jsonp,而是上面谈到的 CSP

备注

https://developer.mozilla.org/zh-CN/docs/Web/HTTP/CORS


Previous Post
JS 柯里化的思考
Next Post
使用 setTimeout 和 clearTimeout 模拟 setInterval 和 clearInterval