Closed jollen closed 8 years ago
這幾天因為專案需要,開始將線上支付的金流平臺,從 Paypal 轉移到 Pay2go。在實作前端與後端的過程中,發現有一些應該要注意的事項。
由於 Pay2go 是以 Form Post(HTML5 的 <form></form>)的方式提交付款請求,所以必須使用 <input> 來紀錄交易參數,如圖一。
<form></form>
<input>
圖一:MPG API
一個稱為 CheckValue 的參數,編碼規則如下:
CheckValue
var data = 'HashKey=' + hashKey + '&Amt=' + amt + '&MerchantID=' + merchantID + '&MerchantOrderNo=' + merchantOrderNo + '&TimeStamp=' + timeStamp + '&Version=1.2' + '&HashIV=' + hashIV; // 使用 SHA256 壓碼後轉大寫 var checkValue = sha256(data).toLocaleUpperCase();
將幾個主要的交易參數、 hashKey 與 hashIV 連接成 Query String 形式後,再用 SHA256 編碼。CheckCode 看起來是為了檢查交易參數的正確性。不過:
hashKey
hashIV
CheckCode
另外,有些前端網頁,直接將 hashKey 與 hashIV 也紀錄在 Form 裡面,也是非常危險的做法。如圖二(為避免不必要的問題,僅做截圖,無註明出處)。
圖二:曝露在外的 hashKey 與 hashIV
類似的安全議題,可以在研發或工程階段,就進行嚴格檢視。不過,把交易機制、平台架構與 SDK 做到非常嚴謹,應該是金流平臺的基本責任。
一個安全且嚴謹的交易平臺,也要針對各種「使用不當」的案例,而造成的安全疑慮,進行完整測試。
NotifyURL 是 Pay2go 用來通知店家後台交易結果的 URL。Pay2go 在訂單完成交易後,會以 HTTP POST 方法,呼叫 NotifyURL。店家的後台則是要實作這個 API,並進行自已的訂單處理流程。
以下是一個 Pay2go 模擬交易,所回送的 POST 資料:
{ JSONData: '{"Status":"SUCCESS","Message":"\\u4ed8\\u6b3e\\u6210\\u529f","Result":"{\\"MerchantID\\":\\"31745140\\",\\"Amt\\":992,\\"TradeNo\\":\\"16033018180593725\\",\\"MerchantOrderNo\\":\\"14593335802368129\\",\\"RespondType\\":\\"JSON\\",\\"CheckCode\\":\\"4E2B2FB0A54CCFECE9616742FE9025EFBBE8FABBC6562268C11D39DFD6E03E9C\\",\\"IP\\":\\"36.231.213.27\\",\\"EscrowBank\\":\\"KGI\\",\\"PaymentType\\":\\"WEBATM\\",\\"PayTime\\":\\"2016-03-30 18:18:05\\",\\"PayerAccount5Code\\":\\"-\\",\\"PayBankCode\\":\\"-\\"}"}' }
這份資料會以 JSON 格式,經由 POST 方法,由 Pay2go 後台傳送到店家的 NotifyURL。將這筆資料的 JSONData 轉換為 JSON 物件:
JSONData
var jsonData = JSON.parse(postData.JSONData); // postData 為上述回傳資料
詳細的交易狀態,則是紀錄在 jsonData 的 Result 裡,再將這個欄位轉換為 JSON 物件:
jsonData
Result
var result = JSON.parse(jsonData.Result);
終於得到以下內容(疑問:為什麼不直接回傳一個 Stringify 過的 JSON 物件就好?):
{ MerchantID: '31745140', Amt: 992, TradeNo: '16033018180593725', MerchantOrderNo: '14593335802368129', RespondType: 'JSON', CheckCode: '4E2B2FB0A54CCFECE9616742FE9025EFBBE8FABBC6562268C11D39DFD6E03E9C', IP: '36.231.213.27', EscrowBank: 'KGI', PaymentType: 'WEBATM', PayTime: '2016-03-30 18:18:05', PayerAccount5Code: '-', PayBankCode: '-' }
這個回傳結果也是頗耐人尋味:
回傳的結果,在交易使用的 Form 裡面幾乎都可以取得。因此向店家發出一個「模擬交易成功」的 NotifyURL 並不是太困難。
NotifyURL
Origin:
Refer:
最後,展示如何用 curl 進行「模擬交易成功」。首先,在店家的 Form 裡面取得 MerchantID、CheckValue、MerchantOrderNo 與 Amt 四個參數。
curl
MerchantID
MerchantOrderNo
Amt
接著,將以上參數帶入以下的內容模板:
{ "JSONData": "{\"Status\":\"SUCCESS\",\"Message\":\"\\u4ed8\\u6b3e\\u6210\\u529f\",\"Result\":\"{\\\"MerchantID\\\":\\\"31745140\\\",\\\"Amt\\\":992,\\\"TradeNo\\\":\\\"16033018180593725\\\",\\\"MerchantOrderNo\\\":\\\"14593258232469307\\\",\\\"RespondType\\\":\\\"JSON\\\",\\\"CheckCode\\\":\\\"4E2B2FB0A54CCFECE9616742FE9025EFBBE8FABBC6562268C11D39DFD6E03E9C\\\",\\\"IP\\\":\\\"36.231.213.27\\\",\\\"EscrowBank\\\":\\\"KGI\\\",\\\"PaymentType\\\":\\\"WEBATM\\\",\\\"PayTime\\\":\\\"2016-03-30 18:18:05\\\",\\\"PayerAccount5Code\\\":\\\"-\\\",\\\"PayBankCode\\\":\\\"-\\\"}\"}" }
將上述內容儲存為 test.json。最後利用 curl 向店家的 NotifyURL 發出 POST 請求:
curl -X POST -d @test.json --header "Content-Type: application/json" http://localhost:80/pay2go/notify
從這個「模擬交易」的測試可以知道,把自已的 NotifyURL 藏好是一個很重要的工作。提供 REST API 來替代 Form Post 方式,可以消除主要的安全疑慮。
這幾天因為專案需要,開始將線上支付的金流平臺,從 Paypal 轉移到 Pay2go。在實作前端與後端的過程中,發現有一些應該要注意的事項。
關於 MPG 參數
由於 Pay2go 是以 Form Post(HTML5 的
<form></form>
)的方式提交付款請求,所以必須使用<input>
來紀錄交易參數,如圖一。一個稱為
CheckValue
的參數,編碼規則如下:將幾個主要的交易參數、
hashKey
與hashIV
連接成 Query String 形式後,再用 SHA256 編碼。CheckCode
看起來是為了檢查交易參數的正確性。不過:CheckCode
放在 Form 的欄位裡,有安全上的疑慮CheckCode
,但 Pay2go 目前不支援這樣的做法另外,有些前端網頁,直接將
hashKey
與hashIV
也紀錄在 Form 裡面,也是非常危險的做法。如圖二(為避免不必要的問題,僅做截圖,無註明出處)。hashKey
與hashIV
類似的安全議題,可以在研發或工程階段,就進行嚴格檢視。不過,把交易機制、平台架構與 SDK 做到非常嚴謹,應該是金流平臺的基本責任。
一個安全且嚴謹的交易平臺,也要針對各種「使用不當」的案例,而造成的安全疑慮,進行完整測試。
關於 NotifyURL
NotifyURL 是 Pay2go 用來通知店家後台交易結果的 URL。Pay2go 在訂單完成交易後,會以 HTTP POST 方法,呼叫 NotifyURL。店家的後台則是要實作這個 API,並進行自已的訂單處理流程。
以下是一個 Pay2go 模擬交易,所回送的 POST 資料:
這份資料會以 JSON 格式,經由 POST 方法,由 Pay2go 後台傳送到店家的 NotifyURL。將這筆資料的
JSONData
轉換為 JSON 物件:詳細的交易狀態,則是紀錄在
jsonData
的Result
裡,再將這個欄位轉換為 JSON 物件:終於得到以下內容(疑問:為什麼不直接回傳一個 Stringify 過的 JSON 物件就好?):
這個回傳結果也是頗耐人尋味:
CheckCode
放在這裡面,不但有安全上的疑慮,必要性也值得探討CheckCode
的目的,如果是為了 Pay2go 與店家,自驗證交易資料使用(沒有被竄改),或許可以在各自的後台運算即可,是否以明文傳送,或許有討論空間回傳的結果,在交易使用的 Form 裡面幾乎都可以取得。因此向店家發出一個「模擬交易成功」的 NotifyURL 並不是太困難。
強化方案
NotifyURL
參數,避免用心人士,向店家發出「模擬交易成功」的通知。可以在 Pay2go 的管理介面設定NotifyURL
,減少NotifyURL
曝露的機會。hashKey
與hashIV
。NotifyURL
可以考慮移除「自動出貨」、「自動付款銷核」等流程,取消部份自動化,改採手工銷核。NotifyURL
實作,可以考慮加入更嚴謹的檢查,例如查看 HTTP 的Origin:
,或是Refer:
等檔頭。不過因為 HTTP headers 也是很容易變造,所以根本之道還是 Pay2go 的後台能強化相關機制。最後,展示如何用
curl
進行「模擬交易成功」。首先,在店家的 Form 裡面取得MerchantID
、CheckValue
、MerchantOrderNo
與Amt
四個參數。接著,將以上參數帶入以下的內容模板:
將上述內容儲存為 test.json。最後利用
curl
向店家的NotifyURL
發出 POST 請求:從這個「模擬交易」的測試可以知道,把自已的
NotifyURL
藏好是一個很重要的工作。提供 REST API 來替代 Form Post 方式,可以消除主要的安全疑慮。