You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
DSH Desktop:认证 Cookie 无界累积导致启动失败(HTTP 431 / Web authentication failed)
#11391
English TL;DR — DSH Desktop launches its local Host on a random port each start
(--port 0), while the auth cookie name is derived from sha256(authority), which includes
that port. Every launch therefore creates a new cookie instead of overwriting the old one.
Cookies live 30 days by default and are never pruned. After ~70 launches the Cookie request
header exceeds Node's default 16 KB maxHeaderSize, the Host replies 431, and startup throws desktop welcome: Web authentication failed. Measured: 62 cookies / 13.79 KB after 9 days on a
single machine — roughly 7 launches from failure.
constready=awaithost.start();hostCookie=awaitauthenticateWebHost(ready.url);// ① Node fetch,不读 Cookie jar
...
welcomeBackend=awaitconnectDesktopWelcome(ready.url,(input,init)=>net.fetch(input,init), ...);// ② Electron net.fetch,读 jar
reacted with thumbs up emoji reacted with thumbs down emoji reacted with laugh emoji reacted with hooray emoji reacted with confused emoji reacted with heart emoji reacted with rocket emoji reacted with eyes emoji
Uh oh!
There was an error while loading. Please reload this page.
DSH Desktop:认证 Cookie 无界累积导致启动失败(HTTP 431)
0. 摘要
DSH Desktop 每次启动都以随机端口拉起本地 Host(
--port 0),而浏览器认证 Cookie 的名称由authority(含端口)的 SHA-256 派生。端口每次变化 ⇒ Cookie 名每次变化 ⇒ Chromium 视为不同 Cookie 新增而非覆盖。该 Cookie 有效期默认 30 天,且代码中没有任何清理逻辑。
累积约 70 条后,Cookie 请求头达到 Node
http的默认maxHeaderSize(16384 字节),本地 Host 在解析请求头阶段直接返回
431 Request Header Fields Too Large,启动流程随即抛出desktop welcome: Web authentication failed,应用无法进入。实测:一台日常使用的机器在 9 天内累积 62 条(13.79 KB,已达阈值 86%),再启动约 7 次即触发。
English TL;DR — DSH Desktop launches its local Host on a random port each start
(
--port 0), while the auth cookie name is derived fromsha256(authority), which includesthat port. Every launch therefore creates a new cookie instead of overwriting the old one.
Cookies live 30 days by default and are never pruned. After ~70 launches the
Cookierequestheader exceeds Node's default 16 KB
maxHeaderSize, the Host replies431, and startup throwsdesktop welcome: Web authentication failed. Measured: 62 cookies / 13.79 KB after 9 days on asingle machine — roughly 7 launches from failure.
1. 环境
@deepseek-ai/dsh-desktop)0.2.1-rc.30.2.1-rc.3be72761ee1a6736ebcdf4f3739363b083ae53c98(dshBuildDirty: false)43.3.022.23.4(随包 runtime)%APPDATA%表示)相关文件(安装目录):
resources/app/lib/main.js、resources/app/dsh/node_modules/@deepseek-ai/dsh-desktop-host/lib/index.js、resources/app/dsh/node_modules/@deepseek-ai/dsh-client-connection/lib/index.js。2. 影响
(机制见 §4.5 / §4.6)。
/api/*调用)都要额外携带数十 KB 的
Cookie头。62 条时单请求约 14.4 KB(Cookie 头 14,116 B + 其余头约 600 B),属纯上行浪费。
dsh web --port <固定值>,使 authority 稳定),则 Cookie 同名覆盖、不会累积。Desktop 的启动路径正是每次都传
--port 0,故必然命中。3. 复现步骤
正常安装并启动 DSH Desktop,登录/使用,然后完全退出(含系统托盘)。
重复启动—退出 N 次(每次间隔不限)。
每次启动后检查
%APPDATA%\@deepseek-ai\dsh-desktop\Network\Cookies(SQLite),可见:每条
name为dsh-auth-<base64url(sha256("127.0.0.1:<port>"))>,<port>各不相同。当
SUM(length(name) + length(value) + 3)接近 16384 时,启动开始失败。无副作用验证 431 阈值(对正在运行的实例发不同大小的 Cookie 头,见附录 B):
阈值落在 16300–16400 之间,与 Node
http默认maxHeaderSize = 16384完全一致。4. 根因分析
4.1 每次启动使用随机端口
dsh-desktop-host/lib/index.js:234-240:实测该机器的 62 条认证 Cookie 对应 62 个互不相同的端口
(19387、6746、7028、7859、9114、48533、40324…),无一条重复。
4.2 Cookie 名绑定 authority(含端口)
dsh-client-connection/lib/index.js:335-336:其中
COOKIE_PREFIX = "dsh-auth-"(:277),authority由requestAudience()生成,在明文 HTTP 下等于
Host头,即127.0.0.1:<port>(:307-316)。⇒ 端口变,名称变,旧的不会被覆盖。
4.3 Cookie 有 30 天有效期
dsh-client-connection/lib/index.js:1017:签发时写入
Max-Age与Expires(:347-349)。实测expires = created + 30d,与代码一致。4.4 没有任何清理逻辑
dsh-client-connection中对该 Cookie 只有set(:473-478、:532-537),无任何
remove/ 删除路径。main.js中仅有的clearStorageData调用位于安装器接管(:6961)与策略登录窗口销毁(
:10261),均不涉及主defaultSession。4.5 失败点与报错措辞
启动链路(
main.js:12208-12214):authenticateWebHost(main.js:8248-8255)使用 Node 全局fetch,不携带 ChromiumCookie jar,因此总能成功拿到 303 + Set-Cookie,不会先报
Desktop Host authentication failed(:8252)。connectDesktopWelcome(main.js:8901-8905)使用 Electronnet.fetch且credentials: "include",会把 jar 中全部 62 条 Cookie 一并发出 ⇒ 命中 431 ⇒authenticated.ok === false⇒ 抛出desktop welcome: Web authentication failed。这解释了为什么用户看到的正是
Web authentication failed,而非更早的认证错误。4.6 为什么"重启没救"
431 发生在 Node 解析 HTTP 请求头阶段,请求尚未进入应用逻辑,
authorizeIndex()不会被调用,因此不会签发新 Cookie(条数冻结,不继续恶化);但同样不会删除旧 Cookie。
用户重启只会重复失败,直到最早一批 Cookie 越过 30 天有效期
(Chromium 的持久化 Cookie 存储会在初始化时清理过期项——按实现推断,未在此次实测中观测到,
因现存数据均未过期)。
5. 实测证据
来源:单台日常使用机器,2026-10-20 → 2026-10-29(9 天)。
cookies表总条数host_key127.0.0.1(无其它域)dsh-auth-*条数name总长dsh-auth-9 + base64url(sha256) 43)value总长v2.<base64(JSON)>.<hmac>)Cookie头maxHeaderSize52 + 1 + 173 + 2 ≈ 227 B16384 / 227 ≈ 72Cookie 结构样例(值已脱敏,仅展示形状):
所有条目
is_persistent = 1、is_httponly = 1。6. 修复建议
方案 A(最小改动,推荐落地):启动时清理陈旧 Cookie
在
main.js的 host 启动完成后、connectDesktopWelcome之前,清掉全部历史dsh-auth-*:注意需在
authenticateWebHost()之后执行,以免误删本次刚签发的条目;或按expiresDate保留最新一条。此方案不改变现有安全模型。方案 B(更优雅,根治):名称按 hostname 派生,authority 仍由 payload 校验
cookieName()的输入改为 hostname(不含端口),但保留authenticate()中payload.authority !== authority的校验(:566):payload.authority不匹配而拒绝,安全性不变;代价:多实例(不同端口)并行时,后启动者会使前者失效——但 Desktop 有单实例锁,实际影响可忽略。
方案 C:固定端口
让 Desktop 使用固定或稳定端口(如优先 26411,占用时递增并持久化)。
可从根上消除 authority 漂移,但依赖端口分配的稳定性,不如 A/B 彻底。
附带建议
WebServer显式设置maxHeaderSize,或至少在超限时返回可诊断的错误页,而非仅有空响应体的 431。
431映射为用户可理解的提示(当前用户只能看到认证失败,无法联想到 Cookie 累积)。dsh-auth-*条数 > N 或请求头 > 阈值,先清理再启动并记录日志。7. 用户侧临时缓解
%APPDATA%\@deepseek-ai\dsh-desktop\Network\Cookies与Cookies-journal。不会丢数据:会话与配置位于
%USERPROFILE%\.dsh(sessions/等),不在该目录;且该 Cookie 库内全部条目均为
127.0.0.1,删除仅丢失本地登录态,重开时自动重新签发一条。配套脚本见附录 A(先检查进程、备份、再删,支持只读报告模式)。
8. 补充
只读探测未修改目标状态。
附录 A:诊断与清理脚本
附录 B:431 阈值探测
实测输出:
报告基于安装包内代码静态阅读 + 运行实例只读探测,未修改目标状态;Cookie 值未在本文中披露。
All reactions