关于OAuth的一些思考

说明:下文中出现的iAuth是为校园内部网络设计的类似OAuth的数据交换协议。本文即是在设计iAuth时对OAuth原理的一些思考

关于客户端

要实现客户端的开发认证,这个和传统的有些很重要的区别,尤其是在授权这里。。

传统的网站对网站的auth中,通常有四个角色,资源服务器,应用服务器(第三方),用户,用户代理(浏览器------第四方)。

而客户端作为app的时候,变成了三个角色,资源服务器,用户,用户代理(客户端------第三方)。

有什么差别么?在传统授权时,授权信息是通过用户代理(浏览器)展示给用户,而浏览器是用户和授权服务器都信任的。授权服务器信任浏览器会把授权信息准确的展示给用户,确保用户知道应用想要什么权限。------至少,因为浏览器和第三方应用服务器没有任何关系,所以应用服务器也没法改我们想展示给用户的授权信息。但是在客户端应用授权时就不同了,此时,第三方==客户端==用户代理。因此无法保证用户知道正确的授权信息。

换种说法,传统授权时用户要输入用户名密码在Auth Server登录,并不需要把用户名和密码交给第三方;而客户端授权时,用户通常都要输入用户名和密码,这等于直接泄露了用户秘密,第三方得到用户名和密码后可以通过传统的登录方式修改用户数据。而授权服务器没有能力鉴别。

iauth想保证的是用户的身份凭据------用户名密码不会由于授权而转交给第三方,且用户必须能随时收回给第三方的授权。所以传统授权是用另一个token来访问。作为部分权限的授权依据。

那么就可以这样解决了:

用户在使用某客户端之前必须用传统方式登录授权服务器给这个客户端应用授权,然后会得到一串字符(客户端密钥),然后用户在客户端用这个字符串登录, -(但是应用不能直接用这个字符串访问用户数据,每次会话必须)-应用使用这个密钥和自己的应用密钥对请求签名后获得数据。这样用户在网站上可以随时更新密钥,删除应用,来避免意外情况造成损失。


关于Request token(未完待续)

到底要不要request token?


oauth2.0删掉了这个。我一开始也觉得没用,后来好像觉得有用,但仔细分析好像又可以不用。。。。。再说吧。


是这样,oauth1.0里Request token最初有3个用途:
  • 帮助client识别用户,因为可能有多个用户在同时授权,因此必须能区分用户,
  • 避免给用户显示app_key,因为在显示授权页面时服务器需要知道是哪个应用请求授权,最简单的方法就是用app_key区分(在重定向到服务器时带上app_key)。然而app_key毕竟是用于区分应用的,没有必要让用户知道。。。知道只能增加危险。
  • 使授权流程必须由一个合法的client发起,而不是任何人随便都能用一个url让server给用户显示授权页面的(也就是说授权页面不是谁都可以访问的)。
可以分析一下:
  • 功能1是必须的
  • 功能2则属于“额外的安全措施”,减少危险概率。。。
  • 功能3则减少了用户被误导的概率。。。
但是oauth1.0a出现后,状况发生了一些改变,为了避免会话固化攻击(参见《OAuth的改变》 ),oauth1.0a新增了一个verifier的参数,在编写1.0a的php实现时我发现这个verifier其实也起到了区分用户的作用,


这样的话request token的功能1就几乎被取代了。。。而2的话我们可以想一些其他发办法,或者放弃都可以。三的话有这样一个问题,就是说3的功能对于client为服务器的情况很好,而当client为客户端时很不方便。。。暂时的想法是能否区分对待这两种情况。。。

关于四种流程

oauth2.0定义了四种流程,参考OAuth的改变
编号 流程名称 可用范围 说明

1

Authorization Code 有服务端的应用 最贴近老版本的方式
2 Implicit Grant 没有服务端的应用 比如Javascript应用
3 Resource Owner Password Credentials 不管有无服务端,此类型都可用 因为涉及用户名和密码,所以此授权类型仅适用于可信赖的应用
4 Client Credentials 不管有无服务端,此类型都可用 仅适用于获取与用户无关的公共信息。PS:不返回Refresh Token

目前可以考虑实现1,4这几种类型,第三种类型需要对应用有足够的信任,而且即使是信任的。。。。一旦应用本身被挂马,就没有任何可以使用的防范措施了。。。。

第二种也准备放弃,因为我不信任任何由客户端实现的安全机制。iauth要保证,不管出现任何情况,一旦发现问题,损失不会再扩大(但是不是通过让全体用户更改密码这种手段)


关于OAuth2.0

从网上查找了很多oauth2.0的资料,SSL也不安全,oauth2相对于1最大的优点是简单,取消了签名。。。


OAuth2.0与1.0相比:

  • 提供了多种非浏览器(第三方用户代理)的接入方式
  • 无须签名,加密,编码
  • access token是短时间的,需要刷新
  • 分离了资源所有Server和授权Server
  • 请求资源时无须client身份信息,只需access token

关于安全性评估

整个系统是一个一层一层从下层搭起来的安全体系,下层失效,上层没有防护措施。如何提高系统安全性?如何评估安全性?

OAuth核心,核心!

以下摘自OAuth2.0中文译本:

基于资源服务器对保险的须要,拜访令牌可能有不同的格局、构造跟 利用方法(例如密码学特点)。拜访令牌的属性跟 用以拜访受保护资源的方法不在本标准的规定范畴之内,而是由相干的其它标准来定义。受权服务器跟 资源服务器之间的交互方法不在本标准的规定范畴之内。

也就是说OAuth2只是一个框架,不像OAuth1是一个明确的协议。

凡是涉及到auth的,其本质都是一样的,就是把现实中的信任问题搬到网络中而已。

现在网络上有很多服务,但是用户不是直接来用的(发送字符串,接收字符串这种事情是人做不了的,必须有计算机,必须有网络,必须有程序才行)。这个都是通过用户代理(随便你怎么想,一个程序,或是一个什么东西)来实现的。通常我们用浏览器来打比方,但并不全是(后面详细说)。服务的提供方要针对不同的用户来提供服务。用户必须要有一个用户代理。然后服务提供方对这个用户代理给与100%的信任。也就是说在服务器看来,用户代理所做的事情就是用户想做的事情。同时用户也给与浏览器100%的信任,也就是用户相信他的所有操作意图都是原本的被反应给服务器(当然,为了不辜负双方这总共200%的信任,浏览器的“品德”必须过关,做好本职工作,同时本职工作以外的事情(收集用户数据,篡改用户操作意图)都不能做。但是这个东西又没有法律保障,因为所有的浏览器在安装时都有免责声明。。。。所以说现有的网络体系其实比较脆弱,不过还好有几个公信力比较强的浏览器,像现在大量使用的火狐,谷歌,微软的虽然能力弱点(经常打补丁),但是品德还是没有问题的)但是注意只是“公信力”,一旦失去人和服务器的信任,用户代理就彻底完了,就好像金融危机归根到底是信任危机一样,

后来出现了第三方这个事情。。。。把问题搞得复杂了。。。。问题变成了用户不止有一个用户代理,这个其实还好,就好像我电脑里有好几个浏览器一样,没有任何问题。更大的问题是不是所有用户代理的品德都过关。。。

这个就叫一粒老鼠屎坏了一锅好汤。。。。如果大家的品德都过关,事情很简单,但是出了一个不过关的(包括cracker试图伪造成用户来获取数据),那么就产生了各种各样的防备措施(老鼠屎进锅里了,捞出来就行,汤还是要喝的。。。。只是得加点佐料遮遮味道,OAuth也算是干这个的一味料)。

如果所有用户代理都能像浏览器那样自觉的仅使用自己需要的API接口,而且不该看的不看,不该听的不听。。。。也就是非礼勿视,非礼勿听,非礼勿动。。。那么用户甚至可以把用户名和密码给第三方。让第三方作为用户代理。唉,真可惜了一锅好汤。。。。

所以说,有了妄想,就又生出了分别,所以服务器就不能“一视同仁”了,要区分每个用户代理,对于“品德”比较好的,提供较高的信任,给与较高的权限....比如浏览器。那品德的好坏有没有一个标准呢?当然有,用户的信任程度就可以作为标准之一。。。。注意是之一,因为用户也可能犯迷糊,被误导。其实吧,用户通常对于每个代理都是比较信任的,但是服务器信不过它们,所以提供了很多区分它们的方式,比如通过不同的接口,通常的http接口是提供给浏览器的。邮件用POP3接口,但是用户代理一多起来,这个方法就不好使了。总不能给每个用户代理搞一个接口吧。。。。所以只好用一个接口,但是需要提供身份证。。。。为了防止身份证造假,需要提供签名。(签名的原理这里不讲)所以就产生了OAuth。然后根据不同的身份提供(信任等级不同)的服务。

有了分别,就生出了执着。比如用户认为用户代理A只访问相册比较保险。用户代理B只读取日志比较保险。也就是只信任用户代理的部分操作(改密码这样的事情只能信任浏览器了。。。。)于是为每个用户代理分配了不同的访问权限(不同的信任等级)。


OAuth在本质上就是服务器针对一个用户的多个用户代理提供不同信任等级的服务,没了。


对应到实际中,就是第三方(肯定是作为用户代理)如何使用快速(意味着不通过用户)获得服务器提供给用户的部分(信任等级不同)服务。注意到,用户代理后面不一定有一个真正的用户在操作,但是这个操作一定是用户已经授权过的。


有了执着,就生出了痛苦。。。。实现起来比较麻烦了,代码写得好辛苦。。。。


在iAuth的设计中,信任等级100%的用户代理有且仅有用户浏览器,所有其他的用户代理必须让用户从浏览器(这个用户代理)授权才可以分发权限。


其实本来可以更加灵活些,比如可以设置信任等级100%的用户代理有多个。而且每个用户代理都可以把子权限再授予其他人。但是太灵活有时候也不好,比如被攻破了,设置了100%的信任等级。那就玩完了。


关于OAuth或CAS(未完待续)

OAuth和CAS在实现上有很多相似的地方。但是出发点貌似不太一样。CAS要实现的是用户身份的认证。就是如何把用户的身份信息从一个网站A安全转给另一个网站B?


先说说什么是身份信息。就是“我是XXX,YYY可以证明”


对于A网站,这句话可能是“我是(用户名),(密码)可以证明”。也就是A网站本身需要(用户名)和(密码)才能确认用户身份。


对于B网站,这句话可能是“我是(A网站的用户名),(A网站)可以证明”。


最简单的方法,用户把用户名密码给B网站,然后B拿给A进行验证。这个没有问题,如果大家都很自觉的话。此时的信任关系是用户100%信任B(不会泄露密码),服务器也100%信任B(不会拿密码干坏事)。


可是用户不相信B的品德100%。仅相信B来获得自己的身份(这个可以用Oauth实现,你发现了吧,部分权限)但是又需要B的服务。于是需要多几个步骤:


B需要知道用户身份,让用户去找A要。


A给用户写了一份证明,签了字,让用户拿着去找B


B看了证明,确认了用户的身份。


以上这过程中A和B都100%信任用户(不会更改、造假证明)。而且搞了一个签名机制来保证这个证明是没法改的。


交给另一个网站。。。。这个不太好,有的时候可能只需要部分用户的信息,而且用户的密码要是改了怎么办?那就动态更新。



OAuth1.0的核心假设

  • 可能存在恶意用户
  • 网络传输信道可能一直被监听
  • 服务器不主动向外发起连接
  • 授权可以失败,但宁可失败也不能草率交出用户数据
  • 即使存在恶意应用或用户,一旦发现,马上可以控制危害面不再继续扩大
  • 不信任由客户端实现的安全机制

OAuth2.0的核心假设

  • 不太可能存在恶意用户(仅使用access token就可以获取数据,无须任何身份及验证信息)
  • 网络传输信道不太可能被监听,特别是长时间被监听(access token有过期时间)
  • TLS/SSL失效是小概率事件(所有安全机制完全基于SSL)
  • 信任应用开发者不会恶意使用其他应用的access token(access token存放于cookie中,应用都可以请求同一Server其他应用留下的access token)
  • 信任由客户端实现的安全机制(允许浏览器中的第三方网站js脚本获取access token,依赖该网站对access token进行保护)
  • 互联网本身并不太安全,因此把OAuth搞得太安全没有必要。。。。。

其他

OAuth2是一个框架,不是一个协议,当然OAuth1是。iAuth对这个框架进行了一些改动,并且把它变成一个可以实现的协议。


论oauth2的refresh token及iAuth2的部分设计



oauth2.0里有一个refresh token,还要结合access token使用。我把oauth2.0的RFC6749看了一遍,然后发现之所以要搞一个refresh token来与access token 结合使用是为了在分布式环境下实现access token的“及时”回收。oauth2.0里access token是Auth Server分发给client的,但是client却是向Resource Provider(oauth2里叫Resource Server)去获得资源的,于是包含了一个隐藏条件就是Auth Server给client的access token一定是RP可以识别和校验的。问题是如果用户在Auth Server取消了授权,但是access token已经发出去了。。。如何让RP知道这个access token失效了呢?oauth2的实现方法是使用一个很快就过期的access token,然后access token失效后用refresh token从Auth Server再要一个。因为refresh token是存在Auth Server上的,所以只要用户取消授权,那就让refresh token失效,这样client就无法换到access token,等最后手里的这个access token失效后,就算是完成access token的“回收”工作了。


其实很容易发现access token的回收其实并不“及时”,因为用户取消授权后的一段时间内client仍然可以凭借手里还没过期的access token来获得用户数据。所以oauth建议如果希望更“及时的”回收access token,就让access token的过期时间越短越好。。。。


如果一个access token的失效时间越来越短,越来越短。。。。短短短短。。。最后,每个access token只能请求一次数据就失效了。那就是说每次请求数据,都要先向Auth Server发一个请求,再向RP发资源请求。真是好麻烦。。。。而且RP还要保存每个client的access token,真是麻烦!!


于是iauth的设计是这样的:client从Auth Server获得access token,然后向RP发起请求,然后RP把这个请求转发给Auth Server进行验证。Auth Server当然什么都知道,然后这个就很容易验证了。验证完之后Auth Server把请求uid发给RP,RP再根据这个id进行其他的操作。。。。如果出错就直接返回错误信息。


可以看出iauth的access token是“实时”回收的,这种及时性来源于每次请求都转发校验,这就好像上面说的access token每次只能请求一次数据就失效。但是iauth不像上面所说是client每次请求数据前先去找Auth Server要数据(一个凭证,断言,XXX),然后再去找RP要东西。而是让RP去向AuthServer要东西。这样总的看起来网络上的数据流量是一样的,但是减少了client开发的工作量,转给了RP。


当然这样设计也是在校园网环境下的一种取舍,校内的话RP和Auth Server的通信应该比较流畅,虽然每次请求都要转发,理论上性能也撑的住。而转发意味着RP无须存储任何client的信息,只需要转发,校验,提供服务即可。这样也简化了RP的设计。这里的假设就是RP和Auth Server的通信效率要高于client和RP及client和Auth Server。


论会话保持与CSRF攻击(未完待续)



oauth2里出现了一个叫state的参数,这个参数还很重要,能防止CSRF攻击。这个参数最主要的作用是保持会话状态。恩恩,会话状态,这个东西听起来很高深,实际上很简单。好像古装电视剧里经常有这个情形:A国派信使给B国送一封和平信,C国不怀好意,派人半路杀掉A国信使,然后把信的内容换成战争的宣战书,派人给B国送去,然后B和A打起来了,C坐收渔利。在这出戏里,A和B之间有一个“会话”,但是这个会话并没有像双方想的那样进行,也就是“保持”,而是被破坏掉了。注意两个地方,在A的信送到之前,B根本不知道A要给他送信。然后A的信送到之后,B也没有能力来甄别信的真伪。


在iauth系统里,Auth Server要把用户授权的access token送到应用手里。

你可能感兴趣的:(原理,cas,OAuth)