標籤:
基於http協議的api介面對於用戶端的身份認證方式以及安全措施
由於http是無狀態的,所以正常情況下在瀏覽器瀏覽網頁,伺服器都是通過訪問者的cookie(cookie中儲存的jsessionid)來辨別用戶端的身份的,當用戶端進行登入伺服器也會將登入資訊存放在伺服器並與用戶端的cookie中的jsessionid關聯起來,這樣用戶端再次訪問我們就可以識別使用者身份了。
但是對於api伺服器,我們不能讓訪問者先登入再進行訪問這樣不安全,也不友好。所以一般情況我們都是需要用戶端提供一個key(每個key跟使用者是一對一關聯的)來識別要求者的身份。
由HTTP協議進行通訊的資料大都是未經加密的明文,包括請求參數、傳回值、 cookie、 head等等資料,因此,外界通過對通訊的監聽,輕而易舉便可根據請求和響應雙方的格式,偽造請求與響應,修改和竊取各種資訊。所以我們還需要對每次請求進行認證,來判斷髮起請求的是不是就是該使用者,以及請求資訊是否被篡改。一般採用對請求資訊(請求uri,參數)進行摘要的方法來解決上述問題。由於摘要演算法的無法復原性,因此這種方式能夠在一定程度上防止資訊被篡改,保障通訊的安全。
1、MD5方式
使用者需要先在網站上申請key、secret,然後校正流程如下:
用戶端:
1.參數排序
2.將參數串接起來加上secret,產生待摘要字串
3.使用MD5等摘要演算法產生摘要串signature
4.將key,signature放入header中一併傳給伺服器
伺服器:
1.參數排序
2.將參數串接起來加上secret(通過header中的key在資料庫擷取),產生待摘要字串
3.使用MD5等摘要演算法產生摘要串
4.服務端產生的摘要串與用戶端通過header傳遞過來的摘要串進行比較
2、HmacSHA256方式
使用者需要先在網站上申請key、secret,然後校正流程如下:
客戶單:
1.將請求參數封裝成json字串,也就是請求體body
2.使用HmacSHA256演算法加secret對(請求url+nonce+body)加密產生摘要signature
3.將key,signature放入header中一併傳給伺服器
伺服器:
1.擷取請求中的請求體body字串
2.使用HmacSHA256演算法加secret(通過header中的key在資料庫擷取)對(請求url+nonce+body)加密產生摘要signature
3.服務端產生的摘要串與用戶端通過header傳遞過來的摘要串進行比較
注意使用HmacSHA256更加安全,而且我們可以直接將請求參數封裝成json字串放入請求體中(也就是通過io流)進行傳遞。
實際使用中遇到的問題:
1、帶有底線的header被過濾
當我們在使用HmacSHA256進行認證的時候,需要用戶端將請求key,signature放入header,name設定為api_key,api_signature,這時出現一個問題是伺服器怎麼都擷取不到這兩個值,但是我在本機測試時沒有問題的。後來才想起來是不是由於使用nginx做叢集而部分頭被過濾了,查看過後果然是nginx將帶有底線的header name過濾了,後來修改nginx配置便可以正常擷取頭資訊。不過後來伺服器使用了第三方的動態加速再次把帶有底線的header name給過濾了,為了避免麻煩索性修改程式將header name中的底線都去掉了。
2、確保每次請求唯一性
由於http都是明文請求,雖然我們可以通過摘要進行一定的安全保證確保資訊不被篡改,但是我們無法保證每次請求的唯一性,也就是如果請求資料被別人擷取再次請求,此時也可能帶來很嚴重的安全性問題。於是我們便需要使用者在每次請求中設定一個遞增的參數nonce,來確保每次請求都是唯一的。不過這樣也可能帶來一個問題,就是如果使用者近乎同時發起兩個請求a b,由於網路阻塞,可能後發起的b先到達伺服器,這樣當a達到的時候,伺服器會認為a的nonce已到期請求非法而拒絕。為瞭解決這樣的問題我們允許使用者佈建一個expire值來避免nonce認證帶來的問題。
3、SNI
由於當時我們有不同的工程(不同的網域名稱,跟不同的認證)位於同一台伺服器,這樣有的用戶端訪問我們api工程會拋異常,說http握手失敗或者說請求網域名稱與伺服器憑證不匹配而失敗。所以我們需要用戶端程式支援sni,它允許用戶端在發起SSL握手請求時(具體說來,是用戶端發出SSL請求中的ClientHello階段),就提交請求的Host資訊,使得伺服器能夠切換到正確的域並返回相應的認證。對於java語言來說jdk7的後續版本已經支援sni,或者使用httpclient 4.3及以後版本都可以很好的支援sni了。
基於http協議的api介面對於用戶端的身份認證方式以及安全措施[轉]