客户端的 Token 要防偷,服务端的 [[HTTPS]] 不能省。
写客户端和服务端协作的软件时,访问令牌(Token)安全是最基本的问题。拿我们正在做的短链引擎项目来说,客户端请求接口时必须带上 Token。
那么,这个 Token 到底有没有必要加密?
答案是:必须加密,但两端的防御重点完全不一样。这是数字领地内构建系统时必须厘清的底层逻辑。
✦ 客户端:防患未然的本地固守 (Client Defense)
客户端的 Token 通常保存在本地。我们的项目里,客户端会把配置和 Token 写入本地的 [[LiteDB]]。
✦ 拒绝裸奔存储 (Avoid Plaintext Storage)
如果把 Token 直接存在纯文本文件或者没加密的本地数据库里,任何能拿到你电脑文件的人,或者潜伏在电脑里的木马,都可以轻松把 Token 拷走。拿到 Token,黑客就能直接假冒你给服务端发请求。
✦ 调用系统级保险箱 (OS Native Protection)
不要在客户端的代码里写死一个密钥去用 AES 算加密,这种做法防不住反编译。正确的做法是调用操作系统自带的安全机制:
- Windows 平台:使用 .NET 的
ProtectedData.Protect。它利用当前登录的 Windows 用户账号信息当密钥,即 [[DPAPI]]。别人就算拷走了你的整个本地数据库,换台电脑或者换个系统用户登录,也根本解不开。 - 跨平台架构:设计一个
ITokenProtector接口。在 Windows 上走 [[DPAPI]],在 macOS 上对接 [[Keychain]],在 Linux 上接入 Secret Service。将复杂性封装在底层。
✦ 服务端:管道护航与状态隔离 (Server Security)
服务端不需要(也无法直接)用 [[DPAPI]] 这种方式给 Token 加密。它的重点在别处。
✦ 内存状态与宿主环境 (Memory and Host)
服务端的 Token 是通过环境变量直接载入内存进行比对的。如果黑客已经攻破服务器,能直接翻看内存或环境变量,那整台服务器其实已经失陷了,这时候在代码里折腾内存加密没有实际作用。此外,容器部署多用 Linux 系统,这里根本没有 Windows 的 [[DPAPI]]。
✦ 传输隧道的绝对控制 (Transmission Tunneling)
客户端解密出 Token 后,会把它放在 HTTP 请求头里(如 Authorization: Bearer <Token>)发出去。
如果服务端只用 HTTP 裸奔,Token 在经过路由器和运营商网络时就会被中间人直接截获。所以,服务端最核心的加密,是配置好 [[HTTPS]],给传输通道建立绝对加密。
✦ 星轨拓展:多用户形态的演进 (Multi-user Architecture)
现在我们做的是单管理员配置,Token 直接写在环境变量里。如果以后项目升级,数据库里要存很多人的 Token(比如 API Key),防线需要再次升级。
核心原则是:存哈希值(Hash),绝对不存密文或明文。
- 不要用对称加密:如果用 AES 把 Token 加密存在数据库里,一旦加密密钥泄露,所有用户的 Token 都会在瞬间崩盘。
- 单向哈希比对:
- 用户生成 Token 时,只给用户看一次明文。
- 服务端把明文 Token 做一次 [[SHA-256]] 哈希,把哈希后的这串乱码存入数据库。
- 用户请求时带上明文,服务端把请求里的明文再做一次 [[SHA-256]],用结果去和数据库里的数据比对。
这样即使数据库被黑客打包下载了,黑客也无法通过哈希值逆推出原始的明文 Token。
✦ 总结 (Summary)
做小工具也要有清晰的安全边界。在客户端借助系统底层锁好数据,在传输时用 [[HTTPS]] 封死通道,两边配合才能把安全隐患降到最低。
| 维度 | 客户端 | 服务端 |
|---|---|---|
| 主要威胁 | 本地文件或数据库被木马偷走 | 网络监听、服务器数据库泄露 |
| 防范重点 | 本地静态存储安全 | 网络传输与运行期安全 |
| 实现手段 | 操作系统原生安全机制 ([[DPAPI]] / [[Keychain]]) | 必须走 [[HTTPS]] 传输 + 环境变量 / 数据库存哈希值 |