1. OpenID Connect是什么
OpenID Connect扩展了OAuth协议,是OAuth基础之上的身份认证层。它添加了一些简单的功能,可以更好地支持OAuth的身份认证。
OAuth最初设计时并没有考虑到身份认证;它是一种在应用程序之间对特定资源进行授权的方法。然而,许多网站开始自定义OAuth,将其用作身份认证机制。为了实现这一点,它们通常为读取用户的一些基础数据从而请求访问权限,如果他们被授予了这种访问权,OAuth提供者则假定用户对他们进行了身份验证。
这种简单的OAuth身份验证机制非常不理想。首先,client应用无法知道用户何时、何地或如何认证的。由于这些实现都是一种自定义解决方案,因此也没有请求用户数据的标准方法。为了正确地支持OAuth,client应用必须为每个OAuth提供者配置单独的OAuth机制,因为每个OAuth服务提供者具有不同的端点、不同的范围等等。
OpenID Connect通过添加标准化的、与身份相关的特性,使通过OAuth进行的身份验证以更可靠和统一的方式工作,从而解决了上述这些问题。
2. OpenID Connect如何工作
OpenID Connect像插槽一样整齐地对接到正常的OAuth流中。从client应用的角度来看,关键的区别在于有一个对所有OAuth提供者都相同的额外的标准化范围,以及一个额外的响应类型:id_token。
HTTP/1.1 200 OK
Content-Type: application/json
Cache-Control: no-store
Pragma: no-cache
{
"access_token": "SlAV32hkKG",
"token_type": "Bearer",
"refresh_token": "8xLOxBtZp8",
"expires_in": 3600,
"id_token":"eyJhbGmtpZCI...ewogImlzc"
}
2.1 OpenID Connect 角色
OpenID Connect的角色本质上与标准OAuth相同。主要的区别是该规范使用了略有不同的术语。
依赖方(Relying party)——请求用户身份验证的应用程序。这与OAuth client是同义的。
最终用户(end user)—正在进行认证的用户。这与OAuth资源所有者是同义词。
OpenID提供者——配置为支持OpenID连接的OAuth服务。
2.2 OpenID Connect 声明和范围
术语“claims”指的是表示资源服务器上用户信息的键值对。例如:“family_name”: “Montoya”。
不同于传统OAuth,对每个服务提供者的范围都不同,所有的OpenID Connect服务使用一组相同的范围。为了使用OpenID Connect,client应用必须在授权请求中指定范围的OpenID。可以包含一个或多个其他标准范围:
profile
email
address
phone
这些范围中的每一个都对应于OpenID规范中定义的关于用户声明的子集的读取访问权。例如,当请求范围为openid profile时,将授予client应用关于用户身份相关的一系列声明的读访问权,如family_name、given_name、birth_date等。
2.3 ID token
OpenID Connect提供的另一个主要功能是id_token响应类型。这将返回一个使用JSON web signature(JWS)进行签名的JSON web token(JWT)。JWT 包含一个基于初始设置范围的键值对列表。它还包含关于用户最后一次通过OAuth服务进行身份验证的方式和时间的信息。client应用可以使用它来决定用户是否已经经过了充分的身份验证。
使用id_token的主要好处是减少了client和OAuth服务之间需要发送的请求数量,这可以提供更好的总体性能。与必须获得access token然后单独请求用户数据不同,在用户通过身份验证后立即将包含用户数据的ID token发送到client。
与基本OAuth中简单依赖可信通道不同,ID令牌中传输数据的完整性基于JWT加密签名保障。出于这个原因,ID令牌的使用可能有助于防止一些中间人攻击。然而,假设用于签名验证的密钥是通过相同的网络通道传输的(通常暴露在/. known/jwks.json上),一些攻击仍然是可能的。
请注意,OAuth支持多种响应类型,所以client应用发送一个同时包含传统OAuth响应类型和OpenID Connect的id_token响应类型的授权请求是完全可以接受的:
response_type=id_token token
response_type=id_token code
在这种情况下,ID token、code或access token同时发送到client。
3. 识别OpenID Connect
如果client正在使用OpenID connect,那么从授权请求中应该可以明显看出这一点。最简单的检查方法是查找openid 范围。
即使登录过程最初没有使用OpenID Connect,仍然值得检查OAuth服务是否支持它。您可以尝试添加openid范围或将响应类型更改为id_token,并观察这是否会导致错误。
与传统的OAuth一样,查看OAuth提供者的文档,看看是否有关于OpenID Connect支持的有用信息也是一个好主意。您还可以从标准端点/. known/openid-configuration访问配置文件。
4. OpenID Connect漏洞
OpenID Connect的规范比传统的OAuth严格得多,这意味着通常不太可能出现明显的漏洞。也就是说,由于它只是OAuth之上的一层,客户端应用程序或OAuth服务可能仍然容易受到我们前面讨论过的一些基于OAuth的攻击。
在本节中,我们将研究OpenID Connect的一些额外特性可能会引入的一些其他漏洞。
4.1 未防护的动态client注册
OpenID规范概述了一种允许client应用向OpenID提供者注册的标准化方法。如果支持动态client注册,client可以通过向专用 /registration 端点发送POST请求来注册自己。这个端点的名称通常在配置文件和文档中提供。
在请求体中,client以JSON格式提交关于自身的关键信息。例如,通常需要包含一个重定向uri数组的白名单。它还可以提交一系列附加信息,例如它们想要公开的端点的名称、它们的应用程序的名称,等等。一个典型的注册请求看起来像这样:
POST /openid/register HTTP/1.1
Content-Type: application/json
Accept: application/json
Host: oauth-authorization-server.com
Authorization: Bearer ab12cd34ef56gh89
{
"application_type": "web",
"redirect_uris": [
"https://client-app.com/callback",
"https://client-app.com/callback2"
],
"client_name": "My Application",
"logo_uri": "https://client-app.com/logo.png",
"token_endpoint_auth_method": "client_secret_basic",
"jwks_uri": "https://client-app.com/my_public_keys.jwks",
"userinfo_encrypted_response_alg": "RSA1_5",
"userinfo_encrypted_response_enc": "A128CBC-HS256",
…
}
OpenID提供者应该要求client对自身进行身份验证。在上面的例子中,他们使用了一个HTTP bearer token。然而,一些提供者允许动态客户端注册而不需要任何身份验证,这使得攻击者可以注册他们自己的恶意client。取决与攻击者如何使用这些可控制的属性,这可能产生各种后果。
例如,您可能已经注意到其中一些属性可以作为uri提供。如果OpenID提供者访问了其中任何一个,这可能会导致second-order SSRF漏洞,除非采取额外的安全措施。
4.2 通过引用允许授权请求
到目前为止,我们已经了解了为授权请求提交所需参数的标准方法,即通过查询字符串。一些OpenID提供商提供给你的选项是将这些作为JSON web token(JWT)传递。如果支持这个特性,你可以发送一个request_uri参数指向一个JSON web token,其中包含其余的OAuth参数和它们的值。根据OAuth服务的配置,这个request_uri参数是SSRF的另一个潜在向量。
您还可以使用此特性绕过对这些参数值的验证。有些服务器可能有效地验证授权请求中的查询字符串,但可能无法对JWT中的参数(包含在redirect_uri)进行充分的验证。
要检查是否支持此选项,您应该在配置文件和文档中查找request_uri_parameter_supported选项。或者,您可以尝试添加request_uri参数,看看它是否有效。您会发现有些服务器支持这个特性,即使它们在文档中没有明确提到它。