API產(chǎn)品的認(rèn)證部分應(yīng)該如何設(shè)計(jì)?本文結(jié)合作者自己的工作實(shí)踐經(jīng)歷,對(duì)身份驗(yàn)證、對(duì)稱簽名身份驗(yàn)證、非對(duì)稱加密的簽名認(rèn)證三種方式進(jìn)行了分析,與大家分享。

做平臺(tái)產(chǎn)品,繞不開API。當(dāng)然API也需要伴侶,就是SDK。SDK可以把API的多次交互封裝起來,讓用戶(開發(fā)者)只需要調(diào)用一個(gè)方法,就能完成一個(gè)需要多次調(diào)用API的業(yè)務(wù)。
在使用API的時(shí)候,它必須知道是誰來使用這個(gè)API,這就需要身份認(rèn)證。最常見的身份認(rèn)證,是通過賬戶密碼登陸網(wǎng)站。不過現(xiàn)在這種操作越來越少了,現(xiàn)在主流的登陸方式,要么是手機(jī)驗(yàn)證碼登陸,要么是微信掃碼登陸,要么是調(diào)用其他賬號(hào)的登陸。
先來看看傳統(tǒng)登陸方式,數(shù)據(jù)庫存放用戶名和密碼。

假設(shè),用戶名為tom,密碼為123456。則用戶登陸時(shí),把tom和123456 POST到服務(wù)器。服務(wù)器去數(shù)據(jù)庫里一找,發(fā)現(xiàn)有tom,密碼也確實(shí)是123456,那么就登陸成功,也就是完成了身份認(rèn)證。
這是典型的明文賬戶系統(tǒng)。它最大的缺點(diǎn),是不安全。一旦數(shù)據(jù)庫泄露,就知道了用戶的密碼,這個(gè)時(shí)候,就可以去撞庫(用tom的密碼123456去登陸tom在別的網(wǎng)站的賬號(hào))。
有時(shí)候,明文保存密碼是黑客特意設(shè)置的,黑客設(shè)置一個(gè)網(wǎng)站吸引別人用郵箱去注冊(cè),明文保存了用戶的密碼。
為了安全起見,數(shù)據(jù)庫采用加密的方式保存密碼,而且一定是單項(xiàng)的,只能加密,不能解密。

如上圖所示,這是經(jīng)過md5處理的密碼。當(dāng)用戶登陸的時(shí)候,發(fā)送用戶名和經(jīng)過md5處理的密碼,后臺(tái)去數(shù)據(jù)庫里查詢,查詢到了說明登陸成功,否則就是登陸失敗。
這樣的好處是,黑客嗅探出了你的密碼,也僅僅能登陸你登陸的這個(gè)網(wǎng)站,他不能還原出你加密前的明文,也就無法去撞庫。即便網(wǎng)站數(shù)據(jù)庫泄露了,也很難知道你真正的密碼。
但是,現(xiàn)在有一些md5搜集器,搜集了很多明文密碼,并使用md5加密后存在數(shù)據(jù)庫里。

這樣,如果你的密碼是常規(guī)的密碼,則很容易被逆向(查詢)出來。比如:

它很容易就能查出你的原始密碼,它僅僅是查詢,而不是破解。因?yàn)橛写罅康某R?guī)的普遍的密碼和加密后的md5被存進(jìn)去。
如果想進(jìn)一步安全,怎么辦?辦法也是有的,那就是使用公私鑰的方式。

用戶登陸,系統(tǒng)產(chǎn)生一個(gè)隨機(jī)數(shù),發(fā)送給用戶。用戶需要使用私鑰對(duì)這個(gè)隨機(jī)數(shù)進(jìn)行簽名,發(fā)給系統(tǒng)。系統(tǒng)用公鑰驗(yàn)簽,完成身份認(rèn)證。
雖然這種方法安全,但是認(rèn)證時(shí)間長,對(duì)服務(wù)器造成的壓力也很大。在做產(chǎn)品設(shè)計(jì)的時(shí)候,成本因素也是一個(gè)必須考慮的問題,包括時(shí)間成本,服務(wù)器算力成本等。
這需要產(chǎn)品經(jīng)理根據(jù)應(yīng)用場景及安全級(jí)別去評(píng)估,取舍。而不是一味的選擇更安全的系統(tǒng)。
剛才提到的是用戶身份驗(yàn)證。而API中,更多的操作,是針對(duì)某個(gè)產(chǎn)品的權(quán)限的。

如上圖所示,一個(gè)用戶可以創(chuàng)建多個(gè)項(xiàng)目,每個(gè)項(xiàng)目都有一個(gè)key。我們假設(shè),應(yīng)用1用的是簡單身份驗(yàn)證,應(yīng)用1的key是一個(gè)具備一定長度的字符串。任何人只要知道這個(gè)字符串,就可以對(duì)其進(jìn)行操作。
應(yīng)用2比應(yīng)用1安全一些,采用的是keyID+keySecret的方式,也就是hmac-sha256簽名機(jī)制。它會(huì)用密鑰對(duì)keyID、調(diào)用的API、參數(shù)、時(shí)間等進(jìn)行簽名,因?yàn)榉?wù)器也有一個(gè)相同的keySecret,可以用來驗(yàn)證簽名,以確定身份。
應(yīng)用3就是更安全的,它有keyID和keyPubkey,即公鑰。調(diào)用的時(shí)候,使用私鑰對(duì)API、方法、參數(shù)、時(shí)間等進(jìn)行簽名,服務(wù)器使用私鑰對(duì)應(yīng)的公鑰對(duì)簽名進(jìn)行驗(yàn)簽,但時(shí)間會(huì)長一些。

總結(jié)一下, 通常API身份驗(yàn)證有兩種:簡單身份驗(yàn)證,和簽名身份驗(yàn)證。下面舉例子,說明哪些領(lǐng)域使用哪些身份驗(yàn)證方法。
通常情況而言,使用JAVAscript調(diào)用的API,基本上使用的是簡單身份驗(yàn)證,而且用的是url傳key的方式。比如,百度地圖的key,在調(diào)用的時(shí)候,是直接構(gòu)建在url里的:
<script type="text/JavaScript" src="http://api.map.baidu.com/api?v=2.0&ak=您的密鑰"></script>
這意味著,你的key很容易泄露。比如,稍微懂前端的,就可以從源碼里看到你的key。以及,懂點(diǎn)網(wǎng)絡(luò)嗅探的,也能輕而易舉能得到key。因此對(duì)于簡單身份驗(yàn)證,還需要一個(gè)ip或域名白名單進(jìn)行配合。這樣,即便別人獲得了你的key,也無法使用你的key,除非他同時(shí)入侵了你的服務(wù)器,在你的ip上面進(jìn)行操作。
post傳key和header傳key沒有本質(zhì)上的區(qū)別。例如,remove.bg 的API,使用的是headers傳遞key,它也是簡單身份驗(yàn)證,沒有進(jìn)行簽名。
$ curl -H 'X-API-Key: YOUR_API_KEY' -F 'image_file=@/path/to/file.jpg' -f https://api.remove.bg/v1.0/removebg -o no-bg.png
而微信的API,則需要簽名。如微信支付的簽名算法:
第一步,設(shè)所有發(fā)送或者接收到的數(shù)據(jù)為集合M,將集合M內(nèi)非空參數(shù)值的參數(shù)按照參數(shù)名ASCII碼從小到大排序(字典序),使用URL鍵值對(duì)的格式(即key1=value1&key2=value2…)拼接成字符串stringA。 特別注意以下重要規(guī)則:
- 參數(shù)名ASCII碼從小到大排序(字典序);
- 如果參數(shù)的值為空不參與簽名;
- 參數(shù)名區(qū)分大小寫;
- 驗(yàn)證調(diào)用返回或微信主動(dòng)通知簽名時(shí),傳送的sign參數(shù)不參與簽名,將生成的簽名與該sign值作校驗(yàn)。
- 微信接口可能增加字段,驗(yàn)證簽名時(shí)必須支持增加的擴(kuò)展字段
第二步,在stringA最后拼接上key得到stringSignTemp字符串,并對(duì)stringSignTemp進(jìn)行MD5運(yùn)算,再將得到的字符串所有字符轉(zhuǎn)換為大寫,得到sign值signValue。
微信支付的驗(yàn)證之所以復(fù)雜,是因?yàn)樗枰踩?/p>
目前很多物聯(lián)網(wǎng)傳感器的接入API,采取了簡單身份驗(yàn)證的方式。而大多數(shù)可遠(yuǎn)程操控的智能設(shè)備,則采取對(duì)稱簽名機(jī)制。

如上圖所示,機(jī)制云使用的是對(duì)稱加密的簽名機(jī)制。
大多數(shù)智能設(shè)備還不能使用非對(duì)稱簽名,比如ecdsa,為什么呢?因?yàn)榇蟛糠謫纹瑱C(jī),不支持非對(duì)稱加密,而一個(gè)支持ecdsa加密算法的安全芯片,會(huì)增加很多硬件成本。
最后總結(jié)一下,不需要特別注意安全的應(yīng)用,可以采用簡單身份驗(yàn)證配合ip白名單的方式。需要安全的應(yīng)用,可以使用對(duì)稱簽名身份驗(yàn)證方式。極端需要高安全性的應(yīng)用,可以采用非對(duì)稱加密的簽名認(rèn)證方式。
最后,不論采用哪種方式,都盡量能提供好用的sdk,以方便開發(fā)者使用。