基于HTML5实现的微信公众号支付漏洞分析与研究
2016-02-06◆敖毅
◆敖 毅
(南京政治学院军事信息管理系 上海 200433)
基于HTML5实现的微信公众号支付漏洞分析与研究
◆敖 毅
(南京政治学院军事信息管理系 上海 200433)
大量微信公众号系统选用HTML5作为技术实现方案。基于HTML5的微信公众号系统在实现微信支付功能时,如果支付流程设计或系统实现不合理,会造成篡改攻击漏洞或短路攻击漏洞,可以被恶意用户用于发起中间人攻击。本文模拟了利用两种漏洞进行中间人攻击的过程,分析了两种漏洞的形成原理,并提出了防范漏洞的解决方案。
HTML5;微信支付;篡改攻击漏洞;短路攻击漏洞
0 引言
腾讯公司的微信(WeChat)推出公众号、服务号以及微信支付功能后,越来越多的商家通过推出微信公众号,开办微商城,提供微服务等形式依托微信开展商业活动。由于HTML5具有跨平台、易实现、方便升级等优势,大量微信公众号系统选用HTML5技术作为技术实现方案。但是,在基于HTML5的微信公众号系统在实现微信支付功能时,如果流程设计或系统实现不合理,容易造成支付漏洞,引来恶意用户攻击。
日前,笔者就处理了一起针对微信公众号支付漏洞的恶意用户攻击事件,经估算,这次攻击共造成了约15万元人民币的经济损失。值得指出的是,本文提到的支付漏洞目前在大量微信公众号中仍然存在,需引起高度重视。
1 微信公众号微信支付的中间人攻击①模拟
经分析,这次攻击是恶意用户采用中间人攻击的方法,通过网络抓包的手段,利用某微信公众号的微信支付流程中存在的篡改攻击漏洞和短路攻击漏洞,伪造网络请求数据,最终实现了恶意消费。
下面将对比该微信公众号的正常支付流程,模拟恶意用户利用漏洞发起中间人攻击的过程。
1.1 微信公众号正常支付流程
图1 微信公众号微信支付流程
该微信公众号设计的正常支付流程共分七步,如图1所示。第一步,通过微信客户端关注微信公众号;第二步,点击微信公众号底部菜单栏中的“购买会员”菜单项;第三步,跳转到“永久VIP会员”购买页面,输入手机号;第四步,点击【立即购买】按钮后,跳转到确认购买页面;第五步,在确认购买页面点击【微信支付】按钮,弹出微信支付对话框;第六步,在微信支付对话框中输入支付密码,进行支付操作;第七步,如果支付成功,会收到消息通知,告知用户购买VIP会员已成功,可以开始使用VIP客户的功能;如果支付失败,将返回第二步。
1.2 恶意用户进行中间人攻击过程模拟
在上述微信公众号的微信支付流程中,因为系统设计和实现不合理,留下了篡改攻击漏洞和短路攻击漏洞,可以被恶意用户利用进行中间人攻击。
(1)利用篡改攻击漏洞实施中间人攻击
篡改攻击漏洞,是由于系统的接口设计或实现存在缺陷造成的漏洞,导致恶意用户可以通过篡改网络交互数据等方法,达到攻击系统的目的。上述微信公众号支付流程的第四步的设计就存在篡改攻击漏洞,可以被利用实施中间人攻击。
当用户点击【立即购买】按钮,跳转到确认购买页面时,如果使用网络抓包工具Fiddle Web Debugger抓取此时微信公众号提交到后端系统的URL请求(如图 2所示),可以在抓取的URL请求中,发现有“totalprice”字段,该字段的值为“12”。从该字段的字面意思推测,其值可能是微信公众号向后台系统提交的订单总价金额。经比对,这个值确实与支付流程的第五步微信支付中显示的价格一致。这意味着如果修改此字段的值,则微信支付系统生成的支付凭证中的金额值也会相应地修改。
图2 篡改攻击模漏洞示例
我们尝试在Fiddle中将此值修改为0.01(也即1分钱),提交该微信公众号后台系统后,果然在随后的第五步中,微信支付页面显示的值为0.01元。我们随即通过微信支付了1分钱,该微信公众号即反回支付成功消息,并取得了VIP用户的所有特权。这意味着恶意用户只需支付1分钱,即可享受本来需要12元钱的服务了。
经验证,篡改攻击漏洞也存在于某些电商平台和团购网站的微信公众号中。
(2)利用短路攻击漏洞实施中间人攻击
短路攻击漏洞,是由于系统的流程设计或实现存在缺陷造成的漏洞,利用该漏洞,恶意用户可以更改脚本代码,造成代码中的条件判断语句“短路”,只执行条件分支中的某部分语句,达到攻击系统的目的。上述微信公众号支付流程的第五步的设计存在短路攻击漏洞,可以被利用实施中间人攻击。
当用户点击【微信支付】按钮,发起微信支付请求时,我们可以使用Fiddle截获后台服务器返回的页面代码,如代码 1所示。分析此段代码发现,该微信公众号是通过判断微信支付接口返回的res对象的err_msg属性值来判断用户是否支付成功。如果支付成功,将调用后台Web API(payover),提交用户支付成功的信息,并修改用户状态。
代码1 存在短路攻击漏洞的JavaScript源代码
因此,如果能够在这里让程序始终执行支付成功的逻辑,就能达到修改用户状态的目的。至少有三种方法可以实现这一目的。
第一种方法是将if表达式中的逻辑判断符号“==”修改为赋值符号“=”,达到使if判断语句“短路”的目的。如下面代码所示。
根据Javascript语言规范,赋值语句返回的是所赋的值(这里即是“get_brand_wcpay_request:ok”);而if语句只有当被判断的值为false、0、“”、undefined、null、NaN时才为假值,其余条件皆为真值。因此这样修改后,这个判断语句就变为了一个恒真式。也意味着,不论用户是否实际支付成功,都将执行if语句中真值分支的代码。
第二种方法是直接删除if语句和后面的else语句块,使得无论用户微信支付是否成功,或者是否实际支付,都会调用后台Web API,提交用户支付成功的信息,并修改用户状态。
第三种方法是在if判断语句前增加一行代码(如代码 2中红框所示),通过修改res的err_msg属性值,达到使if判断语句“短路”的目的。这是因为,Javascript对象的属性值可以在任意时刻修改,甚至当对象不存在某个属性时,也可以通过赋值的方式,动态地为其增加属性。
代码2 通过修改javas对象属性实施短路攻击
经验证,上面三种方法都可以对该微信公众号实施短路漏洞攻击。值得注意的是,有的微信公众号系统考虑到了第一、二种攻击方法,因此采取将微信支付接口返回的res对象直接提交到后台系统,在后台系统中再根据res.err_msg的值来判断用户是否支付成功。但是由于存在第三种攻击方法,仍然可能造成短路攻击漏洞。
2 微信公众号微信支付漏洞剖析
安全性作为微信支付的首要非功能特性,极大地影响着用户的使用意愿和使用体验,因此在设计和实现微信支付业务功能时,应该将安全性作为首要考虑的设计要点。腾讯公司在设计微信公众号支付功能时,已经考虑到了安全的问题,并给出了参考的业务流程,如图3所示。
图3 微信公众号微信支付流程图②
对照微信公众号微信支付流程来分析篡改攻击漏洞和短路攻击漏洞,可以看出,造成这两个漏洞的最主要原因是在实现微信支付功能时,没有按照正确的流程进行设计和实现。
2.1 篡改攻击漏洞分析
按照微信支付流程,当微信支付用户通过点击微信支付消息或扫描二维码,准备在微信客户端浏览器中打开用于支付的HTML5页面时(图 3第2步),将向微信公众号后台系统提交生成支付订单的请求;微信公众号后台系统收到请求后,生成客户订单(图 3第4步);再调用微信支付统一下单API,向微信支付系统提交生成预付单请求,微信支付系统在生成预付单后,返回预付单信息(prepay_id)给微信公众号后台系统(图 3第5步);微信公众号后台系统根据返回的预付单信息(prepay_id)生成JSAPI页面调用的参数并签名(paySign)(图 3第6步)后生成HTML5页面返回用户,等待用户点击进行支付。
导致产生篡改攻击漏洞的主要原因是在上述流程中,“请求生成支付账单”和“生成用户订单”两步间的信息交互设计存在问题。
微信公众号后台系统在生成用于支付的消息或二维码时,通常会使用一个标识号作为前后端交互的依据,这个标识号可以是用户的订单号或产品的产品号。当用户点击消息或扫描二维码请求生成支付订单时,后台系统应根据这个标识号生成对应的订单,包括计算订单的应付金额。
但是部分以提供会员、充值、点卡、外卖等产品的微信公众号系统,由于产品形态单一,而且通常一个产品就是一个订单,因此没有设计独立的标识号,而是将应付金额等敏感数据包含在了前后端交互的信息中;后台系统在收到前端系统的支付请求后,也没有进行必要的验证,就直接将请求数据生成了预付单,这样就会留下篡改攻击漏洞。
2.2 短路攻击漏洞分析
从微信支付流程看,从用户确认支付(图 3第9步)到微信客户端展示支付结果(图 3第15步),共有六个步骤。这其中包含了两个并行任务,用于返回用户支付结果:一是通过异步的方式通知微信公众号后台系统支付结果(图 3第10步),微信公众号后台系统在处理完支付结果后再将处理结果通知微信支付服务(图 3中第11步);二是通过JS API直接返回支付结果给微信客户端(图 3第12步)。在公众号支付开发者文档中明确说明,这两个并行处理的任务并“不保证遵循严格的时序,JS API返回值作为触发商户网页跳转的标志,但商户后台应该只在收到微信后台的支付成功回调通知后,才做真正的支付成功的处理”③。因此,当微信支付的JS API返回支付结果后,要验证用户是否实际支付成功,还应向微信公众号的后台系统提交查询用户实际支付结果的请求(图 3第13步),微信公众号的后台系统通过调用微信支付查询API,查询实际支付结果(图 3第14步),再根据查询结果执行支付后个性化页面展示。
短路攻击漏洞造成损失的直接原因是恶意用户绕过用户支付结果验证流程,将用户状态修改为已正常支付的状态。系统实现中的两个错误共同导致了短路攻击漏洞:一是错误地使用微信客户端收到的JS API返回结果作为验证用户是否支付的依据;二是在前端的Javascript中调用修改用户支付状态的代码。
分析基于HTML5的微信公众号的微信支付页面代码,发现在调用微信支付接口时,都参照了公众号支付开发者文档中提供的示例,如代码3所示。
从代码 3中可以看出,基于HTML5的微信公众号实现微信支付,是通过使用微信内置浏览器提供的内置对象WeixinJSBridge调用getBrandWCPayRequest方法来调用微信支付接口。在该方法的回调函数中,可以根据微信支付接口返回的结果res.err_msg(可能的返回值如表 1所示)进行下一步处理。但是在公众号支付开发者文档中,明确说明:“res.err_msg将在用户支付成功后返回ok,但并不保证它绝对可靠”④。不能保证其绝对可靠的原因如前文所述,就是因为Javascript的动态语言特性。
代码3 JSAPI微信支付模板代码
表1 微信公众号支付接口返回值㈤
因此,导致短路攻击漏洞的一个原因是直接将微信客户端收到的JS API返回结果作为验证用户是否支付成功的依据,没有严格按照微信支付业务流程要求,在后台进行支付结果验证,致使验证流程可能被绕过。
导致短路攻击漏洞的另一个原因是将修改用户支付状态的代码放在了前端Javascript代码中(如前文代码 1所示)。由于可以绕过支付结果验证环节,这就导致既不能保证业务流程的完整性,也不能保证用户状态修改合法。实际上,按照微信支付流程,应该是在微信公众号的后台系统收到微信支付系统的异步通知后才能修改用户支付状态。为了保证能够将支付结果成功通知到微信公众号后台系统,微信支付系统还采取了以下四种措施:
(1)微信支付系统通过一定的策略⑥定期重新发起通知,尽可能提高通知的成功率,但需要注意的是,微信支付系统并不保证通知最终能成功。
(2)当微信公众号后台系统收到异步通知进行处理时,应检查对应业务数据的状态,判断该通知是否已经处理过。
(3)由于异步通知回调地址不能进行签名,微信公众号后台系统必须对支付结果通知的内容要做签名验证,防止数据泄漏导致出现“假通知”,造成资金损失。
(4)微信支付系统提供了“下载对账单”API⑦,供微信公众号后台系统定期下载对账单,比对支付记录,防止资金损失。
3 微信公众号微信支付漏洞的解决方案
要避免在基于HTML5的微信公众号的微信支付模块中留下篡改攻击漏洞或短路攻击漏洞,需要从系统的设计、实现、运维等多个方面着手。
3.1 前后端模块之间的交互应是系统设计时关注的重点
要保证微信支付的安全,系统设计是关键。在系统设计时,应严格按照公众号支付开发者文档中提供的业务流程,对前后端模块的职责、交互接口、传输协议等进行正确设计。
(1)前后端模块职责分明。前端模块只负责页面的展示和部分的输入校验功能,而订单处理、预付单生成、支付结果处理、账单状态查询等关键功能应放在后端模块。
(2)交互接口最小化。在接口设计时,可遵循RESTful风格,按照HTTP协议规则设计API接口;在传输内容上,只传递订单号或产品号等必要信息,特别是在前端模块向后端模块发起PUT、POST请求时,应避免将价格、数量、总价等可能影响到支付结果的敏感信息包含在内。
(3)选择HTTPS协议作为传输协议。与HTTP协议相比,HTTPS协议具有更好的保密特性,可以防止在传输过程中被获取或篡改信息。
3.2 在系统开发过程中应层层设防,避免留下漏洞
由于程序员个体经验的不足,或者是团队没有采用恰当的软件开发方法,在系统实现阶段往往容易造成微信支付功能出现漏洞。通过层层设防的方式,有助于减少系统开发过程中造成的漏洞。
(1)对前端代码进行必要的分离、混淆和压缩。在实现前端模块时,要充分掌握Javascript语言规范和正确的使用方式,避免因误用留下漏洞;将HTML5代码和Javascript代码分离,有利于对Javascript代码进行独立的管理;借助诸如YUI Compressor、Closure Compiler、uglifyjs等工具软件对Javascript脚本文件进行混淆和压缩,可以增加恶意用户逆向分析和篡改程序的难度。
(2)后端模块必须对请求参数进行校验。对于前端模块传递来的任何请求,后端模块都必须对请求所携带的参数进行校验,包括有效性校验、合规性校验、业务规则校验等,保证请求的合法性。校验通过后才能进行后续处理,以杜绝恶意用户在参数传递过程中实施篡改攻击。
(3)采用验收测试、代码审查等软件工程方法,减少人为因素造成漏洞。验收测试可以模拟软件系统在实际使用场景中的不同运行流程,验证软件的功能质量特性;代码审查通过团队成员间交叉审查源代码,达到弥补个体经验不足,减少系统漏洞,提高代码质量的目的。
3.3 在运维中应通过定期核查保障资金安全
除了在系统的设计和实现时需要考虑周全,小心谨慎外,在运维过程中加强定期核查也是保障资金安全的重要手段。定期核查可以通过对账检查和日志审计等方式进行。
(1)在系统中应提供“对账检查”功能,通过微信支付系统提供的“下载对账单”接口,定期或手动下载对账单。运维人员通过自动或人工比对提交到微信支付系统的订单及金额与微信公众号系统中记录的订单和金额,发现是否存在异常订单支付记录。
(2)系统应提供完备的运行日志,可以对支付过程中的运行情况进行跟踪记录。当发现遭受恶意用户攻击或者资金异常情况时,运维人员可以通过分析系统运行日志,查找问题出现的时间和位置,分析恶意用户的攻击方式,及时修补漏洞。
[1]【微信支付】公众号支付开发者文档[EB/OL].https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=7_1#.2016.
[2]【微信支付】公众号支付开发者文档[EB/OL].https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=7_4#.2016.
[3]【微信支付】公众号支付开发者文档[EB/OL].https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=7_7&index =6.2016.
[4]【微信支付】公众号支付开发者文档[EB/OL].https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=9_6.2016.
[5]【微信支付】公众号支付开发者文档[EB/OL].https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=9_7.2016.
注释:
①中间人攻击(Man-in-the-middle attack,缩写:MITM)是指攻击者与通讯的两端分别创建独立的联系,并交换其所收到的数据,使通讯的两端认为他们正在通过一个私密的连接与对方直接对话,但事实上整个会话都被攻击者完全控制。在中间人攻击中,攻击者可以拦截通讯双方的通话并插入新的内容。来源:维基百科,https://zh.wikipedia.org/wiki/中间人攻击,查询于:2016.06.03
②【微信支付】公众号支付开发者文档,来源:https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=7_4#,查询于:2016.05.18
③【微信支付】公众号支付开发者文档,来源:https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=7_1,查询于:2016.05.18
④【微信支付】公众号支付开发者文档,来源:https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=7_7&index =6,查询于:2016.05.20
⑤【微信支付】公众号支付开发者文档,来源:https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=7_7&index =6,查询于:2016.05.18
⑥【微信支付】公众号支付开发者文档,来源:https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=9_7,查询于:2016.05.20
⑦【微信支付】公众号支付开发者文档,来源:https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=9_6,查询于:2016.05.20