OpenID Connect漏洞
OpenID Connect
OpenID Connect 是OAuth 2.0协议顶部的简单身份层。它使客户端能够基于授权服务器执行的身份验证来验证最终用户的身份,以及以可互操作和类似REST的方式获取关于最终用户的基本配置文件信息。
OpenID Connect实现身份验证作为OAuth 2.0授权过程的扩展。通过在授权请求中包含 openid
scope值来请求使用此扩展。关于所执行身份验证的信息在称为 ID token
的JSON Web Token(JWT)中返回。
OpenID Connect角色
在任何OpenID Connect流程中,我们可以识别以下角色:
End-User
- 人类参与者。Relying Party (client)
- 需要最终用户身份验证和来自OpenID Provider的声明的OAuth 2.0客户端应用程序。OpenID Provider
- 能够验证最终用户并向依赖方提供关于身份验证事件和最终用户的声明的OAuth 2.0授权服务器。
协议流程
抽象地说,OpenID Connect协议具有以下流程:

依赖方(客户端)向OpenID Provider发送请求。
OpenID Provider验证最终用户并获得授权。
OpenID Provider以ID令牌和通常是访问令牌响应。
依赖方(客户端)可以使用访问令牌向UserInfo端点发送请求。
UserInfo端点返回关于最终用户的声明。
身份验证
OpenID Connect执行身份验证以登录最终用户或确定最终用户已经登录。OpenID Connect以安全的方式将服务器执行的身份验证结果返回给客户端,以便客户端可以依赖它。因此,在这种情况下,客户端称为依赖方。
身份验证可以遵循以下三条路径之一:
ID令牌
OpenID Connect对OAuth 2.0的主要扩展以启用最终用户身份验证是ID令牌数据结构。ID令牌是包含关于授权服务器使用客户端对最终用户进行身份验证的声明的安全令牌,以及可能的其他请求声明。ID令牌表示为使用JSON Web签名(JWS)签名的JSON Web令牌(JWT)。
以下声明在OpenID Connect使用的所有OAuth 2.0流程中的ID令牌内使用(ID令牌也可能包含其他声明):
iss
必需
响应发布者的发布者标识符。iss值是使用 https
方案的不区分大小写的URL,包含方案、主机,以及可选的端口号和路径组件,没有查询或片段组件。
sub
必需
主题标识符。sub值是不区分大小写的ASCII字符串,长度不超过255个字符。在发布者内为最终用户的本地唯一且永不重新分配的标识符,旨在由客户端使用。
aud
必需
此ID令牌针对的受众。aud值是不区分大小写字符串的数组(或当只有一个受众时为单个不区分大小写的字符串)。它包含依赖方的OAuth 2.0 client_id
,也可能包含其他受众的标识符。
exp
必需
过期时间。
iat
必需
JWT的签发时间。
auth_time
最终用户身份验证发生的时间。
nonce
用于将客户端会话与ID令牌关联的字符串值,并减轻重放攻击。该值未经修改地从身份验证请求传递到ID令牌。
amr
可选
身份验证方法引用。身份验证中使用的方法标识符的字符串JSON数组。例如,值可能表示使用了密码和OTP身份验证方法。
授权码流程
使用授权码流程时,以下ID令牌声明的附加要求适用:
at_hash
可选
访问令牌哈希值。
隐式流程
使用隐式流程时,以下ID令牌声明的附加要求适用:
nonce
必需
此流程需要使用nonce声明。
at_hash
必需
访问令牌哈希值;当未颁发访问令牌时可能不使用,这是 response_type=id_token
的情况。
混合流程
使用混合流程时,以下ID令牌声明的附加要求适用:
nonce
必需
此流程需要使用nonce声明。
at_hash
必需
访问令牌哈希值;当未颁发访问令牌时可能不使用。
c_hash
代码哈希值;当未颁发代码时可能不使用。
OpenID Connect端点
OpenID Connect利用三个端点:
授权端点
令牌端点
UserInfo端点
授权端点
授权端点执行最终用户的身份验证。这是通过将用户代理发送到授权服务器的授权端点进行身份验证和授权,使用OAuth 2.0定义的请求参数和OpenID Connect定义的附加参数和参数值来完成的。
OpenID Connect使用以下OAuth 2.0请求参数:
scope
必需
访问请求的scope。OpenID Connect请求必须包含 openid
scope值。
response_type
必需
OAuth 2.0响应类型值,确定要使用的授权处理流程,包括从使用的端点返回什么参数。
client_id
必需
在授权服务器有效的OAuth 2.0客户端标识符。
redirect_uri
必需
响应将被发送到的重定向URI。
state
推荐
用于在请求和回调之间保持状态的不透明值。通常,通过将此参数的值与浏览器cookie加密绑定来完成跨站点请求伪造缓解。
OpenID Connect还使用附加的请求参数:
nonce
可选
用于将客户端会话与ID令牌关联的字符串值,并减轻重放攻击。
max_age
可选
最大身份验证年龄。指定自上次OpenID Provider主动验证最终用户以来允许的经过时间(以秒为单位)。
response_type如何工作
response_type
请求参数用于通知授权服务器使用哪种流程类型:
code
- 使用授权码流程的值。token
或id_token token
- 使用隐式流程的值。code id_token
、code token
或code id_token token
- 使用混合流程的值。
nonce如何工作
nonce
声明用于将客户端会话与ID令牌关联,并减轻重放攻击。该值未经修改地从身份验证请求传递到ID令牌。如果ID令牌包含 nonce
声明,客户端验证此值等于在身份验证请求中发送的 nonce
参数的值。通常,它根据以下流程工作:
客户端生成安全的随机值并将其原样存储在HttpOnly会话cookie中。
客户端使用该值的加密哈希作为nonce参数并将其作为身份验证请求参数发送。
当ID令牌包含nonce声明时,客户端从持久存储(在这种情况下从cookie)中提取并删除随机值,哈希此值,并与ID令牌中的nonce声明进行比较。如果它们不匹配,客户端拒绝建立身份。
另请参阅 Nonce Implementation Notes。
令牌端点
令牌端点由依赖方(客户端)用于获取访问令牌、ID令牌,以及可选的刷新令牌。令牌端点需要依赖方(客户端)身份验证并支持几种方法,详细信息您可以找到此处。
使用混合流程时,从令牌端点返回的ID令牌的内容与从授权端点返回的ID令牌相同。
UserInfo端点
UserInfo端点是OAuth 2.0受保护的资源,返回关于已验证最终用户的声明。要获取关于最终用户的请求声明,客户端使用通过OpenID connect身份验证获得的访问令牌向UserInfo端点发出请求。这些声明通常由包含声明的名称和值对的JSON对象表示。您可以在此查看标准的基本配置文件声明集此处。
授权码流程
授权码流程 将授权码返回给客户端,然后客户端可以将其交换为ID令牌和访问令牌。
它是如何工作的

客户端准备包含所需请求参数的身份验证请求并将请求发送到授权服务器(另请参阅授权服务器如何验证身份验证请求)。请求可能看起来像这样:
https://auth-server.com/auth? response_type=code& scope=openid%20profile%20email& client_id=s6BhdRkqt3& state=af0ifjsldkj& redirect_uri=https://application-website.com/callback
授权服务器验证最终用户并获得最终用户同意/授权。
授权服务器将最终用户发送回客户端,带有授权码:
https://application-website.com/callback? code=SplxlOBeZQQYbYS6WxSbIA& state=af0ifjsldkj
客户端在令牌端点使用授权码请求响应(另请参阅关于客户端身份验证和授权服务器如何验证令牌请求的附加信息)。请求可能看起来像这样:
POST /token HTTP/1.1 Host: auth-server.com Content-Type: application/json Content-Length: 185 { "client_id": "s6BhdRkqt3", "client_secret": "gX1fBat3bV", "grant_type": "authorization_code", "code": "SplxlOBeZQQYbYS6WxSbIA", "redirect_uri": "https://application-website.com/callback" }
客户端接收包含ID令牌和访问令牌的响应,验证响应并检索最终用户的主题标识符:
HTTP/1.1 200 OK Content-Type: application/json Content-Length: 475 Cache-Control: no-store Pragma: no-cache { "access_token": "SlAV32hkKG", "token_type": "Bearer", "refresh_token": "8xLOxBtZp8", "expires_in": 3600, "id_token": "eyJhbGciOiJSUzI1NiIsImtpZCI6IjFlOWdkazcifQ. eyJpc3MiOiJodHRwczovL2F1dGgtc2VydmVyLmNvbSIs InN1YiI6IjI0ODI4OTc2MTAwMSIsImF1ZCI6InM2Qmhk UmtxdDMiLCJleHAiOjEzMTEyODE5NzAsImlhdCI6MTMx MTI4MDk3MH0. R06v-GNzsQiT8bc4DbkCm6laQ37fHcbLST4Pf52Yr6Jn 36DCP-TaWY6cyko6SyqmfPqqEJ3M9x5AtievsvM-CEGt ggBP7-PeuicHS16199b6syKyQFfkUT8jFj_NcdVc-VL_ Zj6g_bgcjRpB1KLgKqqvA3hDBWhI2NHYeCI7eRY" }
隐式流程
隐式流程 简单得多。客户端应用程序不是首先获取授权码然后交换访问令牌,而是在用户给予同意后立即接收访问码。
它是如何工作的

客户端准备包含所需请求参数的身份验证请求并将请求发送到授权服务器(另请参阅授权服务器如何验证身份验证请求)。请求可能看起来像这样:
https://auth-server.com/auth? response_type=id_token%20token client_id=s6BhdRkqt3& redirect_uri=https://application-website.com/callback& scope=openid%20profile& state=af0ifjsldkj& nonce=n-0S6_WzA2Mj
授权服务器验证最终用户并获得最终用户同意/授权。
授权服务器将最终用户发送回客户端,带有ID令牌和如果请求的访问令牌;客户端验证身份验证响应并检索最终用户的主题标识符:
https://application-website.com/callback? access_token=SlAV32hkKG& token_type=bearer& id_token=eyJ0 ... NiJ9.eyJ1c ... I6IjIifX0.DeWt4Qu ... ZXso& expires_in=3600& state=af0ifjsldkj
混合流程
混合流程 从授权端点返回一些令牌,从令牌端点返回其他令牌。
它是如何工作的

客户端准备包含所需请求参数的身份验证请求并将请求发送到授权服务器(另请参阅授权服务器如何验证身份验证请求)。请求可能看起来像这样:
https://auth-server.com/auth? response_type=code%20id_token& client_id=s6BhdRkqt3& redirect_uri=https://application-website.com/callback& scope=openid%20profile%20email& nonce=n-0S6_WzA2Mj& state=af0ifjsldkj
授权服务器验证最终用户并获得最终用户同意/授权。
授权服务器将最终用户发送回客户端,带有授权码和根据响应类型的一个或多个附加参数:
https://application-website.com/callback? code=SplxlOBeZQQYbYS6WxSbIA& id_token=eyJ0 ... NiJ9.eyJ1c ... I6IjIifX0.DeWt4Qu ... ZXso& state=af0ifjsldkj
客户端在令牌端点使用授权码请求响应(另请参阅关于客户端身份验证和授权服务器如何验证令牌请求的附加信息)。请求可能看起来像这样:
POST /token HTTP/1.1 Host: auth-server.com Content-Type: application/json Content-Length: 185 { "client_id": "s6BhdRkqt3", "client_secret": "gX1fBat3bV", "grant_type": "authorization_code", "code": "SplxlOBeZQQYbYS6WxSbIA", "redirect_uri": "https://application-website.com/callback" }
客户端接收包含ID令牌和访问令牌的响应,验证响应并检索最终用户的主题标识符。
HTTP/1.1 200 OK Content-Type: application/json Content-Length: 475 Cache-Control: no-store Pragma: no-cache { "access_token": "SlAV32hkKG", "token_type": "Bearer", "refresh_token": "8xLOxBtZp8", "expires_in": 3600, "id_token": "eyJ0 ... NiJ9.eyJ1c ... I6IjIifX0.DeWt4Qu ... ZXso" }
OAuth2.0协议中的安全问题
JSON Web Token的安全问题
依赖方中的安全问题
nonce声明的不当处理
如果 nonce
声明是:
缺失的,
永不改变的静态值,
存在但未经验证的,
在客户端以明文对攻击者可用,
OpenID Connect流程可能容易受到重放攻击。要利用这一点,您需要获取身份验证响应并使用它来建立身份。
OpenID提供者中的安全问题
滥用WebFinger服务
为了使OpenID Connect依赖方能够为最终用户利用OpenID Connect服务,依赖方需要知道OpenID提供者在哪里。OpenID Connect使用WebFinger来定位最终用户的OpenID提供者。详细信息您可以在规范中找到。
WebFinger服务在 .well-known/webfinger
端点可用,允许您获取关于服务器上使用的用户和资源的信息。它可用于验证"匿名"用户在服务器上是否有账户:
https://openid-server.com/.well-known/webfinger?
resource=http://x/anonymous&
rel=http://openid.net/specs/connect/1.0/issuer
HTTP/1.1 200
Content-Type: application/json
Content-Length: 132
{
"subject": "http://x/anonymous",
"links": [
"rel": "http://openid.net/specs/connect/1.0/issuer",
"href": "https://auth-server.com/auth"
]
}
rel
参数应该具有 http://openid.net/specs/connect/1.0/issuer
的静态值。
resource
应该包含以下形式之一的有效URL:
http(s)://host/user
acct://user@host
UserInfo端点:CSP错误配置
UserInfo端点应支持使用根据规范的跨源资源共享。
通过动态客户端注册进行SSRF
OpenID Connect Dynamic Client Registration 规范定义了允许客户端应用程序向OpenID提供者注册的标准化方式。如果支持动态客户端注册,客户端应用程序可以通过向 /register
端点发送 POST
请求来注册自己。注册请求可能看起来像这样:
POST /connect/register HTTP/1.1
Host: auth-server.com
Content-Type: application/json
Authorization: Bearer eyJhbGciOiJSUzI1NiJ9.eyJ...
{
"application_type": "web",
"redirect_uris":["https://application-website.com/callback"],
"client_name": "My Example",
"logo_uri": "https://application-website.com/logo.png",
"subject_type": "pairwise",
"sector_identifier_uri": "https://other-application-website.com/file_of_redirect_uris.json",
"token_endpoint_auth_method": "client_secret_basic",
"jwks_uri": "https://application-website.com/my_public_keys.jwks",
"userinfo_encrypted_response_alg": "RSA1_5",
"userinfo_encrypted_response_enc": "A128CBC-HS256",
"contacts": ["[email protected]"],
"request_uris":["https://application-website.com/rf.txt"]
}
其中一些值通过URL链接传入,可能潜在地容易受到SSRF攻击。
以下参数对SSRF攻击特别有趣:
logo_uri
- 引用客户端应用程序logo的URL。在注册客户端后,您可以尝试使用新的"client_id"调用OAuth授权端点。登录后,服务器将要求您批准请求并可能显示来自"logo_uri"的图像。如果服务器自己获取图像,则此步骤应触发SSRF。或者,服务器可能只是通过客户端侧<img>
标签包含logo,这可能导致XSS。jwks_uri
- 客户端的JSON Web Key Set文档的URL。服务器上需要此密钥集以验证在使用JWT进行客户端身份验证时向令牌端点发出的签名请求。为了测试此参数中的SSRF,注册一个带有恶意"jwks_uri"的新客户端应用程序,执行授权过程以获取任何用户的授权码,然后获取带有如下请求体的令牌端点。如果易受攻击,服务器应该对提供的"jwks_uri"执行服务器到服务器的HTTP请求,因为它需要此密钥来检查请求中"client_assertion"参数的有效性。尽管如此,这可能只是一个盲SSRF漏洞,因为服务器期望正确的JSON响应。POST /token HTTP/1.1 Host: auth-server.com Content-Type: application/json { "client_assertion_type": "urn:ietf:params:oauth:client-assertion-type:jwt-bearer", "client_assertion": "eyJhbGci...", "grant_type": "authorization_code", "code": "SplxlOBeZQQYbYS6WxSbIA" }
sector_identifier_uri
- 此URL引用具有redirect_uri
值的单个JSON数组的文件。如果支持,服务器可能在您提交动态注册请求后立即获取此值。如果没有立即获取,尝试在服务器上为此客户端执行授权。由于它需要知道redirect_uris
以完成授权流程,这将强制服务器向您的恶意sector_identifier_uri
发出请求。request_uris
- 此客户端允许的request_uris
的数组。request_uri
参数可能在授权端点上受支持,以提供包含请求信息的JWT的URL,另请参阅此处。要利用授权端点上的SSRF,只需使用request_uri
:https://auth-server.com/auth? response_type=code%20id_token& client_id=s6BhdRkqt3& request_uri=https://attacker-website.com/request.jwt
以下参数也包含URL,但通常不用于发出服务器到服务器的请求。它们用于客户端侧重定向/引用:
redirect_uri
- 用于在授权后重定向客户端的URLclient_uri
- 客户端应用程序主页的URLpolicy_uri
- 依赖方客户端应用程序提供的URL,以便最终用户可以阅读他们的配置文件数据将如何被使用。tos_uri
- 依赖方提供的URL,以便最终用户可以阅读依赖方的服务条款。initiate_login_uri
- 使用https方案的URI,第三方可以使用它来启动依赖方的登录。也应用于客户端侧重定向。
所有参数及其定义您可以找到此处。
通过request_uri参数进行SSRF
request_uri 参数在授权过程开始时由服务器获取(不要将此参数与 redirect_uri
混淆)。
许多服务器不允许任意的 request_uri
值:它们只允许在客户端注册过程中预先注册的允许URL列表。但即使没有启用动态客户端注册,或者它需要身份验证,您也可以尝试通过使用 request_uri
绕过验证并在授权端点上执行SSRF:
https://auth-server.com/auth?
response_type=code%20id_token&
client_id=s6BhdRkqt3&
request_uri=https://attacker-website.com/request.jwt
参考资料
最后更新于