現(xiàn)在流行的是restful風(fēng)格,前端,還有app都采用API接口的形式來與后端服務(wù)進(jìn)行數(shù)據(jù)通信,傳輸?shù)臄?shù)據(jù)很容易被偷窺该窗,抓包,那么我們在設(shè)計(jì)系統(tǒng)的時(shí)候如何設(shè)計(jì)出一套安全的API方案呢蚤霞?
解決方案:
- Token授權(quán)認(rèn)證酗失,防止未授權(quán)的用戶獲取數(shù)據(jù)。
- 時(shí)間戳超時(shí)機(jī)制昧绣。
- URL簽名规肴,防止請求參數(shù)被篡改。
- 防重放夜畴,防止接口被第二次請求拖刃,防采集。
- 采用HTTPS通信協(xié)議贪绘,防止數(shù)據(jù)明文傳輸兑牡。
一、Token授權(quán)認(rèn)證
HTTP協(xié)議是無狀態(tài)的税灌,一次請求結(jié)束均函,連接斷開亿虽,下次服務(wù)器再收到請求,它就不知道這個(gè)請求是哪個(gè)用戶發(fā)過來的边酒,但是對我們有權(quán)限訪問限制的模塊而言经柴,它是需要有狀態(tài)管理的狸窘,以便服務(wù)端能夠準(zhǔn)確的知道HTTP請求是哪個(gè)用戶發(fā)起的墩朦,從而判斷他是否有權(quán)限繼續(xù)這個(gè)請求。
Token的設(shè)計(jì)方案是用戶在客戶端使用用戶名和密碼登錄后翻擒,服務(wù)器會給客戶端返回一個(gè)Token氓涣,并將Token以鍵值對的形式存放在緩存(一般是Redis)中,后續(xù)客戶端對需要授權(quán)模塊的所有操作都要帶上這個(gè)Token陋气,服務(wù)器端接收到請求后進(jìn)行Token驗(yàn)證劳吠,如果Token存在,說明是授權(quán)的請求巩趁。
Token生成的設(shè)計(jì)要求:
1痒玩、應(yīng)用內(nèi)一定要唯一,否則會出現(xiàn)授權(quán)混亂议慰,A用戶看到了B用戶的數(shù)據(jù)蠢古;
2、每次生成的Token一定要不一樣别凹,防止被記錄草讶,授權(quán)永久有效;
3炉菲、一般Token對應(yīng)的是Redis的key堕战,value存放的是這個(gè)用戶相關(guān)緩存信息,比如:用戶的id拍霜;
4嘱丢、要設(shè)置Token的過期時(shí)間,過期后需要客戶端重新登錄祠饺,獲取新的Token越驻,如果Token有效期設(shè)置較短,會反復(fù)需要用戶登錄吠裆,體驗(yàn)比較差伐谈,我們一般采用Token過期后,客戶端靜默登錄的方式试疙,當(dāng)客戶端收到Token過期后诵棵,客戶端用本地保存的用戶名和密碼在后臺靜默登錄來獲取新的Token,還有一種是單獨(dú)出一個(gè)刷新Token的接口祝旷,但是一定要注意刷新機(jī)制和安全問題履澳;
根據(jù)上面的設(shè)計(jì)方案要求嘶窄,我們很容易得到Token=md5(用戶ID+登錄的時(shí)間戳+服務(wù)器端秘鑰)這種方式來獲得Token,因?yàn)橛脩鬒D是應(yīng)用內(nèi)唯一的距贷,登錄的時(shí)間戳保證每次登錄的時(shí)候都不一樣柄冲,服務(wù)器端秘鑰是配置在服務(wù)器端參與加密的字符串(即:鹽),目的是提高Token加密的破解難度忠蝗,注意一定不要泄漏现横;
二、時(shí)間戳超時(shí)機(jī)制
客戶端每次請求接口都帶上當(dāng)前時(shí)間的時(shí)間戳timestamp阁最,服務(wù)端接收到timestamp后跟當(dāng)前時(shí)間進(jìn)行比對戒祠,如果時(shí)間差大于一定時(shí)間(比如:1分鐘),則認(rèn)為該請求失效速种。時(shí)間戳超時(shí)機(jī)制是防御DOS攻擊的有效手段姜盈。
例:http://url/getInfo?id=1&timetamp=1559396263
三、URL簽名
寫過支付寶或微信支付對接的同學(xué)肯定對URL簽名不陌生配阵,我們只需要將原本發(fā)送給server端的明文參數(shù)做一下簽名馏颂,然后在server端用相同的算法再做一次簽名,對比兩次簽名就可以確保對應(yīng)明文的參數(shù)有沒有被中間人篡改過棋傍。
首先我們需要分配給客戶端一個(gè)私鑰用于URL簽名加密救拉,一般的簽名算法如下:
1、首先對通信的參數(shù)按key進(jìn)行字母排序放入數(shù)組中(一般請求的接口地址也要參與排序和簽名舍沙,那么需要額外添加url=http://url/getInfo這個(gè)參數(shù))近上;
2、對排序完的數(shù)組鍵值對用&進(jìn)行連接拂铡,形成用于加密的參數(shù)字符串壹无;
3、在加密的參數(shù)字符串前面或者后面加上私鑰感帅,然后用md5進(jìn)行加密斗锭,得到sign,然后隨著請求接口一起傳給服務(wù)器失球。
例如:
http://url/getInfo?id=1&timetamp=1559396263&sign=e10adc3949ba59abbe56e057f20f883e
服務(wù)器端接收到請求后岖是,用同樣的算法獲得服務(wù)器的sign,對比客戶端的sign是否一致实苞,如果一致請求有效豺撑;
注意:對于客戶端的私鑰一定要妥善處理好,不能被非法者拿到黔牵,如果針對于H5的項(xiàng)目聪轿,H5保存私鑰是個(gè)問題,目前沒有更好的方法猾浦,也是一致困擾我的問題陆错,如果大家有更好的方法可以留言一起探討灯抛。
四、防重放
客戶端第一次訪問時(shí)音瓷,將簽名sign存放到服務(wù)器的Redis中对嚼,超時(shí)時(shí)間設(shè)定為跟時(shí)間戳的超時(shí)時(shí)間一致,二者時(shí)間一致可以保證無論在timestamp限定時(shí)間內(nèi)還是外 URL都只能訪問一次绳慎,如果被非法者截獲纵竖,使用同一個(gè)URL再次訪問,如果發(fā)現(xiàn)緩存服務(wù)器中已經(jīng)存在了本次簽名偷线,則拒絕服務(wù)磨确。如果在緩存中的簽名失效的情況下沽甥,有人使用同一個(gè)URL再次訪問声邦,則會被時(shí)間戳超時(shí)機(jī)制攔截,這就是為什么要求sign的超時(shí)時(shí)間要設(shè)定為跟時(shí)間戳的超時(shí)時(shí)間一致摆舟。拒絕重復(fù)調(diào)用機(jī)制確保URL被別人截獲了也無法使用(如抓取數(shù)據(jù))亥曹。
以上方案流程如下:
1、客戶端通過用戶名密碼登錄服務(wù)器并獲取Token恨诱;
2媳瞪、客戶端生成時(shí)間戳timestamp,并將timestamp作為其中一個(gè)參數(shù)照宝;
3蛇受、客戶端將所有的參數(shù),包括Token和timestamp按照自己的簽名算法進(jìn)行排序加密得到簽名sign
4厕鹃、將token兢仰、timestamp和sign作為請求時(shí)必須攜帶的參數(shù)加在每個(gè)請求的URL后邊
例:
5、服務(wù)端對token剂碴、timestamp和sign進(jìn)行驗(yàn)證把将,只有在token有效、timestamp未超時(shí)忆矛、緩存服務(wù)器中不存在sign三種情況同時(shí)滿足察蹲,本次請求才有效;
五催训、采用HTTPS通信協(xié)議
眾所周知HTTP協(xié)議是以明文方式發(fā)送內(nèi)容洽议,不提供任何方式的數(shù)據(jù)加密,如果攻擊者截取了客戶端和服務(wù)器之間的傳輸報(bào)文漫拭,就可以直接讀懂其中的信息亚兄,因此HTTP協(xié)議不適合傳輸一些敏感信息,比如信用卡號嫂侍、密碼等儿捧。
為了解決HTTP協(xié)議的這一缺陷荚坞,需要使用另一種協(xié)議:安全套接字層超文本傳輸協(xié)議HTTPS,為了數(shù)據(jù)傳輸?shù)陌踩贫埽琀TTPS在HTTP的基礎(chǔ)上加入了SSL協(xié)議颓影,SSL依靠證書來驗(yàn)證服務(wù)器的身份,并為客戶端和服務(wù)器之間的通信加密懒鉴。
HTTPS也不是絕對安全的诡挂,如下圖所示為中間人劫持攻擊,中間人可以獲取到客戶端與服務(wù)器之間所有的通信內(nèi)容临谱。
中間人截取客戶端發(fā)送給服務(wù)器的請求璃俗,然后偽裝成客戶端與服務(wù)器進(jìn)行通信;將服務(wù)器返回給客戶端的內(nèi)容發(fā)送給客戶端悉默,偽裝成服務(wù)器與客戶端進(jìn)行通信城豁。
通過這樣的手段,便可以獲取客戶端和服務(wù)器之間通信的所有內(nèi)容抄课。
使用中間人攻擊手段唱星,必須要讓客戶端信任中間人的證書,如果客戶端不信任跟磨,則這種攻擊手段也無法發(fā)揮作用间聊。
2 針對安全性要求一般的app,可采用通過校驗(yàn)域名抵拘,證書有效性哎榴、證書關(guān)鍵信息及證書鏈的方式;
總結(jié):所有的安全措施都用上的話有時(shí)候難免太過復(fù)雜僵蛛,在實(shí)際項(xiàng)目中需要根據(jù)自身情況作出取舍尚蝌,比如可以只使用簽名機(jī)制就可以保證信息不會被篡改,或者定向提供服務(wù)的時(shí)候只用Token機(jī)制就可以了墩瞳,如何取舍驼壶,全看項(xiàng)目實(shí)際情況和對接口安全性的要求。