特此声明!!!
OpenID Connect(简称OIDC)是一个基于OAuth 2.0协议的身份验证层,用于在客户端应用程序和身份提供者(IdP)之间实现安全的用户身份验证。它的核心功能是允许客户端应用程序验证用户身份并获取用户的基本信息。
这种做法存在以下问题:
非标准化:每个OAuth提供者实现身份验证的方式不同,导致客户端应用程序需要为每个提供者单独配置。
信息不足:客户端无法知道用户如何、何时或何地进行身份验证,降低了安全性。
复杂性:缺乏统一的机制,增加了开发和维护的难度。
OpenID Connect为OAuth 2.0添加了标准化的身份验证层,解决了上述问题。
特定的用户声明claims
在OpenID Connect中,“claims”是指表示有关用户信息的键值对(key:value pairs)。这些信息由身份提供者(IdP)提供,并包含在ID令牌或用户信息响应中。
示例:例如,"family_name": "my"是一个claim,表示用户的姓氏。
用途:Claims用于传递用户的基本信息(如姓名、电子邮件、地址等),供客户端应用程序使用。
OpenID Connect 的范围(Scope)
客户端应用程序必须在授权请求中指定范围 openid;
表示客户端应用程序正在使用OpenID Connect进行身份验证。在授权请求中,客户端必须包含scope=openid。
标准范围包括:
profile:请求用户的基本个人资料信息(如姓名、昵称、头像等)。
email:请求用户的电子邮件地址和电子邮件验证状态。
address:请求用户的地址信息。
phone:请求用户的电话号码。
授权请求示例:
GET /authorize?
client_id=12345&
redirect_uri=https://client-app.com/callback&
response_type=code&
scope=openid profile email&
state=abc123
作用:ID令牌是一个JSON Web Token(JWT),包含用户的身份信息(如用户ID、电子邮件等)。
验证:客户端可以通过验证ID令牌的签名和内容,确认用户的身份。
id_token是OpenID Connect引入的一种响应类型,返回使用JSON Web签名(JWS)签名的JSON Web令牌(JWT)。JWT的有效负载包含基于最初请求范围的声明列表,以及有关用户身份验证方式和时间的信息。客户端应用程序可以通过id_token验证用户身份,并确定用户是否已经过充分身份验证。
id_token在用户验证后立即发送到客户端应用程序,id_token 减少了需要在客户端应用程序和 OAuth 服务之间发送的请求数,提高了整体性能。无需获取访问令牌,然后单独请求用户数据,而是在用户验证自己后立即将包含此数据的 ID 令牌发送到客户端应用程序。
id_token中的数据通过JWT加密签名,确保了数据的完整性和真实性,有助于防止中间人攻击。但用于签名验证的加密密钥通过同一网络通道传输(通常公开在 /.well-known/jwks.json上(用于公开JSON Web Key Set(JWKS)的标准端点) ),因此仍可能发生某些攻击。
标准化的用户信息端点(/userinfo)
作用:客户端可以通过该端点/userinfo端点请求和接收用户的详细资料。获取用户的详细资料(如姓名、地址等)。
标准化:所有OIDC提供者都使用相同的端点,简化了客户端开发。
请求方法:GET或POST
GET /userinfo HTTP/1.1
Host: oidc-provider.com
Authorization: Bearer <access_token>
响应格式:JSON格式。内容:包含用户的详细资料,
{
"sub": "1234567890",
"name": "Inigo Montoya",
"given_name": "Inigo",
"family_name": "Montoya",
"email": "[email protected]",
"email_verified": true,
"address": {
"street_address": "123 Main St",
"locality": "Anytown",
"region": "CA",
"postal_code": "12345",
"country": "USA"
}
}
OAuth 支持多种响应类型,因此客户端应用程序完全可以接受同时发送具有基本 OAuth 响应类型和 OpenID Connect id_token 响应类型的授权请求:
response_type=id_token token
response_type=id_token code
在这种情况下,ID 令牌和代码或访问令牌将同时发送到客户端应用程序。
End user (最终用户 ) - 正在进行身份验证的用户。这与 OAuth 资源所有者同义。通过身份提供者登录并授权客户端应用程序访问其信息。
OpenID 提供程序(身份提供者(IdP)) - 配置为支持 OpenID Connect 的 OAuth 服务。负责验证用户身份并颁发令牌。
触发:用户尝试通过客户端应用程序登录。
请求:客户端向身份提供者(IdP)发送授权请求,包含以下关键参数:
client_id:客户端标识符。
redirect_uri:重定向URI,用于接收响应。
scope:请求的权限范围(如openid、profile、email)。
response_type:指定响应类型(如code或id_token)。
state:用于防止CSRF攻击的随机值。
(2) 用户身份验证
重定向:身份提供者将用户重定向到登录页面。
验证:用户输入凭据(如用户名和密码)进行身份验证。
授权:用户同意客户端请求的权限。
(3) 授权响应
授权码:身份提供者生成授权码,并将其通过重定向URI返回给客户端。
ID 令牌:如果请求中包含id_token,身份提供者还会生成并返回ID令牌。
(4) 令牌交换
请求:客户端使用授权码向身份提供者请求访问令牌和ID令牌。
验证:身份提供者验证授权码和客户端身份。
响应:身份提供者返回访问令牌和ID令牌。
(5) 用户信息获取
请求:客户端使用访问令牌向身份提供者的/userinfo端点请求用户信息。
响应:身份提供者返回用户的基本信息(如姓名、电子邮件等)。
(1) 令牌泄露
问题:ID令牌或访问令牌可能通过不安全的传输(如未加密的HTTP)或客户端存储不当而被泄露。
防御措施:强制使用HTTPS加密通信。客户端应用程序应安全存储令牌,避免暴露在浏览器或日志中。
(2) 重放攻击
问题:攻击者可能截获并重复使用令牌,冒充用户身份。
防御措施:使用短期令牌并设置合理的过期时间。
在令牌中包含唯一标识符(如nonce)以防止重放。
(3) 客户端身份验证不足
问题:如果客户端应用程序的身份验证机制较弱,攻击者可能冒充合法客户端。
防御措施:使用强客户端认证(如client_secret或PKCE)。
定期审查和更新客户端凭证。
(4) 范围扩展攻击
问题:攻击者可能通过修改scope参数获取额外的用户权限。
防御措施:严格验证scope参数,确保其与初始授权请求一致。在令牌交换时限制范围扩展。
(5) 身份提供者(IdP)的信任问题
问题:客户端应用程序默认信任IdP提供的信息,但如果IdP未验证用户信息,可能导致身份冒充。
防御措施:IdP应强制验证用户信息(如电子邮件地址)。客户端应用程序应实施额外的用户验证机制(如多因素身份验证)。
动态客户端注册是OpenID Connect规范中允许客户端应用程序向OpenID提供商注册自身的一种标准化方法。通过向专用的/registration端点发送POST请求,客户端可以提交其关键信息以完成注册。
POST /openid/register HTTP/1.1
Content-Type: application/json
Accept: application/json
Host: oauth-authorization-server.com
Authorization: Bearer ab12cd34ef56gh89
{
"application_type": "web", 客户端应用程序的类型(如web、native)
"redirect_uris": [ 列入白名单的重定向URI,用于接收授权响应。
"https://client-app.com/callback",
"https://client-app.com/callback2"
],
"client_name": "My Application", 客户端应用程序的名称
"logo_uri": "https://client-app.com/logo.png", 客户端应用程序的Logo URI
"token_endpoint_auth_method": "client_secret_basic", 客户端用于验证身份的方法(如client_secret_basic)。
"jwks_uri": "https://client-app.com/my_public_keys.jwks", 客户端公钥集的URI,用于验证JWT签名。
"userinfo_encrypted_response_alg": "RSA1_5", 用户信息响应的加密算法(如RSA1_5)
"userinfo_encrypted_response_enc": "A128CBC-HS256", 用户信息响应的加密方式(如A128CBC-HS256)
…
}
某些OpenID提供商允许在没有任何身份验证的情况下进行动态客户端注册,攻击者可以注册恶意客户端应用程序。注册请求中的某些属性(如logo_uri、jwks_uri)可以作为URI提供。如果OpenID提供商访问这些URI,可能导致二阶SSRF(服务器端请求伪造)漏洞
封装参数:将OAuth参数(如client_id、redirect_uri、scope等)封装在JWT中,避免直接在查询字符串中暴露。
灵活传递:通过request_uri参数传递JWT的URI引用,简化授权请求的构造。
在OpenID提供商的配置文件(如/.well-known/openid-configuration)中查找request_uri_parameter_supported选项,确认是否支持此功能。也可以通过添加request_uri参数测试其是否有效。示例请求:
GET /authorize?
client_id=12345&
request_uri=https://client-app.com/request.jwt
request_uri参数指向的JWT可能位于外部URI,如果OpenID提供商未严格验证URI,可能导致服务器端请求伪造(SSRF)漏洞。某些服务器可能对查询字符串中的参数进行严格验证,但对JWT中的参数验证不足,导致攻击者可以绕过验证。攻击者可能通过JWT中的redirect_uri参数将用户重定向到恶意站点。
措施:OpenID提供商应要求客户端应用程序在注册时进行身份验证(如使用HTTP持有者令牌)。
好处:防止未授权的客户端注册,减少恶意客户端的风险。
(2) 验证URI属性
措施:对注册请求中的URI属性(如logo_uri、jwks_uri)进行严格验证,确保其指向合法的外部资源。
好处:降低SSRF漏洞的风险。
(3) 限制注册权限
措施:仅允许受信任的客户端应用程序进行动态注册,或对注册请求进行人工审核。
好处:进一步减少恶意客户端的可能性。
(4) 严格验证 request_uri
措施:OpenID提供商应对request_uri参数指向的URI进行严格验证,确保其指向合法的外部资源。
好处:降低SSRF漏洞的风险。
(5) 统一参数验证
措施:对查询字符串和JWT中的参数进行统一验证,确保参数值的合法性和安全性。
好处:防止参数验证绕过。
(6) 限制 request_uri 使用
措施:仅允许受信任的客户端应用程序使用request_uri参数,或对JWT内容进行人工审核。
好处:进一步减少恶意行为。
原文始发于微信公众号(老付话安全):OAuth 协议扩展-OpenID Connect的原理及安全问题
- 左青龙
- 微信扫一扫
-
- 右白虎
- 微信扫一扫
-
评论