前言
随着互联网的高速发展,信息安全问题已经成为企业最为关注的焦点之一,而前端又是引发企业安全问题的高危据点。在移动互联网时代,特别是前端人员除了传统的 XSS、CSRF 等安全问题之外,又时常遭遇网络劫持、非法调用 Hybrid API 等新型安全问题。当然,浏览器自身也在不断在进化和发展,不断引入 CSP、SameSite、HttpOnly、Cookies 等新技术来增强安全性,但是仍存在很多潜在的威胁,这需要我们不断进行“查漏补缺”
浏览器安全可以分为三大块:Web页面安全、浏览器网络安全、浏览器系统安全
Web页面安全主要就是同源策略限制
什么是同源策略
同源指的是我们访问站点的:协议
、域名
、端口号
必须一至,才叫同源
。
浏览器默认同源之间的站点是可以相互访问资源和操作DOM的,而不同源之间想要互相访问资源或者操作DOM,那就需要加一些安全策略的限制,俗称同源策略
同源策略主要限制了三个方面:
- DOM层面:不同源站点之间不能相互访问和操作DOM
- 数据层面:不能获取不同源站点的Cookie、LocalStorage、indexDB等数据
- 网络层面:不能通过XMLHttpRequest向不同源站点发送请求
当然同源策略限制也不是绝对隔离不同源的站点,比如link、img、script标签都没有跨域限制,这让我们开发更灵活了,但是也同样带来了一些安全问题,也就是浏览器网络安全问题,最典型的就是XSS攻击和CSRF攻击
说一下XSS攻击
XSS攻击是一种代码注入攻击
,通过恶意注入脚本在浏览器运行,然后盗取用户信息
造成XSS攻击其实本质上还是因为网站没有过滤恶意代码,与正常代码混在一起之后,浏览器没有办法分辨哪些是可信的,然后导致恶意代码也被执行。然后可能导致以下情况:
- 页面数据或用户信息被窃取,如DOM、Cookie、LocalStorage
- 修改DOM,比如伪造登录窗口或在页面生成浮窗广告
- 监听用户行为,比如在登录或银行等站点用 addEventListener 监听键盘事件,窃取账号密码等信息
- 流量被劫持向其他网站
XSS攻击有三种类型:存储型、反射型、DOM型
存储型
:是在有发贴评论等带有数据保存功能的网站的input、textarea将恶意代码提交到网站数据库中,如,然后比如在显示评论的页面就会从数据获取,并直接执行这个script标签里的恶意代码
反射型
:是攻击者将恶意JS脚本作为用户发送给网站请求中的一部分,然后网站又把恶意脚本返回给用户,这时候就会在页面中被执行。比如打开包含带恶意脚本的链接,当打开后会向服务器请求后,服务器会获取URL中的数据然后拼接在HTML上返回,然后执行。它和存储型不同的是不会储存在服务器里基于DOM型
:就是攻击者通过一些劫持手段,在页面资源传输过程中劫持并修改页面的数据,插入恶意代码
怎么防范XSS攻击,有什么解决办法?
-
就是对输入框的内容进行
过滤
或使用转义符进行转码
字符 转义后的字符 < <
> >
" "
' '
/ /
& &
-
使用CSP,就是
白名单
,告诉浏览器哪些外部资源可以加载执行,让即使插入进来恶意代码的也不会执行,或者可以向哪些第三方站点提交数据。开启白名单的方式有两种:- 使用 meta 标签
- 设置http头部的
Content-Security-Policy
- 使用 meta 标签
对一些敏感信息进行保护,在
Cookie
信息中添加httpOnly
,告诉浏览器在保存Cookie,且不要对客户端脚本开放访问权限,然后就不能通过document.cookie获取cookie了
Set-Cookie: widget_session=123456; httpOnly
- 使用
验证码
,避免脚本伪装成用户执行一些操作
说一下CSRF攻击
就是跨站请求伪造攻击
,主要就是利用用户的登录状态发起跨站请求,比如邮箱里的乱七八糟的链接,打开链接的时候邮箱肯定是处于登录状态,然后黑客就可以用这个登录状态,伪造带有正确 Cookie 的 http 请求,直接绕过后台的登录验证,然后冒充用户执行一些操作
发起CSRF攻击有三个必要条件:
- 目标网站一定要有CSRF漏洞
- 用户登录过目标网站,并且浏览器保存了登录状态
- 需要用户主动打开第三方站点
本质是利用cookie在同源请求中携带发送给服务器的特点,来实现冒充用户
CSRF攻击也有三种类型:GET类型、POST类型、链接型
-
自动发GET类型
:比如img
或iframe
标签等,当用户打开这个网站时会自动发起带Cookie的资源请求
-
自动发POST类型
:比如整一个隐藏的表单
,在用户进入页面的时候自动提交表单
-
诱导链接型
:就是诱导用户主动点击链接,比如a标签
点击领取大礼包
点击下载美女视频
怎么防范CSRF攻击,有什么解决办法?
-
在Cookie信息中添加
SameSite
属性,这个属性有三个值:- strict:严格模式,完全禁止使用Cookie
- lax:宽松模式,允许部分情况使用Cookie,
跨域的都行
,a标签跳转,link标签,GET提交表单 - none:任何情况下都会发送Cookie,但必须同时设置Secure属性,意思是需要安全上下文,Cookie 只能通过https发送,否则无效
Chrome 80之前默认值是none,之后是lax
Set-Cookie: widget_session=123456; SameSite=None; Secure
-
验证请求来源
:服务器根据http请求头中的Origin
或Referer
属性判断是否为允许访问的站点,从而对请求进行过滤。优先判断Origin,如果两个都不存在的话就直接阻止。
- Referer:记录了请求是从哪个链接跳过来的并且包含了路径信息,也就是来源地址。不过这家伙不太可靠,所以后来又新增了Origin属性
Referer: https://juejin.cn/editor/drafts/xxxx
- origin:记录了域名信息,没有具体的URL路径
Origin: https://juejin.cn
-
Token验证
:服务器向用户返回一个随机数Token,再次请求时在请求头中以参数的形式添加入这个Token,然后服务器验证这个Token,如果没有或者内容不正确,就拒绝请求。缺点是- 每个请求都得添加比较繁琐
- 单方面验证Cookie可能会被冒用,
- 如果网站不止一台服务器,通过负载均衡转到了其他服务器的话,其他所有服务器中的Session中都得保留Token,不然就验证不了了
-
双重验证Cookie
:利用攻击者只能利用Cookie,不能获取Cookie的特点,用户访问页面时,服务器向请求域名添加一个Cookie随机字符串,然后,用户再次请求时从Cookie中取出这个字符串,添加到URL参数中,然后服务器通过对Cookie中的数据和参数中的数据对比验证,不一样就拒绝请求。缺点是如果网站存在XSS漏洞,这法子就会失效,而且不能做到子域名的隔离
浏览器系统安全 - 安全沙箱
首先,我们设想一下,如果我们下载了一个恶意程序,但是没有执行它,对我们有没有影响?
那肯定没有,而浏览器也是一样的
浏览器可以安全地下载各种网络资源,但是执行的时候就需要谨慎了。比如解析HTML、CSS、执行JS等操作,一不小心黑客就会利用这些操作对有漏洞的浏览器发起攻击
所以需要在渲染进程和操作系统之间建一堵墙,有这堵墙在挡着,黑客能黑进来,也只能获取渲染进的操作权限(如下图),不会影响到外面,而这隔离操作系统和渲染进程的一堵墙就是安全沙箱
安全沙箱最小的保护单位是进程,并且能限制进程对操作系统资源的访问和修改,这就意味着,安全沙箱所在的进程不能有读写操作系统的功能,比如读写本地文件、发起网络请求,调用GPU接口等
安全沙箱怎么影响各个模块功能
-
持久存储
- 存储Cookie的读写,浏览器内核会维护一个存放所有Cookie的Cookie数据库,在渲染进程通过JS读取Cookie时,渲染进程会通过IPC将读取Cookie的信息发送给内核,浏览器内核读取Cookie之后再将内容通过IPC返回给渲染进程
- 缓存文件的读写也是由浏览器内核实现
-
网络访问
:渲染进程不能直接访问网络,也需要通过浏览器内核,而浏览器内核在处理URL请求之前,会检查渲染进程有没有权限请求该URL,比如有没有跨域 -
用户交互
- 输入时:操作系统会将输入事件传给浏览器内核,内核判断如果是地址栏输入事件就直接在内核处理,如果是页面里的就转发给渲染进程
- 渲染时:渲染进程渲染出位图后,需要将生成好的位图发送给浏览器内核,再由内核将位图复制到屏幕上显示
站点隔离你知道吗
我们知道一个标签页会有一个渲染进程,假如这个标签页里面有多个不同站点的 iframe 呢? 这就会导致多个不同站点的内容运行在同一个渲染进程中,这肯定不行
所有操作系统中都存在两个A级漏洞:幽灵(Spectre)和熔毁(Meltdown),这是处理器架构导致的,很难修补
如果银行页面中有一个恶意 iframe ,这个恶意站点利用两个A级漏洞入侵渲染进程,那恶意程序就可以读取银行站点渲染进程内所有内容了,这对用户来说风险就很大了,如果没有沙箱保护,甚至还可以对操作系统发起攻击
所以后来Chrome对渲染进程进行重构,将标签级的渲染进程改成 iframe 级渲染进程,就是说一个标签页内可能运行多个渲染进程,并且相互隔离,这样恶意 iframe 就无法访问页面中其他的内容了,也就无法攻击其他站点了,这就是站点隔离
中间人攻击
在 http 数据提交给 TCP 层之后,会经过用户电脑、路由器、运营商、服务器,这中间每一个环节,都不是安全的
一句话就是:在 http 传输过程中容易被中间人窃取、伪造、篡改,这种攻击方式称为中间人攻击
。
那怎么让数据可以更安全的传输呢?
就是使用 https ,利用 https 安全层对数据进行加解密操作,以保证数据安全。
关于 https性能优化、版本、优缺点、SSL/TLS、握手(RSA、TLS1.2、TLS1.3)三个版本及优化等等,文章太长这里就不展开了,可以看我另一篇文章有详细介绍
那么 https 是如何对数据加解密的呢?这要先说一下它的算法
对称加密算法
就是加密和解密使用同一个密钥。如AES、DES
。加解密过程:
- 浏览器给服务器并发送一个随机数
client-random
和加密套件
(一个支持的加密方法列表) - 服务器生成给浏览器返回另一个随机数
server-random
和加密套件
- 两边分别返回确认消息。然后两者用加密方法将两个随机数混合生成密钥,这就是通信双上加解密的密钥
有了密钥之后就可以对数据进行加密传输了
问题是client-random
和server-random
都是明文的,双方如何安全的传递两个随机数和加密方法呢?直接传给客户端,那过程中就很可能被窃取,中间人还是能解密拿到数据,往下看
不对称加密算法
就是一对密钥,有公钥
(public key)和私钥
(private key),其中一个密钥加密后的数据,只能用另一个密钥进行解密。如RSA
、ECDHE
。加解密过程:
- 浏览器给服务器发送
加密套件
- 服务器选好支持的
加密方法
和公钥
(明文) 传给浏览器 - 两边分别返回确认消息。然后浏览器用公钥对数据进行加密,这个密钥只能用
私钥
解密
这是不是看上去很完美了
其实还存在很严重的问题
- 使用公钥反推出私钥是非常困难,但不是做不到,随着计算机运算能力提高,非对称密钥
至少要2048位才能保证安全性
,这就导致加解密速度慢,效率太低 - 无法保证服务器发送给浏览器的数据安全。因为浏览器可以用公钥来加密,而浏览器就只能用私钥加密,公钥是明文传输的,中间人可以获取到,这样服务器端的数据安全就得不到保证了
所以!
混合加密
TLS实际用的是两种算法的混合加密
。通过 非对称加密算法 交换 对称加密算法 的密钥,交换完成后,再使用对称加密进行加解密传输数据。这样就保证了会话的机密性。过程如下
- 浏览器给服务器发送一个随机数
client-random
、对称和非对称加密套件
- 服务器把另一个随机数
server-random
、加密套件
、公钥
传给浏览器 - 浏览器又生成另一个随机数
pre-random
,并用公钥对pre-random
加密后传给服务器 - 服务器再用私钥解密,得到
pre-random
,并返回确认消息 - 这样浏览器和服务器都有三个随机数了,然后各自将三个随机数用加密方法混合生成最终的对称密钥
然后开始数据加密传输
这样即便被截持,中间人没有私钥就拿不到pre-random
,就无法生成最终密钥
这样就安全了吗?
emmmm......还没
因为问题又来了,如果一开始DNS就被中间人劫持
,那么请求被中间人截获,中间人把他自己的服务器公钥给了浏览器,浏览器收到公钥就把信息发给中间人了,中间人解密拿到数据,并干了一些见不得人的勾当之后,再请求实际服务器,拿到服务器公钥,再把加密处理过后的数据发给服务器
这样不知不觉间信息就被人窃取了,所以在结合对称和非对称加密的基础上,还需要服务器向浏览器证明身份,那怎么证明呢?
所以数字证书
来了,往下看
如何保证数据是否被篡改?
数字证书(数字签名)
它可以帮我们验证服务器身份
,而且数字证书里包含了公钥,而数字证书需要向有权威的认证机构(CA)
获取授权给服务器。
相比之前就变成了
- 服务器不直接返回公钥给浏览器,而是返回数字证书,而公钥就在数字证书中
- 浏览器这边多了一步证书验证,验证成功才能继续后续流程
那么如何申请数字证书呢?
- 首先,服务器准备一套
公钥
和私钥
,私钥留着自己用 - 服务器将公钥和站点等信息提交给CA认证,这个是
要钱
的 - CA
验证
服务器提供的信息 - 审核通过后签发认证的数字证书,包含了
公钥
、CA信息
、有效时间
、证书序列
等这些都是明文的,还有一个CA生成的签名
CA的签名过程
- CA也有一套
公钥
和私钥
- CA使用摘要算法计算服务器提交的明文信息并得出
信息摘要
- 然后CA再用它的私钥和特定的算法对信息摘要加密,生成
签名
- 把
签名
、服务器公钥
等信息打包放入数字证书
,并返回给服务器 - 服务器配置好证书,以后浏览器连接服务器,都先把证书发给客户端验证
摘要算法:主要用于保证信息的完整性。常见的MD5算法、散列函数、hash函数都属于这类算法,其特点就是
单向性
、无法反推原文
浏览器如何验证数字证书
- 浏览器连接服务器,都先把证书发给客户端验证
- 使用CA公钥和声明的签名算法对CA中的签名进行解密,得到服务器的
摘要内容
和服务器公钥 - 再用摘要算法对证书里的服务器公钥生成摘要,再把这个摘要和上一步得到的摘要
对比
- 然后将两个信息摘要对比,如果是一致的,就说明证书是合法的,即证明了服务器自己,否则就是非法的
证书认证又分为单向认证
和双向认证
单向认证:服务器发送证书,客户端验证证书
双向认证:服务器和客户端分别提供证书给对方,并互相验证对方的证书
不过大多数https服务器都是单向认证,如果服务器需要验证客户端的身份,一般通过用户名、密码、手机验证码等之类的凭证来验证。只有更高级别的要求的系统,比如大额网银转账等,就会提供双向认证的场景,来确保对客户身份提供认证性
另外在申请和使用证书的过程中,需要注意
- 申请数字证书是不需要提供私钥的,要确保私钥永远只能由服务器掌握
- 数字证书最核心的是CA使用它的私钥生成的数字签名
- 内置CA对应的证书称为根证书,根证书是最权威的机构,它们自己为自己签名,这称为
自签名证书
有了这些之后就安全了吗?
emmmmm.....没有
虽然不是绝对安全,但是现行架构下最安全的解决文案了,大大增加了中间人的攻击成本
结语
点赞支持、手留余香、与有荣焉
参考
浏览器工作原理与实践
HTTPS:网络安全攻坚战