手機沒網了,卻還能支付,這是什麼原理?

{"type":"doc","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"現在生活已經離不開微信/支付寶電子支付,平常出去喫飯、購物只要帶個手機,就可以解決一切,以致於現在已經好久沒摸過真💰了。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"有一次出去喫飯,排着隊付錢,等着過程非常無聊,準備拔出手機來把荒野亂鬥,卻發現這個地方竟然連不上網 。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/a8/a84331af641fe29ad196fe1bfe59ba32.jpeg","alt":null,"title":"","style":[{"key":"width","value":"25%"},{"key":"bordertype","value":"none"}],"href":"","fromPaste":false,"pastePass":false}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"看着手機明明信號滿格,但是就是顯示網絡無連接,蘋果手機用戶痛,誰用誰知道。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"blockquote","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"畫外音:真的要 Diss 一下使用英特爾基帶的 Iphone,📶好差,沒事網絡就會閃斷~"}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"說回正題,由於沒有網絡,而我又沒帶錢,所以就怕付錢的時候因爲手機沒網,沒辦法使用支付寶扣款。正想着時,已經排到了我,不管三七二十一,先用下支付寶試試,實在不行爺不吃了。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"不過沒想到,當商家用掃碼搶掃描支付寶上付款碼支付以後,雖然我的手機最終沒有彈出支付成功的頁面,但是商家端顯示支付成功,併成功打印出了小票,過了一會,我的手機收到支付寶扣款短信。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"因爲我最近的工作對都是與微信/支付寶有關,整體支付流程還是比較清楚,但是付款碼爲什麼能離線支付確實不是很清楚,所以研究了一番,於是有了今天的文章。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/c3/c3d9ba2fff2bd7727c1b2a751c055df8.jpeg","alt":null,"title":null,"style":null,"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"blockquote","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":" 本文收錄在 https://studyidea.cn/ ,點擊查看更多支付相關文章"}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"科普支付方式"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"在聊付款碼離線原理之前,我們先給不熟悉支付寶/微信支付方式同學先科普一下常見的兩種支付方式。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"微信、支付寶線下支付常用支付方式有兩種,一種是我們打開手機,主動掃描商家提供碼牌,這種支付方式一般稱爲主掃支付(用戶主動掃碼)。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"以支付寶爲例,付款流程如圖所示:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/c5/c5bad511e5286df59e3853faef02dd01.jpeg","alt":"圖片來自支付寶官網","title":null,"style":null,"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"第二種則是我們打開手機,展示我們的付款碼,然後商家使用掃碼槍等工具獲取付款碼完成支付,這種支付方式一般稱爲被掃支付(用戶被掃碼)。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"以支付寶爲例,付款流程如圖所示:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/80/80c157628a163ca89476cb27b145994d.jpeg","alt":"圖片來自支付寶官網","title":null,"style":null,"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"對於第一種方式,需要手機端 APP 掃碼,然後彈窗確認付款,這種方式是沒有辦法在手機沒有網絡的情況完成支付,所以我們上文說的沒有網絡的情況特指付款碼支付的場景。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"付款碼付款流程"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"在聊付款碼離線支付的前提前,我們先來來看下付款碼的整體流程,以超市購物爲例,一次付款碼的支付信息流如圖所示:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/71/71aec9098a7059fd88fa323c2069b461.jpeg","alt":"參考知乎@天順","title":null,"style":null,"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"這個過程商家後臺系統是需要調用的支付寶條碼支付的接口,完成支付。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong"}],"text":"由於商家後臺需要在線聯網與支付寶後臺通訊,所以說付款碼的離線支付,指的是客戶端沒有的網絡的情況,商家端其實必須實時聯網在線。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"一次付款碼接口調用流程如圖所示:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/c1/c10edbb6e15a277089dfc019cccfd7a7.jpeg","alt":null,"title":"","style":[{"key":"width","value":"50%"},{"key":"bordertype","value":"none"}],"href":"","fromPaste":false,"pastePass":false}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"通過上面兩張圖,我們整體瞭解付款碼交互流程。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"付款碼的技術方案其實可以分爲客戶端在線與離線的兩種情況,下面我們來看下兩種方案具體實現方式。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"在線碼方案"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"客戶端在線碼的方案,這個應該比較容易想到,只要支付寶/微信在登錄的情況下,點擊付款按鈕,客戶端調用後臺系統的申請付款碼接口。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"後臺系統受到請求之後,生成一個付款碼,然後在數據庫保存付款碼與用戶的關係,並且返回給客戶端。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"只要客戶端在有效期內展示該付款碼,就可以完成支付,否則該二維碼就將會過期。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"使用這種方案,相對來說比較安全,因爲每次都是服務端生成碼,服務端可以控制冪等,沒有客戶端僞造的風險的。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"另外即使需要對付款碼規則調整,比如付款碼位數增加一位,我們只要調整服務端代碼即可,客戶端都無需升級。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong"}],"text":"不過這種方案缺點也比較明顯,客戶端必須實時在線聯網,沒有網絡則無法獲取付款碼。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"另外,現在有一些智能設備也開始支持支付寶支付,這些設備中很大一部分是沒有聯網的功能("},{"type":"text","marks":[{"type":"italic"}],"text":"比如小米手環四"},{"type":"text","text":"),那這種情況是沒辦法使用在線碼方案。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/54/54d5041f719cb1ee95444170f44e3364.jpeg","alt":null,"title":null,"style":null,"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"基於這種情況,所以開始有了離線碼方案。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"離線碼方案"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"說起離線碼大家可能比較陌生,但是實際上你如果仔細觀察,其實很多場景都用到了離線碼。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"比如說以前去黑網吧玩夢幻西遊的時候,賬號總是被盜。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/de/de468e3dfcd5e1a010f0a956be8cdd70.jpeg","alt":null,"title":"","style":[{"key":"width","value":"25%"},{"key":"bordertype","value":"none"}],"href":"","fromPaste":false,"pastePass":false}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"沒辦法,花了一筆重資買了一個網易將軍令,每次登錄的時候,除了輸入用戶名與密碼以外,還需要輸入動態口令。從此賬號就很少被盜了。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/69/69e90cb115dc988473de86ed963fa78d.jpeg","alt":null,"title":null,"style":null,"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"又比如說每次網易支付的時候,我們除了輸入銀行卡密碼以外,還需要輸入網銀盾上動態碼,這樣才能完成支付。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/59/59506ce072cb0f013e55b6d7d0634618.jpeg","alt":null,"title":null,"style":null,"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"blockquote","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"畫外音:這裏又要吐槽一下,網銀盾以前真的超難用,動不動就驅動不兼容。還記得當初用網銀充值黃鑽,搞了一下午都沒有成功--!"}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"當然上面這些可能已經是"},{"type":"text","marks":[{"type":"italic"}],"text":"老古董"},{"type":"text","text":"了,很多人都可能沒用過,現在比較流行是"},{"type":"text","marks":[{"type":"strong"}],"text":"手機驗證器APP"},{"type":"text","text":",比如 "},{"type":"text","marks":[{"type":"strong"}],"text":"Google Authenticator"},{"type":"text","text":" 等。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/fe/feae1ca520b562e5119ffc5c94505b02.jpeg","alt":null,"title":"","style":[{"key":"width","value":"50%"},{"key":"bordertype","value":"none"}],"href":"","fromPaste":false,"pastePass":false}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"這種令牌器,動態產生一次性口令("},{"type":"text","marks":[{"type":"strong"}],"text":"OTP, One-time Password"},{"type":"text","text":"),可以防止密碼被盜用引發的安全風險。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"其實付款碼離線方案技術原型就是基於這種方案,所以下面我們就基於 Google Authenticator,來了解一下這其中的原理。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"動態口令技術原理"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"首先如果我們需要使用 "},{"type":"text","marks":[{"type":"strong"}],"text":"Google Authenticator"},{"type":"text","text":",我們需要在網站上開啓二次驗證功能,以 Google 賬號爲例,在設置兩步驗證的地方可以找到如下設置:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/36/36b39c1286285d5c09de5195e52b5de3.jpeg","alt":null,"title":null,"style":null,"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"當我們點擊設置,將會彈出一個二維碼,然後使用 "},{"type":"text","marks":[{"type":"strong"}],"text":"Google Authenticator"},{"type":"text","text":" APP 掃碼綁定。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/bf/bf1cac408d560b085c98298c758a54c8.jpeg","alt":null,"title":null,"style":null,"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"當我們綁定之後, "},{"type":"text","marks":[{"type":"strong"}],"text":"Google Authenticator"},{"type":"text","text":" APP 將會展示動態碼。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"我們來解析一下這個二維碼,對應下面這個字符串:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":""},"content":[{"type":"text","text":"otpauth://totp/Google%[email protected]?secret=xxxx&issuer=Google"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"上面的字符串中,最重要就是這一串密鑰 "},{"type":"codeinline","content":[{"type":"text","text":"secret"}]},{"type":"text","text":",這個是一個經過 "},{"type":"text","marks":[{"type":"strong"}],"text":"BASE32"},{"type":"text","text":" 編碼之後的字符串,真正使用時需要將其使用"},{"type":"text","marks":[{"type":"strong"}],"text":"BASE32"},{"type":"text","text":" 解碼,處理僞碼如下:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"java"},"content":[{"type":"text","text":"original_secret = xxxx xxxx xxxx xxxx xxxx xxxx xxxx xxxx\nsecret = BASE32_DECODE(TO_UPPERCASE(REMOVE_SPACES(original_secret)))"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong"}],"text":"這個密鑰客戶端與服務端將會同時保存一份,兩端將會同樣的算法計算,以此用來比較動態碼的正確性。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"我們以客戶端爲例,生成一個動態碼,首先我們需要經過一個簽名函數,這裏 "},{"type":"text","marks":[{"type":"strong"}],"text":"Google Authenticator "},{"type":"text","text":"採用的 "},{"type":"text","marks":[{"type":"strong"}],"text":"HMAC-SHA1"},{"type":"text","text":",這是一種基於哈希的消息驗證碼,可以用比較安全的單向哈希函數(如 SHA1)來產生簽名。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"簽名函數僞碼如下:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"java"},"content":[{"type":"text","text":"hmac = SHA1(secret + SHA1(secret + input))"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"上面函數中的,"},{"type":"codeinline","content":[{"type":"text","text":"input"}]},{"type":"text","text":" 使用當前時間整除 30 的值。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"java"},"content":[{"type":"text","text":"input = CURRENT_UNIX_TIME() / 30"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"這裏時間就充當一個動態變參,這樣可以源源不斷產生動態碼。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong"}],"text":"另外這裏整除 30,是爲了賦予驗證碼一個 30 秒的有效期。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"這樣對於用戶輸入來講,可以有充足時間準備輸入這個動態碼,另外一點客戶端與服務端可能存在時間偏差,30 秒的間隔可以很大概率的屏蔽這種差異。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"blockquote","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"畫外音:這個有效時間其實很考量,如果比較長,安全性就差。"}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":">"}]},{"type":"blockquote","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"如果比較短,用戶體驗就很差,不容易輸入準備。"}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"經過 "},{"type":"text","marks":[{"type":"strong"}],"text":"HMAC-SHA1"},{"type":"text","text":" 簽名函數以後,我們得到一個長度爲 40 的字符串,我們還需要將其轉化爲 6 位數字,方便用戶輸入。處理的僞碼如下:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"java"},"content":[{"type":"text","text":"four_bytes = hmac[LAST_BYTE(hmac):LAST_BYTE(hmac) + 4]\nlarge_integer = INT(four_bytes)\nsmall_integer = large_integer % 1,000,000"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"完整的算法僞碼如下:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"java"},"content":[{"type":"text","text":"original_secret = xxxx xxxx xxxx xxxx xxxx xxxx xxxx xxxx\nsecret = BASE32_DECODE(TO_UPPERCASE(REMOVE_SPACES(original_secret)))\ninput = CURRENT_UNIX_TIME() / 30\nhmac = SHA1(secret + SHA1(secret + input))\nfour_bytes = hmac[LAST_BYTE(hmac):LAST_BYTE(hmac) + 4]\nlarge_integer = INT(four_bytes)\nsmall_integer = large_integer % 1,000,000"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"當客戶端將動態碼上傳給服務端,服務端查詢數據庫獲取到用戶對應的密鑰,然後使用同樣的算法進行處理生成一個動態碼,最後比較客戶端上傳動態碼與服務端生成是否一致。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"付款碼離線方案"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"上面我們瞭解了動態口令的實現方案,付款碼生成原理其實也大致如此。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"不過付款碼離線方案採用動態密鑰的方式("},{"type":"text","marks":[{"type":"strong"}],"text":"全局唯一"},{"type":"text","text":"),定時請求服務端更換密鑰,以此保證更高的安全性。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"另外在一次性動態口令方案,需要雙方基於同樣的祕鑰,所以服務端需要明確知道這"},{"type":"text","marks":[{"type":"strong"}],"text":"背後正確用戶"},{"type":"text","text":"。以上面的登錄場景爲例,登錄過程輸入用戶名,服務端就可以根據這個在數據庫中查詢相應的密鑰。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"但是在付款碼的支付場景中,支付過程僅僅傳遞一個付款碼,就可以向相應的用戶扣款。不用想,這個付款碼這串數字一定包含相應的用戶信息。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"所以付款碼的相應的算法相比動態碼會更加複雜,這樣纔可以有效保證安全性。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"看到這裏,不知道你們是否想了解這套算法那?"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/d9/d9b64db2b5401ef5f936fb31dd75ab53.jpeg","alt":null,"title":"","style":[{"key":"width","value":"50%"},{"key":"bordertype","value":"none"}],"href":"","fromPaste":false,"pastePass":false}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"哈哈,開個玩笑,這種算法豈能是我們能掌握的。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"支付寶核心算法咱不知道,但是我們可以從其他人公開設計方案瞭解一個皮毛。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"這裏小黑哥給你一個知乎網友"},{"type":"link","attrs":{"href":"https://www.zhihu.com/question/49811134/answer/135886638","title":""},"content":[{"type":"text","text":"@反方向的鐘回答"}]},{"type":"text","text":"的離線二維碼實現方式,給你 look look。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/47/475ca053a1b68d44d469ecbdb8e2c372.png","alt":"來自:https://www.zhihu.com/question/49811134/answer/135886638","title":null,"style":null,"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"付款碼離線碼的劣勢"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"最後我們來看下付款碼離線方案的劣勢:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"第一,算法調整不靈活,如果相關算法較大的調整,可能需要升級客戶端,並且這個期間服務端還需要兼容新老算法產生的付款碼。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"第二,安全性問題,正常的情況相關密鑰無法被普通用戶獲取,但是架不住有有心之人。他們可能通過獲取手機用戶 Root 權限或者越獄手機,利用惡意程序獲取密鑰,然後隨意生成付款碼。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"看到這一點,大家可能會擔心自己的錢包安全了。不過這一點,我覺得不過過分擔心,螞蟻集團這麼多大神,不是喫乾飯的,他們肯定有很多措施保證支付安全。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"第三數據碰撞問題,A 用戶生成付款碼算出來與 B 用戶一致,這就 Hash 算法一樣,再怎麼優秀的算法,也有概率才生一樣的額 Hash 值。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"這就導致原本是扣用戶 A 的錢,最後卻扣了 B 用戶。這樣一來,確實很烏龍,對於 B 用戶來講,莫名其妙被扣錢了。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/2d/2dd3230e42017836e2bf8bc04819d6ad.jpeg","alt":null,"title":null,"style":null,"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"不過放心,這種事放到放到現在,我覺得還是比買彩票中獎低,所以這種事還是不用過分擔心了。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"即使真被誤扣了,放心,支付寶這麼大體量肯定會跟客戶賠錢的。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"最後"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"最後總結一下,我們平常使用付款碼支付,其實原理就是商家端獲取我們手機 APP 付款碼("},{"type":"text","marks":[{"type":"strong"}],"text":"其實就是一串數字"},{"type":"text","text":"),然後後臺調用支付寶支付接口完成扣款。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"這個流程商家端後臺程序必須聯網在線,但是對於我們客戶端來講可以在線,也可以離線。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"如果我們客戶端在線,那就可以通過服務端向客戶端發送付款碼,這種方式更加安全,靈活,但是對於弱網環境下,體驗就很差。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"如果我們客戶端沒網,那就通過客戶端通過一定算法生成付款碼,服務端收到經過相關校驗,確認是哪個用戶,確認碼有效性,並且完成扣款。這種方式,適合客戶端沒有網絡的情況,不過相對不靈活,且安全性稍差。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"嘿嘿,瞭解原理,有沒有覺得還是挺有意思的~"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"下次排隊付款錢,如果手機沒網,不要擔心尷尬,放心拿出手機付錢~"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"參考"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"numberedlist","attrs":{"start":"1","normalizeStart":1},"content":[{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":1,"align":null,"origin":null},"content":[{"type":"text","text":"https://www.zhihu.com/question/49811134/answer/135886638"}]}]},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":2,"align":null,"origin":null},"content":[{"type":"text","text":"https://garbagecollected.org/2014/09/14/how-google-authenticator-works/"}]}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}}]}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章