上一篇我们介绍了一些常见的攻击方式,今天我们来探索探索如何进行防御,防止我们的用户信息被泄露,防止我们的服务器被攻击~
防御XSS
我们知道XSS的攻击原理之后,就知道我们千万不能相信用户的输入!!!
过滤用户的输入,我们不用自己造轮子呀~ 前人已经创造了很多工具可以给我们使用
前端
- 主流框架默认防御XSS(因为使用那些框架已经不需要我们手动操作DOM了)
- google-closure-library(如果需要可以使用这个第三方库)
服务端
- DOMPurify
如果用户真的有这样的需求,必须动态⽣成 DOM,那我们要注意在以下几个地方做好过滤
① string -> DOM
将字符串转换成DOM,我们一定要对这个字符串string进行过滤
new DOMParser()
② 上传svg
用户上传svg,可以内嵌script标签,所以对用户上传的所有svg文件,也要进行过滤
<svg>
<script>alert('xss')</script>
</svg>
③ 自定义跳转链接
用户可以自定义跳转链接的话,对跳转的链接也要进行过滤
<a href="javascript:alert('xss')" >点我</a>
④ 自定义样式
这种方式很厉害,通过一个表单,用户点击到指定的按钮,就会添加一个样式,这个样式修改背景图片url,这个url就是一个恶意链接,当用户选中就会将用户选中的信息带到恶意第三方,用户信息就泄露了
所以要尽量不要让用户自定义修改样式,如果非要这样,一定要做好过滤
补充知识
浏览器同源策略 Same-origin Policy
我们知道浏览器有同源策略的限制,只有协议、域名、端口号三者都相同,才满足同源,不满足的我们称之为跨域
同源策略是一个重要的安全策略,它用于限制一个源的文档或者它加载的脚本如何能与另一个源的资源进行交互。这是浏览器安全性的考虑,可以帮助阻隔恶意文档,减少可能被攻击的媒介
内容安全策略 Content Security Policy(CSP)
同源策略大家应该都很熟悉,那你知道什么是内容安全策略吗
- 那些源(域名)被认为是安全的
- 来自安全源的脚本可以执行,否则直接报错
- 对 eval + inline script 说🚫
如何配置CSP呢?
在服务器配置两个响应头
浏览器meta标签中也可以写类似的代码 关于meta标签更多细节,可以看我之前的博文 HTML基础
防御CSRF
请求来源异常应对方式
跨站请求伪造,他的请求都是伪造的,请求来源是异常的,所以我们可以通过限制请求来源,来限制伪造的请求
在请求头信息中可以查看到Origin信息和Referer信息
但是,在同源请求中,GET请求和HEAD请求不会发送Origin,所以我们要思考除了查看Origin和Referer知道请求的源,还可以通过什么方式来判断请求来自合法来源的方式呢?
看到这里,你应该想到了,我们可以使用token的机制,来识别源和用户
- 为什么要用户绑定:攻击者也可以是注册用户,可以获取自己的token
- 为什么要设置过期时间:前向保密,保证攻击者获取到历史的token也不奏效
差不多了吧~
不,你看他(请求)的源,攻击者直接给你来同源请求
在iframe中嵌入一个按钮,用户点击,就会发送请求
此时可以使用这个响应头
X-Frame-Options:DENY/SAMEORIGIN
参看MDN文档
如果设置为 deny,不光在别人的网站 frame 嵌入时会无法加载,在同域名页面中同样会无法加载。
如果设置为sameorigin,那么页面就可以在同域名页面的 frame 中嵌套。
日常开发中,经常把GET请求和POST请求都当成GET请求来出来,其实这是不好的习惯,以后记得要分清楚,不然GET请求的接口被攻击了,就可以使用POST进行添加数据了!!!
利用用户敏感信息 应对方式
避免⽤户信息被携带:SameSite Cookie
Cookie 的SameSite属性用来限制第三方 Cookie,从而减少安全风险。
SameSite 属性有三个值
-
Strict
Strict最为严格,完全禁止第三方 Cookie,跨站点时,任何情况下都不会发送 Cookie。换言之,只有当前网页的 URL 与请求目标一致,才会带上 Cookie。 -
Lax
Lax规则稍稍放宽,大多数情况也是不发送第三方 Cookie,但是导航到目标网址的 Get 请求除外。 -
None Chrome 计划将
Lax变为默认设置。这时,网站可以选择显式关闭SameSite属性,将其设为None。不过,前提是必须同时设置Secure属性(Cookie 只能通过 HTTPS 协议发送),否则无效。
防御 CSRF 的正确姿势
CSRF是攻击一个个接口,我们不可能一个接口一个接口的进行防御,这样肯定不够优雅
我们应该在中间件中处理防御CSRF,比如验证token
防御Injection
- 找到项目中查询SQL的地方
- 使用prepared statement
对操作进行一些限制
- 最小权限原则
- 禁止
sudo || root
- 禁止
- 建立允许名单 + 过滤
- 禁止
rm
- 禁止
- 对URL类型参数进行协议、域名、ip等限制
- 禁止 访问内网
防御 DoS
一般是运维人员进行预防
对一些代码进行Review,遇到贪婪正则就给他禁止掉;进行一些正则的性能测试;直接禁止用户提供的正则
对一些恶意攻击代码使用负载均衡、API网关进行过滤,对一些恶意流量使用CDN、自动扩容、非核心服务降级进行扛量
防御中间人攻击
防御中间人攻击就要引出传输层的的防御方案了,使用HTTPS协议
HTTPS
HTTPS的一些特征
- 可靠性:加密
- 完整性:MAC 验证
- 不可抵赖性:数字签名
可靠性:加密
传输层安全性协议TLS
传输层安全性协议(英语:Transport Layer Security,缩写作TLS),及其前身安全套接层(Secure Sockets Layer,缩写作SSL)是一种安全协议,目的是为互联网通信提供安全及数据完整性保障
TLS握手分为两个过程,使用非对称密钥加密用于传输对称密钥来保证传输过程的安全性,之后使用对称密钥加密进行通信来保证通信过程的效率。
非对称加密: 浏览器向服务器提供一些加密套件的选项,服务器进行选择采用的套件,并返回一个证书,浏览器接收到证书之后进行校验,如果证书是ok的,就可以用双方协商好的加密算法算出一个sessionKey
对称加密: 双方利用刚刚生成的sessionKey,对传输的内容进行加解密
完整性:MAC 验证
除了会传递信息的内容还会传递一个加密的hash值来验证数据的完整性
这样可以保证信息的完整性,没有被篡改
不可抵赖性:数字签名
我们先来看看数字签名是什么?
签名的执行者拥有私钥和公钥,顾名思义,私钥自己藏好,公钥是公开可见的
加密过程:内容+私钥 进行加密生成 ===> 签名
验证过程:拥有公钥的一方对签名进行验证,判断签名是不是被对应的私钥所加密的,是的话就通过验证
我们来看看数字签名是如何帮我们实现不可抵赖的特性的
CA证书机构专门来颁发(服务器)证书
数字签名在HTTPS中是这样工作的: (非对称加密过程中)
- 加密过程:内容 [服务的元信息 + 公钥(sessionKesy)] + CA私钥 ===> 生成 服务器证书
- 验证过程:浏览器使用 CA公钥 对 证书 进行验证 如果验证通过,就取出服务器里的公钥(sessionKeys)
这里会有一个问题,浏览器怎么会有这些CA公钥的呢?
浏览器本地拥有很多CA证书,里面有CA公钥
我们来看一个证书的示例
不知道你是不是还会有疑问,证书不会有假吗,或者证书不会被伪造吗?
实际上,如果签名算法不够健壮,证书是会被破解伪造的,但是近几年来说,签名算法已经很健壮了,很难通过暴力进行破解了~
HSTS
说了那么多HTTPS的安全性,那么如何开启HTTPS呢?
在第一次进行HTTPS请求服务器时,服务器返回一个响应头HSTS,下次再用HTTP进行请求时,就会将请求会自动升级成HTTPS协议。
为什么要第一次HTTPS请求才返回这个响应头呢?因为服务器认为HTTP不安全,所以要等到HTTPS安全的协议才可以返回这样的响应头
所以应该说先有一个HTTPS才有HSTS
SRI
通过对比hash值,保证我们静态资源的内容没有被篡改
总结
安全无小事,一定要注意网络安全防御
我们总认为漏洞总是我们自己写的代码上出现的,其实有时候使⽤的依赖(npm package,甚⾄是 NodeJS)也有可能成为最薄弱的⼀环,比如下面著名的事件:
- left-pad 事件 npm Blog Archive: kik, left-pad, and npm (npmjs.org)
- eslint-scope 事件 Postmortem for Malicious Packages Published on July 12th, 2018 - ESLint - Pluggable JavaScript linter
- event-stream 事件 npm Blog Archive: Details about the event-stream incident (npmjs.org)
有些漏洞和攻击可能都是我们没想到的,所以我们要保持学习心态,持续学习~~