这是我参与「第四届青训营 」笔记创作活动的第6天
Web 开发的安全之旅
安全问题“很常见”,会危害
- 用户
- 公司
- 星字员(祭天)
两个角度看web安全
- 假如你是一个hacker——攻击
- 假如你是一个开发者——防御
攻击篇
Cross-Site Scripting(XSS)
XSS攻击通常指的是通过利用网页开发时留下的漏洞,通过巧妙的方法注入恶意指令代码到网页,使用户加载并执行攻击者恶意制造的网页程序。
XSS主要利用了:
- 盲目信任用户提交的内容
- sring->DOM
- document.wirte
- element.innerHTML = anyString
- SSR(user_data) //伪代码
特点
- 通常难以从 UI 上感知(暗地里执行脚本)
- 窃取用户的信息(cookie/token)
- 绘制UI(例如弹窗),诱骗用户点击/填写表单
XSS demo
public async submit(ctx) {
const { content, id } = ctx.request.body;
//没有对content过滤
await db.save({
content,
id
});
}
public async render(ctx) {
const { content } = await db.query({
id: ctx.query.id
});
//没有对content 过滤
ctx.body = `<div>${content}</div>`;
}
可以直接提交恶意脚本
//提交时候
fetch( "/submit", {
body: JS0N.stringify({
id: "1",
content: '<script>alert( "xss" );</script>`
});
});
ctx.body =`
<div>
<script>alert("xss");</script>
</div>`;
存储型 Stored XSS
- 恶意脚本被存在数据库中
- 访问页面 -> 读数据 === 被攻击
- 危害最大,对全部用户可见
反射型 Reflected XSS
- 不涉及数据库
- 从URL上攻击
public async render(ctx) {
const { param } = ctx.query;
ctx.status = 200;
ctx.body = `<div>${param}</div>`;
}
在URL参数上加入恶意脚本,服务端解析参数放入响应体中,导致恶意脚本注入成功
DOM型 Dom-based XSS
- 不需要服务器的参与
- 恶意攻击的发起 + 执行,都是在浏览器完成的
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的特性(独特优化)
- 不同浏览器,会有区别(按浏览器进行攻击)
过滤工具:平平无奇的title属性
<noscript><p title="</noscript><img src=x onerror=alert(1)>">
<div>
<noscript><p title="</ noscript>
<img src="x" onerror="alert(1)">
""> "
</div>
img标签的src属性出错,导致执行onerror里的内容,这里可以注册恶意脚本,进行攻击
跨站请求伪造 Cross-site request forgery(CSRF)
- 在用户不知情的前提下
- 利用用户的权限(cookie)
- 构造指定HTTP请求,窃取或修改用户敏感信息
demo
- 用户没有访问银行页面
- 银行页面中的特定接被请求
- 请求执行成功
有cookie的请求===合法用户的请求
SQL Injection
Injection demo
- 读取请求字段
- 直接以字符串的形式拼接SQL语句
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" : "applicaiton/ json"
},
body:JS0N.stringify({
username:"any ; DROP TABLE table; ",
})
})
Injection不止于SQL
- CLI
- os command
- server-side Request Forgery(SSRF),服务端伪造请求
- 严格而言,SSRF不是injection,但是原理类似
Denial of Service(Dos)
通过某种方式(构造特定请求),导致服务器资源被显著消耗,来不及响应更多请求,导致请求挤压,进而雪崩效应。
正则表达式——贪婪模式
重复匹配时「?」 vs 「no ?」:满⾜“⼀个” 即可 vs 尽量多
ReDoS 基于正则表达式的Dos
贪婪:n 次不⾏?n – 1 次再试试?——回溯
暴力回溯,会导致系统的响应时间边长,接口的吞吐量下降
Distributed DoS
攻击的特点
- 直接访问IP
- 任意API
- 消耗大量带宽(耗尽)
防御篇
XSS 防御
- 永远不信任用户的提交内容
- 不要将用户提交内容直接转换成DOM
前端
- 主流框架默认防御xsS
- google-closure-library
服务端(Node)
- DOMPurify
Conten Security Policy(CSP)
- 拿些源被认为是安全的
- 来自安全源的脚本可以执行,否则直接抛出错误
- 对eval + inline script直接拒绝
服务器的响应头部
浏览器meta
CSRF 防御
if 伪造请求===异常来源 then 限制请求来源--->限制伪造请求
- origin
- 同源请求中,GET + HEAD不发送
- Referer
token
除了Origin + Referrer
其他判断【请求来自于合法来源】的方式
- if(请求来自合法页面)
- then(服务器接收过页面请求)
- then(服务器可以标识)
- 用户绑定:攻击者也可以是注册用户===可以获取自己的token
- 过期时间:【前向保密】
iframe攻击
anti-pattern
GET!==GET+POST
//将更新+获取逻辑放到同一个GET接口
public async getAndUpdate(ctx){
const { update,id } = ctx.query;
if (update) {
await this.update(update);
}
ctx.body = await this.get( id);
}
避免用户信息被携带:SameSite Cookie
SameSite Cookie
限制的是:
- Cookie domain
- 页面域名
SameSite vs CORS
- SameSite
- Cookie 发送
- domain vs页面域名
- “我跟你说个事儿,出这屋我可就不认了"
- CORS
- 资源读写(HTTP请求)
- 资源域名vs页面域名
- 白名单
SameSite demo
防御CSRF的正确姿势
做一个防御CSRF中间件,负责生成各类防御策略,在一处保证整个Web App都能对CSRF的防御
Injection 防御
- 找到项目中查询SQL的地方
- 使用prepared statenent
PREPARE q FROM 'SELECT user FROM users WHERE gender = ? ';
SET @gender = 'female' ;
EXECUTE q USING @gender;
DEALLOCATE PREPARE q;
Injection beyond SQL
- 最小权限原则
- sudo ll root
- 建立允许名单+过滤
- rm
- 对URL类型参数进行协议、域名、ip等限制
- 访问内网
DOS 防御
Regex DOS防御
- 完善Code Review,拒绝写出类似
/(ab*)+/贪婪的匹配正则表达式 - 使用一些代码扫描工具去——> 代码扫描 + 正则性能测试
- 拒绝用户提供的使用正则
DDoS 防御
- 流量治理
- 负载均衡
- API 网关
- CDN
- 快速自动扩容
- 非核心服务降级
传输层——防御中间人
HTTP3(QUIC)内置了TLS 1.3
HTTPS的一些特性
- 可靠性:加密
- 完整性:MAC验证
- 不可抵赖性:数字签名
TLS 1.2
HTTPS------完整性
插播:数字签名
HTTPS-------不可抵赖:数字签名
CA: Certificate Authority 证书机构
数字签名在HTTPS中的工作
HTTP Strict-Transport-Security (HSTS)
将HTTP主动升级到HTTPS
Subresource Integrity (SRI)
静态资源被劫持篡改?对比 hash
SRI---demo
标签hash(原始内容hash) vs 实际内容hash
<script src="https://example/app.js"
integrity="sha384-{some-hash-value}"
crossorigin="anonymous "></script>
//伪代码
const remoteHash = hash(content);
if (tagHash !== remoteHash) {
throw new Error("wrong hash");
}