关于OAuth,官方是这样讲的:
An open protocol to allow secure authorization in a simple and standard method from web, mobile and desktop applications.
OAuth 即 Open standard for Authorization
OAuth就是一个网络开放协议。为保证用户资源的安全授权提供了简易的标准
OAuth 1.0协议由于过于复杂,适用性也特别的差,在此不做过多描述,下面贴张图自行了解。
关于OAuth 1.0, 想了解的可以看看这些:
http://www.rfcreader.com/#rfc5849
OAuth 1.0已经被OAuth 2.0替代,且2.0不兼容1.0,本篇文章主要讲讲关于OAuth 2.0那点事儿。
先来说一个场景:有一天,小明想要去京东买电脑,但是打开京东商城,发现要登录京东商城。可小明之前一直用淘宝,没有京东商城的账号,注册一个新的吧,又嫌麻烦。小明发现,可以不用注册新的,用QQ就可以登录。于是乎,小明用QQ号登录京东商城,购买了一台Mac Pro回家水贴吧去了。
上处场景中,小明用QQ号登录京东商城购物的这个过程呢,就是一次 OAuth 认证流程。
(1) Third-party application:第三方应用程序,本文中又称"客户端"(client),即上一节例子中的"云冲印"。
(2)HTTP service:HTTP服务提供商,本文中简称"服务提供商",即上一节例子中的Google。
(3)Resource Owner:资源所有者,本文中又称"用户"(user)。
(4)User Agent:用户代理,本文中就是指浏览器。
(5)Authorization server:认证服务器,即服务提供商专门用来处理认证的服务器。
(6)Resource server:资源服务器,即服务提供商存放用户生成的资源的服务器。它与认证服务器,可以是同一台服务器,也可以是不同的服务器。
(A)用户打开客户端以后,客户端要求用户给予授权。(比如说你登陆京东,但是不想注册京东账户,于是京东说:没关系,有QQ号么,用QQ号登录就行。于是,京东就会把你引导到QQ的认证服务器上。)
(B)用户同意给予客户端授权。(这个时候你在手机上弹出是否授权,你点击“是”,这个时候会返回一个授权码给京东,为什么是返回给京东呢?因为第一步中发送的认证服务器中会带有一个重定向url,是京东自己的)
(C)客户端使用上一步获得的授权,向认证服务器申请令牌。(京东拿着这个授权码再去找认证服务器)
(D)认证服务器对客户端进行认证以后,确认无误,同意发放令牌。(京东取得授权码确认无误后,发放accesstoken令牌)
(E)客户端使用令牌,向资源服务器申请获取资源。(京东拿着令牌就可以访问QQ资源服务器)
(F)资源服务器确认令牌无误,同意向客户端开放资源。(QQ资源服务器对比令牌无误后开放资源)
其实在上述过程中,不难看出,(B)在才是整个过程的关键所在,即用户该如何给予客户端授权,有了这个授权过后,客户端才能获取令牌,进而凭令牌获取到相应的资源。
客户端必须得到用户的授权(authorization grant),才能获得令牌(access token)。OAuth 2.0定义了四种授权方式。
授权码模式(authorization code)
简化模式(implicit)
密码模式(resource owner password credentials)
客户端模式(client credentials)
授权码模式(authorization code)是功能最完整、流程最严密的授权模式。它的特点就是通过客户端的后台服务器,与"服务提供商"的认证服务器进行互动。
该模式过程如下:
(A)用户访问客户端,客户端将用户引导向认证服务器。(京东请求QQ)
客户端申请认证的URI,包含以下参数:
response_type:表示授权类型,必选项,此处的值固定为"code"
client_id:表示客户端的ID,必选项
redirect_uri:表示重定向URI,可选项
scope:表示申请的权限范围,可选项
state:表示客户端的当前状态,可以指定任意值,认证服务器会原封不动地返回这个值。
https://www.example.com/v1/oauth/authorize?response_type=code&client_id=CLIENT_ID&redirect_uri=CALLBACK_URL&scope=read&state=xxx
(B)用户选择是否给予客户端授权。
(C)如用户给予授权,认证服务器将用户引导向客户端指定的redirection uri,同时加上授权码code。(用户点击授权后,QQ引导用户访问A步骤中指定的重定向url,并带着授权码和state)
code:表示授权码,必选项。该码的有效期应该很短,通常设为10分钟,客户端只能使用该码一次,否则会被授权服务器拒绝。该码与客户端ID和重定向URI,是一一对应关系。
state:如果客户端的请求中包含这个参数,认证服务器的回应也必须一模一样包含这个参数。
https://client.example.com/cb?code=SplxlOBeZQQYbYS6WxSbIA&state=xxx
(D)客户端收到code后,通过后台的服务器向认证服务器发送code和redirection uri。(京东带着接收到(C)步骤中的请求后,用授权码发送下面的请求去找QQ认证服务器要token)
grant_type:表示使用的授权模式,必选项,此处的值固定为"authorization_code"。
code:表示上一步获得的授权码,必选项。
redirect_uri:表示重定向URI,必选项,且必须与A步骤中的该参数值保持一致。
client_id:表示客户端ID,必选项。
https://www.example.com/v1/oauth/token?client_id=CLIENT_ID&grant_type=authorization_code&code=AUTHORIZATION_CODE&redirect_uri=CALLBACK_URL
(E)认证服务器验证code和redirection uri,确认无误后,响应客户端访问令牌(access token)和刷新令牌(refresh token)。(响应(D)步骤的数据)
access_token:表示访问令牌,必选项。
token_type:表示令牌类型,该值大小写不敏感,必选项,可以是bearer类型或mac类型。
expires_in:表示过期时间,单位为秒。如果省略该参数,必须其他方式设置过期时间。
refresh_token:表示更新令牌,用来获取下一次的访问令牌,可选项。
scope:表示权限范围,如果与客户端申请的范围一致,此项可省略。
{
"access_token":"2YotnFZFEjr1zCsicMWpAA",
"token_type":"example",
"expires_in":3600,
"refresh_token":"tGzv3JOkF0XG5Qx2TlKWIA",
"example_parameter":"example_value"
}
从上面代码可以看到,相关参数使用JSON格式发送。此外,HTTP头信息中明确指定不得缓存。
使用场景
授权码模式是最常见的一种授权模式,在oauth2.0内是最安全和最完善的。
适用于所有有Server端的应用,如Web站点、有Server端的手机客户端。
可以得到较长期限授权。
授权码模式下的安全问题常出现在(A)步骤当中,京东会state并放在session中,然后再在(A)步骤中的请求中带上state,QQ返回url带有授权码和state的链接给淘宝,淘宝这个时候需要在后台对比这个state是否和session中的一样,用来防止csrf攻击,而开发者没有严格按照协议来,请求中没有带上state,就可能造成账户劫持的问题。那我们该如何测试?
1.确认A步骤中是否带有state,如果没有该参数就可能存在漏洞
2.准备AB两个账号,A是攻击者,B是受害者,两个都登陆的情况下,当然是不同浏览器(这里有个场景限制,就是绑定账户的地方才有,比如说我有个京东账户,但是要记住账号密码登陆很麻烦,我将他绑定到QQ,以后就可以直接用QQ登陆啦)
3.A点击绑定QQ,然后用打开burp抓包,点击授权,一步一步forward,知道遇到请求中带有授权码的请求,复制该请求后drop掉,因为code一般都是一次性的
4.拿去B账户的浏览器中打开(实际攻击中就是csrf),会发现B账户绑定了A账户的QQ,以后A就可以用自己的QQ登陆B的京东了。
简化模式(implicit grant type)不通过第三方应用程序的服务器,直接在浏览器中向认证服务器申请令牌,跳过了"授权码"这个步骤,因此得名。所有步骤在浏览器中完成,令牌对访问者是可见的,且客户端不需要认证。
该模式过程如下:
(A)客户端将用户导向认证服务器。
A步骤中,客户端发出的HTTP请求,包含以下参数:
response_type:表示授权类型,此处的值固定为"token",必选项。
client_id:表示客户端的ID,必选项。
redirect_uri:表示重定向的URI,可选项。
scope:表示权限范围,可选项。
state:表示客户端的当前状态,可以指定任意值,认证服务器会原封不动地返回这个值。
https://www.QQ.com/authorize?response_type=token&client_id=CLIENT_ID&redirect_uri=CALLBACK_URL&scope=read&state=xxx
(B)用户决定是否给予客户端授权。
(C)假设用户给予授权,认证服务器将用户导向客户端指定的"重定向URI",并在URI的Hash部分包含了访问令牌。
C步骤中,认证服务器回应客户端的URI,包含以下参数:
access_token:表示访问令牌,必选项。
token_type:表示令牌类型,该值大小写不敏感,必选项。
expires_in:表示过期时间,单位为秒。如果省略该参数,必须其他方式设置过期时间。
scope:表示权限范围,如果与客户端申请的范围一致,此项可省略。
state:如果客户端的请求中包含这个参数,认证服务器的回应也必须一模一样包含这个参数。
https://www.example.com/callback#access_token =ACCESS_TOKEN&state=xyz&token_type=example&expires_in=3600&state=xxx
(D)浏览器向资源服务器发出请求,其中不包括上一步收到的Hash值。
(E)资源服务器返回一个网页,其中包含的代码可以获取Hash值中的令牌。
(F)浏览器执行上一步获得的脚本,提取出令牌。
(G)浏览器将令牌发给客户端。
使用场景
适用于所有无Server端配合的应用
如手机/桌面客户端程序、浏览器插件。
基于JavaScript等脚本客户端脚本语言实现的应用。
简化授权模式下的账户劫持的问题。
(A)https://www.QQ.com/authorize?response_type=token&client_id=CLIENT_ID&redirect_uri=CALLBACK_URL&scope=read&state=xxx
(C)https://www.example.com/callback#access_token =ACCESS_TOKEN&state=xyz&token_type=example&expires_in=3600&state=xxx
(C)步骤中的请求链接实际上来源A请求中的redirect_uri参数(用于指定重定向的链接),最为关键的地方是(C)请求中带有access_token,那么实际场景就是当state参数不存在的时候可以进行csrf攻击,伪造redirect_uri就好了,但正常情况下
QQ至少会对redirect_uri的域名做校验吧(当然也可能不止这一项),所以不能随便重定向。那我们需要做的是找到受信任的网站下的xss漏洞,然后重定向获取劫持access_token,在获取到access_token后完整攻击。可以发现隐式授权如果存在问题一般是指服务提供商没有做好redirect_uri的校验,同时呢开发人员也没有加上state参数。
密码模式(Resource Owner Password Credentials Grant)中,用户向客户端提供自己的用户名和密码。客户端使用这些信息,向"服务商提供商"索要授权。
在这种模式中,用户必须把自己的密码给客户端,但是客户端不得储存密码。这通常用在用户对客户端高度信任的情况下,比如客户端是操作系统的一部分,或者由一个著名公司出品。而认证服务器只有在其他授权模式无法执行的情况下,才能考虑使用这种模式。
该模式过程如下:
(A)用户向客户端提供用户名和密码。
(B)客户端将用户名和密码发给认证服务器,向后者请求令牌。
B步骤中,客户端发出的HTTP请求,包含以下参数:
grant_type:表示授权类型,此处的值固定为"password",必选项。
username:表示用户名,必选项。
password:表示用户的密码,必选项。
scope:表示权限范围,可选项。
https://www.example.com/token?grant_type=password&username=USERNAME&password=PASSWORD&client_id=CLIENT_ID
(C)认证服务器确认无误后,向客户端提供访问令牌。
{
"access_token":"2YotnFZFEjr1zCsicMWpAA",
"token_type":"example",
"expires_in":3600,
"refresh_token":"tGzv3JOkF0XG5Qx2TlKWIA",
"example_parameter":"example_value"
}
在该过程中用户必须把自己的密码给客户端,针对于“输入账号”这个操作,小伙伴们能想到的问题都有哪些,那基本上就存在那些问题了!(比如爆破╮(╯▽╰)╭)
客户端模式(Client Credentials Grant)指客户端以自己的名义,而不是以用户的名义,向"服务提供商"进行认证。严格地说,客户端模式并不属于OAuth框架所要解决的问题。在此不做过多解释,只了解过程。
该模式过程如下:
(A)客户端向认证服务器进行身份认证,并要求一个访问令牌。
granttype:表示授权类型,此处的值固定为"client_credentials",必选项。
scope:表示权限范围,可选项。
https://www.example.com/token?grant_type=client_credentials
(B)认证服务器确认无误后,向客户端提供访问令牌。
{
"access_token":"2YotnFZFEjr1zCsicMWpAA",
"token_type":"example",
"expires_in":3600,
"example_parameter":"example_value"
}
oauth2.0协议本身设计是安全的,制定OAuth 2.0协议的最大动力之一,是希望用一个协议适应多个业务场景授权,这种扩大业务场景的结果,也意味着薄弱点的增多,而在开发过程中没有严格按照协议进行开发,或对流程中的参数校验不完整,造成了一些流程上的漏洞。现在由单一场景变为多个场景直接的交互,也就导致场景之间的安全问题产生了交叉,一些本来属于潜在威胁,很容易通过场景转换而暴露出来,让威胁变得更加严重。对于擅长“由点到面”的攻击者来说,现在只需要针对常见薄弱点(尤其是授权认证流程中的薄弱点)就有可能攻击成功。(攻击成功案例可参考wooyun,hackerone,里面有更详细的攻击流程,更加具体形象)
以上就是针对OAuth2.0学习的过程以及一点点思考。
E
N
D
关
于
我
们
Tide安全团队正式成立于2019年1月,是新潮信息旗下以互联网攻防技术研究为目标的安全团队,团队致力于分享高质量原创文章、开源安全工具、交流安全技术,研究方向覆盖网络攻防、系统安全、Web安全、移动终端、安全开发、物联网/工控安全/AI安全等多个领域。
团队作为“省级等保关键技术实验室”先后与哈工大、齐鲁银行、聊城大学、交通学院等多个高校名企建立联合技术实验室,近三年来在网络安全技术方面开展研发项目60余项,获得各类自主知识产权30余项,省市级科技项目立项20余项,研究成果应用于产品核心技术研究、国家重点科技项目攻关、专业安全服务等。对安全感兴趣的小伙伴可以加入或关注我们。