treg会话Cookie安全设计:treg_session如何防止泄露到上游
【免费下载链接】tregOpenRouter for agent tools. Join community here: https://discord.gg/6mQYYfFMAn项目地址: https://gitcode.com/GitHub_Trending/treg/treg
treg 是一个面向 Agent 工具的 API 网关("OpenRouter for agent tools"),它的核心工作是把用户的请求中继(relay)到上游数据服务商。这种"替浏览器转发请求"的模式天然带有一个安全隐患:用户在仪表盘上登录 treg 时浏览器持有treg_session会话 Cookie,如果它被原样转发给第三方上游,等于把"钥匙"交给了陌生人。这篇文章讲清楚 treg 是如何用签名会话 + 双向 Cookie 清洗 + 受众隔离三层设计,把treg_session牢牢锁在网关内部的。
一、为什么中继网关特别怕会话 Cookie 泄露
treg 的中继层承诺"忠实验证式转发":方法、路径、查询参数、请求头、Cookie、请求体字节流,几乎原样透传。这对 API 调试和凭证注入是必需的,但也意味着——
浏览器里发出的每一个 Cookie,如果不加区分,都会被复制进发往上游的请求头中。
而 treg 的仪表盘前端在 "Try it" 试调用场景下使用了credentials: 'include'(让浏览器自动带上同源 Cookie),会话 Cookie 就必然出现在请求里。上游服务商是第三方,一旦拿到treg_session,理论上可以反向调用 treg 的会话接口,冒充该用户操作其团队、密钥和账单。
所以整个安全设计围绕一个目标:让treg_session永远只属于 treg 这一个域,绝不跨出网关。
二、签发环节:一个"上不了台面"的签名 Cookie
treg_session的定义和签发逻辑位于 session.py,它不是服务端数据库里存的一串随机值,而是一枚无状态 HMAC-SHA256 签名令牌,内嵌三类声明(claims):
| 声明 | 含义 | 安全作用 |
|---|---|---|
uid | 用户 ID | 标识会话归属 |
exp | 过期时间戳 | 固定 7 天有效期(TTL_SECONDS) |
aud | 受众 =session | ⭐ 与 API 密钥(identity)硬性隔离 |
设置 Cookie 时(见 auth.py)同时叠加了浏览器侧的标准防御:
HttpOnly:JavaScript 读不到,XSS 拿不走;SameSite=Lax:跨站请求默认不带,抗 CSRF;Secure:HTTPS 环境下仅加密传输;max_age= 7 天:自动过期。
还有一个细节值得注意:如果没有配置签名密钥,treg不会退化成代码里写死的默认密钥(那会让任何读源码的人伪造任意用户的 Cookie,包括超级管理员),而是用每进程随机的临时密钥——代价是重启后登录失效,但这是"宁可响亮地失败,也不能静默地裸奔"的选择(见 session.py 的注释)。
三、核心防线:中继层的双向 Cookie 清洗
真正的主角在中继函数relay()里,实现在 relay.py。它做了一次双向清洗:
1️⃣ 出方向:treg_session到不了上游
def _scrub_treg_cookies(headers): # 从 Cookie 头中摘掉 treg 自己的 Cookie,其余调用方 Cookie 保留 # 摘空了就整个删掉 Cookie 头转发前,relay()会解析Cookie头,精确剔除treg_session和treg_oauth_state(见 _TREG_COOKIES),而用户自己的其他 Cookie 原样保留——不会误伤。
浏览器发出: Cookie: treg_session=abc123; ordinary=keep-me 转发上游: Cookie: ordinary=keep-me ✅ treg_session 被扣下2️⃣ 入方向:上游也不能反向"种"你的会话 Cookie
更隐蔽的攻击是会话固定:上游返回一个Set-Cookie: treg_session=evil,由于响应经过 treg 同域代理,浏览器会把这个伪造值种到 treg 域名下,覆盖真实会话。
relay()在回传响应头时,凡是针对 treg 自家 Cookie 名的Set-Cookie一律丢弃(见 _is_treg_setcookie),同时给每个中继响应追加X-Content-Type-Options: nosniff和Content-Security-Policy: sandbox,防止上游 HTML 在 treg 源下执行脚本(反射型 XSS 的最后一道闸)。
四、受众隔离:会话令牌变不成 API 密钥
单靠清洗只挡住了"传输路径"。treg 还在令牌本身做了身份互斥:
- 浏览器会话签发的令牌
aud = "session"; - API 密钥 / Bearer 令牌
aud = "identity"; - read_session_claims() 拒绝一切非
session受众的令牌,read_identity_claims() 则无条件拒绝session受众令牌。
即使某条路径的清洗出现漏洞,treg_session被复制走了也没用——它无法通过X-Treg-Token通道重放,反之 API 密钥也不能冒充浏览器登录。两套身份,互不串门。
五、回归测试:F3 用例双向验证
这套设计被固化在调用矩阵测试 F3 中(test_matrix.py,说明见 CASES.md):
- 请求携带
treg_session=caller-secret; ordinary=keep-me→ 断言上游收不到treg_session,自定义头与ordinary保留; - 上游模拟返回
Set-Cookie: treg_session=evil→ 断言该头不出现在回传响应中。
双向各一条断言,防止任何一侧的清洗回归。
六、参考文件清单
| 模块 | 路径 | 说明 |
|---|---|---|
| 会话令牌签发/校验 | src/treg/domain/identity/session.py | HMAC 令牌、受众分离 |
| 中继 Cookie 清洗 | src/treg/infra/upstream/relay.py | 双向 scrub 逻辑 |
| Cookie 属性设置 | src/treg/routers/auth.py | HttpOnly / Lax / Secure |
| 代理模型文档 | docs/context/architecture/proxy-model.md | 转发忠实性契约 |
| 认证与密钥架构 | docs/context/architecture/auth-secrets.md | 会话生命周期 |
| 双向清洗测试 | tests/callmatrix/test_matrix.py | F3 用例 |
七、一句话总结
🔒 treg 的安全思路不是"把 Cookie 藏起来",而是假设它一定会出现在请求里,然后在三个层面同时设防:
- 令牌自身:签名 + 7 天过期 +
aud受众隔离,泄露也变不成别的身份; - 出方向:
_scrub_treg_cookies在中继前精确摘除,只扣自家的、不误伤用户的; - 入方向:拒绝上游反向种植同名 Cookie,杜绝会话固定。
对任何做代理/网关类产品的人来说,这套"清洗 + 隔离 + 固定化测试"的组合拳,都是一份可以直接抄作业的防泄露清单。
【免费下载链接】tregOpenRouter for agent tools. Join community here: https://discord.gg/6mQYYfFMAn项目地址: https://gitcode.com/GitHub_Trending/treg/treg
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考