Java代码审计-CORS

0x01、 问题背景

公司的项目需要前后端分离,vue+java,这时候就需要支持Cors跨域请求了.

0x001 什么是Cors?

CORS 全称是跨域资源共享(Cross-Origin Resource Sharing),是一种 AJAX 跨域请求资源的方式,支持现代浏览器,IE支持10以上。
CORS与JSONP的使用目的相同,但是比JSONP更强大。JSONP只支持GET请求,CORS支持所有类型的HTTP请求。JSONP的优势在于支持老式浏览器,以及可以向不支持CORS的网站请求数据。

简单来说Cors就是用来解决跨域问题的。此外解决的方式还有:Nginx反向代理,Jsoup等方式。不过Nginx不符合Devops理念,不易维护,而Jsoup支持的请求类型只支持Get请求。Cors更为适合;

0x002 什么是跨域?

同源就是指:域名,协议,端口 均相同
跨域就是指: 域名,协议,端口 其中至少有一个是不同的;
当前端请求后端接口的时候,当端口号,ip地址等不一致的时候,为了安全性考量,请求所响应的数据不会被前端展示。等于请求的数据没有被标识为友军,没有被证明是敌是友;

ajax请求才会出现跨域

0x003 Cors能解决什么问题?

通过在服务端配置Cors相关的东西,能识别是否给予跨域权限,然后给予能跨域的标识,通过此标识,浏览器就可以渲染返回来的数据啦~~

0x02案例一. Cors在SpringCloud微服务中的使用;

0x001 环境:

JDK1.8 IDEA SpringCloud微服务 网关使用Zuul

0x002 代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
package com.leyou.gateway.config;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.cors.UrlBasedCorsConfigurationSource;
import org.springframework.web.filter.CorsFilter;

@Configuration
public class CorsConfiguration {

@Bean
public CorsFilter corsFilter(){
//配置初始化对象
CorsConfiguration configuration=new CorsConfiguration();
//允许跨域的域名,如果要携带cookie,不能写* 。 *:代表所有的域名都可以访问
configuration.addAllowedOrigin("http://localhost:9001");
configuration.setAllowCredentials(true);
configuration.addAllowedMethod("*"); //代表所有的请求方法
configuration.addAllowedHeader("*"); //允许携带任何头信息

//初始化cors配置源对象
UrlBasedCorsConfigurationSource configurationSource=new UrlBasedCorsConfigurationSource();
configurationSource.registerCorsConfiguration("/**",configuration);

//返回corsFilter实例,参数:cors配置源对象
return new CorsFilter(configurationSource);
}
}

0x003 什么是zuul微服务网关:

0x03 【HTTP header】【【Access-Control-Allow-Credentials】跨域Ajax请求时是否带Cookie的设置

  • 前端发起AJAX请求都会受到同源策略(CORS)的限制。发起AJAX请求的方法:

    1
    2
    3
    4
    第一个是原生方法 XMLHttpRequest
    第二个是jQuery封装的 JQuery的$.ajax
    第三个是fetch是专门封装的库 Fetch
    第四个是axios
  • 前端在发起AJAX请求时,同域或者直接访问的情况下,因为没有跨域的需求,所以Request的Header中的Origin为空。此时,如果后端代码是response.setHeader(“Access-Control-Allow-Origin”, origin),那么Response的header中不会出现Access-Control-Allow-Origin,因为Origin为空。

  • 注意当我们的客户端和服务端交互的时候使用的是 token,通过 Authorization头发送到服务端,并没有使用到 cookie时,所以客户端没有必要设置 withCredentials: true

0x001 无Cookie跨域Ajax请求

  • 客户端
1
2
3
4
5
6
7
8
9
- 以 jQuery 的 ajax 为例:
$.ajax({
url : 'http://remote.domain.com/corsrequest',
data : data,
dataType: 'json',
type : 'POST',
crossDomain: true,
contentType: "application/json",
// POST时必须主要注意的是参数 crossDomain: true。发送Ajax时,Request header中会包含跨域的额外信息,但不会含cookie。
  • 服务器端

    • 跨域的允许主要由服务器端控制。服务器端通过在响应的 header 中设置 Access-Control-Allow-Origin 及相关一系列参数,提供跨域访问的允许策略。

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      以Java为例:

      /**
      * Spring Controller中的方法:
      */
      @RequestMapping(value = "/corsrequest")
      @ResponseBody
      public Map<String, Object> mainHeaderInfo(HttpServletResponse response) {
      response.setHeader("Access-Control-Allow-Origin", "*");
      ...
      }
  • 通过在响应 header 中设置 星号 来允许来自所有域的跨域请求访问。

1
response.setHeader("Access-Control-Allow-Origin", "*");
  • 只允许来自特定域 http://my.domain.cn:8080 的跨域访问

    1
    response.setHeader("Access-Control-Allow-Origin", "http://my.domain.cn:8080");
  • 较灵活的设置方式,允许所有包含 mydomain.com 的域名访问.从安全的角度去考虑,并不建议使用,因为攻击者很容易绕过。

    1
    2
    if(request.getHeader("Origin").contains("mydomain.com")) {
    response.setHeader("Access-Control-Allow-Origin", request.getHeader("Origin"));

0x002 带Cookie的跨域Ajax请求

  • 客户端

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11

    $.ajax({
    url : 'http://remote.domain.com/corsrequest',
    data : data,
    dataType: 'json',
    type : 'POST',
    xhrFields: {
    withCredentials: true
    },
    crossDomain: true,
    contentType: "application/json",
  • 通过设置 withCredentials: true ,发送Ajax时,Request header中便会带上 Cookie 信息。

  • 服务器端

  • 相应的,对于客户端的参数,服务器端也需要进行设置:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    * Spring Controller中的方法:
    @RequestMapping(value = "/corsrequest")
    @ResponseBody
    public Map<String, Object> getUserBaseInfo(HttpServletResponse response) {
    if(request.getHeader("Origin").contains("woego.cn")) {
    response.setHeader("Access-Control-Allow-Origin", request.getHeader("Origin"));
    }
    response.setHeader("Access-Control-Allow-Credentials", "true");
    ...
  • 对应客户端的 xhrFields.withCredentials: true 参数,服务器端通过在响应 header 中设置 Access-Control-Allow-Credentials = true 来运行客户端携带证书式访问。通过对 Credentials 参数的设置,就可以保持跨域 Ajax 时的 Cookie。这里需要注意的是:

    1
    2
    服务器端 Access-Control-Allow-Credentials = true时,
    参数Access-Control-Allow-Origin 的值不能为 '*' 。

0x04 Nginx导致Cors及修复方案

  • 存在漏洞配置:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    add_header  Access-Control-Allow-Origin *;
    add_header Access-Control-Allow-Methods GET,POST,PUT,DELETE,OPTIONS;
    add_header 'Access-Control-Allow-Credentials' 'true';

    or

    add_header 'Access-Control-Allow-Origin' "$http_origin";
    add_header Access-Control-Allow-Methods GET,POST,PUT,DELETE,OPTIONS;
    add_header 'Access-Control-Allow-Credentials' 'true';
  • 修复方案需要限制origin:

    1
    2
    add_header 'Access-Control-Allow-Origin' https://test.joychou.org;
    add_header 'Access-Control-Allow-Origin' http://test.joychou.org;