天天看點

拒絕接口裸奔,開放API接口簽名驗證

接口安全問題

  • 請求身份是否合法?
  • 請求參數是否被篡改?
  • 請求是否唯一?

AccessKey&SecretKey (開放平台)

請求身份

為開發者配置設定AccessKey(開發者辨別,確定唯一)和SecretKey(用于接口加密,確定不易被窮舉,生成算法不易被猜測)。

防止篡改

參數簽名

  • 按照請求參數名的字母升序排列非空請求參數(包含AccessKey),使用URL鍵值對的格式(即key1=value1&key2=value2…)拼接成字元串stringA;
  • 在stringA最後拼接上Secretkey得到字元串stringSignTemp;
  • 對stringSignTemp進行MD5運算,并将得到的字元串所有字元轉換為大寫,得到sign值。

請求攜帶參數AccessKey和Sign,隻有擁有合法的身份AccessKey和正确的簽名Sign才能放行。這樣就解決了身份驗證和參數篡改問題,即使請求參數被劫持,由于擷取不到SecretKey(僅作本地加密使用,不參與網絡傳輸),無法僞造合法的請求。

重播攻擊

雖然解決了請求參數被篡改的隐患,但是還存在着重複使用請求參數僞造二次請求的隐患。

timestamp+nonce方案

nonce指唯一的随機字元串,用來辨別每個被簽名的請求。通過為每個請求提供一個唯一的辨別符,伺服器能夠防止請求被多次使用(記錄所有用過的nonce以阻止它們被二次使用)。

然而,對伺服器來說永久存儲所有接收到的nonce的代價是非常大的。可以使用timestamp來優化nonce的存儲。

假設允許用戶端和服務端最多能存在15分鐘的時間差,同時追蹤記錄在服務端的nonce集合。當有新的請求進入時,首先檢查攜帶的timestamp是否在15分鐘内,如超出時間範圍,則拒絕,然後查詢攜帶的nonce,如存在已有集合,則拒絕。否則,記錄該nonce,并删除集合内時間戳大于15分鐘的nonce(可以使用redis的expire,新增nonce的同時設定它的逾時失效時間為15分鐘)。

實作

請求接口:http://api.test.com/test?name=hello&home=world&work=java

用戶端

  • 生成目前時間戳timestamp=now和唯一随機字元串nonce=random
  • 按照請求參數名的字母升序排列非空請求參數(包含AccessKey)stringA="AccessKey=access&home=world&name=hello&work=java×tamp=now&nonce=random";
  • 拼接密鑰SecretKeystringSignTemp="AccessKey=access&home=world&name=hello&work=java×tamp=now&nonce=random&SecretKey=secret";
  • MD5并轉換為大寫sign=MD5(stringSignTemp).toUpperCase();
  • 最終請求http://api.test.com/test?name=hello&home=world&work=java×tamp=now&nonce=nonce&sign=sign;

服務端

拒絕接口裸奔,開放API接口簽名驗證

Token&AppKey(APP)

在APP開放API接口的設計中,由于大多數接口涉及到使用者的個人資訊以及産品的敏感資料,是以要對這些接口進行身份驗證,為了安全起見讓使用者暴露的明文密碼次數越少越好,然而用戶端與伺服器的互動在請求之間是無狀态的,也就是說,當涉及到使用者狀态時,每次請求都要帶上身份驗證資訊。

Token身份驗證

  • 使用者登入向伺服器提供認證資訊(如賬号和密碼),伺服器驗證成功後傳回Token給用戶端;
  • 用戶端将Token儲存在本地,後續發起請求時,攜帶此Token;
  • 伺服器檢查Token的有效性,有效則放行,無效(Token錯誤或過期)則拒絕。
  • 安全隐患:Token被劫持,僞造請求和篡改參數。

Token+AppKey簽名驗證

與上面開發平台的驗證方式類似,為用戶端配置設定AppKey(密鑰,用于接口加密,不參與傳輸),将AppKey和所有請求參數組合成源串,根據簽名算法生成簽名值,發送請求時将簽名值一起發送給伺服器驗證。

這樣,即使Token被劫持,對方不知道AppKey和簽名算法,就無法僞造請求和篡改參數。再結合上述的重發攻擊解決方案,即使請求參數被劫持也無法僞造二次重複請求。

實作

登陸和退出請求

拒絕接口裸奔,開放API接口簽名驗證

登陸和退出流程

後續請求

用戶端

  • 和上述開放平台的用戶端行為類似,把AccessKey改為token即可。

服務端

拒絕接口裸奔,開放API接口簽名驗證

服務端流程

資源擷取: