文章主要讲了移动api调用的授权和验证问题,The Ultimate Guide to Mobile API Security
移动API的使用是Stack Overflow和 Stormpath支持频道上经常出现的话题。这是一个已经被解决的问题,但是需要大量必要的知识和充足的理解才能很好地实现。
这篇文章能够让你了解到有关于在移动设备上安全的调用Restful API的一切内容,无论你是在构建一个需要访问Restful API的移动app,还是正在写Restful API并且打算和移动app开发者进行交互。
我的目标不仅仅是解释如何确保移动开发者能够安全调用你的Restful API,同时还解释整个凭证从开始到结束的交换过程,以及如何从安全漏洞中恢复等等。
在我们投身去探究如何确保移动开发者能够安全调用Restful API之前,让我们先讨论一下什么使得移动认证有别于传统的API认证。
API认证最基本的形式就是一般为我们所知的HTTP Basic Authentication。
对于API服务开发者和使用它的人来说,它的工作方式是非常简单的:
。
- 3bb743bbd45d4eb8ae31e16b9f83c9ba:ffb7d6369eb84580ad2e52ca3fc06c9d
中,同时带有关键词
- HTTP Authorization header
(也就是用户名和密码都使用base64加密)。下面展示了开发者在调用API时如何指定API key进行认证,使用命令行工具
- Basic
。
- cURL
- $ curl --user 3bb743bbd45d4eb8ae31e16b9f83c9ba:ffb7d6369eb84580ad2e52ca3fc06c9d https://api.example.com/v1/test
工具可以将获取到的API证书用base64加密,然后创建一个
- cURL
,如下所示:
- HTTP Authorization hearder
。而API服务器将会从
- Basic M2JiNzQzYmJkNDVkNGViOGFlMzFlMTZiOWY4M2M5YmE6ZmZiN2Q2MzY5ZWI4NDU4MGFkMmU1MmNhM2ZjMDZjOWQ=.
中获取字符串,然后用base64解密,获得ID和Secret,验证通过后再处理相应的API请求。
- HTTP Authorization hearder
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之上使用。
在此之前你可能听说过OAuth,并且在争论它是什么和它并没有足够的好。那么让我们明确:OAuth2是一个优秀的协议,在不受信任的设备中用于保护API服务的安全。它通过一种我们称之为token的认证方式来对移动用户进行认证。
下面我们从用户的角度OAuth2 token认证的工作方式。
之所以说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。他们到底是什么?是一串随机生成的数字?是uuid?还是别的?
这是一个很好的问题。
这里是一个比较短的答案:access token在技术上你任何你想要的字符串:
更详细的解释:
JWTs看起来像随机生成的字符串,在使用的时候你可以像字符串那样存储他们。这使得取代传统的access token而使用JWTs非常方便,毕竟它们基本上都是相同的,而JWT有更多的优势。
JWTs总是以加密的方式被验证。他们工作的方式如下:
——header、claims和signature都是base64加密的字符串。
- header.claims.signature
现在从移动客户端中,你可以看到存储在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并进行验证时,它会做以下几件事情:
这是很好的功能,因为它使处理验证/到期/安全更简单。
在使用JWTs时,你唯一需要记住的就是:不要在JWT中存储敏感信息。
谨记以上提到的规则,那么在使用JWTs时就不会出错了。
通常你会存一下两条信息在JWTs中:
以上就是有关于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请求。
这是一个使用
来发送POST请求的实例:
- cURL
- $ 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服务将去取出传入的用户名或电子邮件和密码数据,并验证用户的凭据。
这一步不同的开发平台会有不同,但是一般步骤如下:
6. API Server Generates a JWT that the App Stores
现在服务器已经对应用的OAuth2请求进行了身份验证,需要为该应用生成access token。为此,服务器将使用JWT库来生成有用的access token,然后将其返回给应用程序。
- {
- "access_token": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJzdWIiOiJEUExSSTVUTEVNMjFTQzNER0xHUjBJOFpYIiwiaXNzIjoiaHR0cHM6Ly9hcGkuc3Rvcm1wYXRoLmNvbS92MS9hcHBsaWNhdGlvbnMvNWpvQVVKdFZONHNkT3dUVVJEc0VDNSIsImlhdCI6MTQwNjY1OTkxMCwiZXhwIjoxNDA2NjYzNTEwLCJzY29wZSI6IiJ9.ypDMDMMCRCtDhWPMMc9l_Q-O-rj5LATalHYa3droYkY",
- "token_type": "bearer",
- "expires_in": 3600
- }
正如你上面看到的,我们的JSON响应包含3个字段。第一个字段access_token是实际的OAuth2访问令牌,移动应用程序将从这一点开始使用,以便进行已认证的API请求。
第二个字段
只是告诉移动应用程序我们正在提供什么类型的访问令牌 - 在这种情况下,我们提供了一个 OAuth2 Bearer token。稍后我会再谈这个。
- token_type
最后,提供的第三个字段是
字段。这基本上是提供的access token有效的秒数。
- expires_in
在上面的例子中, 我们要说的是, 我们给这个移动应用程序一个access token, 它可以被用来访问我们的私有 API 长达1小时-没有更多。在1小时 (3600 秒) 之后, 此access token将过期, 并且我们使用该access token所做的任何未来 API 调用都将失败.
在移动应用程序方面,你将检索此 JSON 响应, 解析 API 服务器提供的access token,然后将其存储在本地的安全位置。在 Android 上, 这意味着
, 在 iOS 上, 这意味着
- SharedPreferences
。
- Keychain
既然已经将access token安全地存储在移动设备上,则可以将其用于向API服务器发出所有后续API请求。
7. App Makes Authenticated Requests to API Server
现在要做的就是从您的移动应用程序向您的API服务提出安全的API请求。
在最后一步中,移动应用获得了OAuth2访问令牌,然后将其存储在本地设备上。
为了使用此toekn成功发出API请求,您需要创建一个使用此token来标识您的用户的 HTTP Authorization header。
要做到这一点,你要做的就是将你的access token和
一起插入到HTTP Authorization header中。以下是使用cURL的实例:
- Bearer
- $ 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请求后,会做以下工作:
开始。
- Bearer
(完)
这篇文章介绍了当下移动app API调用的认证方法。讲到了一般的HTTP认证方式、OAuth2.0认证以及JWT,对他们的原理都描述的很清楚了,所以翻译出来给大家看。
唯一觉得有一点会让读者误解的的就是对OAuth2.0中access token和JWT之间的关系的描述。所以作如下梳理:
,代表过期时间。而JWT生成的token,为点分三段式节后,通过base64解密后,就可以获取到过期时间,而不需要单独返回一个字段。
- expires_in
- {
- "access_token": "2YotnFZFEjr1zCsicMWpAA",
- "token_type": "example",
- "expires_in": 3600,
- "example_parameter": "example_value"
- }
来源: http://www.cnblogs.com/Sinte-Beuve/p/7795438.html