◆敖 毅
(南京政治學院軍事信息管理系 上海 200433)
基于HTML5實現的微信公眾號支付漏洞分析與研究
◆敖 毅
(南京政治學院軍事信息管理系 上海 200433)
大量微信公眾號系統選用HTML5作為技術實現方案。基于HTML5的微信公眾號系統在實現微信支付功能時,如果支付流程設計或系統實現不合理,會造成篡改攻擊漏洞或短路攻擊漏洞,可以被惡意用戶用于發起中間人攻擊。本文模擬了利用兩種漏洞進行中間人攻擊的過程,分析了兩種漏洞的形成原理,并提出了防范漏洞的解決方案。
HTML5;微信支付;篡改攻擊漏洞;短路攻擊漏洞
騰訊公司的微信(WeChat)推出公眾號、服務號以及微信支付功能后,越來越多的商家通過推出微信公眾號,開辦微商城,提供微服務等形式依托微信開展商業活動。由于HTML5具有跨平臺、易實現、方便升級等優勢,大量微信公眾號系統選用HTML5技術作為技術實現方案。但是,在基于HTML5的微信公眾號系統在實現微信支付功能時,如果流程設計或系統實現不合理,容易造成支付漏洞,引來惡意用戶攻擊。
日前,筆者就處理了一起針對微信公眾號支付漏洞的惡意用戶攻擊事件,經估算,這次攻擊共造成了約15萬元人民幣的經濟損失。值得指出的是,本文提到的支付漏洞目前在大量微信公眾號中仍然存在,需引起高度重視。
經分析,這次攻擊是惡意用戶采用中間人攻擊的方法,通過網絡抓包的手段,利用某微信公眾號的微信支付流程中存在的篡改攻擊漏洞和短路攻擊漏洞,偽造網絡請求數據,最終實現了惡意消費。
下面將對比該微信公眾號的正常支付流程,模擬惡意用戶利用漏洞發起中間人攻擊的過程。
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的值來判斷用戶是否支付成功。但是由于存在第三種攻擊方法,仍然可能造成短路攻擊漏洞。
安全性作為微信支付的首要非功能特性,極大地影響著用戶的使用意愿和使用體驗,因此在設計和實現微信支付業務功能時,應該將安全性作為首要考慮的設計要點。騰訊公司在設計微信公眾號支付功能時,已經考慮到了安全的問題,并給出了參考的業務流程,如圖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⑦,供微信公眾號后臺系統定期下載對賬單,比對支付記錄,防止資金損失。
要避免在基于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