Web安全 | 青训营笔记
这是我参与「第四届青训营 」笔记创作活动的第4天
不安全的代码会危害用户,公司,程序员, 所以对于web安全应该是每个程序员应该掌握的基本功,只有了解相应的知识,才能写出更健全的代码,本篇文章是对青训营课程web安全之旅的记录,同时也包含了之前所了解的知识,做了一个更全面的归纳总结
1.XSS攻击
开发维护页面中攻击者通过脚本注入的方式,主要利用盲目信任用户的提交内容
string -> DOM
1. document.write
2. element.innerHTML = anything
3. SSR() //伪代码
特点
- 难以从UI上感知(暗地执行脚本)
- 窃取用户信息(cookie/token)
- 绘制UI(诱骗用户点击/填写表单)
分类
-
Stored存储型:恶意脚本存储到数据库中;当用户正常访问页面时,恶意代码从数据库中提取并触发,对所有用户都会产生危害的无差别攻击
-
Reflected反射型:不涉及数据库,从URL上攻击,涉及服务端
// 攻击的流程如下: // 1.攻击者构造特殊的URL,包含恶意代码诱导用户跳转 // 2.浏览器向服务器发送请求 // 3.服务器将带有恶意脚本的网页返回 // 4.浏览器执行恶意脚本发起请求携带用户cookie public async render(ctx) { const { param } = ctx.query; ctx.status = 200; ctx.body = `<div>${param}</div>`; } //url: hos/path/?param=<script>alert('XSS攻击')</script> -
DOM-based型:不需要服务器的参与,攻击的发起和执行都在浏览器完成
// 攻击的流程大概如下: // 1.攻击者构造特殊的URL,包含恶意代码诱导用户跳转 // url: hos/path/?param=<script>alert('XSS攻击')</script> // 2.用户打开带有恶意代码的URL // 3.用户浏览器接收到响应后执行解析,前端js取出url中的恶意代码并执行 // 4.恶意代码窃取用户数据并发送到攻击者的网站,冒充用户行为,调用目标网站接口执行攻击者指定的操作 const content = new URL(location.href).serachParams.get("param"); const div = document.createElement("div"); // 恶意脚本注入 div.innerHTML = content; document.body.append(div);Reflected和DOM-based区别:
完成注入脚本的地方不同,REflected是在服务端执行产生,而DOM based在浏览器前端代码执行产生 -
Mutation-based型(突变型):利用浏览器渲染DOM的特性,一些意外的变化会导致js代码中的其他一些流程输出到DOM中或其他方式被再次渲染,将本没有危害的HTML代码渲染成具有潜在危险的XSS攻击,不同浏览器会有区别
流程:将拼接的内容置于innerHTML这种操作,这将会导致潜在的mXSS攻击。从浏览器角度来讲,mXSS对三大主流浏览器(IE,CHROME,FIREFOX)均有影响。
// 举例 <noscript><p title="</noscript<img src=x onerror=alert(XSS)>"> 解析成如下 <div> <noscript><p title="</noscript> //会由于src解析失败执行onerror代码 <img src="x" onerror="alert(XSS)"> "">" </div>
防御
永远不信任用户的提交内容:不要将用户提交内容直接转换为DOM
可使用的工具:
- 前端
- 主流框架默认防御XSS
- google-closure-library提供了非常完善的防御函数
- 服务端
- DOMPurify提供了非常完善的字符串转义完成
若必须生成DOM时注意事项
- String->Dom: 注意需要对string进行转义
- 上传svg: svg可以嵌入script标签,注意防范
- 自定义跳转链接:尽量不让用户自定义跳转,必要时做好过滤
- 自定义样式:background属性可以包含url请求
2.CSRF攻击
用户不知情的情况利用用户的cookie,构造http请求,窃取和修改用户敏感信息
需要满足的前提条件:
- 用户登录受信任网站,并在本地生成cookie
- 用户在不登出A的情况下访问危险网站B
防范:
-
可以通过origin和refer判断请求来源是否异常,限制请求来源来,
同源请求中,Get和HEAD是不会发送Origin字段,所以Refer字段会被更广泛的利用,验证http Refer字段,如果refer字段和网站的域名相同则说明请求来自网站自己的请求,是合法的请求。若refer是其他网站,则说明可能是来自黑客的攻击。(依赖于浏览器发送的refer,一些老的浏览器可以修改发送的refer,也有可能用户禁止浏览器发送refer) -
请求地址添加token并验证,在请求中放入用户不能伪造的信息token字段,并在服务器建立拦截器验证token(token可以在用户登录后放在session中,每次请求时把token从session中拿出)。黑客可能通过refer的值得到token发动攻击,如果只是考虑token的安全可以使用动态token
浏览器香服务器请求页面->服务器返回页面和token->浏览器在之后的请求会携带该token->服务端会验证token,通过后返回数据
要求:token与用户进行绑定,过期时间(防止token被获取) -
在http头中自定义属性并验证;缺点-需要重写整个网站(需要把所有请求改为xmlhttpRequest请求)
-
避免用户信息被携带——SameSite Cookie
CSRF利用用户权限,权限位于cookie之中,其他页面发出请求无法携带本页面的cookie,从根源上杜绝CSRF攻击, 限制的是Cookie domain和当前页面的域名是否匹配
引发的问题: 依赖Cookie的第三方服务状态:内嵌一个X站播放器,识别不了用户登录状态,发不了弹幕
解决:Set-Cookie: SameSite=None; Secure; //不对SameSite进行限制但,需要确保该站是Secure的SameSite VS CORS
SamSite CORS 针对Cookie的发送 对应资源读写(对HTTP请求的限制) 针对Domain的属性和当前页面域名是否一致 资源的域名和当前域名是否一致 限制在当前页面 白名单
其他注意事项
- iFrame攻击:绕过同源限制的攻击,http响应头部进行防御
X-Frame-Options: DENY/SAMEORIGIN - CSRF之anti-pattern:不要把get请求写成既能get又能post
3.注入攻击
方式:
- 请求SQL参数
- 修改参数对数据库进行操作
public async renderForm(ctx) {
const { username, form_id } = ctx.query;
const result = await sql.query(`
SELECT a, b, c FORM table
WHERE usernam = ${username}
AND form_id = ${form_id}
`);
ctx.body = renderForm(result);
}
// 请求并传参
fetch("api", {
method: "POST",
headers: {
"Content-Type": "application/json"
},
body: JSON.stringify({
username: "any; DROP TABLE table;",
})
})
防御:
- 找到项目中查询SQL的地方
- 使用preparsed statement
PREPARE q FROM 'SELECT user FROM users WHERE gender = ?'; SET @gender = 'female'; EXECUTE q USING @gender; DEALLOCATE PREPARE q; - 最小原则:不要使用sudo | root
- 建立允许名单 + 过滤:拒绝rm等高危操作
- 对URL类型参数进行协议,域名,ip等限制:避免攻击者访问内网资源造成内网瘫痪
4.DOS(Denial of Service)
通过某种方式(构造特定请求),导致服务器资源被显著消耗,来不及响应更多请求,导致请求挤压,引起雪崩效应
ReDos(基于正则表达式的Dos)
由于贪婪模式的回溯,响应时间延长,接口吞吐量降低,响应用户的请求次数下降
防御
- Code Review (避免使用贪婪模式)
- 代码扫描 + 正则性能测试
- 拒绝使用用户提供的正则
Distributed Dos(DDos)
短时间内,来自大量僵尸设备的请求流量,服务器不能及时完成全部请求,导致请求堆积,进而引起雪崩效应,无法响应新请求 特点:
- 直接访问IP
- 任意API
- 消耗大量带宽(耗尽)
原因:三次握手中第二次握手客户端并没有返回ACK,服务器Connection不能被释放,直至最大连接数达到上限,新请求无法到达服务器(被拒绝)
防御: - 流量
- 负载均衡
- API网关
- CDN
- 抗量
- 快速自动扩容
- 非核心服务降级:关闭非核心服务,提升更多的计算资源应对
5.其他
1.基于传输层的攻击方式
- 明文传输
- 信息篡改不可知
- 对方身份未验证
2.CSP(Connect Security Policy)内容安全策略
- 定义哪些源被认为安全,来自安全源的脚本可以执行,否则报错 设置一些meta标签添加安全源
<meta http-equiv="Connect-Security-Policy" connect="script-src self">
//服务器对应的响应头部
Connect-Security-Policy: script-src 'self'
3.node中间件去进行防御
4.HTTPS的特性
-
可靠性:加密
大致流程: TLS握手: 非堆成加密 ->对称加密
非对称加密:- 浏览器将支持的加密套件选项发送给服务端
- 服务端接受后采用套件和证书返回浏览器
- 浏览器校验证书和协商好的加密算法以及额外的随机数算出Session Key
对称加密:双方使用Session Key作为Secret对所有信息进行对称加密,所有传输的信息都是加密后的信息
-
完整性:MAC验证
加密信息和加密信息的hash值,所有接受方对加秘信息重新进行hash计算,对比传递过来的hash内容 -
不可依赖性:数字签名- 私钥和公钥,使用私钥对内容进行加密,具备公钥的人可以用公钥进行校验是否用对应的私钥生成的签名绝对是否通过
- 浏览器会内置大量CA的证书,包含对应的公钥
5.将HTTP升级为HTTPS
- 浏览器先向服务器发送https请求
- 服务器返回一个strict-s 的头部,并包含max-age
- 在之后浏览器发送http请求会自动升级为https,避免用户手动输入http造成的攻击 缺陷:需要先有一次https请求
6.SRI 子资源完整性
- 静态资源放到CDN托管,CDN被攻击导致资源被篡改的攻击
- 通过hash内容对比判断是否被篡改