OAuth工作原理随想——让你的系统提供的服务更加安全



最近这段时间,一直都在和web服务打交道。自己项目组的系统需要别的项目组提供服务接口;别的平台(手机)平台又需要我们这边给它们提供接口。实现、调用、接口文档都有所涉及。从中我发现一个非常重要的问题——安全,这是一个被严重忽略的问题。



我认为在网络这个充满敌意的大环境下,应用和服务的安全性,是一个不得不重视的问题。去年年底的CSDN账号泄露以及口令明文的事件,至少给了企业两个最基本的警示:(1)不要等到出现问题之后,才知道要去挽救,在这个浮躁的社会氛围下,出现哪怕不是什么大问题,都会被群起而攻之;(2)服务的提供方应该认识到安全的重要性。本文参考OAuth协议的工作原理,给出了我个人觉得更为安全的服务认证和授权方式。一家之谈,欢迎各位提出宝贵意见。

 

几个项目组对于web服务的处理方式:

(1)   仅仅是一个简单的明文key,用来标识属于哪个平台;

(2)   服务提供方和调用方协商一种验证码的生成规则,服务的调用方携带用户id以及生成的验证码。在服务提供方一端有个验证的方式,如果按照同样的规则生成出相同的验证码,则认为调用方是合法调用者

简单的谈谈这两种方式吧,其实第一种没什么好谈的。它只是用了一个标识符,标识服务的调用方来自哪个平台,然后在它方法中表明应该走入哪个分支,这显而易见。还有一个我觉得没什么好谈的就是,它简直就是个“肉鸡”,甚至连“肉鸡”都算不上。只要你知道URL,想干嘛干嘛去吧;第二种方式,不得不说至少比第一种方式高级得多。它至少仿照了类似数字签名的做法。但不得不说,还是有些简单并直接了。并且,要知道只要基于了同一种生成验证码的规则(注意这里并不是hash散列算法),那么它的灵活性和随机性就会降低。有一种比较简单而有效的攻击手段——DOS(拒绝服务),上面两种方式都无法逃过这样的攻击。原因是:

(1)   URL是定死的,或者很长一段时间才会改变为另一种形式的,但在服务端并没有关于对来自同一个IP,在一段时间内访问次数的限制

(2)   没有对调用方进行认证

对于采用HTTP GET形式的Web Service调用都会有这些安全问题。后台引用的调用方式也难以幸免于难,因为它们本质上都是一样的。其实,一方需要另一方提供服务,这种场景在互联网中早已司空见惯。新浪、腾讯、人人、网易等开放平台都是服务的提供者,它们是如何来保证服务的安全性以达到保证平台用户资源的安全?没错,它们几乎都是使用——OAuth这种认证和授权协议。

 

OAuth协议简介

具体的文字解释,参见wiki——OAuth

应用场景:


请求过程示意图:


项目目前对于web服务【web service/web page】的调用场景:




OAuth照搬?

OAuth的认证和授权过程中,通常都有一步需要用户授权的过程,也就是需要将用户引导到第三方服务提供者的授权页面(通常需要填写用户名和密码),并引导用户完成授权。在系统中,并不适合引入这样的交互模式。因为我们现在的系统从第三方提取数据对用户来讲是透明的,用户登录我们的系统,并不知道,我们的数据是来自第三方提供的。所以这极其影响用户体验。并且,参照资源的重要级别,并不是所有服务都需要享受这么机密的待遇(从Oath授权可以看到,需要多次的“握手”,也就是需要来回HTTP请求好几次),有时资源重要性是需要考虑的一个方面,而有时保证服务正常提供、提供服务的服务器正常运转则更为侧重。

 

如何兼顾安全、性能?

下图展示了本人对于兼顾安全以及性能的设想:


对于步骤说明:

第一次交互称之为:握手(也就是认证和授权的过程),由于这些服务本质上只对已知系统的特定用户开放,而非互联网应用的开放级别。所以这里省去了第一步验证客户端身份,获取Request Token的过程(或者称之为合二为一)。

步骤1:请求方采用HTTPS协议,封送能够认证调用方合法身份。这里封送什么内容呢?可以是服务提供方分配的用户名或密码对,可以是类似于上面的验证码,还可以是提供方公开的密钥。以上这些针对公共资源服务就足够了,对于类似微博那些设计到私有用户的资源,需要能够提供服务方用户检索特定资源的标识(很明显这些标识在服务调用方必须已知,比如userId等XXXID,如果不得不需要,可以在用户注册服务调用方系统的时候自行填写)。

这里你可能有一个疑问,公共资源就算了,受限资源为什么不需要用户在服务提供方平台的密码?这里还是涉及到信任级别的问题,我认为这确实是一个需要权衡的问题。或者说需要视特定应用场景以及资源的保护级别而定。上面提供的那些认证信息(包括XXXID,服务提供方会在特定的数据表中检索,以再次验证请求的合法性),足以表明服务的调用方确实是可信的,对于保护级别不是特别高(特别是应对公司内部,系统与系统之间的交互场景),最重要的是检索数据的服务而言,就可以允许调用方获取它想提取的信息了。当然如果,确实需要得到资源拥有者的授权,那么把用户定向到服务提供者的授权页面,也未尝不可。

 

步骤2:返回服务调用方Access Token.这是关键的一步。与开始我们谈到的我们既有的两种“验证方式”根本的不同点在于——服务提供者能够控制局面。这里可以实现各种不同的安全机制,限制访问时间,限制访问次数,限制IP在某一时间段内的访问次数等等。你可以在服务提供程序中,对于前来握手的服务调用方的信息进行维护,如:

Application[“172.40.38.1”]=”asdfj2093423uwqesdjfdfepclkjd”;

采用访问者的ip作为key ,服务端生成的一串accsee token作为value,在服务提供端进行维护。当然,你可以定义一个结构:

class visitorInfo{
private int totalCount;//允许的总访问次数
private int currentCount;//当前已经访问次数
private DateTime startTime;//会话的开始时间
private DateTime endTime;//会话的结束时间
Private string accessToken;//访问令牌
Private string reflushToken;//刷新token(可选的)
}


以进行更合理的控制并作日志记录,至于怎么维护这个结构,在内存中还是持久化到数据库,这个不是这篇的话题。

步骤2的输出就是访问令牌当然你也可以加上一个刷新令牌(这个是可选的,主要是为了在一次会话过期后,调用方可以拿着刷新令牌,直接换取一个新的访问令牌,而不需要重新认证,当然你也可以选择让调用方重新认证。

第二次交互:服务的请求和应答,这里就没有什么特别的了。

步骤3:调用方拿着服务提供方授予的access token或者用户检索受限资源的XXXID,就可以请求服务了(只是普通的http请求)。这里,局面就可以完全被服务调用方hold住,它可以对IP和Access Token进行验证,要是有必要,也可以对会话是否过期,或者一段时间内的访问次数进行验证,而无需担心恶意中间人的重放或DOS攻击。因为服务方维护了认证过的调用方的access token以及IP。

步骤4:应答请求。

 

总结

这里参考了OAuth协议的工作原理,并加以精简以适应某些系统对系统提供服务的场景。再次声明,这里只是在安全和性能之间找了一个平衡点,使得你的服务应用更为坚固。如果你想要更为安全的做法,可以完全参照OAuth的设计,强制要求用户进行授权(上面已经解释了应该如何处理)。

无论如何,对于这种共享数据之类的服务,协商和人为交互的频度还是比较大的,并且效率不是太高。有没有从根本上解决这种问题的办法?有,那就是不提供这些服务。那应该怎么办?下一篇推崇——浅谈企业业务数据的整合。敬请期待!



版权声明:本文为博主原创文章,未经博主允许不得转载。