【CVE-2019-3778】Spring-Security-OAuth2 Open Redirection 复现分析
字数 1382 2025-08-26 22:12:03

Spring Security OAuth2 Open Redirection漏洞(CVE-2019-3778)分析与复现指南

0x00 漏洞概述

漏洞编号: CVE-2019-3778
漏洞类型: Open Redirection (开放重定向)
影响组件: Spring Security OAuth2
漏洞描述: 攻击者在使用授权码模式时,可以构造恶意请求发送至授权端点,通过篡改redirect_uri参数值,导致授权服务端将用户回调至攻击者可控的URI,从而泄露授权码。

0x01 影响版本

  • 2.3.0 - 2.3.4
  • 2.2.0 - 2.2.3
  • 2.1.0 - 2.1.3
  • 2.0.0 - 2.0.16

0x02 环境要求

要复现此漏洞,目标应用必须满足以下条件:

  1. 必须是授权服务器角色(使用@EnableAuthorizationServer注解)
  2. 使用DefaultRedirectResolver类来解析redirect_uri

0x03 环境搭建

1. 数据库配置

需要在服务端数据库表oauth_client_details中插入客户端信息:

INSERT INTO oauth_client_details 
(client_id, client_secret, redirect_uri, scope, authorized_grant_types) 
VALUES 
('ananaskr', '123456', 'http://www.baidu.com', 'all', 'authorization_code');

2. 用户信息配置

WebConfig类中添加用户信息(示例):

@Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
    auth.inMemoryAuthentication()
        .withUser("user1").password("password1").roles("USER")
        .and()
        .withUser("user2").password("password2").roles("USER")
        .and()
        .withUser("user3").password("password3").roles("USER");
}

0x04 漏洞分析

关键代码分析

漏洞位于DefaultRedirectResolver类的obtainMatchingRedirect()方法中:

public String obtainMatchingRedirect(String requestedRedirect, ClientDetails client) {
    if (requestedRedirect == null) {
        return null;
    }
    Set<String> redirectUris = client.getRegisteredRedirectUri();
    if (redirectUris == null || redirectUris.isEmpty()) {
        return requestedRedirect;
    }
    for (String redirectUri : redirectUris) {
        if (redirectMatches(requestedRedirect, redirectUri)) {
            return requestedRedirect;  // 直接返回用户输入的redirect_uri
        }
    }
    return null;
}

redirectMatches()方法检查逻辑:

private boolean redirectMatches(String requestedRedirect, String redirectUri) {
    // 检查scheme、host、path和port四部分是否匹配
    UriComponents requestedRedirectUri = UriComponentsBuilder.fromUriString(requestedRedirect).build();
    UriComponents registeredRedirectUri = UriComponentsBuilder.fromUriString(redirectUri).build();
    
    // scheme检查
    if (!registeredRedirectUri.getScheme().equals(requestedRedirectUri.getScheme())) {
        return false;
    }
    
    // host检查
    if (!registeredRedirectUri.getHost().equals(requestedRedirectUri.getHost())) {
        return false;
    }
    
    // port检查
    if (registeredRedirectUri.getPort() != requestedRedirectUri.getPort()) {
        return false;
    }
    
    // path检查
    String registeredPath = registeredRedirectUri.getPath();
    String requestedPath = requestedRedirectUri.getPath();
    if (requestedPath.length() < registeredPath.length()) {
        return false;
    }
    if (!requestedPath.substring(0, registeredPath.length()).equals(registeredPath)) {
        return false;
    }
    
    return true;
}

漏洞利用原理

  1. 原始检查逻辑只验证了scheme、host、port和path,未对userinfo部分进行验证
  2. 攻击者可以利用URL解析差异构造恶意URL:http://www.google.com%ff@www.baidu.com
  3. 服务端解析时,%ff会被转换为非法字符,导致解析错误
  4. 某些浏览器会将此类URL解析为http://www.google.com,从而实现开放重定向

0x05 漏洞复现步骤

  1. 访问授权端点构造恶意请求:

    http://localhost:9090/oauth/authorize?
    response_type=code&
    client_id=ananaskr&
    redirect_uri=http://www.google.com%ff@www.baidu.com&
    scope=all&
    client_secret=123456
    
  2. 系统会跳转至登录页面,输入有效凭据登录

  3. 点击"Authorize"按钮授权

  4. 观察跳转行为:实际会跳转至www.google.com而非注册的www.baidu.com

0x06 补丁分析

在2.3.5版本中,修复措施包括:

  1. DefaultRedirectResolver类中增加了对userinfo的检查
  2. 对请求参数进行了更严格的验证
  3. 修改obtainMatchingRedirect()方法,不再直接返回用户输入的redirect_uri,而是在注册的redirect_uri基础上进行修改后返回

关键修复代码:

public String obtainMatchingRedirect(String requestedRedirect, ClientDetails client) {
    // ... 省略部分代码 ...
    for (String redirectUri : redirectUris) {
        if (redirectMatches(requestedRedirect, redirectUri)) {
            // 不再直接返回用户输入,而是基于注册URI构建
            return UriComponentsBuilder.fromUriString(redirectUri)
                .query(requestedRedirectUri.getQuery())
                .fragment(requestedRedirectUri.getFragment())
                .build()
                .toUriString();
        }
    }
    return null;
}

0x07 防护建议

  1. 升级Spring Security OAuth2到安全版本(2.3.5+)
  2. 自定义RedirectResolver实现,增加更严格的重定向URL验证
  3. 实施白名单机制,严格控制允许的重定向域名
  4. 对所有用户提供的URL参数进行严格验证和过滤

0x08 参考资源

  1. Blackhat 2019: Make Redirection Evil Again URL Parser Issues in OAuth
  2. Spring Security OAuth官方文档
  3. OAuth 2.0安全最佳实践
Spring Security OAuth2 Open Redirection漏洞(CVE-2019-3778)分析与复现指南 0x00 漏洞概述 漏洞编号 : CVE-2019-3778 漏洞类型 : Open Redirection (开放重定向) 影响组件 : Spring Security OAuth2 漏洞描述 : 攻击者在使用授权码模式时,可以构造恶意请求发送至授权端点,通过篡改 redirect_uri 参数值,导致授权服务端将用户回调至攻击者可控的URI,从而泄露授权码。 0x01 影响版本 2.3.0 - 2.3.4 2.2.0 - 2.2.3 2.1.0 - 2.1.3 2.0.0 - 2.0.16 0x02 环境要求 要复现此漏洞,目标应用必须满足以下条件: 必须是授权服务器角色(使用 @EnableAuthorizationServer 注解) 使用 DefaultRedirectResolver 类来解析 redirect_uri 0x03 环境搭建 1. 数据库配置 需要在服务端数据库表 oauth_client_details 中插入客户端信息: 2. 用户信息配置 在 WebConfig 类中添加用户信息(示例): 0x04 漏洞分析 关键代码分析 漏洞位于 DefaultRedirectResolver 类的 obtainMatchingRedirect() 方法中: redirectMatches() 方法检查逻辑: 漏洞利用原理 原始检查逻辑只验证了scheme、host、port和path,未对userinfo部分进行验证 攻击者可以利用URL解析差异构造恶意URL: http://www.google.com%ff@www.baidu.com 服务端解析时, %ff 会被转换为非法字符,导致解析错误 某些浏览器会将此类URL解析为 http://www.google.com ,从而实现开放重定向 0x05 漏洞复现步骤 访问授权端点构造恶意请求: 系统会跳转至登录页面,输入有效凭据登录 点击"Authorize"按钮授权 观察跳转行为:实际会跳转至 www.google.com 而非注册的 www.baidu.com 0x06 补丁分析 在2.3.5版本中,修复措施包括: 在 DefaultRedirectResolver 类中增加了对userinfo的检查 对请求参数进行了更严格的验证 修改 obtainMatchingRedirect() 方法,不再直接返回用户输入的 redirect_uri ,而是在注册的 redirect_uri 基础上进行修改后返回 关键修复代码: 0x07 防护建议 升级Spring Security OAuth2到安全版本(2.3.5+) 自定义 RedirectResolver 实现,增加更严格的重定向URL验证 实施白名单机制,严格控制允许的重定向域名 对所有用户提供的URL参数进行严格验证和过滤 0x08 参考资源 Blackhat 2019: Make Redirection Evil Again URL Parser Issues in OAuth Spring Security OAuth官方文档 OAuth 2.0安全最佳实践