技术知识文章集合TECHNICAL ARCHIVE · 457 DOCUMENTS

显示模式

登录
ARCHIVE DOCUMENTNET

前端基础篇之 HTTP 协议

所属馆藏
NET Protocol
文件格式
Markdown
原始路径
NET Protocol/19-前端基础篇之HTTP协议
本文目录31 个章节
  1. 目录
  2. TCP/IP 协议族
  3. 封装与解封装
  4. 串行连接
  5. 持久连接
  6. 管道化持久连接
  7. HTTP/2 多路复用
  8. WebSocket
  9. HTTP/1.0
  10. HTTP/1.1
  11. HTTP/2
  12. HTTP/3
  13. 请求报文
  14. 响应报文
  15. 请求方法
  16. 状态码
  17. 首部字段
  18. 简单请求
  19. 需要预检的请求
  20. 虚拟主机
  21. 代理服务器
  22. 缓存服务器
  23. 对称加密
  24. 非对称密码学
  25. 证书和数字签名
  26. TLS 的现代握手
  27. HTTPS 的特点和限制
  28. XSS 攻击
  29. CSRF 攻击
  30. 点击劫持
  31. 中间人攻击

前端基础篇之 HTTP 协议

Category(分类): NET Protocol Status: 优

HTTP 是前端开发的重要基础知识。本文参考《图解 HTTP》等资料,尽量用简洁的语言整理 HTTP 的基础概念、版本差异、报文结构、缓存、代理、HTTPS 和常见 Web 安全问题。

目录

HTTP 协议简介

HTTP(HyperText Transfer Protocol,超文本传输协议)是 Web 中常用的应用层协议,用于规定客户端如何向服务器请求资源,以及服务器如何返回响应。

HTTP 采用请求/响应模型:

  • 客户端发送请求报文,请求行中包含方法、请求目标和协议版本,请求头部携带元数据,请求体可选;
  • 服务器返回响应报文,状态行中包含协议版本和状态码,响应头部携带元数据,响应内容可选。

HTTP 本身是无状态协议,协议不会自动保存不同请求之间的会话状态。但应用可以通过 Cookie、Session、Token 等机制建立会话,因此“无状态”不等于服务器永远不知道客户端身份。

浏览器会根据服务器返回的 Set-Cookie 保存符合条件的 Cookie,之后仅在域名、路径、SecureSameSite 等规则允许时自动发送匹配的 Cookie。Cookie 并不是每个请求都会携带。

TCP/IP 协议族

TCP/IP 不是只有 TCP 和 IP 两个协议,而是一组网络协议的集合。HTTP 是其中的应用层协议之一。

TCP/IP 常用四层模型如下:

  • 应用层:为应用程序提供网络服务和数据格式;
  • 传输层:负责主机之间进程到进程的数据传输;
  • 网际层:负责 IP 寻址、路由和数据包转发;
  • 网络接口层:负责相邻节点之间的帧和物理链路传输。

TCP/IP 协议分层示意图

应用层

应用层规定了应用程序之间如何交换数据。TCP/IP 协议族中包含许多应用协议,例如:

  • FTP(File Transfer Protocol,文件传输协议);
  • DNS(Domain Name System,域名系统);
  • HTTP(HyperText Transfer Protocol,超文本传输协议);
  • SMTP、POP3、IMAP 等邮件协议;
  • SSH、NTP、DHCP 等协议。

DNS 用于把域名解析为 IP 地址,也可以查询邮件服务器等记录。例如,www.example.com 可能解析为某个 IPv4 或 IPv6 地址,但实际解析结果可能因 DNS 记录、地域和网络环境不同而变化。

传输层

传输层连接应用层和网际层,负责把数据交付给目标主机上的具体进程,端口号是传输层的重要概念。

常见传输协议包括:

  • TCP(Transmission Control Protocol,传输控制协议);
  • UDP(User Datagram Protocol,用户数据报协议);
  • SCTP 等其他传输协议;
  • QUIC 运行在 UDP 之上,为 HTTP/3 提供可靠、多路复用的传输能力。

TCP 是全双工协议,两个方向可以独立、同时传输。TCP 的可靠性来自序列号、确认、重传、流量控制和拥塞控制等机制,而不是仅仅因为有三次握手和四次挥手。

UDP 是无连接、面向数据报的协议,不保证数据可靠、有序或不重复到达,但头部开销较小,适合由应用自行设计可靠性或对实时性要求较高的场景。

网际层

网际层规定数据包如何通过路由器和其他网络设备到达目标网络,核心协议是 IP。

当目标主机不在本地网络时,主机会将 IP 数据包交给默认网关,由路由器根据路由表选择下一跳。网际层不保证数据一定到达、按序到达或只到达一次,可靠性通常由 TCP 或应用层协议提供。

网络接口层

网络接口层大致对应 OSI 模型的数据链路层和物理层,负责连接网络的链路和硬件相关部分,例如:

  • 以太网、Wi-Fi 等链路技术;
  • MAC 地址和帧格式;
  • 网卡及其驱动;
  • 网线、光纤、无线信号和连接器等物理介质。

它不只是“物理层”,也不等于所有硬件功能。

封装与解封装

发送端在层与层之间传输数据时,数据通常从应用层向下传递,每经过一层就会增加该层需要的控制信息。接收端从网络接口层向上处理,每一层读取并移除或解析属于自己的信息。

典型封装过程如下:

应用层数据
    ↓ 添加 TCP/UDP 首部
TCP 段 / UDP 数据报
    ↓ 添加 IP 首部
IP 数据报
    ↓ 添加链路层首部和尾部
链路层帧

封装后的数据单元通常称为:

  • 应用层:数据;
  • 传输层:TCP 段或 UDP 数据报;
  • 网际层:IP 数据报;
  • 网络接口层:帧。

解封装有点像俄罗斯套娃,但路由器通常只处理链路层帧和 IP 首部,再为下一条链路重新封装帧,不会把端到端的 TCP 和应用层数据完全拆开后再转发。

串行连接、持久连接、管道化持久连接和 HTTP/2 多路复用

串行连接

早期 HTTP/1.0 默认使用非持久连接:一次请求和响应完成后,TCP 连接通常关闭,下一个请求需要重新建立连接。HTTP/1.0 也存在 Keep-Alive 等扩展,因此不能绝对地说 HTTP/1.0 每次都断开连接。

在页面需要加载几十个资源时,频繁建立 TCP 连接会增加握手、慢启动和连接管理开销。浏览器通常会通过多个并行连接缓解 HTTP/1.0 的限制。

“短连接”和“短轮询”不是同一个概念:短连接描述连接生命周期,短轮询描述应用层定期发起请求获取数据的方式。

持久连接

HTTP/1.1 默认使用持久连接。在一条 TCP 连接上,可以依次传输多个 HTTP 请求和响应,从而减少重复建立和关闭 TCP 连接的开销。

持久连接不等于长轮询。长轮询是应用层通信方式,服务器可以暂时不返回响应,直到有数据或超时;持久连接只是允许复用已经建立的连接。

在 HTTP/1.1 普通请求处理中,同一条连接上的下一个请求通常要受到前一个请求处理方式的影响。浏览器也可以建立多个 TCP 连接并行请求。

管道化持久连接

HTTP/1.1 管道化允许客户端不等待前一个响应就发送下一个请求,但服务器必须按照请求顺序返回响应。因此,前一个慢请求仍可能阻塞后续响应,这就是 HTTP/1.1 管道化的队头阻塞问题。

现代浏览器普遍没有默认启用 HTTP/1.1 管道化,实际开发中主要由 HTTP/2 多路复用替代。

HTTP/2 多路复用

HTTP/2 引入了帧(Frame)和流(Stream):

  • 帧是 HTTP/2 的基本传输单元;
  • 每个帧包含 Stream Identifier,用于标识所属的流;
  • 一个流通常对应一个请求/响应交互;
  • 多条流的帧可以在同一个 TCP 连接中交错传输。

这样,多个请求可以并发进行,不需要等待其他请求的 HTTP 响应。HTTP/2 解决了 HTTP/1.1 管道化层面的队头阻塞,但由于底层通常仍是单条 TCP 连接,如果 TCP 层发生丢包,仍可能阻塞该连接中的所有流。

HTTP/2 通常会复用同一个连接,但浏览器不一定对同一域名永远只建立一条连接,实际行为还受到连接状态、证书、代理和实现策略影响。

WebSocket

WebSocket 是一种全双工通信协议,通常先通过 HTTP Upgrade 握手建立连接,之后双方可以独立发送消息。它不只是“HTTP 长连接”,而是握手成功后切换为 WebSocket 帧协议。

生产环境通常使用 wss://,服务端应校验握手中的 Origin 并进行身份认证。

HTTP/1.0、HTTP/1.1 和 HTTP/2 连接模式

URI

HTTP 使用 URI(Uniform Resource Identifier,统一资源标识符)标识资源。常见相关概念包括:

  • URI:统一资源标识符,是标识资源的通用语法;
  • URL(Uniform Resource Locator,统一资源定位符):通过位置或访问方式定位资源;
  • URN(Uniform Resource Name,统一资源名称):通过名称标识资源。

URL 和 URN 都可以看作 URI 的具体形式。URI 不只表示文件,也可以标识服务、文档、实体或其他资源。

例如:

https://example.com/a.html
urn:example:resource:123

前者包含访问资源所需的位置和协议,后者使用名称标识资源。具体 URN 还需要符合对应命名空间的规则。

URI、URL 与 URN 的关系

HTTP 版本

HTTP/1.0

早期 HTTP/1.0 默认使用非持久连接,一次请求和响应完成后通常关闭 TCP 连接。HTTP/1.0 也存在持久连接扩展,但不同客户端和服务器的支持情况并不完全一致。

HTTP/1.1

HTTP/1.1 带来了多项重要能力:

  • 默认使用持久连接;
  • 使用 Host 请求头支持同一 IP 上的虚拟主机;
  • 支持条件请求和更完善的缓存控制;
  • 支持 Range 范围请求和 206 Partial Content
  • 支持分块传输编码和管道化等机制。

HTTP/1.1 中,Host 是请求消息必须包含的首部字段,响应消息不要求包含 Host

HTTP/2

HTTP/2 的主要改进包括:

二进制分帧

HTTP/1.x 的消息语法主要基于文本,但消息体可以是二进制。HTTP/2 使用二进制帧格式传输控制信息和数据,便于解析和扩展。

多路复用

一个 TCP 连接中可以存在多条流,不同流的帧可以交错传输。浏览器通过帧中的流标识把数据交给对应的请求。

HTTP/2 解决了 HTTP 层面的队头阻塞,但 TCP 层面的队头阻塞仍然存在。

头部压缩

HTTP/2 使用 HPACK 压缩头部,通过静态表、动态表和 Huffman 编码减少重复首部字段的传输量。HTTP/3 使用的是 QPACK,设计上适配 QUIC 的传输特性。

服务端推送

HTTP/2 曾支持服务端推送,即服务器在客户端明确请求之前推送资源。但由于缓存判断、资源选择和实现复杂度等问题,现代浏览器已经逐步弃用或移除该能力,不应把它作为新项目的主要优化手段。

HTTP/3

HTTP/2 通常使用单条 TCP 连接进行多路复用。当 TCP 连接中出现丢包时,TCP 必须保证字节流按序交付,这可能使多个 HTTP/2 流共同受到影响。

HTTP/3 使用 IETF 标准化的 QUIC 协议。QUIC 运行在 UDP 之上,但在 UDP 之上实现了:

  • 可靠传输和重传;
  • 每条流独立的有序交付;
  • 流量控制和拥塞控制;
  • TLS 1.3 加密握手;
  • 连接迁移;
  • 连接建立和会话恢复优化。

避免跨流队头阻塞

QUIC 的不同流具有相对独立的传输状态。某一条流的数据包丢失时,其他流在拥有可用数据的情况下可以继续处理,从而避免 TCP 单字节流带来的跨流队头阻塞。不过,所有流仍然共享连接的网络带宽和拥塞控制,严重的网络拥塞仍会影响整个连接。

连接迁移

QUIC 使用 Connection ID 标识连接,不是简单依赖客户端 IP、端口和服务器 IP、端口。因此,移动设备切换 Wi-Fi 和蜂窝网络后,连接有机会继续使用,而不必像 TCP 那样仅因为源 IP 改变就必然重新建立连接。

连接迁移仍然需要路径验证和安全检查,不能简单理解为“只要 ID 不变就完全不需要握手”。0-RTT 可以减少部分握手延迟,但早期数据存在重放风险,不适合直接执行非幂等操作。

HTTP 报文

用于 HTTP 协议交互的信息称为 HTTP 报文。客户端发送请求报文,服务端返回响应报文。

请求报文

请求报文通常由以下部分组成:

  1. 请求行:方法、请求目标、协议版本;
  2. 请求首部;
  3. 空行;
  4. 可选的消息内容。

示例:

GET /index.html HTTP/1.1
Host: example.com
Accept: text/html

请求目标位于请求行,不属于请求首部。请求内容是否存在取决于方法和具体请求。

HTTP 请求行示意图

HTTP 请求报文结构

响应报文

响应报文通常由以下部分组成:

  1. 状态行:协议版本、状态码和原因短语;
  2. 响应首部;
  3. 空行;
  4. 可选的消息内容。

示例:

HTTP/1.1 200 OK
Content-Type: text/html; charset=utf-8
Content-Length: 18

<h1>Hello</h1>

Server 等服务器信息字段是可选的,并非响应报文的固定组成部分。

HTTP 状态行示意图

HTTP 响应报文结构

请求方法

  • GET:请求获取资源,通常应当是安全且幂等的;
  • POST:提交数据,请求服务器进行处理,常用于创建资源或触发操作;
  • PUT:使用请求内容创建或完整替换目标 URI 对应的资源,通常设计为幂等;
  • PATCH:对资源进行部分修改;
  • DELETE:请求删除指定资源,是否立即物理删除由服务器决定;
  • HEAD:获取与 GET 类似的响应首部,但不返回响应内容;
  • OPTIONS:询问目标资源或服务器支持的通信选项;
  • CONNECT:建立到目标服务器的隧道,常用于代理;
  • TRACE:回显请求链路信息,生产环境通常会禁用以降低风险。

方法的幂等性和安全性是协议语义,不等于服务器一定正确实现。POST 也可能用于查询,但不应因此改变其一般语义。

状态码

HTTP 状态码表示服务器对请求的处理结果。

2XX 成功

状态码含义
200 OK请求成功,响应内容取决于具体方法和资源
201 Created请求成功创建了资源
202 Accepted请求已接受,但处理尚未完成
204 No Content请求成功,响应不包含消息内容
206 Partial Content范围请求成功,返回部分内容

3XX 重定向或缓存相关

状态码含义
301 Moved Permanently资源具有新的永久 URI
302 Found资源暂时位于另一个 URI,历史客户端可能将 POST 改为 GET
303 See Other应使用 GET 或 HEAD 请求另一个 URI 获取结果
304 Not Modified条件请求命中缓存,响应不包含内容,与重定向无关
307 Temporary Redirect临时重定向,客户端应保持原方法和请求内容
308 Permanent Redirect永久重定向,客户端应保持原方法和请求内容

4XX 客户端错误

状态码含义
400 Bad Request请求语法或格式错误
401 Unauthorized缺少有效认证信息,通常配合 WWW-Authenticate
403 Forbidden服务器理解请求,但拒绝处理
404 Not Found未找到目标资源,或服务器不愿透露其存在
405 Method Not Allowed目标资源不支持当前请求方法
409 Conflict请求与资源当前状态冲突
429 Too Many Requests请求频率超过限制

5XX 服务器错误

状态码含义
500 Internal Server Error服务器内部发生未知错误
501 Not Implemented服务器不支持完成请求所需的功能
502 Bad Gateway网关或代理从上游收到无效响应
503 Service Unavailable服务器暂时无法处理请求,可能处于过载或维护状态
504 Gateway Timeout网关或代理等待上游响应超时

首部字段

HTTP 首部字段名称不区分大小写。HTTP/2 和 HTTP/3 在传输层面要求首部名称使用小写。下面按常见用途整理部分首部字段。

通用或连接相关首部

首部作用
Cache-Control控制缓存,如 no-cache 表示使用前重新验证,no-store 表示不存储,max-age 表示最大新鲜时间,public/private 控制共享缓存使用方式
ConnectionHTTP/1.1 的逐跳连接控制,如 close;HTTP/2 和 HTTP/3 不应使用该首部
Date报文生成时间
Pragma主要用于 HTTP/1.0 兼容,例如旧式缓存控制
Via记录经过的代理或网关
Transfer-EncodingHTTP/1.1 的传输编码,例如 chunked,HTTP/2 和 HTTP/3 不使用 HTTP/1.1 分块编码
Upgrade请求协议升级,例如 WebSocket,通常配合 Connection: Upgrade
Warning历史缓存警告字段,已不建议在现代 HTTP 中使用

请求首部

首部作用
Accept客户端可接受的媒体类型,如 application/jsontext/html
Accept-Charset可接受的字符集,已基本废弃,浏览器通常不发送
Accept-Encoding可接受的内容编码,如 gzipbrdeflate
Accept-Language可接受的语言,如 zh-CN,zh;q=0.9,en;q=0.8
Authorization客户端认证信息,如 BasicBearer 凭据
Cookie客户端发送给服务器的 Cookie
Expect请求客户端希望服务器执行的行为,常见值是 100-continue
From请求方邮箱地址,较少使用
Host请求目标主机和端口,HTTP/1.1 请求中必须包含;HTTP/2/3 使用 :authority 表示类似信息
If-Match根据 ETag 进行条件请求,失败时通常返回 412
If-Modified-Since根据 Last-Modified 时间进行缓存验证
If-None-Match根据 ETag 进行缓存验证或条件操作
User-Agent客户端软件信息,可被伪造
Max-Forwards限制 TRACEOPTIONS 等请求经过代理的次数
Proxy-Authorization向代理服务器发送认证信息
Range请求资源的某一部分,例如 bytes=0-1023
Referer请求来源页面,可能因隐私策略被省略或截断
TE声明客户端可接受的传输编码或 trailers,不能与 Accept-Encoding 混淆

响应首部

首部作用
Accept-Ranges告知是否支持范围请求,常见值为 bytes
Age响应在共享缓存中存在的秒数
ETag资源表示的验证标识,可用于条件请求
Location重定向目标,或 201 Created 中新建资源的位置
Proxy-Authenticate代理要求客户端认证时发送的认证挑战
Server服务器软件信息,可选,不建议暴露过多版本细节
WWW-Authenticate401 响应中的认证挑战
Set-Cookie指示浏览器保存或更新 Cookie

表示和内容相关首部

首部作用
Allow目标资源支持的请求方法列表
Content-Encoding内容编码,如 gzipbr
Content-Language内容使用的语言,如 zh-CN
Content-Length消息内容的字节长度,可用于请求或响应
Content-Location当前表示的备用位置
Content-MD5历史字段,MD5 是摘要而不是加密,现代应用不建议使用
Content-Range部分响应对应的资源范围
Content-Type内容媒体类型,如 application/json; charset=utf-8
Expires内容过期时间,缓存应结合 Cache-Control 判断
Last-Modified资源表示的最后修改时间

HTTP 常用首部字段示意图

两种 CORS 请求

浏览器进行跨源 HTTP 请求时,会根据方法、请求头和内容类型等条件判断是否需要预检。常用分类是简单请求和需要预检的请求。

简单请求

通常需要同时满足以下条件:

  1. 请求方法是 GETHEADPOST
  2. 手动设置的请求头满足 CORS safelisted request-header 限制;
  3. Content-Type 是以下三种之一:
    • text/plain
    • multipart/form-data
    • application/x-www-form-urlencoded
  4. XMLHttpRequest 上传对象没有注册会触发预检的事件监听器。

简单请求通常会先发送实际请求,再由浏览器根据响应头判断页面脚本能否读取响应。即使浏览器阻止脚本读取响应,请求也可能已经造成服务器副作用,因此 CORS 不能代替 CSRF 防护。

需要预检的请求

以下情况通常会触发预检:

  • 使用 PUTPATCHDELETE 等方法;
  • 使用 application/json 等不属于简单请求的内容类型;
  • 设置不满足 safelist 条件的自定义请求头;
  • 使用其他会改变 CORS 预检条件的请求配置。

仅仅携带凭据本身不会自动触发预检,但服务端仍必须正确返回 Access-Control-Allow-Credentials: true,并使用明确的 Access-Control-Allow-Origin

浏览器会先发送 OPTIONS 请求,并携带:

Origin: https://app.example.com
Access-Control-Request-Method: PUT
Access-Control-Request-Headers: Content-Type, Authorization

服务器需要返回允许的源、方法和请求头:

Access-Control-Allow-Origin: https://app.example.com
Access-Control-Allow-Methods: GET, POST, PUT, OPTIONS
Access-Control-Allow-Headers: Content-Type, Authorization
Access-Control-Max-Age: 600

如果预检失败,浏览器通常不会发送真正的请求。反向代理让浏览器请求同源地址时,不会因为后端代理目标不同而自动触发浏览器 CORS 预检。

Web 服务器

这里不重点讨论服务器是 Apache 还是 Nginx,而是介绍服务器在 HTTP 通信中的常见角色。一台服务器可以作为源服务器、代理服务器或反向代理,也可以通过虚拟主机承载多个网站。

虚拟主机

HTTP/1.1 允许一台服务器根据请求中的 Host 为多个域名提供服务。例如:

  1. DNS 将 www.example.com 解析到某个 IP 地址;
  2. 客户端连接该 IP;
  3. HTTP/1.1 请求通过 Host 告知目标域名;
  4. 服务器根据 Host 选择对应的虚拟主机配置。

HTTPS 场景下,TLS 握手通常还会通过 SNI 提供目标域名,以便服务器选择证书;HTTP/2 和 HTTP/3 使用 :authority 表示类似的请求目标主机信息。

同一个 IP 上可以部署多个域名,但不要求一定只有一台物理服务器,实际也可能通过负载均衡将请求分发到多个后端实例。

代理服务器

代理服务器位于客户端和源服务器之间,负责转发、缓存、鉴权、访问控制或负载均衡。

正向代理

正向代理代表客户端访问源服务器:

  1. 客户端向代理发送请求,并指定目标服务器;
  2. 代理向目标服务器发起请求;
  3. 代理获取目标响应;
  4. 代理将响应返回客户端。

客户端通常知道自己使用了正向代理。代理可能隐藏客户端地址,但如果通过请求头传递 X-Forwarded-For 等信息,目标服务器仍可能知道原始客户端信息。

反向代理

反向代理代表服务器接收客户端请求:

  1. 客户端请求统一入口;
  2. 反向代理根据域名、路径、负载策略选择上游服务;
  3. 代理将请求转发给后端;
  4. 代理把响应返回客户端。

客户端通常只需要知道反向代理地址,不需要知道具体后端实例。Nginx、Caddy、Apache HTTP Server 和云负载均衡器都可以承担反向代理角色。

通过开发服务器代理解决跨域

本地前端开发服务器可能运行在:

http://localhost:8080

目标接口运行在:

http://api.example.com

如果浏览器直接请求目标接口,就会受到同源策略限制。开发服务器代理的做法是:

浏览器 -> localhost:8080/api -> 开发服务器 -> api.example.com

浏览器只请求同源的 /api,开发服务器再从服务端请求目标接口。服务器到服务器的请求不受浏览器同源策略限制。

Vue CLI 示例配置:

// vue.config.js
module.exports = {
  devServer: {
    port: 8080,
    proxy: {
      '/api': {
        target: 'https://api.example.com',
        changeOrigin: true,
        secure: true,
        // 如果目标接口不需要 /api 前缀,可以取消注释
        // pathRewrite: { '^/api': '' }
      }
    }
  }
}

前端请求应使用与代理规则匹配的相对路径:

axios.create({
  baseURL: '/api'
})

baseURL 不一定必须是 '/',关键是请求 URL 需要保持同源,并且能够匹配代理配置。开发代理不等同于生产环境的安全防护,生产环境通常使用 Nginx、网关或后端服务进行统一代理。

缓存服务器

缓存服务器或 CDN 会在浏览器和源服务器之间保存可复用的响应,以减少源站压力和网络延迟。缓存是否可以保存和复用响应,需要根据 Cache-ControlExpiresETagLast-ModifiedVary 等字段判断。

缓存服务器不一定只有一层,也不一定总是先访问缓存再访问源站。对于带用户身份的响应,应谨慎配置缓存,避免把一个用户的数据返回给另一个用户。

HTTPS

HTTP 本身不提供加密、完整性保护和服务器身份认证。直接使用 http:// 时,网络中的攻击者可能读取或篡改传输内容。

对称加密

对称加密使用同一个秘密密钥加密和解密,速度通常较快,适合大量应用数据传输。但通信双方需要安全地得到同一个密钥,这就是密钥分发问题。

非对称密码学

非对称密码学使用公钥和私钥:

  • 公钥加密、私钥解密可以用于机密性;
  • 私钥签名、公钥验证用于身份认证和完整性。

“私钥加密、公钥解密”常被用来类比数字签名,但不能把它当成普通机密性加密流程。攻击者可以看到公钥,真正需要保护的是私钥以及证书信任链。

证书和数字签名

服务器证书通常是 X.509 证书,包含:

  • 服务器公钥;
  • 域名信息,现代证书主要通过 SAN 表示;
  • 有效期;
  • 签发者和用途;
  • CA 对证书内容的数字签名。

CA 不是用自己的私钥“加密服务器公钥”,而是对证书内容计算摘要并生成数字签名。浏览器会检查证书链、域名匹配、有效期、用途和本地信任库。

免费证书和付费证书都存在,是否收费取决于 CA、验证类型和服务商。证书签名不能保证服务器本身没有被入侵,也不能保护已经被恶意软件控制的客户端。

TLS 的现代握手

HTTPS 更准确的表达是:

HTTPS = HTTP over TLS

SSL 已经废弃,现代 HTTPS 主要使用 TLS 1.2 或 TLS 1.3。

TLS 1.3 通常使用临时 ECDHE 进行密钥协商:

  1. 客户端发送 ClientHello 和密钥交换参数;
  2. 服务端返回 ServerHello、证书和握手签名;
  3. 客户端验证证书链、域名和签名;
  4. 双方通过 ECDHE 计算共享秘密;
  5. 双方使用 HKDF 派生会话密钥;
  6. 应用数据使用 AES-GCM 或 ChaCha20-Poly1305 等 AEAD 算法保护。

早期 TLS 1.2 可以使用 RSA 密钥交换,但现代 TLS 1.3 不使用静态 RSA 密钥交换。ECDHE 还可以提供前向保密:即使服务器长期私钥日后泄露,也不能直接解密过去捕获的 TLS 1.3 会话数据。

HTTPS 与 TLS 握手示意图

HTTPS 的特点和限制

  • 机密性:防止传输中的第三方直接读取 HTTP 内容;
  • 完整性:通过 AEAD 等机制检测传输内容是否被篡改;
  • 身份认证:通过证书和信任链验证服务器身份;
  • 端口:HTTPS 通常使用 443,但端口不是安全性的来源;
  • 底层协议:HTTP/1.1 和 HTTP/2 通常运行在 TCP + TLS 上,HTTP/3 运行在 QUIC + TLS 上。

HTTPS 主要保护传输过程,不能替代应用层认证、权限控制、输入校验和端点安全。IP 地址、连接时间、流量大小等部分元数据也可能被观察到。

Web 安全防范

XSS 攻击

XSS(Cross-Site Scripting,跨站脚本攻击)是攻击者让不可信输入以脚本或恶意 HTML 的形式在受信任页面中执行。常见类型包括:

  • 反射型 XSS:恶意内容来自当前请求,服务器立即反射到响应;
  • 存储型 XSS:恶意内容被保存到数据库或其他存储中,之后被展示给用户;
  • DOM 型 XSS:前端 JavaScript 不安全地处理 URL、输入框等数据。

XSS 不只是“在 URL 中插入 <script>”,也可能通过 HTML 属性、事件处理器、JavaScript 字符串、CSS 或 URL 上下文发生。不能依赖浏览器自动拦截所有 XSS。

防护重点包括:

  • 在正确的上下文进行输出编码;
  • 使用模板引擎的自动转义;
  • 不要把不可信内容直接交给 innerHTML
  • 对确实需要展示的 HTML 使用经过配置的白名单清洗器;
  • 配置 Content Security Policy(CSP);
  • Cookie 使用 HttpOnlySecure 等属性降低被脚本读取的风险。

示例中的 xss 库只能用于特定 HTML 清洗场景,不能替代 JavaScript、CSS、URL 等不同上下文的安全编码:

const xss = require('xss')

const html = xss(
  '<h1 id="title">XSS Demo</h1><script>alert("xss")</script>'
)

console.log(html)

CSRF 攻击

CSRF(Cross-Site Request Forgery,跨站请求伪造)利用浏览器自动携带 Cookie 或其他凭据的行为,让用户在不知情的情况下向目标网站发送有副作用的请求。

例如,一个不安全的关注接口使用 GET 修改状态:

https://example.com/follow?id=123

恶意页面可能嵌入:

<img src="https://example.com/follow?id=123" alt="">

如果浏览器携带了目标网站的有效 Cookie,就可能执行关注操作。即使改成 POST,也可能通过跨源表单提交,因此不能只依赖同源策略。

CSRF 的本质是:身份凭据可能自动被浏览器携带,但服务器无法确认请求是否得到用户主动批准。

常见防护方式:

  1. GET、HEAD 等安全方法不应修改服务器状态;
  2. 使用不可预测且与会话绑定的 CSRF Token;
  3. Cookie 配置合理的 SameSiteSecureHttpOnly
  4. 校验请求的 Origin,必要时校验 Referer
  5. 对敏感操作进行重新认证或二次确认;
  6. 不要把“禁止 CORS”当作 CSRF 防护,CORS 主要控制脚本读取响应。

HttpOnly 只能减少脚本读取 Cookie 的风险,不能阻止浏览器在符合条件时自动发送 Cookie。

点击劫持

点击劫持是一种视觉欺骗攻击。攻击者把目标网站嵌入自己的页面,通过透明或伪装的 iframe 诱导用户点击目标页面中的按钮。

点击劫持示意图

防护可以使用:

X-Frame-Options: DENY

或:

X-Frame-Options: SAMEORIGIN

ALLOW-FROM 已被现代浏览器废弃或支持不一致,不建议作为主要方案。更现代的方案是配置 CSP:

Content-Security-Policy: frame-ancestors 'none'

如果业务确实允许部分站点嵌入,可以在 frame-ancestors 中明确列出允许的源。

中间人攻击

中间人攻击是攻击者同时与客户端和服务端建立连接,并让双方误以为彼此正在直接通信。攻击者可以读取、转发甚至修改通信内容。

中间人攻击示意图

单纯的对称加密、非对称加密或密钥交换不能自动解决身份认证问题。如果公钥被中间人替换,客户端可能把数据加密给攻击者。

HTTPS 通过证书、CA 信任链、握手签名和会话密钥协商来验证服务端身份,并保护传输中的数据。但如果用户安装了恶意根证书、关闭证书校验、客户端或服务器已经被入侵,HTTPS 也不能保证绝对安全。

小结

本文整理了前端需要掌握的 HTTP 相关知识:

  • HTTP 是无状态的应用层请求/响应协议;
  • HTTP/1.1 通过持久连接降低连接开销;
  • HTTP/2 通过二进制分帧和多路复用提升并发传输效率;
  • HTTP/3 使用 QUIC,减少 TCP 层面的跨流队头阻塞;
  • URI、请求方法、状态码和首部共同描述 HTTP 交互;
  • CORS、缓存、代理和虚拟主机是实际 Web 开发的重要内容;
  • HTTPS 通过 TLS 提供传输加密、完整性保护和服务器身份认证;
  • XSS、CSRF、点击劫持和中间人攻击需要分别采取针对性防护。

推荐阅读:

457 DOCUMENTS · 10 COLLECTIONS
ARCHIVE SEARCH457 篇文章

SEARCH GUIDE

输入关键词开始搜索

支持搜索文章标题、所属分类和原始文档路径。

按分类浏览

10 COLLECTIONS