1. 客戶端直接verify蘋果的receipt 若是verify成功 自行發放商品 html
2. 客戶端將receipt傳給server,由server進行驗證併發放商品ios
按照安全性原則, 客戶端的全部信息都是不可信的,並且支付是業務中的核心模塊,因此應該選擇第二種。安全
下面簡要介紹下,第二種方式的簡單流程。併發
1. 客戶端支付成功,拿到receiptapp
2. 客戶端將receipt傳到服務端 ide
3. 服務端去apple驗證receipt 若是驗證成功 就發放receipt中的商品ui
做爲支付,安全性是第一位的,下面簡要分析一下經常使用的攻擊手段。加密
一、劫持apple server攻擊 => 經過dns污染,讓客戶端支付走到假的apple_server,並返回驗證成功的response。 這個主要針對支付方式一 若是是支付方式二 就無效。code
二、重複驗證攻擊 => 一個receipt重複使用屢次server
三、跨app攻擊 => 別的app的receipt用到咱們app中來
四、換價格攻擊 => 低價商品代替高價商品
五、歧義攻擊 => iap支付以前的status=0表示verify成功 而如今變爲status=0只能表示receipt合法 具體支付詳情須要經過in_app字段決定 For iOS 6 style transaction receipts, the status code reflects the status of the specific transaction’s receipt.
六、中間人攻擊 => 僞造apple_server,若是用戶支付就將
經過dns污染,讓客戶端經過假的apple_server進行verify,從而認爲本身支付成功。這個主要針對**支付方式一**,若是是支付方式二,就沒效果了。常見的iap hack軟件@iAPFree @iAP Cracker 就是用的相似原理。
由於同一個receipt,若是第一次驗證成功,那麼以後每次驗證都會成功。若是服務端沒有判重機制,就會致使一個receipt被當作屢次充值處理。
爲了預防這種狀況,咱們能夠將receipt作一次md5獲得receipt_md5, 每次發送充值請求的時候就按照receipt_md5判重,若是重複就中止商品發放。
經過在別的app中拿到receipt,而後發送到咱們app中。由於這個receipt是合法的並且apple不會驗證請求的源,因此這個receipt是能夠驗證經過的。
對於這種狀況,咱們能夠判斷apple verify的返回值apple_callback_data中對應的bundle_id和咱們app的bundle_id是否同樣來進行驗證。
在同一個app中,用低價商品的receipt僞造購買高價商品。這時候bundle_id和咱們app的bundle_id是一致的。
針對這種狀況, 咱們能夠從apple verify的返回值apple_callback_data中拿到對應的PRoduct_id, 並按照product_id來進行充值。 **不要信任客戶端的product_id**
在iOS6的時候,status=0表示這次支付成功,而如今變爲status=0只表示receipt**總體上**合法。
因此,對iOS7即便是一個過時訂單,也會返回status=0,若是還按照iOS6的邏輯處理,就會致使假充值。針對iOS7,咱們應該不僅經過status,還要經過in_app中的內容,來決定如何發放商品。
```
For iOS 6 style transaction receipts, the status code reflects the status of the specific transaction’s receipt.
For iOS 7 style app receipts, the status code is reflects the status of the app receipt as a whole. For example, if you send a valid app receipt that contains an expired subscription, the response is 0 because the receipt as a whole is valid.
僞造apple server,將咱們的支付請求轉發到真的apple_server,拿到合法的receipt,並弄個假的receipt給客戶端。這樣就拿到一個合法的憑證。利用這個合法的receipt,僞造別人充值的請求,從而達到幫別人充值的目的。
針對中間人攻擊,最重要的是保證a用戶的支付receipt,不能被b用戶使用。可是apple爲了保護隱私,receipt中沒有任何用戶的我的信息,這就須要咱們本身來保證。目前咱們用加密的手段來作這個保證。
客戶端拿到apple的receipt 併發送到serverserver拿到這個receipt,向蘋果驗證獲得apple_callback_data若是apple_callback_data的status是21007,說明是沙盒模式(不用花錢就能夠購買) 要根據具體需求判斷處理邏輯,須要注意的是,ios的審覈在支付的時候就採用的沙盒模式。
若是apple_callback_data的status是0,就要從apple_callback_data[‘receipt’][‘in_app’]這個list中拿到全部的記錄,每個進行充值。而後記錄transaction_id和original_transaction_id來防止同一個transaction被重複使用。
https://developer.apple.com/library/content/documentation/NetworkingInternet/Conceptual/StoreKitGuide/Chapters/Restoring.html
https://developer.apple.com/library/content/releasenotes/General/ValidateAppStoreReceipt/Chapters/ReceiptFields.html#//apple_ref/doc/uid/TP40010573-CH106-SW1 => Original Transaction Identifier
返回全部充值成功和重複的transaction_id, 有client來complete transaction
支付做爲核心模塊,除了技術上的保證,商務也應該每週進行一次對帳。若是發現apple上的收入和服務端記錄的收入有比較大的差距,就應該抓緊查看緣由。
最後給出一個apple_callback_data的例子
{
"status": 0,
"environment": "Production",
"receipt": {
"download_id": 75017873837267,
"adam_id": 1149703708,
"request_date": "2017-01-13 06:57:20 Etc/GMT",
"app_item_id": 1149703708,
"original_purchase_date_pst": "2016-11-17 18:57:09 America/Los_Angeles",
"version_external_identifier": 820252187,
"receipt_creation_date": "2017-01-13 05:04:52 Etc/GMT",
"in_app": [
{
"is_trial_period": "false",
"purchase_date_pst": "2017-01-12 21:04:52 America/Los_Angeles",
"original_purchase_date_pst": "2017-01-12 21:04:52 America/Los_Angeles",
"product_id": "com.lucky917.live.gold.1.555",
"original_transaction_id": "350000191094279",
"original_purchase_date": "2017-01-13 05:04:52 Etc/GMT",
"original_purchase_date_ms": "1484283892000",
"purchase_date": "2017-01-13 05:04:52 Etc/GMT",
"purchase_date_ms": "1484283892000",
"transaction_id": "350000191094279",
"quantity": "1"
}
],
"original_purchase_date_ms": "1479437829000",
"original_application_version": "26",
"original_purchase_date": "2016-11-18 02:57:09 Etc/GMT",
"request_date_ms": "1484290640800",
"bundle_id": "com.lucky917.ios.Live",
"receipt_creation_date_pst": "2017-01-12 21:04:52 America/Los_Angeles",
"application_version": "32",
"request_date_pst": "2017-01-12 22:57:20 America/Los_Angeles",
"receipt_creation_date_ms": "1484283892000",
"receipt_type": "Production"
}
}