文章目录
一、什么是OAuth?
OAuth的英文全称是Open Authorization,它是一种开放授权协议。OAuth目前共有2个版本,2007年12月的1.0版(之后有一个修正版1.0a)和2010年4月的2.0版。
OAuth 是一种常用的授权框架,它使网站和 Web 应用程序能够请求对另一个应用程序上的用户帐户进行有限访问。OAuth允许用户授予此访问权限,而无需将其登录凭据暴露给请求的应用程序。这意味着用户不必将其帐户的完全控制权交给第三方。
基本OAuth的流程广泛用于集成第三方功能,这些功能需要访问用户帐户中的某些数据。例如,应用程序可能使用OAuth请求访问您的电子邮件联系人列表。但是,同样的机制也用于提供第三方认证服务,允许用户使用他们的帐户在不同网站上登录。
二、OAuth2.0
OAuth 2.0 最初是作为一种在应用程序之间共享对特定数据的访问的方式而开发的。它的工作原理是定义三个不同方之间的一系列交互,即客户端应用程序、资源所有者和 OAuth 服务提供者。
1、客户端应用程序:想要访问用户数据的网站或者应用程序。
2、资源所有者:客户端应用程序想要访问其数据的用户。
3、OAuth服务提供者:控制用户数据及其访问权限的网站或者应用程序。它们通过提供用于与授权服务器和资源服务器交互的API来支持OAuth。
三、OAuth授权类型
有许多不同的方式可以实现实际的OAuth过程,这些被称为 OAuth“流程”或“授权类型”。
OAuth授权类型决定了OAuth过程中涉及的步骤的确切顺序。授权类型还会影响客户端应用程序在每个阶段与OAuth服务通信的方式,包括访问令牌本身的发送方式。因此,授权类型通常称为“OAuth 流”。
OAuth服务必须配置为支持特定的授权类型,然后客户端应用程序才能启动相应的流程。客户端应用程序指定它要在发送到 OAuth 服务的初始授权请求中使用哪种授权类型。有几种不同的授权类型,每一种都有不同程度的复杂性和安全考虑。本文关注“授权代码”和“隐式”授权类型,因为它们是迄今为止最常见的。
3.1、OAuth范围
对于任何 OAuth 授权类型,客户端应用程序必须指定它想要访问的数据以及它想要执行的操作类型。它使用scope参数来执行此操作。
对于基本 OAuth,客户端应用程序可以请求访问的范围对于每个 OAuth 服务都是唯一的。由于范围的名称只是一个任意的文本字符串,因此提供者之间的格式可能会有很大差异。有些甚至使用完整的 URI 作为范围名称,类似于 REST API 端点。例如,当请求对用户联系人列表的读取访问权限时,范围名称可能采用以下任何形式,具体取决于所使用的 OAuth 服务:
scope=contacts
scope=contacts.read
scope=contact-list-r
scope=https://oauth-authorization-server.com/auth/scopes/user/contacts.readonly
但是,当使用 OAuth 进行身份验证时,通常会使用标准化的 OpenID Connect 范围。例如,范围openid profile将授予客户端应用程序对有关用户的一组预定义基本信息的读取权限,例如他们的电子邮件地址、用户名等。
3.2、授权码授权类型
客户端应用程序和 OAuth 服务首先使用重定向来交换启动流的一系列基于浏览器的 HTTP 请求。询问用户是否同意所请求的访问。如果他们接受,则客户端应用程序被授予“授权代码”。然后,客户端应用程序与 OAuth 服务交换此代码以接收“访问令牌”,他们可以使用该令牌进行 API 调用以获取相关用户数据。
从代码/令牌交换开始发生的所有通信都通过安全的、预配置的反向通道在服务器到服务器之间发送,因此对最终用户是不可见的。此安全通道在客户端应用程序首次向 OAuth 服务注册时建立。此时,client_secret还会生成 a,客户端应用程序在发送这些服务器到服务器请求时必须使用它来验证自己。
由于最敏感的数据(访问令牌和用户数据)不是通过浏览器发送的,因此这种授权类型可以说是最安全的。
1、授权请求
客户端应用程序向 OAuth 服务的/authorization端点发送请求,请求获得访问特定用户数据的权限。
GET /authorization?client_id=12345&redirect_uri=https://client-app.com/callback&response_type=code&scope=openid%20profile&state=ae13d489bd00e3c24 HTTP/1.1
Host: oauth-authorization-server.com
此请求包含以下值得注意的参数,通常在查询字符串中提供:
1、client_id
包含客户端应用程序唯一标识符的必需参数。该值是在客户端应用程序向 OAuth 服务注册时生成的。
2、redirect_uri
向客户端应用程序发送授权代码时,用户浏览器应重定向到的URI,这也称为“回调”URI”或“回调端点”。许多OAuth攻击都基于利用此参数验证中的缺陷。
3、response_type
确定客户端应用程序期望哪种类型的响应,对于授权码授予类型,该值应为code。
4、scope
用于指定客户端应用程序想要访问的用户数据子集。请注意,这些可能是OAuth提供者设置的自定义范围或OpenID Connect规范定义的标准化范围。
5、state
存储与客户端应用程序上的当前会话相关联的唯一的、不可猜测的值。OAuth服务应在响应中返回此确切值以及授权代码。此参数用作客户端应用程序的CSRF令牌形式,确保对其/callback端点的请求来自发起 OAuth流的同一个人。
2、用户登录和同意授权
当授权服务器收到初始请求时,它将用户重定向到登录页面,在那里他们将被提示使用 OAuth提供商登录他们的帐户。
然后将向他们显示客户端应用程序想要访问的数据列表,授权请求中定义了可访问的范围,用户可以选择是否同意此访问。一旦用户批准了客户端应用程序的给定范围,只要用户仍然与OAuth服务保持有效会话,此步骤就会自动完成。换句话说,用户第一次选择"使用社交媒体登录"时,他们需要手动登录并表示同意,但如果他们稍后重新访问客户端应用程序,他们通常可以使用单击。
3、授权码授予
如果用户同意请求的访问,他们的浏览器将被重定向到授权请求参数callback指定的端点redirect_uri。GET请求将包含授权代码作为查询参数。
GET /callback?code=a1b2c3d4e5f6g7h8&state=ae13d489bd00e3c24 HTTP/1.1
Host: client-app.com
4、请求访问令牌
客户端应用程序收到授权码后,需要将其交换为访问令牌。为此,它将服务器POST请求发送到 OAuth 服务的/token端点。此后的所有通信都在安全的反向通道中进行,因此通常无法被攻击者观察或控制。
POST /token HTTP/1.1
Host: oauth-authorization-server.com
…
client_id=12345&client_secret=SECRET&redirect_uri=https://client-app.com/callback&grant_type=authorization_code&code=a1b2c3d4e5f6g7h8
除了client_id和授权之外code,还需注意以下新参数:
1、client_secret
客户端应用程序必须通过向OAuth服务注册时分配的密钥来验证自身。
2、grant_type
用于确保新端点知道客户端应用程序想要使用哪种授权类型。应该设置为authorization_code。
5、访问令牌授予
OAuth 服务将验证访问令牌请求。如果符合预期,服务器会通过向客户端应用程序授予具有请求范围的访问令牌来进行响应。
{
"access_token": "z0y9x8w7v6u5",
"token_type": "Bearer",
"expires_in": 3600,
"scope": "openid profile",
…
}
6、API 调用
现在客户端应用程序有了访问代码,它最终可以从资源服务器获取用户的数据。为此,它会对 OAuth 服务的/userinfo端点进行 API 调用。访问令牌在Authorization: Bearer标头中提交,以证明客户端应用程序有权访问此数据。
GET /userinfo HTTP/1.1
Host: oauth-resource-server.com
Authorization: Bearer z0y9x8w7v6u5
7、资源授予
资源服务器应验证令牌是否有效以及它是否属于当前客户端应用程序。如果是这样,它将通过发送请求的资源(即基于访问令牌的范围的用户数据)来响应。
{
"username":"carlos",
"email":"carlos@carlos-montoya.net",
…
}
在OAuth身份验证的情况下,通常用作ID来授予用户经过身份验证的会话,让他们登录。
3.3、隐式授权类型
客户端应用程序不是先获取授权代码,然后将其交换为访问令牌,而是在用户同意后立即接收访问令牌。
客户端应用程序并不总是使用隐式授权类型的原因:
安全性比较差。
使用隐式授权类型时,所有通信都通过浏览器重定向进行 - 没有像授权代码流那样的安全反向通道。这意味着敏感的访问令牌和用户的数据更容易受到潜在的攻击。
隐式授权类型更适用于单页应用程序和本机桌面应用程序,它们不能轻松地将存储在client_secret后端,因此,使用授权代码授权类型的好处不大。
1、授权请求
隐式流程的开始方式与授权代码流程大致相同。主要区别是该response_type参数必须设置为token。
GET /authorization?client_id=12345&redirect_uri=https://client-app.com/callback&response_type=token&scope=openid%20profile&state=ae13d489bd00e3c24 HTTP/1.1
Host: oauth-authorization-server.com
2、用户登录和同意授权
用户登录并决定是否同意请求的权限。此过程与授权代码流程完全相同。
3、访问令牌授予
用户同意请求的访问之后,从这一步开始变化,OAuth服务会将用户的浏览器重定向到redirect_uri授权请求中指定的位置。但是,它不会发送包含授权代码的查询参数,而是将访问令牌和其他特定于令牌的数据作为URL片段发送。
GET /callback#access_token=z0y9x8w7v6u5&token_type=Bearer&expires_in=5000&scope=openid%20profile&state=ae13d489bd00e3c24 HTTP/1.1
Host: client-app.com
由于访问令牌是在URL片段中发送的,因此它不会直接发送到客户端应用程序。相反,客户端应用程序必须使用合适的脚本来提取片段并存储它。
4、API调用
一旦客户端应用程序成功从URL片段中提取访问令牌,就可以使用它对OAuth的/userinfo端点进行API调用。与授权代码流不同的是这一步也通过浏览器发生。
GET /userinfo HTTP/1.1
Host: oauth-resource-server.com
Authorization: Bearer z0y9x8w7v6u5
5、资源授予
资源服务器应验证令牌是否有效以及它是否属于当前客户端应用程序。如果是,它将根据与访问令牌关联的范围,通过发送请求的资源(即用户数据)来做出响应。
{
"username":"carlos",
"email":"carlos@carlos-montoya.net"
}
客户端应用程序最终可以将此数据用于其预期目的。
参考链接:https://portswigger.net/web-security