代码审计之CORS

admin 2025年4月8日13:32:04评论13 views字数 6754阅读22分30秒阅读模式

介绍

本篇为代码审计系列的第十一篇《代码审计之CORS》,预计本系列为30篇左右。

CORS全称是Cross-Origin Resource Sharing,翻译为跨域资源共享,跨域资源共享是一种浏览器安全机制,它可用于控制不同源(域名、协议或端口)之间的HTTP请求。CORS漏洞主要是由于错误配置CORS策略,导致任何域的请求都可以访问目标站点。

这种情况下就会造成一些问题,攻击前提是同一个浏览器,且用户已登录的情况下,比如:

1、可以用来获取目标用户的敏感信息,例如用户个人信息页包含了账号、邮箱、余额、密钥等敏感信息,攻击者可构造一个恶意的地址,然后地址上的脚本用来请求目标站点的个人信息页,并将获取到的信息发送到攻击者自己的服务器,从而导致信息泄露问题。

2、CORS也可以用来绕过CSRF Token的限制,可以先请求要攻击的页面,然后把获取到的信息发送到自己的攻击服务器,其中就包含了Token,之后再伪造对应的CSRF请求,并把Token带上,从而完成攻击。

SpringMVC配置

下面来看一个漏洞示例代码,比如用SpringBoot新建一个接口,用来模拟用户的个人信息页面:

@RestController
@RequestMapping("/api")
publicclassUserController{

@GetMapping("/user/info")
public Map<String, String> getUserInfo(){
        Map<String, String> userInfo = new HashMap<>();
        userInfo.put("username""admin");
        userInfo.put("email""[email protected]");
        userInfo.put("balance""100");
        userInfo.put("key""0x1x2x3x4x5x6x7x8x9");
return userInfo;
    }
}

然后我们先以SpringMVC配置为例,以自带的CorsRegistry为基础,继续扩展配置,这里配置的允许所有域、所有方法、所有头和凭证,如下代码。

@Configuration
publicclassTestWebConfigimplementsWebMvcConfigurer{
@Override
publicvoidaddCorsMappings(CorsRegistry registry){
        registry.addMapping("/**")
                .allowedOriginPatterns("*"// 允许所有域
                .allowedMethods("*"// 允许所有方法
                .allowedHeaders("*"// 允许所有头
                .allowCredentials(true); // 允许凭证
    }
}

这个时候访问刚才定义的接口时,在请求头中添加Origin字段来进行测试,可以看到响应头中的相关字段。

代码审计之CORS

注意上面允许所有域用的是allowedOriginPatterns,之前的写法可以使用allowedOrigins,区别在于allowedOriginPatterns类似于正则匹配,而allowedOrigins设置*代表允许所有域。

如果allowedOrigins为星号,allowCredentials为true,则是访问不了的,一个是Springboot不会允许有这样的配置,这样配置不安全,另一个就是浏览器会遵循CORS规则,不会允许发送这样的请求。

这种如果要修复,使用allowedOriginPatterns配置了正则的话,就要看正则是否有绕过的可能,或者使用allowedOrigins配置指定的域,示例如下。

publicvoidaddCorsMappings(CorsRegistry registry){
    registry.addMapping("/**")
            .allowedOrigins(
"https://example.com"
"https://admin.example.com"
            )
            .allowedMethods("GET""POST""PUT""DELETE")  // 只允许需要的方法
            .allowedHeaders("Authorization""Content-Type")  // 只允许需要的头
            .allowCredentials(true);
}

CORS的防护主要在于请求域的限定和是否允许凭证,而像allowedHeaders这样的配置,是允许的头,它的作用只限制了JavaScript可以在跨域请求中设置哪些自定义头,属于功能级别。

建议限定域并把凭证设置为false。

SpringSecurity

如果框架用了SpringSecurity,那么CORS也可以在SpringSecurity中进行配置,配置及注释如下:

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.cors.CorsConfigurationSource;
import org.springframework.web.cors.UrlBasedCorsConfigurationSource;

import java.util.Arrays;
import java.util.Collections;

@Configuration
publicclassTestWebConfig{

@Bean
public SecurityFilterChain securityFilterChain(HttpSecurity http)throws Exception {
        http
                .cors().and()  // 启用CORS支持
                .csrf().disable()  // 启用CSRF保护
                .authorizeRequests()
                .anyRequest().authenticated()
                .and()
                .httpBasic();

return http.build();
    }

// 存在CORS漏洞
@Bean
public CorsConfigurationSource corsConfigurationSource(){
        CorsConfiguration configuration = new CorsConfiguration();

// 允许所有源
        configuration.setAllowedOriginPatterns(Collections.singletonList("*"));
// 允许所有头
        configuration.setAllowedHeaders(Arrays.asList("*"));
// 允许所有方法
        configuration.setAllowedMethods(Arrays.asList("*"));
// 允许发送凭证
        configuration.setAllowCredentials(true);

        UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
// 这里第一个参数代表生效的url路径,比如/api,那就代表只有api下的相关请求才会用到cors的配置,第二个参数是上面配置好的cors
        source.registerCorsConfiguration("/**", configuration);
return source;
    }
}

此时访问请求,需要登录,登录后,可以发现请求中包含了Authorization认证字段,这个就是SpringSecurity增加的认证,然后我们添加一个Origin头进行测试,可以看到相关的响应。

代码审计之CORS

修复的话,在CORS配置中进行相关配置即可,参考代码如下:

// 安全的CORS配置
@Bean
public CorsConfigurationSource corsConfigurationSource(){
    CorsConfiguration configuration = new CorsConfiguration();

// 修复1: 明确指定允许的源,而不是使用通配符
    configuration.setAllowedOrigins(Arrays.asList(
"https://example.com"
"https://admin.example.com"
    ));

// 修复2: 只允许必要的头,这个项不是决定性的因素
    configuration.setAllowedHeaders(Arrays.asList(
"Authorization"
"Content-Type",
"Accept"
    ));

// 修复3: 只允许必要的HTTP方法,这个项也不是决定性的因素
    configuration.setAllowedMethods(Arrays.asList(
"GET"
"POST"
"PUT"
"DELETE"
"OPTIONS"
    ));

// 修复4: 安全地允许凭证,这个建议为false,如果指定了源,则可以为true,但要保证源设置没问题
    configuration.setAllowCredentials(true);

    UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
    source.registerCorsConfiguration("/**", configuration);
return source;
}

控制器配置

CORS配置还可以在相关的类上进行单独的配置,比如下面这个例子,主要是通过CrossOrigin注解。

import org.springframework.web.bind.annotation.*;

import java.util.HashMap;
import java.util.Map;

// 漏洞:允许所有源访问并发送凭证
@CrossOrigin(origins = "*", allowCredentials = "true")
@RestController
@RequestMapping("/api")
publicclassUserController{

@GetMapping("/user/info")
public Map<String, String> getUserInfo(){
        Map<String, String> userInfo = new HashMap<>();
        userInfo.put("username""admin");
        userInfo.put("email""[email protected]");
        userInfo.put("balance""100");
        userInfo.put("key""0x1x2x3x4x5x6x7x8x9");
return userInfo;
    }
}

这种修复在注解中做相关配置即可,如下代码:

package org.example.cors.controller;

import org.springframework.web.bind.annotation.*;

import java.util.HashMap;
import java.util.Map;

// 修复:明确指定允许的源
@CrossOrigin(
    origins = {"https://your-app.com""https://admin.your-app.com""http://localhost:3000"}, 
    allowCredentials = "true",
    allowedHeaders = {"Authorization""Content-Type"}
)
@RestController
@RequestMapping("/api")
publicclassUserController{

@GetMapping("/user/info")
public Map<String, String> getUserInfo(){
        Map<String, String> userInfo = new HashMap<>();
        userInfo.put("username""admin");
        userInfo.put("email""[email protected]");
        userInfo.put("balance""100");
        userInfo.put("key""0x1x2x3x4x5x6x7x8x9");
return userInfo;
    }
}

方法级别配置

除了类上配置外,也可以针对某个方法进行单独的配置,和类上的配置用法基本一样。

import org.springframework.web.bind.annotation.*;

import java.util.HashMap;
import java.util.Map;

@RestController
@RequestMapping("/api")
publicclassApiController{

// 漏洞:公共API使用宽松的CORS配置
@CrossOrigin(origins = "*", allowCredentials = "true")
@GetMapping("/public/data")
public Map<String, String> getPublicData(){
        Map<String, String> data = new HashMap<>();
        data.put("version""1.0");
        data.put("status""active");
return data;
    }
}

修复的话和类上配置也是一样的,如下代码:

import org.springframework.web.bind.annotation.*;

import java.util.HashMap;
import java.util.Map;
import java.util.Arrays;

@RestController
@RequestMapping("/api")
publicclassApiController{

// 修复:公共数据可以允许更多源访问,但不发送凭证
@CrossOrigin(
        origins = "*"
        allowCredentials = "false",
        maxAge = 3600
    )
@GetMapping("/public/data")
public Map<String, String> getPublicData(){
        Map<String, String> data = new HashMap<>();
        data.put("version""1.0");
        data.put("status""active");
return data;
    }
}

以上就是关于CORS在审计中的一些知识点。

关于我们

我们是《AI安全攻防》,致力于分享AI安全、渗透测试、代码审计等内容,欢迎您的关注!

原文始发于微信公众号(AI安全攻防):代码审计之CORS

免责声明:文章中涉及的程序(方法)可能带有攻击性,仅供安全研究与教学之用,读者将其信息做其他用途,由读者承担全部法律及连带责任,本站不承担任何法律及连带责任;如有问题可邮件联系(建议使用企业邮箱或有效邮箱,避免邮件被拦截,联系方式见首页),望知悉。
  • 左青龙
  • 微信扫一扫
  • weinxin
  • 右白虎
  • 微信扫一扫
  • weinxin
admin
  • 本文由 发表于 2025年4月8日13:32:04
  • 转载请保留本文链接(CN-SEC中文网:感谢原作者辛苦付出):
                   代码审计之CORShttps://cn-sec.com/archives/3929037.html
                  免责声明:文章中涉及的程序(方法)可能带有攻击性,仅供安全研究与教学之用,读者将其信息做其他用途,由读者承担全部法律及连带责任,本站不承担任何法律及连带责任;如有问题可邮件联系(建议使用企业邮箱或有效邮箱,避免邮件被拦截,联系方式见首页),望知悉.

发表评论

匿名网友 填写信息