4. SaaS 鉴权挑战:多租户下的用户身份混淆、跨租户数据越权风险、租户路由与域名绑定

在单租户架构中,用户身份与租户边界天然重合;但在多租户 SaaS 系统中,一个身份认证系统需要同时服务成百上千个逻辑隔离的租户。JWT 作为无状态令牌,如果不在签发和验证阶段显式绑定租户上下文,就会引入三类核心挑战:身份混淆、数据越权、路由错乱。本章逐一拆解这些挑战的成因与应对策略。

4.1 用户身份混淆:租户上下文缺失

当两个不同租户的用户恰好拥有相同的用户名(如 admin@example.com),且 JWT 中仅包含用户 ID 而不包含租户 ID 时,认证系统无法区分该令牌属于哪个租户。攻击者可以通过猜测或窃取令牌,在租户 A 的上下文中使用租户 B 的身份。

核心问题:JWT 载荷中缺少 tenant_id 或 org_id 字段,导致令牌在跨租户场景下成为“通用钥匙”。
场景 JWT 载荷示例(错误) JWT 载荷示例(正确)
用户登录 { "sub": "user_123", "role": "admin" } { "sub": "user_123", "tenant_id": "t_abc", "role": "admin" }
API 请求 仅凭 sub 查询用户数据 同时校验 sub 和 tenant_id
数据库查询 SELECT * FROM orders WHERE user_id = ? SELECT * FROM orders WHERE user_id = ? AND tenant_id = ?
最佳实践:在 JWT 签发时,将 tenant_id 作为标准声明(claim)写入令牌;在 API 网关或中间件层,强制从令牌中提取 tenant_id 并注入到请求上下文,确保后续所有数据操作都携带租户过滤条件。

4.2 跨租户数据越权风险:水平权限提升

即使 JWT 中包含了 tenant_id,如果后端服务在数据访问层没有严格执行租户隔离,攻击者仍可通过篡改请求参数(如 ?tenant_id=t_evil)或遍历 ID 来访问其他租户的数据。这种漏洞被称为水平权限提升(Horizontal Privilege Escalation),是 SaaS 应用中最常见的高危漏洞之一。

跨租户数据越权攻击流程 攻击者(租户A) 合法 JWT (tenant_id=A) 篡改请求参数 tenant_id=B 后端服务 未校验 tenant_id 一致性 数据库 SELECT * FROM orders WHERE id=? 返回租户B的敏感数据
防御要点:永远不要信任客户端传入的 tenant_id。后端应从 JWT 中提取租户标识,并在所有数据查询中强制附加 WHERE tenant_id = jwt_tenant_id 条件。使用 ORM 层全局过滤器(如 Laravel 的 Global Scope、Hibernate 的 Filter)可以降低遗漏风险。

4.3 租户路由与域名绑定

多租户系统通常需要将请求路由到正确的租户上下文。常见的路由策略包括:

  • 子域名模式:tenant-a.example.com、tenant-b.example.com
  • 路径前缀模式:example.com/tenant-a/、example.com/tenant-b/
  • 自定义域名模式:租户绑定自己的域名,如 app.tenant.com 指向 SaaS 平台

JWT 在这种场景下需要与路由策略协同工作:

路由模式 JWT 验证要点 常见风险
子域名 从 Host 头提取租户,与 JWT 中的 tenant_id 比对 DNS 劫持、Host 头注入
路径前缀 从 URL 路径提取租户,与 JWT 中的 tenant_id 比对 路径遍历、URL 篡改
自定义域名 通过域名映射表查找租户 ID,再与 JWT 中的 tenant_id 比对 域名绑定未验证、证书管理混乱
推荐策略:采用“子域名 + JWT 双重校验”模式。API 网关根据子域名解析出预期租户 ID,然后与 JWT 中的 tenant_id 进行交叉验证。如果两者不匹配,立即拒绝请求并记录安全事件。

4.4 综合防御架构

将上述挑战的解决方案整合为统一的鉴权链路,形成以下多租户 JWT 安全架构:

多租户 JWT 安全鉴权架构 客户端 API 网关 认证服务 网关校验层 1. 提取子域名 → tenant_id 2. 验证 JWT 签名 + 过期 业务服务 数据访问层(租户过滤) 关键校验点 子域名 vs JWT tenant_id 一致性
架构要点总结:
  1. JWT 签发阶段:强制包含 tenant_id、role、permissions 声明。
  2. API 网关阶段:从子域名/路径解析租户,与 JWT 中的 tenant_id 交叉校验。
  3. 业务服务阶段:从 JWT 提取租户上下文,注入到所有数据访问操作。
  4. 数据访问层:使用全局租户过滤器,确保 SQL 查询自动附加 tenant_id 条件。

通过以上分层防御,SaaS 系统可以有效消除用户身份混淆、跨租户数据越权和路由错乱三大风险,构建起坚实的多租户鉴权防线。