1
本文作者: 知情人士 | 2017-02-16 15:29 |
雷鋒網(wǎng)按:本文由2015年支付寶特別獎(jiǎng)得主丁羽、黎桐辛、韋韜共同編寫,其中丁羽和韋韜來(lái)自百度x-lab,黎桐辛來(lái)自北京大學(xué)。文章來(lái)自微信公眾號(hào)“百度安全實(shí)驗(yàn)室”,雷鋒網(wǎng)獲授權(quán)發(fā)布。
引言
電子商務(wù)已經(jīng)成為當(dāng)今互聯(lián)網(wǎng)中重要的組成部分。同時(shí)“錢包”類服務(wù)成為了電子商務(wù)的關(guān)鍵組件。越來(lái)越多的電商服務(wù)通過(guò)“錢包”服務(wù)來(lái)進(jìn)行支付?!板X包”提供的接口簡(jiǎn)單易用,任何一個(gè)開(kāi)發(fā)者都可以快速的將“錢包”服務(wù)供應(yīng)商提供的SDK整合進(jìn)自己的App中,提供App內(nèi)的快速支付手段。目前國(guó)內(nèi)最大的“錢包”類服務(wù)包括:支付寶錢包、微信錢包、百度錢包等,各有長(zhǎng)處。
因此,支付過(guò)程的安全問(wèn)題也成為了關(guān)鍵。如果錢包服務(wù)出現(xiàn)了安全漏洞,那么很可能會(huì)影響到成千上萬(wàn)的商家,數(shù)十億的現(xiàn)金流,后果往往非常嚴(yán)重。對(duì)于支付平臺(tái)的安全研究自從其誕生之日起就開(kāi)始了。經(jīng)過(guò)數(shù)次血的教訓(xùn),幾大支付平臺(tái)均修正了數(shù)個(gè)大大小小的漏洞,反復(fù)改進(jìn)設(shè)計(jì)和實(shí)現(xiàn)。現(xiàn)今的支付平臺(tái)已經(jīng)相當(dāng)安全可靠。
本文對(duì)借助支付平臺(tái)進(jìn)行的支付流程進(jìn)行分析,對(duì)支付平臺(tái)的安全進(jìn)行討論。同時(shí)我們展示了商戶端和支付平臺(tái)出現(xiàn)過(guò)的幾個(gè)嚴(yán)重安全漏洞。攻擊者通過(guò)這幾個(gè)安全漏洞可以達(dá)到修改金額、任意購(gòu)買等效果,使得支付平臺(tái)和商戶的利益收到巨大損失。本文中涉及的安全漏洞均已得到修正。
一、支付流程概述
目前市場(chǎng)上的大大小小的第三方支付平臺(tái)有許多家,規(guī)模有大有小但是從整個(gè)支付流程上看這些支付平臺(tái)的同質(zhì)化程度很高。只有少數(shù)很大的支付平臺(tái)做出了比較大的改動(dòng),進(jìn)一步增強(qiáng)了安全性。這一節(jié)我們對(duì)支付平臺(tái)普遍采用的支付協(xié)議做下介紹。
在支付流程中主要包括四個(gè)實(shí)體:商戶前端、商家服務(wù)器、錢包模塊、以及錢包服務(wù)器。商戶前端是用戶直接交互的部分,用戶通過(guò)操作商戶前端來(lái)購(gòu)買商品等。這里商戶前端不僅限于移動(dòng)設(shè)備上安裝的App,也可以是商家提供的網(wǎng)站。商家服務(wù)器是商戶的后端,提供相應(yīng)的服務(wù)。錢包模塊是進(jìn)行支付的“中介”,如支付寶、微信錢包、百度錢包等都有對(duì)應(yīng)的支付模塊。
通常支付模塊可以是用戶App中的一個(gè)SDK,與商戶App一起安裝在用戶的手機(jī)上。支付模塊也可以以web的形式提供服務(wù)。錢包服務(wù)器負(fù)責(zé)處理支付請(qǐng)求,并通知商家服務(wù)器支付結(jié)果。錢包服務(wù)需要與錢包模塊交互以獲得訂單信息,同時(shí)需要與相關(guān)的機(jī)構(gòu)(例如銀行)進(jìn)行扣款處理,最后與商家服務(wù)和錢包模塊進(jìn)行通訊以通知支付結(jié)果。因此整個(gè)支付過(guò)程是一個(gè)"四方通訊"的過(guò)程。一筆成功交易的后面通常包含四方之間數(shù)十次的復(fù)雜交互,任何一個(gè)環(huán)節(jié)的安全隱患都會(huì)擴(kuò)大整個(gè)支付過(guò)程的攻擊面。多個(gè)安全隱患的疊加可能使得攻擊者可以進(jìn)行訂單篡改等攻擊,使用戶、商戶、錢包服務(wù)的利益收到損失。
一個(gè)完整的經(jīng)由支付平臺(tái)的支付過(guò)程通??梢詣澐譃橐韵聨撞剑?/p>
1.商戶前端對(duì)錢包初始化
2.商戶前端與商戶服務(wù)交互,創(chuàng)建訂單
3.商戶前端經(jīng)由錢包,對(duì)訂單進(jìn)行支付
4.錢包與錢包服務(wù)器通訊,錢包服務(wù)器進(jìn)行扣款,通知錢包和商戶服務(wù)器支付結(jié)果。
5.商戶前端從錢包的支付返回中獲取結(jié)果,并和商戶服務(wù)器進(jìn)行確認(rèn)。
6.支付完成,商戶獲得對(duì)應(yīng)款項(xiàng)。
下面我們來(lái)介紹支付的四方通訊的詳細(xì)過(guò)程。
圖1 支付過(guò)程的四方通訊
1. 商戶前端與錢包模塊進(jìn)行通訊,對(duì)錢包進(jìn)行初始化。在這個(gè)過(guò)程中通常需要用戶登錄自己的錢包賬戶,進(jìn)行必要的認(rèn)證等操作。
2. 錢包模塊初始化完成,返回到用戶App中。
3. 商戶前端與商家服務(wù)器通訊,建立訂單。帶有的參數(shù)通常包括用戶信息、需要購(gòu)買的商品id、時(shí)間戳、商品金額、錢包類型等。
4. 商家將訂單參數(shù)返回給商戶前端,帶有的參數(shù)通常包括:訂單號(hào)(order_no)、支付金額(total_fee)、支付結(jié)果通知地址(notify_url)、消息完整性簽名(sign)等。此時(shí)也有另一種實(shí)現(xiàn),即虛線部分的4'。在4'中,商戶服務(wù)將大量參數(shù)直接通知給錢包服務(wù),而只返回給用戶一個(gè)簡(jiǎn)短的消息,包括一個(gè)錢包服務(wù)返回給商家服務(wù)的交易事務(wù)id。此后用戶只需通過(guò)錢包對(duì)這個(gè)交易事務(wù)id進(jìn)行支付即可。
5. 商戶前端對(duì)商戶返回的信息進(jìn)行包裝,發(fā)送給錢包模塊。
6. 錢包模塊與錢包服務(wù)進(jìn)行交互,帶有的參數(shù)包括用戶的錢包session等信息,以及步驟4中返回的訂單信息等。
7. 錢包服務(wù)在進(jìn)行必要的扣款處理(如與銀行進(jìn)行交互)和風(fēng)控處理后,將支付結(jié)果以同步通知7(1)和異步通知7(2)的方式分別通知錢包模塊和商戶服務(wù)。在支付結(jié)果通知中一般包括支付結(jié)果、支付金額、支付的訂單號(hào)、支付事務(wù)的流水號(hào)、商戶號(hào),以及消息完整性簽名等??蛇x的參數(shù)包括商品信息、字符集/編碼方式、幣種信息等。商戶服務(wù)器在獲得7(2)的異步通知后,也需要驗(yàn)證其消息的完整性,并對(duì)訂單狀態(tài)進(jìn)行對(duì)應(yīng)的更新(支付成功或者支付異常)。
8. 錢包模塊接收到錢包服務(wù)返回的同步通知后,進(jìn)行必要的完整性驗(yàn)證,并將支付結(jié)果返回給商戶前端。
9. 商戶前端在得到錢包模塊返回的消息,驗(yàn)證其完整性。若獲得的是支付成功的消息,則需要向商戶服務(wù)器發(fā)起請(qǐng)求,驗(yàn)證支付結(jié)果(查賬)。商戶服務(wù)器此時(shí)即可進(jìn)行訂單支付完成后的處理。
在整個(gè)支付過(guò)程中,各個(gè)消息的完整性是最為關(guān)鍵的。如果消息完整性保護(hù)存在漏洞,攻擊者即可發(fā)起修改金額、修改訂單號(hào)、構(gòu)造虛假訂單等攻擊。作為消息完整性保護(hù)的關(guān)鍵——簽名機(jī)制,是支付協(xié)議的核心之一。目前應(yīng)用在第三方支付平臺(tái)中的簽名機(jī)制可以分成兩種:基于非對(duì)稱密碼體制的簽名,和基于散列函數(shù)的簽名?;诜菍?duì)稱密碼的簽名機(jī)制只在極少數(shù)支付平臺(tái)上得到實(shí)現(xiàn)。而基于散列函數(shù)的簽名則基本被所有平臺(tái)應(yīng)用或曾經(jīng)應(yīng)用過(guò)。
在基于非對(duì)稱密碼體制的簽名機(jī)制中,每個(gè)商戶和支付平臺(tái)都生成自己的一套公鑰-私鑰對(duì),并互相告知對(duì)方自己的公鑰。在進(jìn)行支付時(shí),發(fā)送消息方使用自己的私鑰對(duì)消息(或消息的散列值)進(jìn)行簽名,接受消息方使用對(duì)方(發(fā)送消息方)的公鑰進(jìn)行驗(yàn)簽。這個(gè)方法的安全性來(lái)自于非對(duì)稱密碼體制的安全性,例如RSA的大質(zhì)數(shù)分解難度,或計(jì)算橢圓曲線離散對(duì)數(shù)的難度。在這個(gè)機(jī)制中,最關(guān)鍵的是支付平臺(tái)的私鑰。攻擊者一旦獲得支付平臺(tái)私鑰,就可以對(duì)任意消息進(jìn)行簽名,從而欺騙商戶。其次是商戶的私鑰。攻擊者獲得商戶私鑰后,結(jié)合商戶App的其他漏洞,就可以進(jìn)行各類攻擊。
基于散列函數(shù)的簽名機(jī)制安全性來(lái)自于哈希函數(shù)的不可逆性。在支付平臺(tái)中此類簽名機(jī)制幾乎得到所有平臺(tái)的使用。每個(gè)商戶與支付平臺(tái)預(yù)先共享一個(gè)密鑰,以及協(xié)商一個(gè)hash函數(shù)(例如MD5或者SHA1)。在發(fā)送消息時(shí),每個(gè)商戶活支付平臺(tái)在消息中附加上與對(duì)方共享的這個(gè)密鑰,再對(duì)整體進(jìn)行hash運(yùn)算,得到簽名值,與原始消息合并作為最后的消息發(fā)送給對(duì)方。在接受消息時(shí)則將簽名值剝離,將剩下的部分與密鑰組合后進(jìn)行hash運(yùn)算,檢驗(yàn)生成的散列值是否與發(fā)來(lái)的簽名值相符。在這個(gè)體制中,最關(guān)鍵的無(wú)疑就是商戶和支付平臺(tái)預(yù)先共享的這個(gè)密鑰了。一旦這個(gè)密鑰泄露,攻擊者既可以模仿商戶給支付平臺(tái)發(fā)信息,又可以模仿支付平臺(tái)給商戶發(fā)信息,以進(jìn)行各類欺騙和攻擊,危害無(wú)窮。在目前支付平臺(tái)所采用的簽名機(jī)制中,以基于MD5的簽名最為常見(jiàn)。
此外,以上提到的私鑰泄露,其實(shí)等同于令商戶/支付平臺(tái)對(duì)指定字符串進(jìn)行簽名的能力。如果攻擊者可以在很少代價(jià)的情況下對(duì)指定的“畸形”/“惡意”串進(jìn)行簽名的話,也相當(dāng)于獲得了任意簽名的能力,從而以很小的代價(jià)發(fā)起攻擊。
值得注意的是,基于MD5的簽名機(jī)制并不僅限于"支付協(xié)議"中使用,在相當(dāng)多類型的通信中均得到大量應(yīng)用。而MD5如果不嚴(yán)格限定輸入并使用正確的模式將會(huì)是相當(dāng)脆弱的,我們可以構(gòu)造通用的簽名碰撞攻擊,將在后繼文章中進(jìn)行介紹。
同時(shí),支付結(jié)果的同步、異步通知?jiǎng)t是最容易受到攻擊的點(diǎn)。支付結(jié)果的同步通知可以在端上被攻擊者篡改(例如使用代理或者Xposed)。對(duì)于異步通知,由于異步通知經(jīng)常缺乏可靠的對(duì)發(fā)送者的身份鑒定,因此攻擊者可以自行構(gòu)造異步通知來(lái)通知商戶服務(wù)已支付成功,從而完成攻擊。此外,異步通知的地址往往是可變的,以參數(shù)的形式傳遞給支付平臺(tái)。攻擊者一旦獲得了修改異步通知地址的能力,也會(huì)對(duì)支付過(guò)程的安全性造成威脅。
二、簽名機(jī)制
1、基于MD5的消息完整性簽名機(jī)制
在目前國(guó)內(nèi)大部分支付平臺(tái)以及諸如anySDK平臺(tái)等平臺(tái)的接口中均使用或曾使用基于MD5的消息完整性簽名機(jī)制。該機(jī)制主要用戶保證圖1中四方通訊時(shí)消息傳遞的完整性。
基于MD5的消息完整性簽名機(jī)制如圖2所示。該方法的關(guān)鍵在于:商家和錢包服務(wù)之間共享一個(gè)簽名密鑰。該簽名密鑰參與到每個(gè)簽名生成以及簽名驗(yàn)證過(guò)程中。該密鑰不能泄露,一旦泄露則會(huì)造成極大安全隱患。攻擊者可以借助泄露的密鑰來(lái)偽造消息,修改訂單,發(fā)送支付成功消息等。
在簽名過(guò)程中,簽名方將待簽名的原請(qǐng)求中的key-value對(duì)按照key的字母序進(jìn)行排序,然后連接在一起。這個(gè)連接可以使用‘&’組合,也可以不使用‘&’。再將簽名密鑰附帶在組合的結(jié)尾,生成“待簽字符串”。有的簽名方案使用‘&key=’來(lái)連接key,有的則直接附加key在末尾,區(qū)別不大。然后使用MD5算法生成待簽字符串的散列值作為簽名。最后將該散列值作為一個(gè)域附加在原請(qǐng)求中,得到最終的請(qǐng)求。
驗(yàn)簽過(guò)程和簽名過(guò)程是基本相同的。首先從最終請(qǐng)求中分離出簽名域,再將需要驗(yàn)簽的部分按照key的順序排列并重新組合,附加上簽名密鑰,生成簽名過(guò)程中的“待簽字符串”,最后計(jì)算其MD5值,判斷其與最終請(qǐng)求中所帶的散列值是否相符。
2、基于非對(duì)稱密碼體制的簽名機(jī)制
應(yīng)用這一類簽名機(jī)制的平臺(tái)較少,其支付過(guò)程可參見(jiàn)圖3。
以RSA為例。商戶生成一對(duì)RSA公私鑰對(duì),錢包服務(wù)生成一對(duì)RSA公私鑰對(duì)。雙方把各自的公鑰(金色鑰匙)發(fā)給對(duì)方。
對(duì)消息進(jìn)行簽名的過(guò)程和基于MD5的過(guò)程類似,也是首先將請(qǐng)求按key-value對(duì)排序,再使用RSA-SHA1算法(先SHA1再變形再RSA)和對(duì)方的RSA公鑰生成簽名,最后將簽名附在原請(qǐng)求中形成完整的請(qǐng)求。 驗(yàn)簽過(guò)程使用自己的RSA私鑰進(jìn)行驗(yàn)簽,具體過(guò)程不表。
3、待簽字符串的生成
以上兩類簽名機(jī)制均依賴于“待簽字符串”的生成。在待簽字符串的生成過(guò)程中有以下四個(gè)主要問(wèn)題:
參數(shù)值為空的情況。在某些平臺(tái)中,參數(shù)值為空的參數(shù)在待簽字符串中被忽略。在某些平臺(tái)中則不被忽略。
參數(shù)值的編碼問(wèn)題。在某些平臺(tái)中,參數(shù)值編碼后(編碼方式也有不同)進(jìn)入待簽字符串。在某些平臺(tái)中則在解碼后進(jìn)入待簽字符串。
特殊字符問(wèn)題。由于待簽字符串使用&和=作為元字符,因此參數(shù)中存在的&和=等字符會(huì)影響待簽字符串的結(jié)構(gòu)。不同平臺(tái)對(duì)特殊字符的處理也不同。
進(jìn)入待簽字符串的參數(shù)選擇。某些平臺(tái)中,所有參數(shù)均進(jìn)入待簽字符串中參與簽名生成。而某些平臺(tái)中只有指定參數(shù)才會(huì)進(jìn)入待簽字符串中參與運(yùn)算。
待簽字符串的種種性質(zhì)導(dǎo)致了其“二義性”的出現(xiàn)。在某些情況下,同一個(gè)待簽字符串可以等價(jià)于兩個(gè)不同請(qǐng)求。例如這個(gè)待簽字符串
a=A&b=B&c=C&d=D
可以由一個(gè)包含四個(gè)key-value對(duì)的原請(qǐng)求
{"a":"A", "b":"B", "c":"C", "d":"D"}
生成。在某些情況下,還可以由以下包含五個(gè)key-value對(duì)的原請(qǐng)求生成
{"a":"A", "b":"B", "c":"C", "d":"D", "junk":"JUNK"}
或者,在另一些情況下,可以由以下只包含三個(gè)key-value對(duì)的原請(qǐng)求生成
{"a":"A&b=B", "c":"C", "d":"D"}
這些變形依賴于“待簽字符串”的生成方法。攻擊者可以通過(guò)構(gòu)造畸形請(qǐng)求來(lái)生成具有相同“待簽字符串”的請(qǐng)求,從而繞過(guò)簽名驗(yàn)證限制。
三、支付協(xié)議的安全漏洞
由以上分析,第三方支付過(guò)程的安全嚴(yán)重依賴于以下三點(diǎn):
密鑰的安全管理
支付平臺(tái)簽名算法的正確實(shí)現(xiàn)
商戶對(duì)支付協(xié)議的正確使用
然而在生產(chǎn)環(huán)境中每個(gè)環(huán)節(jié)都有可能出錯(cuò),引起嚴(yán)重的安全隱患。
1、密鑰泄露
這是最常見(jiàn)的一種安全漏洞。密鑰泄漏并不是一種罕見(jiàn)的情況,不少app在開(kāi)發(fā)時(shí),將密鑰硬編碼在app代碼中(用于本地實(shí)現(xiàn)簽名計(jì)算)。消息的完整性依賴于簽名的計(jì)算,密鑰泄漏后消息將無(wú)法保證未被篡改或偽造。但對(duì)稱密鑰和不對(duì)稱密鑰泄漏后的利用和危害有所區(qū)別。
圖4展示了最常見(jiàn)的情況。MD5簽名密鑰編碼在用戶App中造成密鑰泄露。在對(duì)相當(dāng)多的app進(jìn)行逆向工程后我們發(fā)現(xiàn),有部分app直接照搬一些樣例代碼,導(dǎo)致key被直接明文編碼到程序中,非常容易提取。還有一部分app作者使用了一些變形手段,例如將key拆成奇數(shù)位、偶數(shù)位分別存儲(chǔ),或使用特定常數(shù)進(jìn)行異或存儲(chǔ)。這些簡(jiǎn)單變形在熟練的攻擊者面前是徒勞的。
由于商戶和支付平臺(tái)共享密鑰,密鑰泄漏后,攻擊者既可以冒充商戶向支付平臺(tái)發(fā)送訂單消息,又可以冒充支付平臺(tái)向商戶發(fā)送支付結(jié)果。當(dāng)然,后者更加直接(如圖4)。
例如,若攻擊者準(zhǔn)備購(gòu)買一件商品,其訂單消息為
notify_url=http://seller.com/notify&out_trade_no=12345&seller=alice&total_fee=100&sign=XXX,
攻擊者可以首先通過(guò)修改notify_url到攻擊者掌控的地址,如http://attacker.com/,提交請(qǐng)求:
notify_url=http://attacker.com/notify&out_trade_no=12345&seller=alice&total_fee=100&sign=XXX
來(lái)獲得notify_url的結(jié)構(gòu)。再偽造以下消息簽名后發(fā)送給商戶,偽造異步通知,實(shí)現(xiàn)免費(fèi)購(gòu)物。
target_url: http://seller.com/notify
post_data: put_trade_no=12345&seller=alice&total_fee=100&trade_status=SUCCESS&sign=XXX.
商戶收到消息后驗(yàn)證簽名正確,所有參數(shù)均正確,將完成攻擊者的訂單。而事實(shí)上,攻擊者并未進(jìn)行過(guò)任何支付。
另一方面,基于非對(duì)稱密碼體制的簽名方案中,私鑰泄露后攻擊者也可以進(jìn)行攻擊。但是仍依賴于其他的邏輯漏洞。攻擊者只能獲取商戶的私鑰,而支付平臺(tái)的私鑰往往被妥善保護(hù)無(wú)法獲得。因此,攻擊者無(wú)法冒充支付平臺(tái)向商戶發(fā)送支付成功的消息,而只能冒充商戶向支付平臺(tái)偽造訂單或者篡改訂單,修改支付金額。
如圖5所示,若攻擊者準(zhǔn)備購(gòu)買一件商品,其訂單消息為
notify_url=http://seller.com/notify&out_trade_no=12345&seller=alice&total_fee=100&sign=XXX
攻擊者修改金額,使用私鑰重新簽名,并提交支付訂單
notify_url=http://seller.com/notify&out_trade_no=12345&seller=alice&total_fee=1&sign=XXX
成功支付1元后,商戶會(huì)收到支付結(jié)果消息
out_trade_no=12345&seller=alice&total_fee=1&trade_status=SUCCESS&sign=XXX
商戶進(jìn)行消息的驗(yàn)證,會(huì)發(fā)現(xiàn)簽名正確,商戶號(hào)正確,訂單12345支付成功。若商戶沒(méi)有驗(yàn)證支付金額與訂單是否匹配,將完成攻擊者的訂單。從而攻擊者以1元購(gòu)買了100元的商品。在許多App中,曾出現(xiàn)過(guò)只驗(yàn)證簽名和訂單id的情況,沒(méi)有驗(yàn)證實(shí)付金額,因此可以通過(guò)這種金額篡改進(jìn)行攻擊。
為了防御這樣的攻擊,商家一定要修改app和服務(wù)端的設(shè)計(jì),使得簽名全部在服務(wù)端進(jìn)行。網(wǎng)上充斥著大量可以直接照搬的富含漏洞的樣例代碼,一定不要簡(jiǎn)單修改這些代碼就直接接入支付平臺(tái)。此外,每筆交易均要進(jìn)行查賬,驗(yàn)證錢真的得到了支付,才可以標(biāo)記訂單為成功支付。
一旦出現(xiàn)這樣的秘鑰泄漏商家將面臨嚴(yán)峻的安全風(fēng)險(xiǎn),支付平臺(tái)也將面臨嚴(yán)重的連帶品牌危機(jī)。發(fā)生這樣的危機(jī)時(shí),如果簡(jiǎn)單的替換秘鑰將會(huì)直接導(dǎo)致老App客戶端無(wú)法進(jìn)行交易,如果不替換則將面臨嚴(yán)峻的支付風(fēng)險(xiǎn)。目前臨時(shí)緩解方案是依靠商家服務(wù)后臺(tái)與錢包服務(wù)后臺(tái)的增強(qiáng)校驗(yàn)和風(fēng)控來(lái)探測(cè)和抵抗攻擊。
2、簽名算法實(shí)現(xiàn)錯(cuò)誤
簽名泄露只影響個(gè)別自己實(shí)現(xiàn)錯(cuò)誤的商家,而支付平臺(tái)的漏洞則會(huì)影響千萬(wàn)商家。在這一節(jié)我們討論我們提交給兩個(gè)國(guó)內(nèi)著名支付平臺(tái)的平臺(tái)漏洞,他們均已得到修復(fù)。
案例一
第三方支付平臺(tái)A采用了對(duì)稱密鑰的設(shè)計(jì),并提供了服務(wù)端SDK供商家集成。服務(wù)端SDK提供了API驗(yàn)證簽名是否正確。
在PHP和C#的SDK實(shí)現(xiàn)中,當(dāng)簽名字段不存在時(shí),SDK會(huì)直接返回簽名正確,這就導(dǎo)致了攻擊者可以直接冒充支付平臺(tái)向商戶發(fā)送偽造的支付結(jié)果消息并通過(guò)簽名認(rèn)證。
以PHP版代碼為例
public function CheckSign()
{
if(!$this->IsSignSet()){
returntrue;
}
$sign = $this->MakeSign();
if($this->GetSign() == $sign){
returntrue;
}
throw newException("簽名錯(cuò)誤!");
}
public function IsSignSet()
{
return array_key_exists('sign', $this->values);
}
檢查簽名時(shí),首先會(huì)利用函數(shù) IsSignSet判斷簽名是否存在。若簽名不存在,直接認(rèn)為簽名正確。
由于該支付平臺(tái)要求商戶服務(wù)器將訂單(包括通知URL)發(fā)送給支付服務(wù)器獲取一個(gè)ID,隨后商戶應(yīng)用將ID傳遞給支付客戶端調(diào)起支付界面,在實(shí)際攻擊中, 攻擊者還需要從其他渠道獲取通知URL(例如路徑猜測(cè)、URL硬編碼或存在網(wǎng)絡(luò)請(qǐng)求中等)才可偽造支付結(jié)果。
案例二
支付平臺(tái)B采用了非對(duì)稱密鑰的設(shè)計(jì),每個(gè)商戶有自己的一套公鑰私鑰,但支付平臺(tái)的公鑰私鑰僅一套,即所有商戶使用同一個(gè)公鑰驗(yàn)證來(lái)自支付平臺(tái)的消息。
除此以外,訂單消息和支付結(jié)果消息中包含字段body描述商品信息,且訂單消息和對(duì)應(yīng)支付結(jié)果消息中的body一致。
如果攻擊者希望免費(fèi)(低價(jià))購(gòu)物,應(yīng)該如何進(jìn)行呢?回顧上文中關(guān)于待簽字符串二義性的討論,待簽字符串為形如:
key1=value1&key2=value2&key3=value3
的格式。&和=作為了連接符號(hào)。
如果某一個(gè)參數(shù)值(value)中包含&和=符號(hào),待簽字符串和原始的參數(shù)集合就可能不再是一對(duì)一,即存在多組參數(shù)集合對(duì)應(yīng)同一組待簽字符串。
例如:參數(shù)集合
{"key1":"value1","key2":"value2&key3=fake_value&zend_key=a", "key3":"value3"}
的待簽字符串為
key1=value1&key2=value2&key3=fake_value&zend_key=a&key3=value3
考慮另一個(gè)參數(shù)集合
{"key1":"value1","key2":"value2", "key3":"fake_value","zend_key":"a&key3=value3"}
的待簽字符串同為
key1=value1&key2=value2&key3=fake_value&zend_key=a&key3=value3
兩組集合的待簽字符串一樣,但key3的值不同。攻擊者若知道其中一組的簽名,便知道另一組參數(shù)在相同密鑰下的簽名了。
有了這個(gè)發(fā)現(xiàn),如何在實(shí)際中利用并實(shí)現(xiàn)免費(fèi)(低價(jià))購(gòu)物呢? 攻擊者需要“騙一個(gè)畸形訂單支付成功的簽名”!
首先,攻擊者需要擁有一個(gè)商戶evil的私鑰(可自行注冊(cè)或利用已泄漏密鑰)
現(xiàn)在攻擊者準(zhǔn)備向商戶alice購(gòu)買一件商品,正常的訂單消息參數(shù)為
{"body":"商品A","notify_url":"http://seller.com/notify","out_trade_no":"12345","seller":"alice", "total_fee":"100","sign":"XXX"}
若訂單支付成功,支付結(jié)果的參數(shù)應(yīng)為
{"body":"商品A","out_trade_no":"12345", "seller":"alice", "total_fee":"100", "trade_status":"SUCCESS","sign":"YYY"},
因此,攻擊者的目標(biāo)是向http://seller.com/notify發(fā)送一個(gè)支付完成的消息,并且包含
{"body":"商品A", "out_trade_no":"12345", "seller":"alice", "total_fee":"100", "trade_status":"SUCCESS"}
這些參數(shù)和正確的簽名。
這樣一條消息會(huì)經(jīng)過(guò)支付平臺(tái)的簽名,攻擊者無(wú)法直接偽造。但是前面提到,所有商戶使用同一個(gè)公鑰驗(yàn)證來(lái)自支付平臺(tái)的消息,也就是說(shuō)支付平臺(tái)發(fā)給商戶evil的消息若轉(zhuǎn)發(fā)給商戶alice,簽名可以通過(guò)驗(yàn)證,僅僅是商戶號(hào)等參數(shù)值不正確。因此,攻擊者可以考慮利用evil的支付結(jié)果來(lái)偽造給alice的支付結(jié)果。
攻擊者首先發(fā)給支付平臺(tái)一個(gè)屬于商戶Evil的訂單消息
{“body”:“商品A&out_trade_no=12345&seller=alice&total_fee=100&trade_status=SUCCESS&z=”,“notify_url”:“http://evil.com/notify”, “out_trade_no”:”12345”,“seller”:“evil”, “total_fee”:“1”, “sign”:“XXXX”},
支付1元后,http://evil.com/notify會(huì)收到支付結(jié)果,并且經(jīng)過(guò)了支付平臺(tái)簽名。
{“body”:“商品A&out_trade_no=12345&seller=alice&total_fee=100&trade_status=SUCCESS&z=”, “out_trade_no”:”12345”, “seller”:“evil”, “total_fee”:“1”,“trade_status”:“SUCCESS”, “sign”:“signed by payment platform”},
攻擊者可以將其變換為
{“body”:“商品A”,“out_trade_no”:“12345”,“seller”:“alice”, “total_fee”:“100”, “trade_status”:“SUCCESS”,“z”:“&out_trade_no=12345&seller=evil&total_fee=1&trade_status=SUCCESS”, “sign”:“signed by payment platform”}
然后發(fā)送給商戶Alice的URL http://seller.com/notify。這些數(shù)據(jù)具有正確的簽名和期望的商家號(hào)、訂單號(hào)、支付金額,將會(huì)通過(guò)alice驗(yàn)證,從而alice會(huì)通過(guò)攻擊者的訂單。
攻擊者因此實(shí)現(xiàn)了免費(fèi)(低價(jià))購(gòu)物。
3、商戶驗(yàn)證支付結(jié)果時(shí)存在邏輯錯(cuò)誤
1)簽名驗(yàn)證
盡管支付平臺(tái)提供了服務(wù)端SDK,商戶后端在實(shí)現(xiàn)邏輯時(shí)可能并未使用SDK。那么,商戶應(yīng)正確實(shí)現(xiàn)簽名的驗(yàn)證邏輯,避免相關(guān)邏輯錯(cuò)誤,如簽名不存在時(shí)通過(guò)簽名驗(yàn)證。
2)總金額、商戶號(hào)的驗(yàn)證
商戶在收到支付結(jié)果通知,驗(yàn)證完簽名后,還應(yīng)正確處理支付結(jié)果中的相關(guān)參數(shù)。支付金額、商戶號(hào)都應(yīng)該被驗(yàn)證。
當(dāng)支付金額未被驗(yàn)證,攻擊者可以支付較低的費(fèi)用實(shí)現(xiàn)購(gòu)物。支付金額不一致可能是訂單消息在簽名之前金額被篡改或是攻擊者偽造了訂單消息(參考不對(duì)稱密鑰泄漏)。
若商戶號(hào)未被驗(yàn)證,攻擊者可以考慮復(fù)用另一商戶的支付結(jié)果,對(duì)當(dāng)前商戶進(jìn)行攻擊。攻擊發(fā)生的條件在于發(fā)給不同商戶的支付結(jié)果使用了同樣的私鑰簽名,同時(shí)攻擊者注冊(cè)了自己商戶。攻擊者生成一個(gè)訂單,支付給自己的商戶后,將支付結(jié)果復(fù)用。
若商戶號(hào)和總金額均未被驗(yàn)證,攻擊者還可以再次考慮復(fù)用另一商戶的支付結(jié)果對(duì)當(dāng)前商戶進(jìn)行攻擊。雖然復(fù)用支付結(jié)果仍要求發(fā)給不同商戶的支付結(jié)果使用了同樣的私鑰簽名,但攻擊者不再需要注冊(cè)自己的商戶,而是利用已泄漏的商戶密鑰。實(shí)現(xiàn)攻擊時(shí),攻擊者生成一個(gè)費(fèi)用較低的訂單,完成支付后復(fù)用支付結(jié)果。
3)只在客戶端驗(yàn)證
在用戶完成支付后,支付平臺(tái)往往既會(huì)異步通知商戶服務(wù)器支付結(jié)果,又會(huì)同步通知商戶客戶端支付結(jié)果以向用戶展示。
然而,支付結(jié)果的驗(yàn)證應(yīng)在服務(wù)端完成。僅在客戶端驗(yàn)證將容易受到攻擊。攻擊者可以直接修改客戶端邏輯實(shí)現(xiàn)免費(fèi)購(gòu)物。
總結(jié)
本文總結(jié)了常見(jiàn)支付平臺(tái)的支付過(guò)程的機(jī)制,探討了其中存在和可能存在的問(wèn)題,并通過(guò)實(shí)例展現(xiàn)了商戶端和支付平臺(tái)出現(xiàn)的安全問(wèn)題。除了本文中探討的問(wèn)題,還有許許多多安全隱患在近年得到不斷修正??偟膩?lái)說(shuō)現(xiàn)在的網(wǎng)上支付還是比較可靠的。在下篇中我們繼續(xù)對(duì)基于MD5的消息簽名機(jī)制進(jìn)行討論,發(fā)掘更多的安全問(wèn)題。
雷峰網(wǎng)版權(quán)文章,未經(jīng)授權(quán)禁止轉(zhuǎn)載。詳情見(jiàn)轉(zhuǎn)載須知。