2013-01-14 56 views
5

我已經爲部分遵循this very good blog post by Riyad Kalla的項目創建了一個簡單的RESTful API。現在,我已經閱讀了堆棧溢出的幾十個類似問題,但我似乎無法找到我的安全問題的答案。保護無會話RESTful API端點

簡單地說,我的要求是這樣的:

  1. 客戶端有一個公共的API密鑰(純文本,任何人嗅探網絡流量或正確地檢查代碼源訪問)
  2. 客戶端發送對服務器的請求,與公衆的API密鑰
  3. 該服務器有一個祕密的API密鑰(祕密到任何人,但開發者)
  4. 服務器創建客戶端的請求數據組成的HMAC-SHA1散列和祕密API密鑰
  5. 服務器發送相同的客戶端的請求,API服務器的請求,但其中所產生的HMAC-SHA1
  6. 的API服務器查找基於公共API密鑰收到
  7. 在其數據庫中的祕密API密鑰API服務器重新創建使用作爲開發者的服務器相同的數據
  8. 如果哈希匹配,該請求被認爲是有效的,並且通常處理

我擔心用我的服務了別人HMAC-SHA1哈希可能獲取公共API密鑰(通過嗅探網絡流量),然後簡單地捲曲sam e請求客戶最初通過使用AJAX的瀏覽器直接向開發人員的服務器完成。因此,惡意用戶可以被認證爲合法用戶並使用其他人的祕密API密鑰訪問API。

我會試着舉一個具體的例子。通常我會這樣做:

  1. AJAX a get request to my server。
  2. 我的服務器用我的API密鑰散列我的請求並將它發送到API服務器。
  3. API服務器驗證我的請求並返回有效負載。

但我很害怕,:

  1. 邪惡博士將嗅出我的公開API密鑰。
  2. Dr. Evil將使用我的公共API密鑰向我的服務器發送請求。
  3. 我的服務器會將Dr. Evil的請求與我的API祕密一起打散併發送給API服務器。
  4. API服務器驗證並返回有效載荷以完成Dr. Evil的惡意計劃。
  5. Dr. Evil笑一個邪惡的笑。

我什麼都不知道,或者這只是RESTful API遊戲的一部分?

更新:我自願忽略任何形式的時間戳驗證,以保持簡單,只關注身份驗證問題。

更新2:我已經爲該過程添加了一個$_SERVER['HTTP_REFERER']驗證。這裏的目標是客戶端必須發送一個referrer和請求,並且它必須匹配API側的數據庫中列出的引用者。不幸的是,HTTP引用者很容易僞造。這是另一個安全級別,但仍不完美。

更新3:我已更改我的服務器端代碼以將引用鏈接設置爲遠程IP地址。這會強制發送到我的服務器的每個請求都要使用祕密API密鑰進行散列,最終會使用原始請求IP地址訪問API服務器。這個IP然後可以被驗證並且請求可以通過。我相信它還有可能僞造$_SERVER['REMOTE_ADDR'],但它比假冒$_SERVER['HTTP_REFERER']更復雜...我猜還是不完美。

UPDATE 4:根據這些帖子:How to fake $_SERVER['REMOTE_ADDR'] variable?https://serverfault.com/questions/90725/are-ip-addresses-trivial-to-forge,僞造的$_SERVER['REMOTE_ADDR']是可能的,雖然困難。但是,由於您無法控制僞造的網絡,因此無法收到來自僞造請求的回覆。這個請求可以被成功驗證,但是它的迴應不會落入惡意的手中。

回答

0

我發現從使用公共API密鑰,將請求發送到服務器端的HMAC散列腳本阻止其他腳本的解決方案是與請求一起發送原始請求者的身份。我使用$_SERVER['REMOTE_ADDR']來確定原始請求者的身份,因爲它很難僞造,僞造它通常意味着他們不會得到迴應。

/* $this as a class that handles requests */ 

// Build hash and include timestamp 
$this->vars['timestamp'] = time(); 
$this->vars['hash'] = hash_hmac('sha1', http_build_query($this->vars).$this->vars['token'], API_SECRET); 

// Send request to API 
curl_setopt_array($this->curl, array(
    CURLOPT_RETURNTRANSFER => 1, 
    CURLOPT_URL => $url, 
    CURLOPT_POST => $this->method == 'post' ? 1 : NULL, 
    CURLOPT_POSTFIELDS => $this->method == 'post' ? $this->vars : NULL, 
    CURLOPT_CONNECTTIMEOUT => 15, 
    CURLOPT_TIMEOUT => 15, 
    CURLOPT_REFERER => $_SERVER['REMOTE_ADDR'], // Referer here! 
    CURLOPT_MAXREDIRS => 3, 
    CURLOPT_HTTPGET => $this->method == 'get' ? true : false 
)); 

一旦發送出去,該API不僅檢查從數據庫中祕密API密鑰,但它還會檢查$_SERVER['HTTP_REFERER']被列爲不允許的!這也允許API在每個用戶的基礎上接受服務器。

4

您正在通過使用HMAC正確的軌道。但是,還有兩件事會讓你的應用程序更安全。

  1. 在客戶端POST中需要一個時間戳,需要在服務器時間的5分鐘內進行驗證。它也應該包含在HMAC世代中。如果有人試圖改變這種情況,HMAC簽名將是無效的,除非他們有密鑰來更新HMAC簽名。
  2. 使用SSL和證書驗證。防止中間人受到攻擊。不允許任何非SSL請求。
+0

謝謝。我主動排除時間戳部分以使事情變得簡單,因爲在我當前的代碼中實現並不是什麼大事,我應該提到這一點。我不確定我是否正確理解SSL的功能。它是否使交通嗅探不可能?惡意用戶仍然可以從JavaScript源代碼中竊取它,不是嗎?我絕對錯過了這裏的東西...... –

+0

SSL是傳輸加密。它使得嗅探更加困難。即使使用API​​,這也是一個好主意。 – datasage

+0

但是如果惡意用戶可以從源頭獲取公共API密鑰,我仍然會遇到同樣的問題,對吧? –