客户端向不同的源发送数据请求时,会发生跨域问题。
为什么会有跨域问题呢?
浏览器默认两个相同的源是可以相互操作DOM和访问资源的,两个不同的源之间相互操作DOM和访问资源会受到安全策略的制约,这就是同源策略。两个源的协议、域名和端口有任一个不同都属于非同源。
没有同源策略的限制不好吗?那样就不会有跨域问题了。
可以想象一下没有同源策略的制约的web世界是什么样子的。当你打开了一个购物网站,而又不小心打开了一个恶意站点,那么恶意站点就可以做如下事:
- 修改你站点的DOM和CSSOM
- 获取你站点的账号和密码
- 获取你的Cookie
黑客获取了你的敏感数据后,就可以做一些恶意操作。所以,没有同源策略的web是不安全的。
同源策略制约了哪些行为呢?
- 当前域下的JS脚本不能操作其他域下的DOM。
- 当前域下的JS脚本不能访问其他域下的Cookie、localStorage、IndexDB等数据。
- 当前域下的ajax不能发起跨域请求。
同源策略保证了web的安全,但也制约了WEB的自由,有时我们确实又需要解除同源策略的限制,那如何解决跨域问题呢?
1、 JSONP
JSONP是利用script标签不受同源策略限制的特性来解决跨域问题的。
JSONP解决跨域问题有如下步骤:
- 声明回调函数,形参为服务器返回的数据,并把函数名作为参数传递给服务器。
- 创建
script标签,script的src属性值为接口地址。 - 服务器接收到请求后,开始处理请求,把函数名和需要返回的数据拼接成字符串,返回到客户端。
- 客户端接收到服务端返回的数据后,执行声明的回调函数。
function jsonp(url,params,callback) {
return new Promise((resolve, reject) => {
let script = document.createElement('script');
window[callback] = function(data){
resolve(data)
}
params = {...params, callback};
let searchParams = [];
for(let key in params) {
searchParams.push(`${key}=${params[key]}`)
}
script.src = `${url}?${searchParams.join('$')}`;
document.body.appendChild(script)
})
}
jsonp({
url: localhost:8080,
params: {name: 'red'},
callback: 'show'
}).then(data => {
console.log(data)
})
JSONP的局限性:
JSONP只能发送GET请求。
2、 CORS
CORS是通过后端实现的,后端通过设置Access-Control-Allow-Origin指定哪些域可以访问资源,也可以使用通配符*让所有站点都可以访问资源。
使用CORS解决跨域问题,发送的请求分两种情况,分别为简单请求和复杂请求。
简单请求有如下条件:
- 请求方式为
GET、POST或HEAD。 Content-type为下列三者之一:- text/plain
- multipart/form-data
- application/x-www-form-urlencoded
通过CORS发送的请求不是简单请求,那么它就是复杂请求了。复杂请求在正式请求之前会发送一个请求方式为OPTIONS的预检请求。预检请求的请求头带有origin字段,表示请求的来源,服务端根据这个字段判断是否允许请求。服务器返回的响应头中有Access-Control-Allow-Origin,则说明允许请求。
发送预检请求是为了确认本次请求是否在服务器允许请求的范围内,如果在,就发送正式请求。浏览器默认发送跨域请求时是不带Cookie的,如果要带Cookie,则需要设置withCredentials为true。
3、postMessage
用于两个不能窗体之间的通信。
4、nginx反向代理
跨域访问不同源的资源给WEB世界带来了自由,但同时也会WEB带来了安全隐患,如XSS攻击和CSRF攻击。
XSS攻击
什么是XSS攻击呢?
XSS跨站脚本攻击,是指黑客把恶意JS脚本注入到用户的页面上,把用户的敏感数据提交到恶意服务器上的攻击手段。
XSS攻击有哪些类型呢?
1、 存储型XSS攻击
攻击者把恶意脚本存储到网站服务器上,当用户打开含有恶意脚本的页面时,JS恶意脚本就会被加载并执行,把用户的敏感数据提交到恶意服务器。
2、 反射型XSS攻击
当用户点击含有恶意代码的URL时,服务器把URL上的恶意代码提取出来,返回给浏览器,浏览器加载并执行恶意代码,把用户的敏感信息提交到恶意服务器上。
3、 基于DOM的XSS攻击
这中攻击方式不会经过服务器,攻击者利用各种手段把恶意代码注入到页面中,如利用网络劫持。
如何预防XSS攻击:
- 为cookie加上
Http-only,这样cookie就只能应用了发送HTTP请求,而不能被javascript访问。 - 利用CSP
CSP有两个作用:
- 限制加载其他的域下的资源文件
- 禁止向其他域提交资源文件
浏览器是允许加载第三方资源文件的,如果第三方资源文件含有恶意代码,那么就会遭到XSS攻击,CSP禁止向第三方域提交资源文件,这样就可以预防XSS攻击。
- 服务端对输入的脚本进行转码或过滤。
存储型XSS攻击是利用往用户页面中注入恶意JS脚本,把用户的关键数据提交到恶意服务器的攻击手段。那么在服务端对输如的JS脚本进行转码或过滤,那么恶意脚本就不会在浏览器上执行。
CSRF攻击
CSRF是跨站伪造攻击,CSRF攻击利用用户的登录信息和第三方站点实施攻击的手段。
实施CSRF攻击需要满足三个条件:
- 用户登录过目标站点,并且在浏览器保持有该站点的登录状态。
- 目标站点存在CSRF漏洞。
- 用户打开第三方站点
CSRF攻击有如下类型:
- get类型CSRF攻击
- POST类型CSRF攻击
- 链接类型CSRF攻击
有人可能会问,不是有同源策略制约吗,第三方站点怎么能向其他站点发送请求呢。其实,同源策略限制的是XMLHttpRequest,像表单请求或能造成CSRF攻击的GET请求,如<img src="http://csrf.com">是不受同源策略的限制的。
如何防止CSRF攻击呢?
1、使用sameSite
CSRF攻击利用的是用户在目标站点的登录状态,而用户的登录状态是由Cookie维持的,所以只要不把Cookie发送到第三方站点不就可以了吗。sameSite就是做这个事情的,它有三个值,分别为strict/lat/none。
strict:想同站点发送请求时,会携带Cookie;向第三方站点发送请求时,不会携带Cookie。
lat: 发送GET请求时会携带Cookie。
none: 任何请求都不会携带Cookie。
所以,可以在Cookie中加入sameSite=strict来避免CSRF攻击。
2、验证请求源
通过origin字段验证请求源,如果请求源不是被允许请求的源,则服务端不对请求作出响应。
3、CSRF Token
当浏览器第一发送请求时,服务端返回响应数据,并将CSRF Token发送给浏览器嵌入到页面中,当再次发送请求时,就带上这个CSRF Token,如果没有这个CSRF Token,则服务端不对请求作出响应。