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.

浙公网安备 33010602011771号