zoukankan      html  css  js  c++  java
  • CROS与其安全问题处理

    1、CORS定义:

    CORS(Cross-Origin Resource Sharing, 跨源资源共享)是W3C出的一个标准,其思想是使用自定义的HTTP头部让浏览器与服务器进行沟通,从而决定请求或响应是应该成功,还是应该失败。当一个请求url的协议、域名、端口三者之间任意一与当前页面地址不同即为跨域。因此,要想实现CORS进行跨域,需要服务器进行一些设置,同时前端也需要做一些配置和分析。

    出于安全原因,浏览器限制从脚本内发起的跨源HTTP请求。(并不一定是浏览器限制了发起跨站请求,也可能是跨站请求可以正常发起,但是返回结果被浏览器拦截了。)
    安全问题:如果一个网页可以随意地访问另外一个网站的资源,那么就有可能在客户完全不知情的情况下出现安全问题。比如下面的操作就有安全问题:
    1)用户访问www.mybank.com ,登陆并进行网银操作,这时cookie啥的都生成并存放在浏览器
    2)用户突然想起件事,并迷迷糊糊地访问了一个邪恶的网站 www.xiee.com
    3)这时该网站就可以在它的页面中,拿到银行的cookie,比如用户名,登陆token等,然后发起对www.mybank.com 的操作。
    如果这时浏览器不予限制,并且银行也没有做响应的安全处理的话,那么用户的信息有可能就这么泄露了。

    既然有安全问题,那为什么又要跨域呢? 有时公司内部有多个不同的子域,比如一个是location.company.com ,而应用是放在app.company.com , 这时想从 app.company.com去访问 location.company.com 的资源就属于跨域。

    2、CORS问题处理
    如果能够控制住允许某些ip访问特定的资源,这样貌似既可以享受跨域的好处,又可以避免安全问题,下面是一次实验,请各位指正:

    1.后台接口代码,有两个方法,两个区别只是返回内容不同,格式一致
    package com.xbrother.report.custom.controller;
    
    import java.text.SimpleDateFormat;
    import java.util.Date;
    import java.util.Enumeration;
    import java.util.HashMap;
    import java.util.Map;
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletResponse;
    import org.springframework.stereotype.Controller;
    import org.springframework.web.bind.annotation.RequestMapping;
    import org.springframework.web.bind.annotation.ResponseBody;
    import com.alibaba.fastjson.JSON;
    
    @Controller
    @RequestMapping("tc")
    public class TestCorsController {
    	@ResponseBody
    	@RequestMapping("tm1")
    	public String testMethod1(HttpServletRequest request, HttpServletResponse response){
    		Enumeration<String> headerNames = request.getHeaderNames();
    		System.out.println("请求时间:"+new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date()));
    		System.out.println("请求接口:tm1");
    		Map<String, String> map = new HashMap<>();
    		map.put("tm", "tm1");
    		while (headerNames.hasMoreElements()) {
    			String headerName = (String) headerNames.nextElement();
    			String value = request.getHeader(headerName);
    			map.put(headerName, value);
    			System.out.println("请求头-"+headerName + ":"+value);
    		}
    		return JSON.toJSONString(map);
    	}
    	@RequestMapping("tm2")
    	public String testMethod2(HttpServletRequest request, HttpServletResponse response){
    		Enumeration<String> headerNames = request.getHeaderNames();
    		System.out.println("请求时间:"+new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date()));
    		System.out.println("请求接口:tm2");
    		Map<String, String> map = new HashMap<>();
    		map.put("tm", "tm2");
    		while (headerNames.hasMoreElements()) {
    			String headerName = (String) headerNames.nextElement();
    			String value = request.getHeader(headerName);
    			map.put(headerName, value);
    			System.out.println("请求头-"+headerName + ":"+value);
    		}
    		return JSON.toJSONString(map);
    	}
    }
    
    2.将上面的代码部署到3.27
    
    3.浏览器直接访问接口tm1的url
    1)后台打印日志如下:
    请求时间:2020-06-01 15:23:45
    请求接口:tm1
    请求头-host:192.168.3.27:4143
    请求头-connection:keep-alive
    请求头-cache-control:max-age=0
    请求头-upgrade-insecure-requests:1
    请求头-user-agent:Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/80.0.3987.132 Safari/537.36
    请求头-accept:text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9
    请求头-accept-encoding:gzip, deflate
    请求头-accept-language:zh-CN,zh;q=0.9,en-US;q=0.8,en;q=0.7
    请求头-cookie:MODE=0; X_GU_SID=XSS_FpplS7-OOb9L5mdBUqmiLdD-rASGm6AqjO71H2cbhnxNCv4; USER_ID=1; ACCOUNT=YWRtaW4=; USER_NAME=57O757uf566h55CG5ZGY; THEME=default; X_PRODUCT=gu; JSESSIONID=dummy
    
    2)浏览器收到的响应如下:
    {
        "accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9",
        "accept-encoding": "gzip, deflate",
        "accept-language": "zh-CN,zh;q=0.9,en-US;q=0.8,en;q=0.7",
        "cache-control": "max-age=0",
        "connection": "keep-alive",
        "cookie": "MODE=0; X_GU_SID=XSS_FpplS7-OOb9L5mdBUqmiLdD-rASGm6AqjO71H2cbhnxNCv4; USER_ID=1; ACCOUNT=YWRtaW4=; USER_NAME=57O757uf566h55CG5ZGY; THEME=default; X_PRODUCT=gu; JSESSIONID=dummy",
        "host": "192.168.3.27:4143",
        "tm": "tm1",
        "upgrade-insecure-requests": "1",
        "user-agent": "Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/80.0.3987.132 Safari/537.36"
    }
    
    3.编写如下的跨域访问html/js代码,部署到3.207和
    <!DOCTYPE HTML>
    <html>
    <head>
        <meta charset="utf-8">
        <title>Test</title>
        <script src="../js/jquery.min.js"></script>
    </head>
    <body>
    	<div class="divs" id="div1"></div><button class="bts" onclick="cors1()">div1</button>
    	<div class="divs" id="div2"></div><button class="bts" onclick="cors2()">div2</button>
    </body>
    <script type="text/javascript">
    	var url1 = "http://192.168.3.27:4143/sb/demo/tc/tm1";
    	var url2 = "http://192.168.3.27:4143/sb/demo/tc/tm2";
    	function cors(divid,url,method) {
    	  var xhttp = new XMLHttpRequest();
    	  xhttp.onreadystatechange = function() {
    		 debugger;
    	     if (this.readyState == 4 && this.status == 200) {
    	       document.getElementById(divid).innerHTML = this.responseText;
    	     }
    	  };
    	  xhttp.open(method, url, true);
    	  xhttp.withCredentials =  true;//浏览器跨域请求默认不携带cookie,要想携带跨域的cookie,必须配置此配置
    	  xhttp.send();
    	}
    	function cors1() {
    		cors("div1",url1,"get")
    	}
    	function cors2() {
    		cors("div2",url2,"put")
    	}
    </script>
    </html>
    
    4.浏览器访问3.207的html,点击div1按钮通过其中的js代码进行跨域访问
    1)后台打印日志如下:
    请求时间:2020-06-01 15:41:06
    请求接口:tm1
    请求头-host:192.168.3.27:4143
    请求头-connection:keep-alive
    请求头-origin:http://192.168.3.207
    请求头-user-agent:Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/80.0.3987.132 Safari/537.36
    请求头-accept:*/*
    请求头-referer:http://192.168.3.207/xbreport/view/page/test_cors.html
    请求头-accept-encoding:gzip, deflate
    请求头-accept-language:zh-CN,zh;q=0.9,en-US;q=0.8,en;q=0.7
    
    2)浏览器没有响应显示
    响应中提示
    This request has no response data available.
    控制台提示以下错误:
    Access to XMLHttpRequest at 'http://192.168.3.27:4143/sb/demo/tc/tm1' from origin 'http://192.168.3.207' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource.
    
    5.在3.27的工程中加入以下的过滤器
    package com.example.demo1.t1;
    
    import java.io.IOException;
    import java.util.ArrayList;
    import java.util.List;
    import javax.servlet.Filter;
    import javax.servlet.FilterChain;
    import javax.servlet.FilterConfig;
    import javax.servlet.ServletException;
    import javax.servlet.ServletRequest;
    import javax.servlet.ServletResponse;
    import javax.servlet.annotation.WebFilter;
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletResponse;
    import org.springframework.stereotype.Component;
    
    /**
     *通过注解添加一个过滤器,并指定过滤器的名称和过滤的URL规则
     */
    @Component
    @WebFilter(filterName="CORSFilter",urlPatterns="/*")
    public class CORSFilter implements Filter {
      public void init(FilterConfig filterConfig) throws ServletException { }
      
      //0.准备一个允许访问的资源列表和允许跨域的IP列表,在后台程序启动时加载
      //允许跨域访问的接口
      List<String> allowUri = new ArrayList<String>();
      {	  
    	  allowUri.add("/sb/demo/tc/tm1");
      }
      //允许跨域访问的IP
      List<String> allowIp = new ArrayList<String>();
      {	  
    	  allowIp.add("192.168.3.207");
      }
      public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
    	  HttpServletResponse response = (HttpServletResponse) servletResponse;
    	  HttpServletRequest request = (HttpServletRequest) servletRequest;
    	  //1.先获取请求的来源IP,验证此IP是否在允许跨域的IP范围内
    	  //这里origin是带协议的,可以在后面判断中通过一些处理忽略协议
    	  String origin = request.getHeader("Origin");
    	  if(origin == null){
    		  origin = request.getHeader("Referer");
    	  }
    	  if(testOrigin(origin)){
    		  //2.先获取要访问的资源路径,验证是否是允许跨域访问的资源
    		  String uri = request.getRequestURI();
    		  if(allowUri.contains(uri)){
    			  //3.设置跨域相关参数
    			  //服务器是否允许跨域与三个参数有关:Access-Control-Allow-Origin、Access-Control-Allow-Methods、Access-Control-Allow-Credentials,其中前两个是必须的
    			  //是否允许携带cookie,这个配置网络上说有关,但是在这设置貌似没什么作用,真正起到配置cookie作用的是js中的 xhttp.withCredentials =  true 这一配置
    			  response.setHeader("Access-Control-Allow-Credentials", "true");
    			  //允许的origin,可以设置为*,但是如果设置Access-Control-Allow-Credentials为true,则不能再设置为*
    			  //response.setHeader("Access-Control-Allow-Origin", "*");
    			  response.setHeader("Access-Control-Allow-Origin", origin);
    			  //允许的请求方法
    			  response.setHeader("Access-Control-Allow-Methods", "POST, GET, OPTIONS, DELETE");
    		  }else{
    			  //如果不是,可以做些其他操作,这里直接抛出异常
    			  throw new RuntimeException("收到跨域请求攻击,请求来自" + origin + ",请求资源:" + uri);
    		  }
    	  }else{
    		  //如果不是,可以做些其他操作
    		  System.out.println("收到跨域请求攻击,请求来自" + origin + ",跨域IP验证未通过");
    	  }
          filterChain.doFilter(request, servletResponse);
      }
      public void destroy() { }
      
      private boolean testOrigin(String origin){
    	  for (String ip : allowIp) 
    		if(origin.indexOf(ip) > -1)
    			return true;
    	  return false;
      }
    }
    
    
    6.浏览器访问3.207的html,点击div1按钮通过其中的js代码进行跨域访问
    1)后台打印日志
    请求时间:2020-06-01 16:15:39
    请求接口:tm1
    请求头-host:192.168.3.27:4143
    请求头-connection:keep-alive
    请求头-origin:http://192.168.3.207
    请求头-user-agent:Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/80.0.3987.132 Safari/537.36
    请求头-accept:*/*
    请求头-referer:http://192.168.3.207/xbreport/view/page/test_cors.html
    请求头-accept-encoding:gzip, deflate
    请求头-accept-language:zh-CN,zh;q=0.9,en-US;q=0.8,en;q=0.7
    请求头-cookie:MODE=0; X_GU_SID=XSS_FpplS7-OOb9L5mdBUqmiLdD-rASGm6AqjO71H2cbhnxNCv4; USER_ID=1; ACCOUNT=YWRtaW4=; USER_NAME=57O757uf566h55CG5ZGY; THEME=default; X_PRODUCT=gu; JSESSIONID=dummy
    
    
    2)浏览器收到响应:
    {
        "accept": "*/*",
        "accept-encoding": "gzip, deflate",
        "accept-language": "zh-CN,zh;q=0.9,en-US;q=0.8,en;q=0.7",
        "connection": "keep-alive",
        "host": "192.168.3.27:4143",
        "origin": "http://192.168.3.207",
        "referer": "http://192.168.3.207/xbreport/view/page/test_cors.html",
        "tm": "tm1",
        "user-agent": "Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/80.0.3987.132 Safari/537.36",
    	"cookie":"MODE=0; X_GU_SID=XSS_FpplS7-OOb9L5mdBUqmiLdD-rASGm6AqjO71H2cbhnxNCv4; USER_ID=1; ACCOUNT=YWRtaW4=; USER_NAME=57O757uf566h55CG5ZGY; THEME=default; X_PRODUCT=gu; JSESSIONID=dummy"
    }
    
    7.浏览器访问3.207的html,点击div2按钮通过其中的js代码进行跨域访问
    1)后台打印日志
    六月 01, 2020 4:17:29 下午 org.apache.catalina.core.StandardWrapperValve invoke
    严重: Servlet.service() for servlet [dispatcherServlet] in context with path [/sb/demo] threw exception
    java.lang.RuntimeException: 收到跨域请求攻击,请求来自http://192.168.3.207,请求资源:/sb/demo/tc/tm2
    	at com.example.demo1.t1.CORSFilter.doFilter(CORSFilter.java:62)
    	at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)
    	at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
    	at org.springframework.web.filter.RequestContextFilter.doFilterInternal(RequestContextFilter.java:99)
    	at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
    	at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)
    	at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
    	at org.springframework.web.filter.FormContentFilter.doFilterInternal(FormContentFilter.java:92)
    	at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
    	at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)
    	at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
    	at org.springframework.web.filter.HiddenHttpMethodFilter.doFilterInternal(HiddenHttpMethodFilter.java:93)
    	at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
    	at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)
    	at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
    	at org.springframework.web.filter.CharacterEncodingFilter.doFilterInternal(CharacterEncodingFilter.java:200)
    	at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
    	at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)
    	at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
    	at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:200)
    	at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:96)
    	at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:490)
    	at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:139)
    	at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:92)
    	at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:74)
    	at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:343)
    	at org.apache.coyote.http11.Http11Processor.service(Http11Processor.java:408)
    	at org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:66)
    	at org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:834)
    	at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1415)
    	at org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:49)
    	at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)
    	at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
    	at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61)
    	at java.lang.Thread.run(Thread.java:748)
    
    2)浏览器报错;
    test_cors.html:39 OPTIONS http://192.168.3.27:4143/sb/demo/tc/tm2 500
    
    test_cors.html:1 Access to XMLHttpRequest at 'http://192.168.3.27:4143/sb/demo/tc/tm2' from origin 'http://192.168.3.207' has been blocked by CORS policy: Response to preflight request doesn't pass access control check: It does not have HTTP ok status.
    

      

  • 相关阅读:
    文本检测和识别 代码结构梳理
    UnicodeDecodeError: 'utf-8' codec can't decode byte
    GPU 显存释放
    DCM 图片查看
    hive SQL 字母大小写转换
    vim常用命令之多行注释和多行删除
    js 模拟call、apply、bind实现
    CommonJS、AMD、CMD和ES6模块化区别
    js setTimeout setInterval 第三个参数说明
    js instanceof 实现原理
  • 原文地址:https://www.cnblogs.com/ShouWangYiXin/p/13037975.html
Copyright © 2011-2022 走看看