HTTP----跨域解决方式
1. 什么是跨域
在说跨域之前,需要先了解一下什么是同源策略,跨域就是因为浏览器这个安全策略引起的。
1.1http同源策略
同源策略是一个重要的安全策略,它用于限制一个源的文档或者它加载的脚本如何能与另一个源的资源进行交互。
它能帮助阻隔恶意文档,减少可能被攻击的媒介。例如,它可以防止互联网上的恶意网站在浏览器中运行 JS 脚本,从第三方网络邮件服务(用户已登录)或公司内网(因没有公共 IP 地址而受到保护,不会被攻击者直接访问)读取数据,并将这些数据转发给攻击者。
源的定义:如果两个 URL 的协议、端口(如果有指定的话)和域名都相同的话,则这两个 URL 是同源的。
下表给出了与 URL http://store.company.com/dir/page.html
的源进行对比的示例:
URL | 结果 | 原因 |
---|---|---|
http://store.company.com/dir2/other.html | 同源 | 只有路径不同 |
http://store.company.com/dir/inner/another.html | 同源 | 只有路径不同 |
https://store.company.com/secure.html | 失败 | 协议不同 |
http://store.company.com:81/dir/etc.html | 失败 | 端口不同(http:// 默认端口是 80) |
http://news.company.com/dir/other.html | 失败 | 主机不同 |
跨域就是一个请求url的协议、端口和域名其中有任意一个与当前页面url不同即为跨域。
1.2非同源限制
cookie、LocalStorage、IndexDB无法读取
DOM和JS对象无法获得
AJAX请求不能发送
2.跨域解决方法
2.1CORS(跨域资源共享)
跨域资源共享是一种基于 HTTP 头的机制,该机制通过允许服务器标示除了它自己以外的其他源(域、协议或端口),使得浏览器允许这些源访问加载自己的资源。跨源资源共享还通过一种机制来检查服务器是否会允许要发送的真实请求,该机制通过浏览器发起一个到服务器托管的跨源资源的“预检”请求。在预检中,浏览器发送的头中标示有 HTTP 方法和真实请求中会用到的头。
跨域资源共享标准新增了一组 HTTP 标头字段,允许服务器声明哪些源站通过浏览器有权限访问哪些资源。另外,规范要求,对那些可能对服务器数据产生副作用的 HTTP 请求方法(特别是
GET
以外的 HTTP 请求,或者搭配某些 MIME 类型的POST
请求),浏览器必须首先使用OPTIONS
方法发起一个预检请求(preflight request),从而获知服务端是否允许该跨源请求。服务器确认允许之后,才发起实际的 HTTP 请求。在预检请求的返回中,服务器端也可以通知客户端,是否需要携带身份凭证(例如 Cookie 和 HTTP 认证相关数据)。
简单请求
某些请求不会触发CORS预检请求,称为简单请求
简单请求的满足条件如下:
- 使用下列方法之一:
- GET
- HEAD
- POST
- 除了被用户代理自动设置的表头字段(例如:Connection、User-Agent),运行人为设置的标头字段有:
- Accept
- Accept-Language
- Content-Language
- Content-Type
- Range
- Content-Type标头所指的媒体类型权限的值仅限于:
- text/plain
- multipart/from-data
- application/x-www-form-urlencoded
举个例子
站点http://foo.example的网页想要访问http://bar.other的资源。
站点http://foo.example发送一个简单请求,请求头中Origin字段表示该请求来源与http://foo.example
GET /resources/public-data/ HTTP/1.1 Host: bar.other User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10.14; rv:71.0) Gecko/20100101 Firefox/71.0 Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8 Accept-Language: en-us,en;q=0.5 Accept-Encoding: gzip,deflate Connection: keep-alive Origin: http://foo.example
服务器响应标头中的Access-Control-Allow-Origin标头的值设置为*表示该资源可以被任意其他站点访问
HTTP/1.1 200 OK Date: Mon, 01 Dec 2008 00:23:53 GMT Server: Apache/2 Access-Control-Allow-Origin: * Keep-Alive: timeout=2, max=100 Connection: Keep-Alive Transfer-Encoding: chunked Content-Type: application/xml
使用Orign和Access-Control-Allow-Origin标头就可以完成简单的访问控制。
预检请求
与简单请求不同,“需要预检的请求”要先使用OPTIONS方法发送一个预检请求到服务器,由此来获取服务器是否允许该实际请求的发送。
举个例子
下面是一个需要执行预检的请求的HTTP请求:
const xhr = new XMLHttpRequest(); xhr.open("POST", "https://bar.other/resources/post-here/"); xhr.setRequestHeader("X-PINGOTHER", "pingpong"); xhr.setRequestHeader("Content-Type", "application/xml"); xhr.onreadystatechange = handler; xhr.send("<person><name>Arun</name></person>");
上述的HTTP请求中自定义了一个X-PINGOTHER请求头,Content-Type的值使用了application/xml所以该请求需要进行预检。
下面是服务器和客户端的交互
OPTIONS /doc HTTP/1.1 Host: bar.other User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10.14; rv:71.0) Gecko/20100101 Firefox/71.0 Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8 Accept-Language: en-us,en;q=0.5 Accept-Encoding: gzip,deflate Connection: keep-alive Origin: https://foo.example Access-Control-Request-Method: POST Access-Control-Request-Headers: X-PINGOTHER, Content-Type
HTTP/1.1 204 No Content Date: Mon, 01 Dec 2008 01:15:39 GMT Server: Apache/2 Access-Control-Allow-Origin: https://foo.example Access-Control-Allow-Methods: POST, GET, OPTIONS Access-Control-Allow-Headers: X-PINGOTHER, Content-Type Access-Control-Max-Age: 86400 Vary: Accept-Encoding, Origin Keep-Alive: timeout=2, max=100 Connection: Keep-Alive
我们来分析一下上面的报文,首先浏览器如愿的发送了一个OPTIONS请求,在请求中有Access-Control-Request-Method: POST;Access-Control-Request-Headers: X-PINGOTHER, Content-Type这两行信息,告诉服务器真实请求的方法为POST方法,还有自定义了两个header。
服务器返回的报文中有这么四行信息Access-Control-Allow-Origin: https://foo.example Access-Control-Allow-Methods: POST, GET, OPTIONS Access-Control-Allow-Headers: X-PINGOTHER, Content-Type Access-Control-Max-Age: 86400
第一行限制了请求的地址,第二行表示服务器允许使用POST、GET、OPTIONS方法,第三行表示服务器允许携带 X-PINGOTHER, Content-Type字段,第四行给定了该预检请求可供缓存的时间长短,单位为秒,默认值是 5 秒。
预检完成之后,发送真正的请求:
POST /doc HTTP/1.1 Host: bar.other User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10.14; rv:71.0) Gecko/20100101 Firefox/71.0 Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8 Accept-Language: en-us,en;q=0.5 Accept-Encoding: gzip,deflate Connection: keep-alive X-PINGOTHER: pingpong Content-Type: text/xml; charset=UTF-8 Referer: https://foo.example/examples/preflightInvocation.html Content-Length: 55 Origin: https://foo.example Pragma: no-cache Cache-Control: no-cache <person><name>Arun</name></person>
HTTP/1.1 200 OK Date: Mon, 01 Dec 2008 01:15:40 GMT Server: Apache/2 Access-Control-Allow-Origin: https://foo.example Vary: Accept-Encoding, Origin Content-Encoding: gzip Content-Length: 235 Keep-Alive: timeout=2, max=99 Connection: Keep-Alive Content-Type: text/plain
2.2CORS应用
CORS对于前端来说一般是无感的,只需要在服务器实现CORS接口即可
/**
* 跨域配置
*/
@Configuration
public class CorsConfig {
@Bean
public CorsFilter corsFilter() {
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
CorsConfiguration corsConfiguration = new CorsConfiguration();
corsConfiguration.addAllowedOrigin("*"); // 1 设置访问源地址
corsConfiguration.addAllowedHeader("*"); // 2 设置访问源请求头
corsConfiguration.addAllowedMethod("*"); // 3 设置访问源请求方法
source.registerCorsConfiguration("/**", corsConfiguration); // 4 对接口配置跨域设置
return new CorsFilter(source);
}
}
UrlBasedCorsConfigurationSource是Spring Framework中用于基于URL的跨域配置的类。它允许为特定URL路径配置跨域规则,以便更精细地控制跨域访问策略。
3.XSS(跨站脚本攻击)
3.1XSS原理
恶意攻击者往web页面里插入恶意的Script代码,当用户浏览该页面时,嵌入web里面的恶意Script代码会被执行,从而达到恶意攻击用户的目的。XSS攻击针对的是用户层面的攻击。
XSS分为:存储型、反射型和DOM型
反射型XSS
又称非持久型XSS。之所以称为反射型XSS,是因为这种攻击方式的注入代码是从目标服务器通过错误的信息、搜索结果等等方式“反射”回来的:发出请求时,XSS代码出现在URL中,作为输入提交到服务器端,服务器端解析后响应,XSS代码随响应内容一起传回给浏览器,最后浏览器解析执行XSS代码。这个过程像一次反射,故叫反射型XSS。 而称为非持久型XSS,则是因为这种攻击方式具有一次性,由于代码注入的是一个动态产生的页面而不是永久的页面,因此这种攻击方式只在点击链接的时候才产生作用。
攻击流程
- 张三经常浏览某个网站,这个网站为李四所拥有。李四的这个网站需要张三提供用户名和密码进行登录。
- 黑客发现李四的网站存在反射型的XSS漏洞
- 黑客利用李四网站的反射型漏洞编写了一个恶意的脚本作为李四网站的输入,向服务器发送一个请求,服务器接将恶意脚本作为数据的输入放在网页中进行返回。这样一个含有XSS攻击的URL就做好了。然后诱使张三点击点击这个URL
- 张三点击黑客提供的恶意URL之后,服务器将含有恶意脚本的网页数据返回到张三的浏览器上。
- 恶意脚本在张三的浏览器中执行,此脚本盗取了张三的敏感信息(cookie 账号等信息)。然后在张三不知情的情况下,将这些信息发送给了黑客
- 黑客利用获取到的cookie就可以以张三的身份登录李四的网站
存储型XSS
存储型XSS,又称持久型XSS,他和反射型XSS最大的不同就是,攻击脚本将被永久地存放在目标服务器端(数据库,内存,文件系统等),下次请求目标页面时不用再提交XSS代码。
攻击流程
- 李四有一个博客网站,博客网站允许用户发布博客、浏览其他用户的博客
- 黑客检测到李四的博客网站存在存储型XSS漏洞
- 黑客在李四的博客网站上发布了一篇含有恶意脚本的博客,该博客内容存储在了李四服务器的数据库中。
- 其他用户浏览黑客发布的这一篇博客之后,恶意脚本就会执行
- 黑客的恶意脚本执行之后,就可以对浏览该页面的用户进行一次XSS攻击
DOM型
客户端的脚本程序可以动态地检查和修改页面内容,而不依赖于服务器端的数据。例如客户端如从 URL 中提取数据并在本地执行,如果用户在客户端输入的数据包含了恶意的 JavaScript 脚本,而这些脚本没有经过适当的过滤和消毒,那么应用程序就可能受到 DOM-based XSS 攻击。需要特别注意以下的用户输入源 document.URL、 location.hash、 location.search、 document.referrer 等。DOM 型 XSS 跟前两种 XSS 的区别:DOM 型 XSS 攻击中,取出和执行恶意代码由浏览器端完成,属于前端 JavaScript 自身的安全漏洞,而其他两种 XSS 都属于服务端的安全漏洞。
3.2XSS的防御
XSS防御的总体思路是:对用户的输入(和URL参数)进行过滤,对输出进行html编码。也就是对用户提交的所有内容进行过滤,对url中的参数进行过滤,过滤掉会导致脚本执行的相关内容;然后对动态输出到页面的内容进行html编码,使脚本无法在浏览器中执行。
Vue项目防止XSS攻击
- 使用模板语法代替直接操作 DOM
Vue的模板语法可以帮助开发者避免直接操作 DOM,从而降低 XSS 攻击的风险。在 Vue的模板中,可以使用 {{}} 插值语法或 v-bind 指令绑定属性,来渲染数据到 HTML 中。
- 使用 Vue提供的过滤器
Vue提供了过滤器的功能,可以对数据进行处理和过滤,从而防止 XSS 攻击。在 Vue.js 中,可以使用 $sanitize 过滤器或类似的库来过滤用户输入的 HTML,以去除其中的恶意脚本。
- 使用 CSP 策略
在 Vue应用程序中,可以使用 CSP(Content Security Policy)策略,以限制页面上可以加载的资源和内容,从而防止跨站脚本攻击。在 HTTP 响应头中设置 CSP 策略,告知浏览器允许加载哪些内容,哪些不允许加载。
Spring Boot项目防止XSS攻击
mica-xss
- 对表单绑定的字符串类型进行 xss 处理。
- 对 json 字符串数据进行 xss 处理。
- 提供路由和控制器方法级别的放行规则。
- 对表单和 json 字符串 trim 处理。
配置
配置项 | 默认值 | 说明 |
---|---|---|
mica.xss.enabled | true | 开启xss |
mica.xss.trim-text | true | 【全局】是否去除文本首尾空格 |
mica.xss.mode | clear | 模式:clear 清理(默认),escape 转义 |
mica.xss.pretty-print | false | clear 专用 prettyPrint,默认关闭: 保留换行 |
mica.xss.enable-escape | false | clear 专用 转义,默认关闭 |
mica.xss.path-patterns | /** | 拦截的路由,例如: /api/order/** |
mica.xss.path-exclude-patterns | 放行的路由,默认为空 |
注解
可以使用 @XssCleanIgnore
注解对方法和类级别进行忽略。
自定义 xss 清理
如果内置的 xss 清理规则不满足需求,可以自己实现 XssCleaner
,注册成 Spring bean 即可。
- 注册成 Spring bean
@Bean
public XssCleaner xssCleaner() {
return new MyXssCleaner();
}
- MyXssCleaner
public class MyXssCleaner implements XssCleaner {
@Override
public String clean(String html) {
Document.OutputSettings settings = new Document.OutputSettings()
// 1. 转义用最少的规则,没找到关闭的方法
.escapeMode(Entities.EscapeMode.xhtml)
// 2. 保留换行
.prettyPrint(false);
// 注意会被转义
String escapedText = Jsoup.clean(html, "", XssUtil.HtmlWhitelist.INSTANCE, settings);
// 3. 反转义
return Entities.unescape(escapedText);
}
}
未启用xss处理时,评价信息输入时会被存入数据库,启用之后(默认配置)数据库中未空。正常输入评价,可以存入数据库。