介绍
本篇为代码审计系列的第十一篇《代码审计之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 代码审计之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配置中进行相关配置即可,参考代码如下:
// 安全的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安全、渗透测试、代码审计等内容,欢迎您的关注!
介绍
本篇为代码审计系列的第十一篇《代码审计之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字段来进行测试,可以看到响应头中的相关字段。
注意上面允许所有域用的是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配置
@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
- 左青龙
- 微信扫一扫
-
- 右白虎
- 微信扫一扫
-
评论