[译]移动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认证的工作方式。
- 用户打开移动app,输入账号和密码。
- 移动app发送一个带有用户的用户名和密码的POST请求到API服务器,服务器进行验证,成功后返回一个code。
- 移动app通过code向认证服务器发送请求,认证成功后为用户生成一个一段时间后就会过期的access token。
- 将access token存储在移动设备本地,就像一个能够访问API服务的API key一样。
- 一旦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服务将去取出传入的用户名或电子邮件和密码数据,并验证用户的凭据。
这一步不同的开发平台会有不同,但是一般步骤如下:
- 通过用户名和密码查询用户的账户信息。
- 比对加密的密码信息。
- 如果验证成功,进行下一步,否则返回错误信息,认证不合法。
6. API Server Generates a JWT that the App Stores
现在服务器已经对应用的OAuth2请求进行了身份验证,需要为该应用生成access token。为此,服务器将使用JWT库来生成有用的access token,然后将其返回给应用程序。
- 根据JWT的规则生成access token。
- 以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请求后,会做以下工作:
- 检查 HTTP Authorization header的值,是否从
Bearer
开始。 - 获取后面字符串的值,作为token。
- 验证这个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检查。
[译]移动API安全终极指南的更多相关文章
- 每周一书《Oracle 12 c PL(SQL)程序设计终极指南》
本周为大家送出的书是<Oracle 12 c PL(SQL)程序设计终极指南>,此书由机械工业出版社出版, 孙风栋,王澜,郭晓惠 著. 内容简介: <Oracle 12c PL/SQ ...
- 【转】使用JMeter进行负载测试——终极指南
使用JMeter进行负载测试——终极指南 这篇教程讨论的是JMeter,它是一款基于Java的.集合了几个应用程序.具有特定用途的负载和性能测试工具. 本篇主要涉及的内容: 解释一下JMeter的用途 ...
- const extern static 终极指南
const extern static 终极指南 不管是从事哪种语言的开发工作,const extern static 这三个关键字的用法和原理都是我们必须明白的.本文将对此做出非常详细的讲解. co ...
- 第六代智能英特尔® 酷睿™ 处理器图形 API 开发人员指南
欢迎查看第六代智能英特尔® 酷睿™ 处理器图形 API 开发人员指南,该处理器可为开发人员和最终用户提供领先的 CPU 和图形性能增强.各种新特性和功能以及显著提高的性能. 本指南旨在帮助软件开发人员 ...
- atitit.api设计 方法 指南 手册 v2 q929.docx
atitit.api设计 方法 指南 手册 v2 q929.docx atitit.api设计原则与方法 1. 归一化(锤子钉子理论)1 1.1. 链式方法2 1.2. 规则5:建立返回值类型2 1. ...
- 15个Linux Wget下载实例终极指南
15个Linux Wget下载实例终极指南 Linux wget是一个下载文件的工具,它用在命令行下.对于Linux用户是必不可少的工具,尤其对于网络管理员,经常要下载一些软件或从远程服务器恢复备份到 ...
- [产品相关] A/B测试终极指南(翻译)
转载地址: http://blog.sina.com.cn/s/blog_9149268d0100zrx7.html 还记得以前导师说看了英文的文章就把它翻译一下吧,这样会对文章更好地理解,也会有更深 ...
- Docker终极指南:为什么Docker能做这么多事
Docker终极指南:为什么Docker能做这么多事 http://www.aboutyun.com/thread-11499-1-1.html
- 如何编写更好的SQL查询:终极指南-第二部分
上一篇文章中,我们学习了 SQL 查询是如何执行的以及在编写 SQL 查询语句时需要注意的地方. 下面,我进一步学习查询方法以及查询优化. 基于集合和程序的方法进行查询 反向模型中隐含的事实是,建立查 ...
随机推荐
- 【Linux笔记(002) 】-- centos7 文档操作基本命令
一.cd -- ChangeDirectory a) 切换到 /DemoLM/ 文件夹 b) 回到用户 Home 根目录:是哪个账户登录的就会进入哪个用户的根目录 二.pwd -- PrintWork ...
- Android 从ImageView中获取Bitmap对象方法
showImageView.setDrawingCacheEnabled(true); Bitmap bitmap=showImageView.getDrawingCache(); showImage ...
- 记一次坑爹的RSA旅程____快哭了555555555(来自实验吧的warmup的wp和感想)
这么简单的题目搞了我那么久,森森感觉自己菜的不行....哎,努力吧少年,BXS已经全国第二了. 嗯,废话不说,这道题目来自实验吧的"warmup",附上链接 http://www. ...
- apollo实现c#与android消息推送(二)
安装完成apache apollo后,org.eclipse.paho是很方便的测试软件,下来介绍paho的安装和使用 2. 搭建paho: a 下载 org.eclipse.paho.ui.app- ...
- Linux系统centOS7在虚拟机下的安装及XShell软件的配置
前面的话 本文将详细介绍Linux系统centOS7在虚拟机下的安装 准备工作 [系统下载] 在安装centOS7之前,首先在官网下载合适的版本 然后,选择一个链接下载即可 [虚拟机配置] 接下来,需 ...
- An Introduction to Variational Methods (5.1)
在这篇文章中,我引用Bishop书中的一个例子,来简单介绍一下Variational Methods的应用.想要更详细地理解这个例子,可以参考Bishop的书Pattern Recongnition ...
- Spring详解(七)------事务管理
PS:本篇博客源码下载链接:http://pan.baidu.com/s/1mi3NhX2 密码:3io2 1.事务介绍 事务(Transaction),一般是指要做的或所做的事情.在计算机术语中是指 ...
- python对列表的联想
python的列表与字典,已经接触无数次了.但是很多用法都记不住,个人觉得归根原因都是只是学了知识点而少用,也少思考.在此试图用宫殿记忆法对它们的用法做个简单的梳理. 首先,说说列表的删除,删除有三种 ...
- Sql语句varchar或nvarchar字段条件前加N的性能差异
Sql语句varchar或nvarchar字段条件前加N的话是对这个字段进行Unicode编码, 这样做的目的是避免在这种字段中存入俄文.韩文.日文的情况下有可能会出现乱码. 但这样做也会有性能问题, ...
- 【转】Python-__builtin__与__builtins__的区别与关系(超详细,经典)
在学习Python时,很多人会问到__builtin__.__builtins__和builtins之间有什么关系.百度或Google一下,有很 多答案,但是这些答案要么不准确,要么只说了一点点,并不 ...