有一次出去吃飯,排著隊付錢,等著過程非常無聊,準備拔出手機來把荒野亂鬥,卻發現這個地方竟然連不上網 。
看著手機明明訊號滿格,但是就是顯示網路無連線,蘋果手機使用者痛,誰用誰知道。
畫外音:真的要 Diss 一下使用英特爾基帶的 Iphone,好差,沒事網路就會閃斷~
說回正題,由於沒有網路,而我又沒帶錢,所以就怕付錢的時候因為手機沒網,沒辦法使用支付寶扣款。正想著時,已經排到了我,不管三七二十一,先用下支付寶試試,實在不行爺不吃了。
不過沒想到,當商家用掃碼搶掃描支付寶上付款碼支付以後,雖然我的手機最終沒有彈出支付成功的頁面,但是商家端顯示支付成功,併成功打印出了小票,過了一會,我的手機收到支付寶扣款簡訊。
因為我最近的工作對都是與微信/支付寶有關,整體支付流程還是比較清楚,但是付款碼為什麼能離線支付確實不是很清楚,所以研究了一番,於是有了今天的文章。
科普支付方式在聊付款碼離線原理之前,我們先給不熟悉支付寶/微信支付方式同學先科普一下常見的兩種支付方式。
以支付寶為例,付款流程如圖所示:
圖片來自支付寶官網
第二種則是我們開啟手機,展示我們的付款碼,然後商家使用掃碼槍等工具獲取付款碼完成支付,這種支付方式一般稱為被掃支付(使用者被掃碼)。
以支付寶為例,付款流程如圖所示:
圖片來自支付寶官網
對於第一種方式,需要手機端 APP 掃碼,然後彈窗確認付款,這種方式是沒有辦法在手機沒有網路的情況完成支付,所以我們上文說的沒有網路的情況特指付款碼支付的場景。
付款碼付款流程在聊付款碼離線支付的前提前,我們先來來看下付款碼的整體流程,以超市購物為例,一次付款碼的支付資訊流如圖所示:
參考知乎@天順
這個過程商家後臺系統是需要呼叫的支付寶條碼支付的介面,完成支付。
由於商家後臺需要線上聯網與支付寶後臺通訊,所以說付款碼的離線支付,指的是客戶端沒有的網路的情況,商家端其實必須實時聯網線上。
一次付款碼介面呼叫流程如圖所示:
來自支付寶官網
通過上面兩張圖,我們整體了解付款碼互動流程。
付款碼的技術方案其實可以分為客戶端線上與離線的兩種情況,下面我們來看下兩種方案具體實現方式。
線上碼方案客戶端線上碼的方案,這個應該比較容易想到,只要支付寶/微信在登入的情況下,點選付款按鈕,客戶端呼叫後臺系統的申請付款碼介面。
後臺系統受到請求之後,生成一個付款碼,然後在資料庫儲存付款碼與使用者的關係,並且返回給客戶端。
只要客戶端在有效期內展示該付款碼,就可以完成支付,否則該二維碼就將會過期。
使用這種方案,相對來說比較安全,因為每次都是服務端生成碼,服務端可以控制冪等,沒有客戶端偽造的風險的。
另外即使需要對付款碼規則調整,比如付款碼位數增加一位,我們只要調整服務端程式碼即可,客戶端都無需升級。
不過這種方案缺點也比較明顯,客戶端必須實時線上聯網,沒有網路則無法獲取付款碼。
另外,現在有一些智慧裝置也開始支援支付寶支付,這些裝置中很大一部分是沒有聯網的功能(比如小米手環四),那這種情況是沒辦法使用線上碼方案。
基於這種情況,所以開始有了離線碼方案。
離線碼方案說起離線碼大家可能比較陌生,但是實際上你如果仔細觀察,其實很多場景都用到了離線碼。
比如說以前去黑網咖玩夢幻西遊的時候,賬號總是被盜。
沒辦法,花了一筆重資買了一個網易將軍令,每次登入的時候,除了輸入使用者名稱與密碼以外,還需要輸入動態口令。從此賬號就很少被盜了。
又比如說每次網易支付的時候,我們除了輸入銀行卡密碼以外,還需要輸入網銀盾上動態碼,這樣才能完成支付。
畫外音:這裡又要吐槽一下,網銀盾以前真的超難用,動不動就驅動不相容。還記得當初用網銀充值黃鑽,搞了一下午都沒有成功--!
當然上面這些可能已經是老古董了,很多人都可能沒用過,現在比較流行是手機驗證器APP,比如 Google Authenticator 等。
這種令牌器,動態產生一次性口令(OTP, One-time Password),可以防止密碼被盜用引發的安全風險。
其實付款碼離線方案技術原型就是基於這種方案,所以下面我們就基於 Google Authenticator,來了解一下這其中的原理。
動態口令技術原理首先如果我們需要使用 Google Authenticator,我們需要在網站上開啟二次驗證功能,以 Google 賬號為例,在設定兩步驗證的地方可以找到如下設定:
當我們繫結之後, Google Authenticator APP 將會展示動態碼。
我們來解析一下這個二維碼,對應下面這個字串:
otpauth://totp/Google%[email protected]?secret=xxxx&issuer=Google
上面的字串中,最重要就是這一串金鑰 secret,這個是一個經過 BASE32 編碼之後的字串,真正使用時需要將其使用BASE32 解碼,處理偽碼如下:
original_secret = xxxx xxxx xxxx xxxx xxxx xxxx xxxx xxxxsecret = BASE32_DECODE(TO_UPPERCASE(REMOVE_SPACES(original_secret)))
這個金鑰客戶端與服務端將會同時儲存一份,兩端將會同樣的演算法計算,以此用來比較動態碼的正確性。
我們以客戶端為例,生成一個動態碼,首先我們需要經過一個簽名函式,這裡 **Google Authenticator **採用的 HMAC-SHA1,這是一種基於雜湊的訊息驗證碼,可以用比較安全的單向雜湊函式(如 SHA1)來產生簽名。
簽名函式偽碼如下:
hmac = SHA1(secret + SHA1(secret + input))
上面函式中的,input 使用當前時間整除 30 的值。
input = CURRENT_UNIX_TIME() / 30
這裡時間就充當一個動態變參,這樣可以源源不斷產生動態碼。
另外這裡整除 30,是為了賦予驗證碼一個 30 秒的有效期。
這樣對於使用者輸入來講,可以有充足時間準備輸入這個動態碼,另外一點客戶端與服務端可能存在時間偏差,30 秒的間隔可以很大概率的遮蔽這種差異。
畫外音:這個有效時間其實很考量,如果比較長,安全性就差。
如果比較短,使用者體驗就很差,不容易輸入準備。
經過 HMAC-SHA1 簽名函式以後,我們得到一個長度為 40 的字串,我們還需要將其轉化為 6 位數字,方便使用者輸入。處理的偽碼如下:
four_bytes = hmac[LAST_BYTE(hmac):LAST_BYTE(hmac) + 4]large_integer = INT(four_bytes)small_integer = large_integer % 1,000,000
完整的演算法偽碼如下:
original_secret = xxxx xxxx xxxx xxxx xxxx xxxx xxxx xxxxsecret = BASE32_DECODE(TO_UPPERCASE(REMOVE_SPACES(original_secret)))input = CURRENT_UNIX_TIME() / 30hmac = SHA1(secret + SHA1(secret + input))four_bytes = hmac[LAST_BYTE(hmac):LAST_BYTE(hmac) + 4]large_integer = INT(four_bytes)small_integer = large_integer % 1,000,000
當客戶端將動態碼上傳給服務端,服務端查詢資料庫獲取到使用者對應的金鑰,然後使用同樣的演算法進行處理生成一個動態碼,最後比較客戶端上傳動態碼與服務端生成是否一致。
付款碼離線方案上面我們了解了動態口令的實現方案,付款碼生成原理其實也大致如此。
不過付款碼離線方案採用動態金鑰的方式(全域性唯一),定時請求服務端更換金鑰,以此保證更高的安全性。
另外在一次性動態口令方案,需要雙方基於同樣的祕鑰,所以服務端需要明確知道這背後正確使用者。以上面的登入場景為例,登入過程輸入使用者名稱,服務端就可以根據這個在資料庫中查詢相應的金鑰。
但是在付款碼的支付場景中,支付過程僅僅傳遞一個付款碼,就可以向相應的使用者扣款。不用想,這個付款碼這串數字一定包含相應的使用者資訊。
所以付款碼的相應的演算法相比動態碼會更加複雜,這樣才可以有效保證安全性。
看到這裡,不知道你們是否想了解這套演算法那?
哈哈,開個玩笑,這種演算法豈能是我們能掌握的。
支付寶核心演算法咱不知道,但是我們可以從其他人公開設計方案了解一個皮毛。
這裡小黑哥給你一個知乎網友@反方向的鐘回答的離線二維碼實現方式,給你 look look。
來自:/file/2020/09/03/20200903122829_16.jpg Root 許可權或者越獄手機,利用惡意程式獲取金鑰,然後隨意生成付款碼。
看到這一點,大家可能會擔心自己的錢包安全了。不過這一點,我覺得不過過分擔心,螞蟻集團這麼多大神,不是吃乾飯的,他們肯定有很多措施保證支付安全。
第三資料碰撞問題,A 使用者生成付款碼算出來與 B 使用者一致,這就 Hash 演算法一樣,再怎麼優秀的演算法,也有概率才生一樣的額 Hash 值。
這就導致原本是扣使用者 A 的錢,最後卻扣了 B 使用者。這樣一來,確實很烏龍,對於 B 使用者來講,莫名其妙被扣錢了。
不過放心,這種事放到放到現在,我覺得還是比買彩票中獎低,所以這種事還是不用過分擔心了。
即使真被誤扣了,放心,支付寶這麼大體量肯定會跟客戶賠錢的。
最後最後總結一下,我們平常使用付款碼支付,其實原理就是商家端獲取我們手機 APP 付款碼(其實就是一串數字),然後後臺呼叫支付寶支付介面完成扣款。
這個流程商家端後臺程式必須聯網線上,但是對於我們客戶端來講可以線上,也可以離線。
如果我們客戶端線上,那就可以通過服務端向客戶端傳送付款碼,這種方式更加安全,靈活,但是對於弱網環境下,體驗就很差。
如果我們客戶端沒網,那就通過客戶端通過一定演算法生成付款碼,服務端收到經過相關校驗,確認是哪個使用者,確認碼有效性,並且完成扣款。這種方式,適合客戶端沒有網路的情況,不過相對不靈活,且安全性稍差。
嘿嘿,了解原理,有沒有覺得還是挺有意思的~
下次排隊付款錢,如果手機沒網,不要擔心尷尬,放心拿出手機付錢~
參考/file/2020/09/03/20200903122829_17.jpg