百度360必应搜狗淘宝本站头条
当前位置:网站首页 > 技术文章 > 正文

JSONP安全攻防技术

zhezhongyun 2025-04-29 06:52 69 浏览

关于JSONP

JSONP全称是JSON with Padding,是基于JSON格式的为解决跨域请求资源而产生的解决方案。它的基本原理是利用HTML的元素标签,远程调用JSON文件来实现数据传递。如果想在a.com域下获取b.com下的JSON数据(getUsers.JSON):

那么可以首先通过JSONP的“Padding”这个getUsers.JSON输出为:

对于实际应用过程中callback的名称,后台实现是动态输出的。上面例子用PHP实现如下:

然后在a.com使用<script>进行远程调用,在jQuery中可以直接这样调用:

然而,安全问题一直伴随着业务发展,JSONP同样带来各种安全问题。本文就将梳理JSONP实现过程中的安全攻防。

JSON劫持

JSON劫持又称“JSON Hijacking”,2008年国外安全研究人员开始提到由JSONP带来的风险。这个问题属于CSRF(Cross-site request forgery跨站请求伪造)攻击范畴,当某网站通过JSONP的方式跨域(一般为子域)传递用户认证后的敏感信息时,攻击者可以构造恶意的JSONP调用页面,诱导被攻击者访问,以达到截取用户敏感信息的目的。一个典型的JSON Hijacking攻击代码如下:

这是乌云上报告的一个攻击案例(WooYun-2012-11284),当被攻击者登录360网站并访问该网页时,个人隐私数据(如用户名、邮箱等)可能被攻击者劫持。

虽然这种攻击已出现多年,但目前在大的门户网站还普遍存在,而且由于安全意识薄弱,许多企业并未意识到这一问题的重要性。

不过许多甲方公司开始重视此类安全问题,着手研究解决方案。方案之一就是验证JSON文件调用的来源(Referer)。它主要利用<script>远程加载JSON文件时会发送Referer的机制,在网站输出JSON数据时,判断Referer是否包含在白名单内。这个方法理论上可行,但具体实现过程容易出现两种逻辑问题。

【Referer过滤(正则)不严谨】

【空Referer】

在很多情况下,开发者部署过滤Referer来源时,忽视了空Referer的过滤。一般情况下浏览器直接访问某URL是不带Referer的,因此很多防御部署允许空Referer。恰恰由于这个疏忽,导致了整个防御系统奔溃,因为在通过跨协议调用JavaScript时,发送的HTTP请求中Referer为空。跨协议调用的一个简单例子如下:

代码中我们使用<iframe>调用JavaScript伪协议来实现空Referer调用JSON文件。

另外一种手段是通过随机token防御,这项技术在腾讯的网站上应用较多,例如通过
http://r.qzone.qq.com/cgi-bin/tfriend/friend_show_qqfriends.cgi?uin=[QQ号]&g_tk=[随机token]输出JSON。这个方案是有效的,但同样存在防御实现不严谨的问题。例如此token可通过以下方式暴力破解:

当然,这些都是单纯针对“JSON劫持”本身展开的攻防战。在现实中,许多漏洞是相互配合实现突破的。例如上面提到的限制Referer+部署随机token实现都很完美,理论上无懈可击,但只要该网站存在XSS漏洞,就可能让你的防御体系瞬间崩溃!这里顺带一提,以上是一些通用实现“JSON劫持”的方法,但现实中,某些浏览器的一些特有处理机制(如CSS加载、错误信息显示等),也能引发类似“JSON劫持”(攻击对象不一定是JSON)的攻击。

callback可定义导致的安全问题

为了方便前端开发调用,输出一般都是可定义的,前文提到的PHP实现的代码:

就是因为可定义callback名输出点,导致了各种安全问题。当然严格来说,里面提到的具体数据输出也是可以利用的,只是本文重点强调callback这个输出点。

【Content-Type与XSS漏洞】

在JSON刚出现时,大多数开发者还没有良好的编码习惯。输出JSON时,没有严格定义Content-Type(Content-Type: application/json),再加上callback输出点没有进行过滤,直接导致了一个典型的XSS漏洞,上文演示的getUsers.php就存在这个问题:

对Content-Type来说,早期还有一部分人比较喜欢使用application/javascript,而这个头在IE等浏览器下一样可以解析HTML导致XSS漏洞。对于这种类型的漏洞,防御主要从以下两点部署。

a. 严格定义Content-Type: application/json

这样的防御机制导致了浏览器不解析恶意插入的XSS代码(直接访问提示文件下载)。但凡事都有例外,在IE的进化过程中就曾出现通过一些技巧,可以绕过Content-Type防御解析HTML的事件,例如在IE6、7等版本请求的URL文件后加一个/x.html就可以解析HTML(
http://127.0.0.1/getUsers.php/x.html?callback=<script>alert(/xss/)</script>),具体可参考
http://hi.baidu.com/hi_heige/item/f1ecde01c4af3ed61ef04646。

b. 过滤callback以及JSON数据输出

这种机制是比较传统的攻防思维,对输出点进行XSS过滤——又是一个看上去很完美的解决方案,但往往都“事与愿违”。2011年,一个utf7-BOM就复活了n个XSS漏洞。这种攻击方式主要存在于IE中(新版IE已修复),当我们在callback点输出+/v8这样的utf7-BOM时,IE浏览器会把当前执行的编码认为是utf7,因此我们通过utf7提交的XSS代码会被自动解码并执行。例如:

其中:

URLdecode为:

+/v8为utf7-BOM,其后为我们注入通过utf-7编码后的XSS代码:

<htm><body><script>alert(1);

</script></body></htm>。

利用utf7-BOM是一种非常有代表性的通用方法,除了升级IE进行防御,开发者也可直接指定Content-Type的编码(Content-Type: application/json; charset=utf-8)。然而尽管如此,仍有绕过这些防御措施的可能。

上文提到的a、b两种防御缺一都可能出现问题,那么我们使用“a+b方案”是否就万无一失了呢?一切皆有可能,我们拭目以待。

其他文件格式(Content-Type)与JSON

【MHTML与JSONP】

2011年,IE曾出现一个通过MHTML协议解析跨域的漏洞:MHTML Mime-Formatted Request Vulnerability(CVE-2011-0096,
https://technet.microsoft.com/library/security/ms11-026)。而当时的一种常见攻击方式就是利用JSONP调用机制中的callback函数名输出点:

它充分利用了callback输出点直接输出MHTML文件格式,然后利用<iframe>调用MHTML标签解析并执行HTML及JavaScript代码,这也是一个通用性的XSS漏洞(UXSS),随后微软紧急推出了解决方案及漏洞补丁程序。在微软推出安全补丁前,这个漏洞已影响Google等大型网站,当时Google为了防御这类攻击启用的措施是,在JSON输出callback时,在文件开头增加了多个换行符,让远程MHTML调用时解析失败。

在攻击角度来说,它充分利用了计算机体系的各种文件格式识别机制。在这个思维的引导下,之后还出现过多次由文件格式加载带来的安全问题,例如CSS文件格式加载导致的类“JSON劫持”,JavaScript加载及各种文件格式编码带来的安全问题等。历史进程往往会出现各种惊人的相似,JSONP与文件格式的传奇还在上演。

【FLASH与JSONP】

该来的终究会来,只是没想到相似的场景上演得这么快。就在最近的一次Flash安全更新(security bulletin APSB14-17)中修复了一个安全漏洞:

These updates include additional validation checks to ensure that Flash Player rejects malicious content from vulnerable JSONP callback APIs (CVE-2014-4671).

这个漏洞因影响了Google、Facebook、Tumblr等大网站而备受媒体关注。其攻击技术与JSONP的callback点息息相关。这个问题主要发生在HTML通过<embed>、<object>调用远程Flash文件时,会直接忽视Content-Type,而JSONP的callback输出一般都在文件开头,那么完全可以通过callback点输出一个swf的文件,然后远程HTML调用并运行swf文件。例如:

这是早在2012年就提出的通过callback输出的swf文件流,其实际效果是在被攻击的网站上存放了一个恶意swf文件,HTML远程调用这个swf文件,可直接导致CSRF攻击。

细心的朋友可能发现,上面代码callback输出的swf文件流中存在各种特殊字符,通过前文提到的“b. 过滤callback以及JSON数据输出”方案可以直接拦截,对于Goolge、Facebook这样久经考验的大网站来说,防御不在话下。

在Flash更新“security bulletin APSB14-17”发布后,该漏洞发现者给出了漏洞细节,其中一个亮点,就是作者实现了一个纯alphanumeric输出swf文件的方法:

因此,对于纯alphanumeric输出来说,那些针对XSS的过滤显然可以直接忽略,这个漏洞也证明前文我们提到的“a+b方案”能被直接绕过。

防御

通过上面的攻防对抗演练,很多开发者可能会感觉有点悲剧,各种防御机制好像都有办法绕过。这里我想到一个真理:没有绝对的安全。那么我们防御的意义在哪里呢?我认为防御的意义就是虽然没办法让程序最安全(绝对安全),但可以让它更安全。提高攻击者的技术成本和门槛是安全防御的一个主要并重要的思路。回到具体的JSONP防御上,可以总结如下几点。

  1. 严格安全地实现CSRF方式调用JSON文件:限制Referer、部署一次性token等。
  2. 严格安装JSON格式标准输出Content-Type及编码(Content-Type: application/json; charset=utf-8)。
  3. 严格过滤callback函数名及JSON数据的输出。
  4. 严格限制对JSONP输出callback函数名的长度(例如防御上面Flash输出的方法)。
  5. 其他一些比较“猥琐”的方法:例如在callback输出前加入其他字符(如/**/、换行符等)这样不影响JSON文件加载,又能一定程度预防其他文件格式的输出。Gmail就曾使用AJAX的方式获取JSON,通过在输出JSON之前加入while(1);这样的代码防止JavaScript远程调用。

作者简介:周景平,知道创宇首席安全官,曾是一名拥有5年多从业经验的外科医生,2012年加入知道创宇,转行安全。网络ID:Superhei(黑哥)

本文选自程序员电子版2014年8月刊,该期更多文章请查看这里。2000年创刊至今所有文章目录请查看程序员封面秀。欢迎订阅程序员电子版(含iPad版、Android版、PDF版)。

相关推荐

Python入门学习记录之一:变量_python怎么用变量

写这个,主要是对自己学习python知识的一个总结,也是加深自己的印象。变量(英文:variable),也叫标识符。在python中,变量的命名规则有以下三点:>变量名只能包含字母、数字和下划线...

python变量命名规则——来自小白的总结

python是一个动态编译类编程语言,所以程序在运行前不需要如C语言的先行编译动作,因此也只有在程序运行过程中才能发现程序的问题。基于此,python的变量就有一定的命名规范。python作为当前热门...

Python入门学习教程:第 2 章 变量与数据类型

2.1什么是变量?在编程中,变量就像一个存放数据的容器,它可以存储各种信息,并且这些信息可以被读取和修改。想象一下,变量就如同我们生活中的盒子,你可以把东西放进去,也可以随时拿出来看看,甚至可以换成...

绘制学术论文中的“三线表”具体指导

在科研过程中,大家用到最多的可能就是“三线表”。“三线表”,一般主要由三条横线构成,当然在变量名栏里也可以拆分单元格,出现更多的线。更重要的是,“三线表”也是一种数据记录规范,以“三线表”形式记录的数...

Python基础语法知识--变量和数据类型

学习Python中的变量和数据类型至关重要,因为它们构成了Python编程的基石。以下是帮助您了解Python中的变量和数据类型的分步指南:1.变量:变量在Python中用于存储数据值。它们充...

一文搞懂 Python 中的所有标点符号

反引号`无任何作用。传说Python3中它被移除是因为和单引号字符'太相似。波浪号~(按位取反符号)~被称为取反或补码运算符。它放在我们想要取反的对象前面。如果放在一个整数n...

Python变量类型和运算符_python中变量的含义

别再被小名词坑哭了:Python新手常犯的那些隐蔽错误,我用同事的真实bug拆给你看我记得有一次和同事张姐一起追查一个看似随机崩溃的脚本,最后发现罪魁祸首竟然是她把变量命名成了list。说实话...

从零开始:深入剖析 Spring Boot3 中配置文件的加载顺序

在当今的互联网软件开发领域,SpringBoot无疑是最为热门和广泛应用的框架之一。它以其强大的功能、便捷的开发体验,极大地提升了开发效率,成为众多开发者构建Web应用程序的首选。而在Spr...

Python中下划线 ‘_’ 的用法,你知道几种

Python中下划线()是一个有特殊含义和用途的符号,它可以用来表示以下几种情况:1在解释器中,下划线(_)表示上一个表达式的值,可以用来进行快速计算或测试。例如:>>>2+...

解锁Shell编程:变量_shell $变量

引言:开启Shell编程大门Shell作为用户与Linux内核之间的桥梁,为我们提供了强大的命令行交互方式。它不仅能执行简单的文件操作、进程管理,还能通过编写脚本实现复杂的自动化任务。无论是...

一文学会Python的变量命名规则!_python的变量命名有哪些要求

目录1.变量的命名原则3.内置函数尽量不要做变量4.删除变量和垃圾回收机制5.结语1.变量的命名原则①由英文字母、_(下划线)、或中文开头②变量名称只能由英文字母、数字、下画线或中文字所组成。③英文字...

更可靠的Rust-语法篇-区分语句/表达式,略览if/loop/while/for

src/main.rs://函数定义fnadd(a:i32,b:i32)->i32{a+b//末尾表达式}fnmain(){leta:i3...

C++第五课:变量的命名规则_c++中变量的命名规则

变量的命名不是想怎么起就怎么起的,而是有一套固定的规则的。具体规则:1.名字要合法:变量名必须是由字母、数字或下划线组成。例如:a,a1,a_1。2.开头不能是数字。例如:可以a1,但不能起1a。3....

Rust编程-核心篇-不安全编程_rust安全性

Unsafe的必要性Rust的所有权系统和类型系统为我们提供了强大的安全保障,但在某些情况下,我们需要突破这些限制来:与C代码交互实现底层系统编程优化性能关键代码实现某些编译器无法验证的安全操作Rus...

探秘 Python 内存管理:背后的神奇机制

在编程的世界里,内存管理就如同幕后的精密操控者,确保程序的高效运行。Python作为一种广泛使用的编程语言,其内存管理机制既巧妙又复杂,为开发者们提供了便利的同时,也展现了强大的底层控制能力。一、P...