[譯]移動API安全終極指南


文章主要講了移動api調用的授權和驗證問題,原文鏈接:The Ultimate Guide to Mobile API Security

移動API的使用是Stack Overflow和 Stormpath支持頻道上經常出現的話題。這是一個已經被解決的問題,但是需要大量必要的知識和充足的理解才能很好地實現。

這篇文章能夠讓你了解到有關於在移動設備上安全的調用Restful API的一切內容,無論你是在構建一個需要訪問Restful API的移動app,還是正在寫Restful API並且打算和移動app開發者進行交互。

我的目標不僅僅是解釋如何確保移動開發者能夠安全調用你的Restful API,同時還解釋整個憑證從開始到結束的交換過程,以及如何從安全漏洞中恢復等等。

移動API安全問題

在我們投身去探究如何確保移動開發者能夠安全調用Restful API之前,讓我們先討論一下什么使得移動認證有別於傳統的API認證。

API認證最基本的形式就是一般為我們所知的HTTP Basic Authentication

對於API服務開發者和使用它的人來說,它的工作方式是非常簡單的:

  • 開發者會獲取到一個API key(一般是ID和Secret)。這個API key一般像這樣:3bb743bbd45d4eb8ae31e16b9f83c9ba:ffb7d6369eb84580ad2e52ca3fc06c9d
  • 開發者有責任將API key存放在一個安全的地方,沒有人可以得到它。
  • 開發者發起API請求時,需要把API key放到HTTP Authorization header中,同時帶有關鍵詞Basic(也就是用戶名和密碼都使用base64加密)。下面展示了開發者在調用API時如何指定API key進行認證,使用命令行工具cURL
 $ curl --user 3bb743bbd45d4eb8ae31e16b9f83c9ba:ffb7d6369eb84580ad2e52ca3fc06c9d https://api.example.com/v1/test 

cURL工具可以將獲取到的API證書用base64加密,然后創建一個HTTP Authorization hearder,如下所示:Basic M2JiNzQzYmJkNDVkNGViOGFlMzFlMTZiOWY4M2M5YmE6ZmZiN2Q2MzY5ZWI4NDU4MGFkMmU1MmNhM2ZjMDZjOWQ=.。而API服務器將會從HTTP Authorization hearder中獲取字符串,然后用base64解密,獲得ID和Secret,驗證通過后再處理相應的API請求。

HTTP Basic Authentication雖然簡單,但是很好用。開發者在調用API時附帶API key可以很好地與API服務器進行驗證。

但是,由於移動app無法安全存儲API秘鑰,使得HTTP Basic Authentication並不是一個很好的選擇。同時,HTTP Basic Authentication要求在每次請求時使用原始的API keys,這就導致了keys會被長期使用,這是不安全的。

因此在大多數情況下,這種驗證方式是不切實際的,因為無法安全的將API keys嵌入到分配給許多用戶的移動app中。

比如說,你將API keys嵌入到你所構建的移動app中,然而一個精明的用戶能夠對你app進行反向工程,獲取到keys,從而濫用你的API服務。

這就是為什么在不可信的環境中,HTTP Basic Authentication不是一個最好的選擇,例如web瀏覽器和移動app。

注意:和其他所有認證協議一樣,HTTP Basic Authentication必須在SSL之上使用。

為移動安全引入OAuth2.0

在此之前你可能聽說過OAuth,並且在爭論它是什么和它並沒有足夠的好。那么讓我們明確:OAuth2是一個優秀的協議,在不受信任的設備中用於保護API服務的安全。它通過一種我們稱之為token的認證方式來對移動用戶進行認證。

下面我們從用戶的角度OAuth2 token認證的工作方式。

  1. 用戶打開移動app,輸入賬號和密碼。
  2. 移動app發送一個帶有用戶的用戶名和密碼的POST請求到API服務器,服務器進行驗證,成功后返回一個code。
  3. 移動app通過code向認證服務器發送請求,認證成功后為用戶生成一個一段時間后就會過期的access token。
  4. 將access token存儲在移動設備本地,就像一個能夠訪問API服務的API key一樣。
  5. 一旦access token過期就不再工作,需要再提示用戶輸入用戶名密碼,重復1的步驟。

之所以說OAuth2在保障APIs安全上是極好的,是因為我們不需要在一個不安全的環境中長期存儲API keys,取而代之的是生成一個臨時的access tokens。這可以抵御一些潛在的攻擊。

現在,當您的API服務生成您的移動應用程序需要的Oauth2 token時,您當然需要將其存儲在您的移動應用程序中。

但是存在哪里?!

token存儲的位置取決於你所處的開發平台。如果你開發的是一個android app,你會將access tokens存儲在SharedPreferences中,如果你的IOS開發者,你將會將access tokens存在Keychain中。

如果你仍然存在疑問,下面這兩個Stack Overflow的posts可能會非常有用,它詳細闡述了如何在移動app中存儲access token。

現在你應該對OAuth2有所了解,為什么要使用它,以及它的工作原理。

Access Tokens

讓我們來討論一下access tokens。他們到底是什么?是一串隨機生成的數字?是uuid?還是別的?
這是一個很好的問題。
這里是一個比較短的答案:access token在技術上你任何你想要的字符串:

  • 一個隨機數
  • 一個隨機字符串
  • 一個uuid
  • 更多

更詳細的解釋:

  • 為客戶端而生成的。
  • 驗證token的所有者是你(使用強的簽名)
  • 存在過期時間。
    有了以上的解釋,你可能會希望去遵守一定的規范。為了不自己處理這些,你可以直接使用JWT(Json Web Token)。這是一個比較新的規范,允許你生成access token。這個規范(RFC7519)滿足一下幾點:
  • 可以為客戶端生成
  • 可以被創建者驗證
  • 在某一具體時間后自動過期
  • 可以容納可變的JSON信息
  • 無需查詢API服務,允許用戶本地驗證API憑證,從而減少對API調用的次數。

JWTs看起來像隨機生成的字符串,在使用的時候你可以像字符串那樣存儲他們。這使得取代傳統的access token而使用JWTs非常方便,畢竟它們基本上都是相同的,而JWT有更多的優勢。

JWTs總是以加密的方式被驗證。他們工作的方式如下:

  • 在API服務器的某處存儲一個安全隨機字符串。一般是一個比較長的隨機字符串(40個字符左右差不多)
  • 當你創建一個新的JWT時,你需要將這個隨機串傳遞個JWT類庫來簽署token,同時帶有一些你想存儲的數據,例如用戶ID,email等等。
  • token將會被生成,看起來像header.claims.signature——header、claims和signature都是base64加密的字符串。
  • 講生成的token給用戶,一般是API的使用者,例如移動app。

現在從移動客戶端中,你可以看到存儲在JWT中的任何東西。so如果我有一個JWT,我可以輕松地查看到里面包含的JSON數據,通常如下所示:

{
  "user_id": "e3457285-b604-4990-b902-960bcadb0693",
  "scope": "can-read can-write"
}

當然,這是一個100%虛構的例子,但是你可以從中知道,如果你得到這個JWT的副本,你也可以看到存儲在里面的JSON信息。

JWT標准支持自動的過期標記,因此你也能夠驗證這個JWT是有效的。無論你使用什么開發語言,只要在使用JWT庫,你就能驗證JWT的有效性直到過期!

這意味着,如果你使用JWT訪問API服務,則只需驗證JWT,就可以知道你的API調用是否可以工作,不需要API調用。

現在,一旦你有了一個有效的JWT,你也可以在服務器端用它做很酷的事情。

假設您已經向移動app發布了一個包含以下數據的JWT:

{
  "user_id": "e3457285-b604-4990-b902-960bcadb0693",
  "scope": "can-read can-write"
}

假設移動app上有惡意代碼能夠修改你的JWT,比如說:

{
  "user_id": "e3457285-b604-4990-b902-960bcadb0693",
  "scope": "can-read can-write can-delete"
}

如果這個修改過的token被發送到我們的API服務器會發生什么?它會起作用嗎?我們的服務器會接受這個修改的JWT嗎?

注意!!
當你的API服務收到JWT並進行驗證時,它會做以下幾件事情:

  • 通過使用只有服務器才知道的隨機生成的字符串來檢查token,確保它沒有被篡改。如果JWT完全被修改,這個檢查就會失敗,你會知道有人正在試圖做一些令人討厭的事情。
  • 同時服務器會檢查JWT的過期時間,確保它的合法性。so如果客戶端用一個已經過期的token來進行請求,你可以立刻拒絕。

這是很好的功能,因為它使處理驗證/到期/安全更簡單。

在使用JWTs時,你唯一需要記住的就是:不要在JWT中存儲敏感信息。

謹記以上提到的規則,那么在使用JWTs時就不會出錯了。

通常你會存一下兩條信息在JWTs中:

  • 用戶帳戶的某種唯一ID。這樣,當您收到此JWT進行身份驗證時,您可以從用戶數據庫中查找此用戶。
  • 用戶權限。當然如果構建的是所有用戶都能訪問的簡單的API,那么這個可能是不必要的。但是通過這個,用戶就不需要通過看API文檔來了解自己可以做什么,不可以做什么,只需要根據看自己的JWT就可以。

以上就是有關於JWT的全部。

JWT是如何工作的

在本節中,我們將深入討論整個流程,包括從開始到結束的整個流程,以及構建安全API服務所需的所有低級技術細節,這些API服務可以從移動設備安全地使用。

具體的工作流程如下圖所示:

1. User Opens App
用戶打開app,然后下一步。
2. App Asks for Credentials
由於我們准備使用OAuth2密碼授權類型方案來根據我們的API服務對用戶進行身份驗證,因此您的應用程序需要詢問用戶的用戶名,電子郵件地址和密碼。
現在幾乎所有的移動應用程序都要求這樣做,所以用戶習慣於輸入這些信息。
3. User Enters their Credentials
用戶輸入自己的憑證信息,下一步。
4. App Sends POST Requests to API Service
這是最初的OAuth2流程開始的地方。你將要做的是從你的移動應用程序向你的API服務發送一個簡單的HTTP POST請求。
這是一個使用cURL來發送POST請求的實例:

$ curl --form 'grant_type=password&username=USERNAMEOREMAIL&password=PASSWORD' https://api.example.com/v1/oauth

我們在這里做的是將用戶名或電子郵件和密碼發送到我們的 API 服務,使用 OAuth2 密碼授予類型: (有幾種授予類型, 但這是一個我們將談論這里, 因為它是唯一相關的, 當討論建立自己的可移動的 API)。

5. API Server Authenticates the User
接下來發生的事情是,API服務將去取出傳入的用戶名或電子郵件和密碼數據,並驗證用戶的憑據。

這一步不同的開發平台會有不同,但是一般步驟如下:

  1. 通過用戶名和密碼查詢用戶的賬戶信息。
  2. 比對加密的密碼信息。
  3. 如果驗證成功,進行下一步,否則返回錯誤信息,認證不合法。

6. API Server Generates a JWT that the App Stores
現在服務器已經對應用的OAuth2請求進行了身份驗證,需要為該應用生成access token。為此,服務器將使用JWT庫來生成有用的access token,然后將其返回給應用程序。

  1. 根據JWT的規則生成access token。
  2. 以JSON的形式返回,如下
{
  "access_token": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJzdWIiOiJEUExSSTVUTEVNMjFTQzNER0xHUjBJOFpYIiwiaXNzIjoiaHR0cHM6Ly9hcGkuc3Rvcm1wYXRoLmNvbS92MS9hcHBsaWNhdGlvbnMvNWpvQVVKdFZONHNkT3dUVVJEc0VDNSIsImlhdCI6MTQwNjY1OTkxMCwiZXhwIjoxNDA2NjYzNTEwLCJzY29wZSI6IiJ9.ypDMDMMCRCtDhWPMMc9l_Q-O-rj5LATalHYa3droYkY",
  "token_type": "bearer",
  "expires_in": 3600
}

正如你上面看到的,我們的JSON響應包含3個字段。第一個字段access_token是實際的OAuth2訪問令牌,移動應用程序將從這一點開始使用,以便進行已認證的API請求。

第二個字段token_type只是告訴移動應用程序我們正在提供什么類型的訪問令牌 - 在這種情況下,我們提供了一個 OAuth2 Bearer token。稍后我會再談這個。

最后,提供的第三個字段是expires_in字段。這基本上是提供的access token有效的秒數。

在上面的例子中, 我們要說的是, 我們給這個移動應用程序一個access token, 它可以被用來訪問我們的私有 API 長達1小時-沒有更多。在1小時 (3600 秒) 之后, 此access token將過期, 並且我們使用該access token所做的任何未來 API 調用都將失敗.

在移動應用程序方面,你將檢索此 JSON 響應, 解析 API 服務器提供的access token,然后將其存儲在本地的安全位置。在 Android 上, 這意味着 SharedPreferences, 在 iOS 上, 這意味着Keychain

既然已經將access token安全地存儲在移動設備上,則可以將其用於向API服務器發出所有后續API請求。

7. App Makes Authenticated Requests to API Server
現在要做的就是從您的移動應用程序向您的API服務提出安全的API請求。

在最后一步中,移動應用獲得了OAuth2訪問令牌,然后將其存儲在本地設備上。

為了使用此toekn成功發出API請求,您需要創建一個使用此token來標識您的用戶的 HTTP Authorization header。

要做到這一點,你要做的就是將你的access token和Bearer一起插入到HTTP Authorization header中。以下是使用cURL的實例:

$ curl -H "Authorization: Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJzdWIiOiJEUExSSTVUTEVNMjFTQzNER0xHUjBJOFpYIiwiaXNzIjoiaHR0cHM6Ly9hcGkuc3Rvcm1wYXRoLmNvbS92MS9hcHBsaWNhdGlvbnMvNWpvQVVKdFZONHNkT3dUVVJEc0VDNSIsImlhdCI6MTQwNjY1OTkxMCwiZXhwIjoxNDA2NjYzNTEwLCJzY29wZSI6IiJ9.ypDMDMMCRCtDhWPMMc9l_Q-O-rj5LATalHYa3droYkY" https://api.example.com/v1/test

最終, Authorization header長這樣:

Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJzdWIiOiJEUExSSTVUTEVNMjFTQzNER0xHUjBJOFpYIiwiaXNzIjoiaHR0cHM6Ly9hcGkuc3Rvcm1wYXRoLmNvbS92MS9hcHBsaWNhdGlvbnMvNWpvQVVKdFZONHNkT3dUVVJEc0VDNSIsImlhdCI6MTQwNjY1OTkxMCwiZXhwIjoxNDA2NjYzNTEwLCJzY29wZSI6IiJ9.ypDMDMMCRCtDhWPMMc9l_Q-O-rj5LATalHYa3droYkY

當你的API服務器收到這個HTTP請求后,會做以下工作:

  1. 檢查 HTTP Authorization header的值,是否從Bearer開始。
  2. 獲取后面字符串的值,作為token。
  3. 驗證這個JWT是否有效,若有效,base64解密后獲取用戶ID、權限等信息,進行余下的API接口調用。

(完)

總結

這篇文章介紹了當下移動app API調用的認證方法。講到了一般的HTTP認證方式、OAuth2.0認證以及JWT,對他們的原理都描述的很清楚了,所以翻譯出來給大家看。

唯一覺得有一點會讓讀者誤解的的就是對OAuth2.0中access token和JWT之間的關系的描述。所以作如下梳理:

  • OAuth2.0是一種授權框架,JWT是一種認證協議。
  • 在OAuth2.0中,access token其實就是一個隨機串,不同開發語言和不同的安全框架生成的access token均不同。當然也可以像文中提到的使用JWT作為其access token。
  • OAuth2.0通過code,去請求授權服務器時,返回信息如下。返回的數據中包含expires_in,代表過期時間。而JWT生成的token,為點分三段式節后,通過base64解密后,就可以獲取到過期時間,而不需要單獨返回一個字段。
{
  "access_token":"2YotnFZFEjr1zCsicMWpAA",
  "token_type":"example",
  "expires_in":3600,
  "example_parameter":"example_value"
}
  • OAuth2.0中的access token完全是隨機產生,本身無任何意義,為了進行驗證需要將生成的access token和過期時間存數據庫。而JWT本身可以攜帶一些非敏感信息和過期時間,並且本身可以通過base64解密,不需要存數據庫。由於這個原因,如上文所說,就將JWT引入OAuth2.0中,作為access token,這樣服務器就不需要維護Token存儲,資源服務器也不必要求Token檢查。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM