2017-10-12 82 views
1

我正在爲傳統PHP站點添加REST API。這是爲內部應用程序提供端點,所以我在設計事物和做什麼以及不支持時都很自由。爲PHP REST API實現簡單身份驗證

我現在需要添加到此API中的方式是登錄,然後以特定用戶身份執行操作。該網站已建成多年前,並不一定與當時的最佳做法,所以我不幸在我如何做到這一點上受到限制。所有這些都需要使用MySQL 5.6運行在PHP 5.4中。

我一直在閱讀常見的設計和OAuth1/2看起來像最常見的標準。然而,這對於我的目的來說似乎是大規模的矯枉過正,因爲它具有我不需要的各種功能,並且實現起來似乎非常複雜。

相反,我打算只是在做這樣的事情:

  • 客戶端調用get_session API端點,它生成一個隨機會話ID,將保存在數據庫中的表,並返回到客戶。
  • 客戶端保存此會話ID。
  • 然後,客戶端通過向login端點發送請求進行身份驗證,發送用戶名,密碼和會話ID(顯然通過HTTPS)。
  • 服務器將數據與用戶表進行比較,如果登錄正確,則更新會話表以將會話ID與相應的用戶ID相關聯。這需要通過某種方式來限制速度,以防止暴力強制。
  • 現在,客戶端可以調用任何其他端點,只提供其授權的會話ID。
  • 在每個請求上,服務器查找會話ID,查看與哪個用戶關聯並執行正確的操作。
  • 客戶端可以記住會話ID以備將來使用,直到它被手動刪除或在一段時間後過期。
  • 要註銷,客戶端向logout端點發送請求,服務器將刪除與該用戶帳戶的關聯。

這是一個合理的設計?這顯然不是很複雜,但我正在尋找一些我可以實現而不需要很大麻煩或需要第三方庫的東西。

+0

只需使用基本訪問驗證和HTTPS即可。 – DanielO

+2

和往常一樣,downvoters應該留下解釋性評論。對於它的價值來說,這似乎是一個經過深入研究的詳細問題。我也不同意這是「主要基於意見」。肯定有一套最佳實踐,這是一個很好的答案,就像Sammitch所做的那樣。 –

回答

-1

那麼你寫了很多如何從零開始創建cookie以及如何將它們存儲在數據庫中。

與此同時,您已經擁有用於安全數據傳輸的用戶名,密碼和HTTPS。你爲什麼不使用cookies?

+0

我的印象是Cookie不適合用於REST API。我甚至不確定我用於應用程序的框架是否支持它們。 – Nils

+0

Cookie只是HTTP(S)請求的一部分。它是頭文件請求中的一行(甚至是一些行)。你可以把它叫做你想要的,但是你的「會話」機制在第一個帖子中是一個cookie。 – user1597430

2

我會說你應該生成一個唯一的令牌並將其用於通信。 基本上:

  • 客戶端發送用戶名/密碼到login資源。
  • 服務器驗證用戶名/密碼組合。如果它是正確的,它會生成一個唯一的toquen,將它保存在sessions表中,並將其發回給用戶,以及狀態更新如logged_in = TRUE
  • 現在,用戶發送的每個其他請求都應包含一個token字段(作爲POST字段或GET參數)。在這一點上,我將重新考慮使用REST,並且只使用POST請求所有內容,將operation作爲POST字段。這不會將令牌添加到URL中,因此可以讓它在網絡瀏覽歷史記錄,路由器和其他東西上註冊。
  • 對於每個請求,服務器應該檢查令牌是否存在並且是否有效。如果不是,只需返回一個錯誤消息,如403 Forbiddenlogged_in = FALSE

該系統還可以要求再派數據,使其更安全,像客戶端生成unique id之類的東西,這應該與令牌發送和檢查服務器端。

+0

所以你的意思是我會跳過第一步('get_session'),並且一旦登錄成功就簡單地生成會話ID /令牌?那是個很好的觀點。然而,我希望使用'get_session'機制來跟蹤登錄嘗試的次數以防止暴力破解,所以我需要找到一個替代方案。 – Nils

+0

是的,你可以使用一個cookie。您應該閱讀CodeIgniter框架如何執行它。它基本上生成一個請求/會話標記(基本上是一個字符串),這是作爲一個cookie發送給用戶的整個數據。然後它使用它從數據庫讀取一個寄存器並獲取整個會話數據(因此沒有敏感數據發送給用戶)。通常,應用程序允許您在執行HTTP操作時配置Cookie數據(例如,iOS上的「NSHTTPCookieStorage」,我不知道在Android上但它應該類似)。 –

0

您的計劃中的要點基本上是OAuth的基本功能。這取決於你的要求。如果您的API僅供內部使用,您可以使用HMAC-SHA身份驗證發送密鑰。

8

REST作爲一個概念的主要觀點之一是避免使用會話狀態,以便更容易地水平擴展REST端點的資源。如果您打算使用您的問題中概述的PHP $_SESSION,那麼您將發現自己處於難以實現共享會話存儲的困境之中,因爲您希望擴展。

雖然OAuth將是您想要做的首選方法,但完整的實現可能比您想要的更多工作。但是,您可以創建一些半角度的東西,但仍然保留會話少。你以前可能甚至見過類似的解決方案。

  1. 當調配API帳戶時,生成2個隨機值:一個令牌和一個祕密。
  2. 當客戶發出請求時,他們提供:
    • 令牌,明文。
    • 根據唯一但已知的值和祕密計算得出的值。例如:HMAC或加密簽名
  3. 然後,REST端點可以維護一個簡單的集中式令牌和祕密鍵值存儲,並通過計算該值來驗證請求。

通過這種方式,您可以保持「無會話」REST理想,並且在交換的任何部分都不會實際發送祕密。

客戶機示例:

$token = "Bmn0c8rQDJoGTibk";     // base64_encode(random_bytes(12)); 
$secret = "yXWczx0LwgKInpMFfgh0gCYCA8EKbOnw"; // base64_encode(random_bytes(24)); 
$stamp = "2017-10-12T23:54:50+00:00";  // date("c"); 
$sig = hash_hmac('SHA256', $stamp, base64_decode($secret)); 
// Result: "1f3ff7b1165b36a18dd9d4c32a733b15c22f63f34283df7bd7de65a690cc6f21" 

$request->addHeader("X-Auth-Token: $token"); 
$request->addHeader("X-Auth-Signature: $sig"); 
$request->addHeader("X-Auth-Timestamp: $date"); 

服務器實例:

$token = $request->getToken(); 
$secret = $auth->getSecret($token); 
$sig = $request->getSignature(); 

$success = $auth->validateSignature($sig, $secret); 

值得一提的是,如果決定使用時間戳作爲一個隨機數,你應該只接受最後幾分鐘內產生時間戳,以防止反對重播攻擊。大多數其他身份驗證方案將在簽名數據中包含附加組件,例如資源路徑,標頭數據的子集等,以進一步鎖定簽名以僅應用於單個請求。