背景

在 Web 应用中,如果要根据用户身份限制图片的加载,最直接的方式是使用 cookies 验证身份。然而,在某些场景下,这种方式会带来问题。

当应用需要同时支持浏览器和桌面客户端(如 Tauri)时,图片权限的控制变得更加复杂。

JWT 方案的问题

最初的设计采用了 JWT(JSON Web Token)来实现图片权限控制。方案如下:

  1. 用户通过身份验证后获得 JWT Token
  2. 将 Token 添加到图片 URL 的查询参数中
  3. 服务器验证 Token 的签名和有效期

这个方案虽然可行,但存在以下问题:

Token 暴露风险:将 Token 放在 URL 中会通过浏览器历史记录、服务器日志、Referer 头等渠道泄露。

一次性 Token 的开销:为了安全性,需要使用一次性 Token 或极短过期时间的 Token,这意味着:

  • 服务器需要维护 Token 的使用状态(防止重放攻击)
  • 或者依赖极短的过期时间(增加客户端复杂性)
  • 每次访问图片都需要重新签发 Token

性能开销:频繁的 Token 签发增加了服务器的计算负担。

为什么不用 Cookies

既然 JWT 方案这么麻烦,为什么不直接用 cookies 呢?答案与 Tauri 桌面应用的特殊性有关。

跨域请求的限制

Tauri 使用 asset://localhost 协议来加载页面,但后端 API 可能运行在不同的地址(如 http://localhost:8080)。这导致了跨协议/跨域请求的问题:

  • 跨协议请求:从 asset:// 发出的请求无法自动携带 cookies 到 http://https:// 地址
  • HTTP-only Cookie 失效:HTTP-only Cookie 绑定在特定域名,而 asset:// 与 API 地址不在同一域,无法正确传递
  • CORS 限制:浏览器的安全策略限制了跨域请求时的 Cookie 传递

CSP 配置

Tauri 的 Content Security Policy(CSP)配置需要明确声明允许加载的资源协议:

"csp": "default-src blob: data: filesystem: http: https: tauri: asset: asset://localhost https://asset.localhost http://asset.localhost 'self'"

跨平台差异

在 Linux 平台上,asset:// 协议的实现与 Windows 不同,可能导致相同的 CSP 设置无法正常工作。这增加了开发和维护的复杂性。

HMAC-sig 方案

为了解决上述问题,我们采用了 HMAC 签名方案,使用 BLAKE3 keyed hash 算法。

签名算法

签名使用一个密钥(从 KEY 环境变量派生)对消息进行哈希:

  • Messageuser_id + file_id
  • Key:从环境变量 KEY 派生
  • Encoding:Base64-URL(无填充)

URL 格式

/img/{user_id}/{file_id}?sig={signature}

验证流程

服务器收到请求后,使用相同的密钥对 user_id + file_id 进行 BLAKE3 keyed hash 哈希,然后与 URL 中的 sig 参数进行比对。这种方式完全无状态,不需要服务器存储任何信息。

方案对比

特性JWTCookiesHMAC-sig
Token 暴露URL 中可见HTTPOnly CookieURL 中可见但不可伪造
服务器状态可选
过期时间需要自动可选
Tauri 兼容良好良好
验证性能中等

安全性分析

为什么 HMAC-sig 比 JWT 更安全

JWT 的问题在于:即使 Token 只能使用一次,攻击者仍然可以在有效期内窃取并使用它。而 HMAC-sig 方案中:

  • 签名基于 user_idfile_id,任何参数的改变都会导致签名失效
  • 签名无法被伪造,因为没有密钥无法生成有效的签名
  • 不需要设置过期时间,因为签名本身已经绑定了用户和文件

重放攻击防护

虽然 HMAC-sig 是无状态的,但我们可以通过以下方式防止重放攻击:

  1. 文件级绑定:签名与具体的 file_id 绑定,窃取的签名只能访问特定文件
  2. 可选时间戳:在签名消息中加入时间戳,可以实现自动过期

总结

从 JWT 迁移到 HMAC-sig 方案的主要原因:

  1. 简化架构:无状态验证,不需要服务器存储 Token 状态
  2. 提升性能:避免了频繁的 Token 签发
  3. 增强安全性:签名与资源绑定,无法被重用于其他资源
  4. 兼容 Tauri:解决了 Cookies 的各种问题

HMAC-sig 方案在保持安全性的同时,提供了更好的性能和兼容性,特别适合需要同时支持浏览器和桌面客户端的应用场景。