如何设计好的RESTful API之安全性

原文:http://blog.csdn.net/ywk253100/article/details/25654101算法

导读:安全是恒久的话题,对于基于WSDL和SOAP的Web Service,咱们有WS-Security这样的安全规范来指导实现认证、受权、身份管理等安全需求。如何保证RESTful API的安全性呢。编程

关键词:RESTful API API安全性 浏览器

前面讲了好的RESTful API具备的一些特征,本文会继续探讨RESTful API的安全性问题。七牛云存储

InfoQ:安全是恒久的话题,对于基于WSDL和SOAP的Web Service,咱们有WS-Security这样的安全规范来指导实现认证、受权、身份管理等安全需求。那么,RESTful API有无成熟可用规范或实现框架呢?如何保证RESTful API的安全性呢?安全

李锟:保证RESTful API的安全性,主要包括三大方面:
a) 对客户端作身份认证
b) 对敏感的数据作加密,而且防止篡改
c) 身份认证以后的受权
对客户端作身份认证,有几种常见的作法:
在请求中加签名参数 服务器

1.为每一个接入方分配一个密钥,而且规定一种签名的计算方法。要求接入方的请求中必须加上签名参数。这个作法是最简单的,可是须要确保接入方密钥的 安全保存,另外还要注意防范replay攻击。其优势是容易理解与实现,缺点是须要承担安全保存密钥和按期更新密钥的负担,并且不够灵活,更新密钥和升级 签名算法很困难。 网络

使用标准的HTTP身份认证机制 架构

HTTP Basic身份认证安全性较低,必须与HTTPS配合使用。HTTP Digest身份认证能够单独使用,具有中等程度的安全性。 框架

HTTP Digest身份认证机制还支持插入用户自定义的加密算法,这样能够进一步提升API的安全性。不过插入自定义加密算法在面向互联网的API中用的不是不少。
这个作法须要确保接入方“安全域-用户名-密码”三元组信息的安全保存,另外还要注意防范replay攻击。 异步

优势:基于标准,获得了普遍的支持(大量HTTP服务器端、客户端库)。在服务器端作HTTP身份认证的职责能够由Web Server(例如 Nginx)、App Server(例如Tomcat)、安全框架(例如Spring Security)来承担,对应用开发者来讲是透明的。HTTP 身份认证机制(RFC 2617)很是好地体现了“分离关注点”的设计原则,并且保持了操做语义的可见性。

2.缺点:这类基于简单用户名+密码机制的安全性不可能高于基于非对称密钥的机制(例如数字证书)。

使用OAuth协议作身份认证

OAuth协议适用于为外部应用受权访问本站资源的状况。其中的加密机制与HTTP Digest身份认证相比,安全性更高。须要注意,OAuth 身份认证与HTTP Digest身份认证之间并非相互取代的关系,它们的适用场景是不一样的。OAuth协议更适合于为面向最终用户维度的API提供授 权,例如获取隶属于用户的微博信息等等。若是API并非面向最终用户维度的,例如像七牛云存储这样的存储服务,这并不是是OAuth协议的典型适用场景。
3.对敏感的数据作加密,而且防止篡改,常见的作法有:

部署SSL基础设施(即HTTPS),敏感数据的传输所有基于SSL。 
仅对部分敏感数据作加密(例如预付费卡的卡号+密码),并加入某种随机数做为加密盐,以防范数据被篡改。
 
身份认证以后的受权,主要是由应用来控制。一般应该实现某种基于角色+用户组的受权机制,这方面的框架有很多(例如Spring Security),不过大多数开发团队仍是喜欢本身来实现相关功能。

李建业:我不认为安全是RESTful API须要考虑的问题,事实上我以为这是两个正交的问题。固然,若是使用RESTful API来提供认证、受权和身份管理,那也算是双方有关系,可是这和其它风格的API设计所要考虑的问题彷佛没什么区别,不值得特别注意。

可是在具体设计层面,这二者的“正交点”上彷佛确实有些问题,由于REST是一个推崇状态无关原则的架构风格,而认证和受权一般基于第三方解决方案,因此每每会出现违背有状态约束的问题,这个地方我也没有特别的想法,固然这个困难和原问题关系不大。
至于WS-族的协议,我不太了解,不太能参与讨论。

丁雪丰:对于RESTful API,常见的安全措施都是能够继续使用的。例如,为了防篡改,能够对所有参数进 行签名;为了防范重放攻击能够在请求中增长一次性的Token,或者短期内有效的Token;对内容加密能够实现数据防泄露……;对于DDoS攻击,各 种HTTP流量清洗策略,均可以继续发挥做用,由于这就是基本的HTTP请求。

在受权和认证方面,OAuth 2.0已经基本成熟了,而且获得了普遍地应用。若是能够,接入第三方帐户体系是个不错的选择,好比Google和Facebook的,国内的固然也有几个候选。

马钧:我的认为RESTful的安全性分为几个层次,在安全要求较高的场合,能够经过HTTPs这样的加密协议来保证网络层的安全,应用层的安全能够经过OAuth实现认证,而对于资源的访问受权,则只能依靠应用程序来实现了。

InfoQ:如何对RESTful API进行版本控制,请分享您认为实用的作法?

李锟:一个比较简单实用的作法是直接在URI中插入版本号,这样作容许多个版本的API并行运行。

另外一个作法是在HTTP请求中加入自定义头信息,标明使用的版本号。不过这个作法其实对浏览器不够友好,简单地使用浏览器+HTML没法测试。

李建业:目前比较好的方式仍是在uri设计中添加版本信息,其它方法都不如这个实用。

丁雪丰:我的认为最好的版本化,就是没有明显的版本。在对已发布的服务进行变动时,要尽可能作到兼容,其中包括 URI、连接和各类不一样的表述的兼容,最关键的就是在扩展时不能破坏现有的客户端。例如,要变动一个参数,能够选择同时兼容新旧两种输入,或者保持老参数 不动,提供一个新的参数,在文档中必须作出说明,不推荐新用户再继续使用以前的参数。

若是必需要进行不兼容的变动,那么能够选择标记不一样的版本号,这时能够选择在路径或参数中增长版本信息。也有作法是增长HTTP标头,只是在调用时会稍有不便,推荐前两种方法。

马钧:RESTfulAPI 的版本升级,尽可能兼容以前的版本,保证原有的API都能正常工做,能够经过HTTP 301转跳到新的资源。另一种实用的作法就是在url中保留版本 号,同时提供多个版本供客户端使用,如 v1.rest.com 或者 rest.com/v1/ 这样。

InfoQ:HTTP1.1规范中给出的动词对于设计RESTful API够用吗?您在实际项目中会扩展本身的动词吗?在什么状况下须要扩展?

李锟:这个问题取决于设计者如何看待和设计资源。若是资源抽象作的很好,对于某个资源的任何操做,一般都可以映 射到CRUD四个类别中。CRUD四个类别对于操做资源来讲,绝大多数状况下是完备的。HTTP的GET/POST/PUT/DELETE四个方法,对于 CRUD四个类别的操做来讲是足够的,映射关系是Create-POST/Retrieve-GET/Update-PUT/Delete- DELETE。

咱们一般不会选择建立本身的动词,这样作对于客户端开发者来讲,须要更多的学习成本。若是在资源上定义的操做过多,咱们会选择拆分出更多的资源。

李建业:一 般是够用的,有时一些“不够用”的场景是因为咱们没有设计出合理的资源,好比批量操做。可是,正如以前所说的那样,对于某些内部的、传统的(所以模型稳定 且已知)系统,API提供者和调用者会有自已的固定动词表,此时不必拘泥。另外,我不建议扩展动词,一旦扩展了动词,其实已经破坏了我以前说的*“尽可 能少的先验信息”*,那么,扩展动词和从新设计动词的成本差异不大。基于这个考虑,我建议尽量保持动词不变,除非你想从新设计动词表。
丁雪丰:一 般状况下,经常使用的HTTP动词是够用的,并无出现必定要本身扩展动词的状况。其实,最经常使用的也就是GET、POST、DELETE和PUT,而 HEAD、OPTIONS、TRACE则基本用不太到。若是出现一时找不到合适的动词,安全幂等的操做用GET,其余均可以用POST,在设计资源时稍加 考虑便可。
马钧:在个人实际项目中,只用到了POST,PUT,DELETE,GET这四个动词。

InfoQ:今年5月份发布的JAX-RS 2.0规范对于RSTfulAPI的设计最有价值的特性是哪一个(些)? 它(们)用于解决什么问题?

李锟:REST开发框架RESTEasy项目负责人Bill Burke,去年写了一篇文章介绍JAX-RS 2.0。

我赞成Bill在文章中的观点,在JAX-RS 2.0增长的内容中,最重要的三部分为:

a) Client API——用来规范化JAX-RS客户端的开发方式。
b) Server-side Asynchronous HTTP——用来实现服务器端推送功能,而不须要依靠低效的轮询方式。
c) Filters and Interceptors——用来分离关注点,将鉴权、日志等逻辑与业务逻辑分离开,更好地实现代码重用。

这三部分的内容对于开发者来讲都颇有用。遵循JAX-RS规范作开发,能够确保服务器端以及客户端代码的可移植性。

李建业:我我的关注异步API这部分,主要是由于流式服务将会愈来愈多,那将大量须要这类支持。

InfoQ:可否为InfoQ的读者推荐一款实用的RESTful API开发框架,并说明您的推介理由。

李锟:这 个问题我就不详细回答了。不一样的编程语言有不一样的REST开发框架,对于REST的支持程度也不一样。开发RESTful API的需求范围很广,可选择的 开发框架的范围也很广。保持多样性是繁荣生态环境的基础。像Java就有支持JAX-RS规范的Jersey、RESTEasy、Restlet、 Apache CXF,和不支持JAX-RS规范的Spring MVC等等不少框架。这些框架目前都作的不错。我对框架的选择没有倾向性。 RESTful API设计的最佳实践应该是通用的,而不是必须依赖某种特定的开发框架。

李建业:很差意思,这个我不过重视,无法推荐,不过我能够解释一下为何对RESTful API框架不感冒的缘由。

REST做为一个架构风格,对咱们的系统开发有很大影响,可是这些影响通常是针对架构(例如状态无关)或者设计(例如资源识别)上的,因此一旦涉及 到具体实现,主要工做就基本结束了,此时开发框架能作的事也就只有简化编程了(相较而言,有的框架还能起到引导设计的做用),而因为RESTful会抽象 动词,因此实现层面中和API规范相关的工做原本就很少,那么框架的价值就更小了。

固然,咱们也不可能直接基于servlet/rakc/wsgi来开发,不过通常的编程语言都会提供一些简单的url route/match策 略,咱们使用这些就足够了。另外,有些框架能帮咱们生成所有的动词支持,但这也未必是好事,我通常倾向于按需实现——用到了再支持,这就更不须要太关注开 发框架对RESTful的支持了。

丁雪丰:因为本人是Spring的拥护者,工做中也一直在使用Spring,因此在选择框架时会更多地倾向 Spring MVC(并非说别的框架很差,这里有些我的主观的成份)。若是必定要选择其余框架,也要选择可以方便与Spring集成的框架。若是在项 目中已经使用了Spring,那么没有什么理由不选择Spring MVC,鉴于目前Spring在各类项目中的高出镜率,相信通常状况下都会选择 Spring MVC。

REST的成熟度模型中,第三层就是HATEOAS,Spring目前还提供了Spring Hateoas子项目,对连接、资源等方面的支持都作了必定的加强。

马钧:我目前在实际项目中使用的是Spray,这是一个开源的 REST/HTTP 工具包和底层网络 IO 包,基于 Scala 和 Akka 构建。轻量级、异步、非堵塞、基于 actor 模式、模块化和可测试是Spray的特色。

InfoQ:HTTP2.0规范正在制定当中,您对它的期待是什么?

李锟:个人期待包括两个方面:应该作的和不该该作的。

HTTP/2.0规范应该作的:

1.与HTTP/1.1协议保持兼容。兼容的含义是说二者能够并存,客户端应用能够根据服务器端的能力,自由地选择使用HTTP/2.0仍是HTTP/1.1,并且选择过程对应用来讲是透明的。 
2.改进HTTP协议(做为资源的统一接口)之中操做语义表达方式的语法,提升网络传输效率。 
3.更好地模块化,这样HTTP/2.0协议的实现可以更好地模块化。应用程序可根据须要选择适当的模块,而不是要么全有、要么全无。 
4.废弃掉HTTP/1.1协议中一些不多有人用到的部分,例如采用管道(pipelining)方式发送请求。 
5.增长更多的动词,以适应除CRUD以外的其余场景。
 
HTTP/2.0规范不该该作的:
HTTP/2.0协议不该该把底层的数据加密机制(即SSL)做为必选项。
 
HTTP/2.0协议不该该背离REST架构风格的约束,尤为是要确保操做语义对于中间组件的可见性。 
在上面这两个方面,Roy Fileidng曾经与SPDY协议设计者Mike Belshe发生过激烈争论,详情请看:Roy Fielding谈Google SPDY协议

李建业:对 此规范关注很少,不知道会不会有对于流的支持,目前我所知道的只有chunk方式进行简单的支持,可是真正的流须要区分数据通道和控制通道——哪怕是逻辑 上的区分,这样就直接对REST风格产生了很大冲击,考虑到流式服务在将来的发展潜力,我特别期待业界在这方面有所进展。
丁雪丰:HTTP 2.0 很大程度上是借鉴了Google的SPDY,就我而言,首先,但愿这个规范能作到与HTTP 1.1的兼容,使用者若是只认识1.1,那么2.0能优雅 “降级”;其次,但愿2.0能带来更好的性能,SPDY在这方面仍是有所改进的,但愿HTTP 2.0能再接再砺;最后,但愿这个规范能在最终定稿时附带 一个最佳实践,正确引导人们合理地使用HTTP 2.0。

 

马钧:没研究过,估计即便出来,1.1还有很长的生命周期,不会很快被取代