同源策略
- 同源策略是web页面安全中心最基础、最核心的安全策略。
- 如果两个URL的协议、域名和端口都相同,我们就称这两个URL同源。
同源策略的表现
1. DOM层面
同源策略限制了不同源的JavaScript脚本对当前DOM对象读写的操作。
// a网页控制台输入
window.open("b.test.com")
// b网页控制台输入
opener.document.body.style.display = "none"
如果两个网页同源,第二个b网页控制台可以修改a网页控制台DOM。如果两个网页不同源,b网页无法获取到a网页的document对象。
2. 数据层面
同源策略限制了不同源的站点读取当前站点的Cookie、IndexDB、LocalStorage等数据。
// a网页控制台输入
window.open("b.test.com")
// b网页控制台输入
opener.localStorage
3. 网络层面
跨域问题
安全与便利的权衡
不同源之间绝对隔离无疑是最安全的,但是也使得Web项目难以开发和使用。因此浏览器实现上出让了同源策源的部分安全性。
1. 页面中可以引入第三方资源
同源策略需要一个页面的所有资源都来自于同一个源,无疑违背了Web开放的初衷。所以最初的浏览器都是支持外部引用资源文件的。但是这也造成了Web页面被XSS攻击的风险。
2. 跨域资源共享与跨文档消息机制
- 为了解决跨域问题,引入了跨域资源共享(CORS),使用该机制可以进行跨域访问控制,从而保证跨域数据传输的安全进行,但是这也使得页面存在被CSRF攻击的风险。
- 为了解决不同源的DOM之间进行通信,浏览器引入了跨文档消息机制,可以通过window.postMessage来和不同源的DOM进行通信(使用过程中message事件必须要使用origin和source属性来检查消息的发送者的身份,否则存在XSS攻击的风险)
// a网页控制台输入
window.open("b.test.com")
window.addEventListener("message",event=>{
console.log(event)
})
// b网页控制台输入
opener.postMessage("123","http://a.test.com")
跨站脚本攻击(XSS)
XSS全称Cross Site Scripting,为了与css样式区分开,所以成为XSS。XSS攻击是指黑客往HTML文件或者DOM中注入恶意脚本,从而在用户浏览网页时对用户实施攻击的一种手段。最开始的时候,这种攻击是通过跨站点实现的,但是发展到后面,注入恶意脚本的方法越来越多,但是XSS的这个名字一直保留下来。
XSS的危害
恶意脚本可以做很多事情,下面举几个例子。
- 窃取Cookie信息,恶意脚本获取到用户cookie信息,通过fetch或者XMLHttpRequest加上CORS功能将数据发动给恶意服务器,恶意服务器拿到cookie以后就可以在其他电脑上模拟用户的登录。
- 监听用户行为,恶意脚本通过“addEventListener”监听键盘事件,获取用户输入的银行卡、密码等信息,并将其发送到恶意服务器。
- 通过修改DOM伪造假的登录窗口,欺骗用户输入用户名与密码。
- 在页面内生成浮窗广告。
常见的XSS攻击方式
1. 存储型XSS攻击
存储型XSS攻击的几个步骤:
- 黑客利用网站漏洞将恶意脚本提交到网站的数据库中;
- 用户向网站请求包含了恶意脚本的网页,或者ajax请求返回的恶意脚本数据被插入到DOM中。
上面的例子中,如果前端网页直接将后端返回的数据显示在网页上,所有访问此文章的用户都将会收到攻击。
2. 反射型XSS攻击
服务端将未经处理的url参数直接渲染到页面上。存在此漏洞的网站黑客可以通过聊天软件或者邮箱诱导用户点击恶意链接。
// 正常请求
www.test.com?id=123
// 恶意链接
www.test.com?id=<script>alert(123)</script>
3. 基于DOM的XSS攻击
不涉及Web服务器的情况下也可以进行XSS攻击。比如通过网络劫持修改HTML页面的内容,例如通过路由器或者本地恶意软件来劫持。
XSS真实案例
如何阻止XSS攻击
存储型XSS与反射型XSS攻击属于是服务端的安全漏洞,基于DOM的XSS攻击属于前端漏洞。但是无论哪种形式的攻击共同点都是首先向页面中插入恶意脚本。
1. 服务器对输入进行转码
// 转码前
<script>alert('XSS攻击')</script>
// 转码后
<script>alert('XSS攻击')</script>
转码后的内容即使插入到页面中也不会执行。
有些站点会对<script>关键字进行过滤,但是这样并不完全安全,例如利用less可以执行JavaScript的漏洞进行攻击。
<!DOCTYPE html>
<html lang="en">
<style type=text/less>a{a:`alert(123)`}</style>
<script src="https://cdnjs.cloudflare.com/ajax/libs/less.js/2.7.1/less.min.js" ></script>
</html>
2. 使用CSP策略
csp策略可以通过配置"content-security-policy"响应头有效的防范XSS攻击。CSP策略有如下几个功能:
- 限制加载其他域的资源;
- 禁止像第三方域提交数据;
- 禁止执行内联脚本和未授权的脚本;
- 上报机制,帮助站点尽快发现有哪些XSS攻击。
虽然CSP可以有效的防御XSS攻击,但是由于限制较多所以实际使用的web网页并不多。可以参考github、知乎。
3. 使用HttpOnly属性
由于很多XSS攻击都是用来盗用Cookie的,因此还可以通过HttpOnly属性标记重要的cookie,是得其只能在HTTP请求过程中访问,无法通过javaScript脚本读取。
跨站请求伪造(CSRF)
CSRF全称是Cross-site request forgery。CSRF攻击是指黑客利用了用户的登录状态,并通过第三方的站点做一些坏事。
常见的CSRF攻击方式
1. 自动发起Get请求
<!DOCTYPE html>
<html>
<body>
<h1>恶意站点</h1>
<img src="https://www.test.com?user=hacker&number=100">
</body>
</html>
黑客将请求接口隐藏在img标签内,当页面被加载时就会自动发起img的资源请求,如果服务器没有做过滤判断的话,服务器就会认为该请求是一个转账请求。
2. 自动发起POST请求
<!DOCTYPE html>
<html>
<body>
<h1>恶意网站</h1>
<form id='hacker-form' action="https://www.test.com" method=POST>
<input type="hidden" name="user" value="hacker" />
<input type="hidden" name="number" value="100" />
</form>
<script> document.getElementById('hacker-form').submit(); </script>
</body>
</html>
3. 引诱用户点击链接
<div>
<a href="https://www.test.com?user=hacker&number=100" taget="_blank">
点击下载
</a>
</div>
与XSS不同,CSRF攻击不需要将恶意代码注入到用户的页面,仅仅是利用服务器的漏洞和用户的登录状态来实施攻击。发起CSRF攻击有三个必要的条件:
- 目标站点一定要有CSRF漏洞;
- 用户登陆过目标站点,并且在浏览器上保持有站点的登录状态;
- 用户打开一个第三方站点,可以是黑客的恶意站点,也可以是一些论坛。
CSRF真实案例
如何防止CSRF攻击
1. 利用好Cookie的SameSite属性
想要进行CSRF攻击就必须要有用户的登录状态,大部分网站中Cookie都是浏览器与服务器之间维护登录状态的一个关键数据。并且大多数CSRF攻击是需要从第三方站点发起,如果我们从第三方站点发送请求时禁止关键Cookie的发送,就可以有效降低CSRF攻击的风险。
Cookie中的SameSite属性正是为了解决这个问题。SameSite有个值:
- Strict: 完全禁止第三方Cookie。
- Lax: 相对宽松。在跨站点(顶级域名+二级域名相同的为同一站点)的情况下第三方站点的<a>标签链接、Get方式的表单提交、预加载会携带Cookie。Post表单、ajax请求、img、iframe等都不会携带Cookie。
- None: 任何情况下都会携带Cookie。
兼容性问题:
大多数主流浏览器的最新版本中,默认值都为Lax。如果想要设置为None则必须要Secure一起使用,也就是说只能在https请求中使用None。
最新的safari浏览器中,默认值为Strict,并且不支持None,设置为None时会被当成默认值处理。
具体浏览器兼容版本查看 MDN SameSite Cookie。
2. 验证请求来源
由于CSRF攻击大多来自于第三方站点,所以我们可以通过判断请求是否来自于第三方站点来过滤请求。
- Referer:Referer请求头字段记录了HTTP请求的来源地址。但是有些场景为了安全考虑不适合把原站点的详细路径暴露给服务器,所以浏览器提供了设置关闭Referer值的上传。
- Origin:由于验证Referer属性并不可靠,所以又制定了Origin属性,Origin属性只包括域名信息,没有具体的URL路径。
可以在使用CORS(跨站资源共享)时设置Access-Control-Allow-Origin响应头来指示可以共享给哪些域。
3. CSRF Token
CSRF Token的实现方法有很多,目的都是为了验证请求是否是来自第三方。
介绍几种常见的方式
- 响应页面时将token渲染到页面上,表单提交的时候通过隐藏域提交到服务端;
- 将token设置在Cookie中,在提交请求时将提交Cookie,并通过header或者body带上Cookie中的token,服务端进行对比校验;
- 信任带有特定header的请求。(比较容易被绕过)
XST攻击
XST全程是Cross-Site Tracing。虽然cookies设置为httpOnly以后被XSS攻击也无法轻易盗用cookie,但是针对支持TRACE请求的服务器,客户端可以通过发送TRACE请求获取到完整的头信息。通过这种方式也是可以拿到设置为httpOnly的Cookie。
可以通过禁止trace,track等危险请求类型来防范被攻击。
现代浏览器已经禁止了trace、track梁总请求,会抛出异常。
HPP攻击
Http Parameter Pollution(HPP),即 HTTP 参数污染攻击。在 HTTP 协议中是允许同样名称的参数出现多次,而由于应用的实现不规范,攻击者通过传播参数的时候传输 key 相同而 value 不同的参数,从而达到绕过某些防护的后果。HPP 可能导致的安全威胁有:
- 绕过防护和参数校验。
- 产生逻辑漏洞和报错,影响应用代码执行。
可以通过针对相同key的请求时,强制使用第一个或者最后一个来避免hpp攻击。
HPP真实案例
SSRF攻击
通过 Server-Side Request Forgery(SSRF)攻击,攻击者可以发起网络请求访问或者操作内部网络的资源。
一般来说,SSRF 安全漏洞常见于开发者在服务端直接请求客户端传递进来的 URL 资源,一旦攻击者传入一些内部的 URL 即可发起 SSRF 攻击。
通常我们会基于内网 IP 黑名单的形式来防范 SSRF 攻击,通过对解析域名后得到的 IP 做过滤,禁止访问内部 IP 地址来达到防范 SSRF 攻击的目的。