忘机山人
在第三章结束时,我们已经解决了一个关键问题:
懒猫的 Authorization Code 不会被随意偷用。
通过 PKCE,ENV 查看器终于可以确信一件事:
来
/sys/oauth/token换 token 的请求,确实来自当初那个发起授权的 Client 实例。
于是,流程自然向前推进。
ENV 查看器拿到了懒猫 SSO 返回的一组 token,其中最重要的一个是:
id_token
在实际懒猫 SSO 返回里,它看起来像这样:
eyJhbGciOiJSUzI1NiIsImtpZCI6IjE2In0.
eyJpc3MiOiJodHRwczovL2FsaWNlLmhlaXl1LnNwYWNlL3N5cy9vYXV0aCIsInN1YiI6ImNpWUtIaGVpeXUiLCJhdWQiOiJ4dS5kZXBsb3kuZW52IiwiZXhwIjoxNzE2NDE5NzM0LCJpYXQiOjE3MTYzMzMzMzQsIm5vbmNlIjoieXl5IiwiZW1haWwiOiJhbGljZUBleGFtcGxlLmNvbSIsImVtYWlsX3ZlcmlmaWVkIjp0cnVlLCJuYW1lIjoiQWxpY2UiLCJwcmVmZXJyZWRfdXNlcm5hbWUiOiJhbGljZSIsImdyb3VwcyI6WyJhZG1pbiJdLCJhdF9oYXNoIjoidGFxdl8uLiJ9.
XK9k3...
很多懒猫 App 在这一刻会做一件非常“自然”的事:
把 token 贴到 jwt.io → 复制里面的
但在我们继续之前,必须先解决一个极其关键、却经常被误解的问题:
这个 token 到底是不是“加密的”?
当人们第一次看到懒猫 SSO 吐出来的 JWT 时,几乎都会产生同一个直觉:
“这看起来像一段乱码,
应该是加密过的吧?”
这种直觉非常危险。
因为它会直接导致一个错误的安全假设:
“既然 token 是加密的,那把它放 URL、放 localStorage、打到 container log 里,都没事。”
但事实是:
JWT(更准确地说,懒猫 SSO 用的 JWS)默认不是加密的。
一个标准的 JWT 由三部分组成:
header.payload.signature
这三部分并不是“加密块”,而是:
alg=RS256, kid=<某个 ID>)iss sub aud exp email groups ……前两部分只是 Base64URL 编码。
这意味着一件非常重要的事情:
任何人,只要拿到 token,都可以解码 header 和 payload。
不需要密钥。
不需要破解。
不需要权限。
你随手在 jwt.io 上把懒猫 ID Token 粘进去,能看到:
{
"iss": "https://alice.heiyu.space/sys/oauth",
"sub": "ciYKHheiyu",
"aud": "xu.deploy.env",
"exp": 1716419734,
"iat": 1716333334,
"nonce": "yyy",
"email": "alice@example.com",
"email_verified": true,
"name": "Alice",
"preferred_username": "alice",
"groups": ["admin"],
"at_hash": "taqv_.."
}
全是明文。
这是一个必须正面回答的问题。
如果 JWT 是明文的,那岂不是意味着:
groups(比如 admin)是公开的?preferred_username 是公开的?答案是:
是的,这些信息对“任何拿到 token 的人”都是可读的。
但这里隐藏着一个非常重要的区分:
可读(readable) ≠ 可伪造(forgeable)
JWT 的设计,从一开始就没有试图解决“保密性”问题。
它要解决的是两个完全不同的问题:
groups: ["admin"] 有没有被塞进去)这两个目标,不需要加密。
现在我们可以重新定义 JWT 的本质了:
ID Token 不是一段加密数据,
而是一组“被懒猫 SSO 签名的声明”。
每一个字段,都是一个声明:
sub:这个用户在懒猫 SSO 里的稳定 IDiss:https://alice.heiyu.space/sys/oauth,说明是这台盒子签的aud:xu.deploy.env,说明是给 ENV 查看器的exp:声明什么时候失效email / preferred_username / groups:懒猫账号的基本信息懒猫 SSO 的安全模型是:“你可以看,但你不能改;
你可以拿到,但你不能伪造。”
我们来对比两个世界:
groups: ["admin"] 是不是原本就有在一个多用户、多应用共享一个懒猫 SSO 的系统里,这是灾难。
email、preferred_username、groups这恰恰符合家庭服务器这种 SSO 场景的需求。
token 终究会出现在不完全可信的环境中。
例如:
在这样的前提下,试图“依赖 token 的保密性”是脆弱的。
于是,懒猫遵循 OIDC 的设计,做了一个非常明确的选择:
不把安全性建立在“别人看不到”上,
而建立在“别人改不了、造不出”上。
到这里,可能会有人产生另一个误解:
“既然 JWT 不靠加密,那懒猫给我装证书让
heiyu.space走 HTTPS 还有什么用?”
这是对职责边界的混淆。
groups: ["admin"] 被塞进来)它们解决的是不同层面的问题。
HTTPS 保护的是“传输”,
JWT 保护的是“声明”。
懒猫两者都上,缺一不可。
在懒猫 SSO 中,ID Token 是:
alg=RS256这不是因为“懒猫 SSO 做不到 JWE”,而是因为:
如果你在这里误以为:
“只要 token 是加密的,就可以少校验
iss/aud/nonce”
那么你将直接跳过懒猫 SSO 最重要的安全设计。
在继续深入之前,我们必须明确 JWT 的边界。
xu.deploy.env(要靠 aud)exp / nonce)iss)groups: ["admin"] 意味着什么业务权限(这是你的业务层)这些问题,全部在签名之外。
到目前为止,我们只解决了一件事:
这个 token 是真的。
但“真的”,并不等于“适用”。
一个合法签名的懒猫 ID Token,仍然可能是:
aud 不是你)所以,从下一章开始,我们要进入 JWT 的下一层意义:
验证的不再是 token 本身,
而是 token 里的每一个声明。

我们可以用几句话,结束这一章:
RS256)懒猫 ID Token 的设计目标不是“不让你看到”,
而是“让你无法伪造”。
评论
0暂无评论