APP、前后端分離項目都采用API接口形式與服務器進行數據通信,傳輸的數據被偷窺、被抓包、被偽造時有發生,那么如何設計一套比較安全的API接口方案呢?
一般的解決方案如下:
1、Token授權認證,防止未授權用戶獲取數據;
2、時間戳超時機制;
3、URL簽名,防止請求參數被篡改;
4、防重放,防止接口被第二次請求,防采集;
5、采用HTTPS通信協議,防止數據明文傳輸;
一、Token授權認證
HTTP協議是無狀態的,一次請求結束,連接斷開,下次服務器再收到請求,它就不知道這個請求是哪個用戶發過來的,但是對我們有權限訪問限制的模塊而言,它是需要有狀態管理的,以便服務端能夠准確的知道HTTP請求是哪個用戶發起的,從而判斷他是否有權限繼續這個請求。
Token的設計方案是用戶在客戶端使用用戶名和密碼登錄后,服務器會給客戶端返回一個Token,並將Token以鍵值對的形式存放在緩存(一般是Redis)中,后續客戶端對需要授權模塊的所有操作都要帶上這個Token,服務器端接收到請求后進行Token驗證,如果Token存在,說明是授權的請求。
Token生成的設計要求:
1、應用內一定要唯一,否則會出現授權混亂,A用戶看到了B用戶的數據;
2、每次生成的Token一定要不一樣,防止被記錄,授權永久有效;
3、一般Token對應的是Redis的key,value存放的是這個用戶相關緩存信息,比如:用戶的id;
4、要設置Token的過期時間,過期后需要客戶端重新登錄,獲取新的Token,如果Token有效期設置較短,會反復需要用戶登錄,體驗比較差,我們一般采用Token過期后,客戶端靜默登錄的方式,當客戶端收到Token過期后,客戶端用本地保存的用戶名和密碼在后台靜默登錄來獲取新的Token,還有一種是單獨出一個刷新Token的接口,但是一定要注意刷新機制和安全問題;
根據上面的設計方案要求,我們很容易得到Token=md5(用戶ID+登錄的時間戳+服務器端秘鑰)這種方式來獲得Token,因為用戶ID是應用內唯一的,登錄的時間戳保證每次登錄的時候都不一樣,服務器端秘鑰是配置在服務器端參與加密的字符串(即:鹽),目的是提高Token加密的破解難度,注意一定不要泄漏;
二、時間戳超時機制
客戶端每次請求接口都帶上當前時間的時間戳timestamp,服務端接收到timestamp后跟當前時間進行比對,如果時間差大於一定時間(比如:1分鍾),則認為該請求失效。時間戳超時機制是防御DOS攻擊的有效手段。
例:http://url/getInfo?id=1&timetamp=1559396263
三、URL簽名
寫過支付寶或微信支付對接的同學肯定對URL簽名不陌生,我們只需要將原本發送給server端的明文參數做一下簽名,然后在server端用相同的算法再做一次簽名,對比兩次簽名就可以確保對應明文的參數有沒有被中間人篡改過。
首先我們需要分配給客戶端一個私鑰用於URL簽名加密,一般的簽名算法如下:
1、首先對通信的參數按key進行字母排序放入數組中(一般請求的接口地址也要參與排序和簽名,那么需要額外添加url=http://url/getInfo這個參數);
2、對排序完的數組鍵值對用&進行連接,形成用於加密的參數字符串;
3、在加密的參數字符串前面或者后面加上私鑰,然后用md5進行加密,得到sign,然后隨着請求接口一起傳給服務器。
例如:
http://url/getInfo?id=1&timetamp=1559396263&sign=e10adc3949ba59abbe56e057f20f883e
服務器端接收到請求后,用同樣的算法獲得服務器的sign,對比客戶端的sign是否一致,如果一致請求有效;
注意:對於客戶端的私鑰一定要妥善處理好,不能被非法者拿到,如果針對於H5的項目,H5保存私鑰是個問題,目前沒有更好的方法,也是一致困擾我的問題,如果大家有更好的方法可以留言一起探討。
四、防重放
客戶端第一次訪問時,將簽名sign存放到服務器的Redis中,超時時間設定為跟時間戳的超時時間一致,二者時間一致可以保證無論在timestamp限定時間內還是外 URL都只能訪問一次,如果被非法者截獲,使用同一個URL再次訪問,如果發現緩存服務器中已經存在了本次簽名,則拒絕服務。如果在緩存中的簽名失效的情況下,有人使用同一個URL再次訪問,則會被時間戳超時機制攔截,這就是為什么要求sign的超時時間要設定為跟時間戳的超時時間一致。拒絕重復調用機制確保URL被別人截獲了也無法使用(如抓取數據)。
以上方案流程如下:
1、客戶端通過用戶名密碼登錄服務器並獲取Token;
2、客戶端生成時間戳timestamp,並將timestamp作為其中一個參數;
3、客戶端將所有的參數,包括Token和timestamp按照自己的簽名算法進行排序加密得到簽名sign
4、將token、timestamp和sign作為請求時必須攜帶的參數加在每個請求的URL后邊
例:
http://url/request?token=h40adc3949bafjhbbe56e027f20f583a&timetamp=1559396263&sign=e10adc3949ba59abbe56e057f20f883e
5、服務端對token、timestamp和sign進行驗證,只有在token有效、timestamp未超時、緩存服務器中不存在sign三種情況同時滿足,本次請求才有效;
五、采用HTTPS通信協議
眾所周知HTTP協議是以明文方式發送內容,不提供任何方式的數據加密,如果攻擊者截取了客戶端和服務器之間的傳輸報文,就可以直接讀懂其中的信息,因此HTTP協議不適合傳輸一些敏感信息,比如信用卡號、密碼等。
為了解決HTTP協議的這一缺陷,需要使用另一種協議:安全套接字層超文本傳輸協議HTTPS,為了數據傳輸的安全,HTTPS在HTTP的基礎上加入了SSL協議,SSL依靠證書來驗證服務器的身份,並為客戶端和服務器之間的通信加密。
HTTPS也不是絕對安全的,如下圖所示為中間人劫持攻擊,中間人可以獲取到客戶端與服務器之間所有的通信內容。
中間人截取客戶端發送給服務器的請求,然后偽裝成客戶端與服務器進行通信;將服務器返回給客戶端的內容發送給客戶端,偽裝成服務器與客戶端進行通信。
通過這樣的手段,便可以獲取客戶端和服務器之間通信的所有內容。
使用中間人攻擊手段,必須要讓客戶端信任中間人的證書,如果客戶端不信任,則這種攻擊手段也無法發揮作用。
2 針對安全性要求一般的app,可采用通過校驗域名,證書有效性、證書關鍵信息及證書鏈的方式;
總結:所有的安全措施都用上的話有時候難免太過復雜,在實際項目中需要根據自身情況作出取舍,比如可以只使用簽名機制就可以保證信息不會被篡改,或者定向提供服務的時候只用Token機制就可以了,如何取舍,全看項目實際情況和對接口安全性的要求。