[译]移动API安全终极指南

1,383 阅读9分钟

文章主要讲了移动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。

· Where should I store access tokens on Android?

· Where should I store access tokens on iOS?

现在你应该对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的全部。