API產品的認證部分應該如何設計?本文結合作者自己的工作實踐經歷,對身份驗證、對稱簽名身份驗證、非對稱加密的簽名認證三種方式進行了分析,與大家分享。
做平臺產品,繞不開API。當然API也需要伴侶,就是SDK。SDK可以把API的多次交互封裝起來,讓用戶(開發者)只需要調用一個方法,就能完成一個需要多次調用API的業務。
在使用API的時候,它必須知道是誰來使用這個API,這就需要身份認證。最常見的身份認證,是通過賬戶密碼登陸網站。不過現在這種操作越來越少了,現在主流的登陸方式,要么是手機驗證碼登陸,要么是微信掃碼登陸,要么是調用其他賬號的登陸。
先來看看傳統登陸方式,數據庫存放用戶名和密碼。
假設,用戶名為tom,密碼為123456。則用戶登陸時,把tom和123456 POST到服務器。服務器去數據庫里一找,發現有tom,密碼也確實是123456,那么就登陸成功,也就是完成了身份認證。
這是典型的明文賬戶系統。它最大的缺點,是不安全。一旦數據庫泄露,就知道了用戶的密碼,這個時候,就可以去撞庫(用tom的密碼123456去登陸tom在別的網站的賬號)。
有時候,明文保存密碼是黑客特意設置的,黑客設置一個網站吸引別人用郵箱去注冊,明文保存了用戶的密碼。
為了安全起見,數據庫采用加密的方式保存密碼,而且一定是單項的,只能加密,不能解密。
如上圖所示,這是經過md5處理的密碼。當用戶登陸的時候,發送用戶名和經過md5處理的密碼,后臺去數據庫里查詢,查詢到了說明登陸成功,否則就是登陸失敗。
這樣的好處是,黑客嗅探出了你的密碼,也僅僅能登陸你登陸的這個網站,他不能還原出你加密前的明文,也就無法去撞庫。即便網站數據庫泄露了,也很難知道你真正的密碼。
但是,現在有一些md5搜集器,搜集了很多明文密碼,并使用md5加密后存在數據庫里。
這樣,如果你的密碼是常規的密碼,則很容易被逆向(查詢)出來。比如:
它很容易就能查出你的原始密碼,它僅僅是查詢,而不是破解。因為有大量的常規的普遍的密碼和加密后的md5被存進去。
如果想進一步安全,怎么辦?辦法也是有的,那就是使用公私鑰的方式。
用戶登陸,系統產生一個隨機數,發送給用戶。用戶需要使用私鑰對這個隨機數進行簽名,發給系統。系統用公鑰驗簽,完成身份認證。
雖然這種方法安全,但是認證時間長,對服務器造成的壓力也很大。在做產品設計的時候,成本因素也是一個必須考慮的問題,包括時間成本,服務器算力成本等。
這需要產品經理根據應用場景及安全級別去評估,取舍。而不是一味的選擇更安全的系統。
剛才提到的是用戶身份驗證。而API中,更多的操作,是針對某個產品的權限的。
如上圖所示,一個用戶可以創建多個項目,每個項目都有一個key。我們假設,應用1用的是簡單身份驗證,應用1的key是一個具備一定長度的字符串。任何人只要知道這個字符串,就可以對其進行操作。
應用2比應用1安全一些,采用的是keyID+keySecret的方式,也就是hmac-sha256簽名機制。它會用密鑰對keyID、調用的API、參數、時間等進行簽名,因為服務器也有一個相同的keySecret,可以用來驗證簽名,以確定身份。
應用3就是更安全的,它有keyID和keyPubkey,即公鑰。調用的時候,使用私鑰對API、方法、參數、時間等進行簽名,服務器使用私鑰對應的公鑰對簽名進行驗簽,但時間會長一些。
總結一下, 通常API身份驗證有兩種:簡單身份驗證,和簽名身份驗證。下面舉例子,說明哪些領域使用哪些身份驗證方法。
通常情況而言,使用JAVAscript調用的API,基本上使用的是簡單身份驗證,而且用的是url傳key的方式。比如,百度地圖的key,在調用的時候,是直接構建在url里的:
<script type="text/JavaScript" src="http://api.map.baidu.com/api?v=2.0&ak=您的密鑰"></script>
這意味著,你的key很容易泄露。比如,稍微懂前端的,就可以從源碼里看到你的key。以及,懂點網絡嗅探的,也能輕而易舉能得到key。因此對于簡單身份驗證,還需要一個ip或域名白名單進行配合。這樣,即便別人獲得了你的key,也無法使用你的key,除非他同時入侵了你的服務器,在你的ip上面進行操作。
post傳key和header傳key沒有本質上的區別。例如,remove.bg 的API,使用的是headers傳遞key,它也是簡單身份驗證,沒有進行簽名。
$ 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,則需要簽名。如微信支付的簽名算法:
第一步,設所有發送或者接收到的數據為集合M,將集合M內非空參數值的參數按照參數名ASCII碼從小到大排序(字典序),使用URL鍵值對的格式(即key1=value1&key2=value2…)拼接成字符串stringA。 特別注意以下重要規則:
- 參數名ASCII碼從小到大排序(字典序);
- 如果參數的值為空不參與簽名;
- 參數名區分大小寫;
- 驗證調用返回或微信主動通知簽名時,傳送的sign參數不參與簽名,將生成的簽名與該sign值作校驗。
- 微信接口可能增加字段,驗證簽名時必須支持增加的擴展字段
第二步,在stringA最后拼接上key得到stringSignTemp字符串,并對stringSignTemp進行MD5運算,再將得到的字符串所有字符轉換為大寫,得到sign值signValue。
微信支付的驗證之所以復雜,是因為它需要更安全。
目前很多物聯網傳感器的接入API,采取了簡單身份驗證的方式。而大多數可遠程操控的智能設備,則采取對稱簽名機制。
如上圖所示,機制云使用的是對稱加密的簽名機制。
大多數智能設備還不能使用非對稱簽名,比如ecdsa,為什么呢?因為大部分單片機,不支持非對稱加密,而一個支持ecdsa加密算法的安全芯片,會增加很多硬件成本。
最后總結一下,不需要特別注意安全的應用,可以采用簡單身份驗證配合ip白名單的方式。需要安全的應用,可以使用對稱簽名身份驗證方式。極端需要高安全性的應用,可以采用非對稱加密的簽名認證方式。
最后,不論采用哪種方式,都盡量能提供好用的sdk,以方便開發者使用。