Skip to Content
Ⅳ · 扩展与边界15 · 文件、数据与安全边界

15 · 文件、数据与安全边界

ComfyUI 同时处理模型文件、用户上传、生成输出、工作流设置、执行历史和可选资产索引。它们并不都存在同一个数据库,也不共享同一种安全策略。

最重要的纠正: Prompt 队列和默认执行 history 是进程内存;user/comfyui.db 不会自动把未完成 Prompt 变成可恢复任务。

数据版图

folder_paths:逻辑目录注册表

核心目录不是到处硬编码,而是通过 getter/setter 与 folder_names_and_paths 注册:

  • checkpoints、loras、vae、embeddings 等模型类别;
  • input、output、temp、user;
  • custom_nodes 与 web extensions;
  • legacy 名称到当前类别的映射。

节点和路由通过逻辑类别查路径。extra model paths 可以为同一类别添加多个根,并携带支持扩展名集合。

annotated filepath

字符串可带尾部 annotation:

foo.png [input] bar.webp [output] preview.png [temp]

annotated_filepath() 分离文件名与 base directory;无 annotation 时常以 input 为 fallback。

folder_paths.pyL257-273input/output/temp annotation 的解析。

Annotation 是协议内的命名空间,不是允许客户端给任意绝对目录。

路径包含检查

安全目标:用户给出的相对路径解析后仍必须位于授权根目录。

is_within_directory():

  1. 对 base 与 target 都做 realpath();
  2. 用 commonpath() 比较;
  3. 对 null byte、跨 Windows drive 等 ValueError fail closed。
folder_paths.pyL325-342同时防 `..` 与 symlink escape 的真实路径包含检查。

只检查字符串里有没有 .. 不够:根内 symlink 可以指向根外。只检查 startswith(base) 也不够:/data/output-evil 会匹配 /data/output 前缀。realpath + commonpath 才表达目录边界。

get_annotated_filepath() 越界时抛 ValueError;exists_annotated_filepath() 对越界返回 false,避免用存在性接口探测任意路径。

folder_paths.pyL343-369解析、拒绝 traversal 与安全存在性检查。

图片上传

POST /upload/image:

  1. 根据 type 选 input/output/temp 根;
  2. 规范化 subfolder;
  3. 绝对化目标并检查 commonpath;
  4. 创建子目录;
  5. 若不允许 overwrite,检测同名同内容;
  6. 同名异内容自动加 (1)、(2);
  7. 写文件;
  8. 可选登记 Assets;
  9. 返回 name/subfolder/type。
server.pyL389-462上传目录边界、同名去重、覆盖策略与资产登记。

hash compare 的作用

已有同名文件时,读取两者计算 hash;内容相同就不重复写入。它是上传体验优化,不是全局 content-addressed store:不同比例路径/文件名仍可存同内容,除非 Assets hash 层进一步去重。

Mask 上传

Mask route 还接收 original_ref,读取原图、保留 PNG metadata,并用上传图片 alpha 替换原 alpha。它再次检查 filename、subfolder 与授权目录,防止 original_ref 变成任意文件读写原语。

/view 文件读取

查看端点支持:

  • [input]/[output]/[temp] 或 type 参数;
  • subfolder;
  • webp/jpeg 缩略预览与 quality;
  • RGB 或 alpha channel 转换;
  • 可选 blake3: asset hash 解析;
  • 普通文件响应。
server.pyL507-650文件解析、预览转换与危险内容响应策略。

每条分支都必须保持相同路径边界。新增一种“按 hash 查看”不能绕过 owner / reference 授权;Assets resolver 返回经过服务层解析的绝对路径和下载名。

Stored XSS 防护

用户可以上传文件,服务又在 UI 同源下返回它。如果 HTML、JavaScript 或 SVG 被 inline 打开,脚本可能获得 ComfyUI origin 权限。

集中危险类型集合包括:

HTML / XHTML JavaScript / ECMAScript CSS SVG XML 与 +xml 家族 MHTML / RFC822
folder_paths.pyL274-324危险 MIME 规范化、XML 家族与 SVG image 特例。

危险内容默认:

  • 改为/按 octet-stream 处理;
  • Content-Disposition: attachment;
  • X-Content-Type-Options: nosniff;
  • 正确转义 filename;
  • 对依赖 Sec-Fetch-Dest 的响应设置适当 Vary / cache 语义。

SVG 作为 <img> 加载时浏览器使用受限 static mode,代码只在可信的 Sec-Fetch-Dest: image 情况下允许这一特例;缺 header 时 fail closed。

这段设计说明 MIME 安全不仅是“设置 Content-Type”,还涉及 disposition、nosniff、请求目的和缓存重放。

用户目录与多用户模式

UserManager 默认只有 default。启用 --multi-user 后,用户记录保存在 users.json,请求通过 comfy-user header 选择 profile;userdata 路径限制在该用户 public root。

app/user_manager.pyL25-100profile 选择、系统用户拒绝与用户路径包含检查。

它不是强身份认证

客户端 header 选择已存在 user ID,核心逻辑主要提供 profile 数据隔离和 UI 存储。不要把它等价于有密码、session、RBAC 的互联网多租户认证层。

需要公网服务时,应在受信反向代理/网关层实现认证、TLS、请求限制和 header 注入/清洗,并限制 ComfyUI 监听面。

System Users

内部系统用户使用保留前缀 __。HTTP 请求不能选择此前缀,创建普通用户也拒绝此前缀,防止外部 profile 假冒内部主体。

userdata

读取、列出、写入和删除 userdata 均先调用 get_request_user_filepath():

  • URL decode 后重新绝对化;
  • base root 和 user root 两级 commonpath 检查;
  • 创建目录也只能在允许父路径内;
  • glob 列表从已验证 root 开始。

任何“先验证编码前字符串,再 decode”都可能引入 traversal;这里在含 % 时先 unquote 再做最终绝对路径检查。

SQLite 与数据库启动

默认 URL:

sqlite:///<absolute ComfyUI>/user/comfyui.db

可用 --database-url 覆盖,包括内存 SQLite。启动会取得 .lock 文件、设置 SQLite pragma、执行迁移,并在特定迁移路径创建备份。

app/database/db.pyL65-190DB URL、文件锁、连接配置、备份与迁移。

文件锁用来阻止两个进程无协调地迁移/写同一 SQLite。横向多实例不应简单共享本地 DB 文件;应选择支持并发的外部数据库策略,并确认当前服务实际支持的 dialect/迁移。

Prompt history 为什么不是数据库历史

PromptQueue.task_done() 把:

prompt outputs / UI metadata execution status extra_data(敏感项已分离)

写入内存 history,最大约 10,000 项,之后淘汰旧记录。服务重启会消失。

数据库文件存在并不改变这一事实。若业务要求审计、重试或跨重启查询,应在外层消费事件/结果并持久化,或明确实现 durable queue,而不是依赖 /history。

Assets:可选的内容与引用层

只有 --enable-assets 才启动路由、数据库同步和 background scanning;--enable-asset-hashing 进一步计算 blake3,默认关闭以避免大模型扫描成本。

comfy/cli_args.pyL263-272数据库与 Assets 两个独立开关。

为什么拆 Asset 与 AssetReference

Asset 表达内容身份(hash、大小、MIME);AssetReference 表达某用户/路径/名字对内容的引用,以及 mtime、缺失状态、preview、metadata、job ID。

app/assets/database/models.pyL26-169内容与引用分离、索引、owner 与缓存状态。

同一内容可有多个路径/用户引用;文件移动或缺失不必抹掉内容身份。hash 为 null 的 seed asset 还能先快速建索引,后续 enrichment 再计算。

Asset Seeder

启动时若启用 Assets,后台 seeder 扫描 models/input/output:

  • prune_first 标记/处理消失路径;
  • 批量插入 seed assets 与 references;
  • 可暂停、恢复、取消;
  • 通过事件报告进度;
  • Prompt 执行期间可 pause,任务结束 enqueue output enrichment 后 resume;
  • hashing 开关控制重 I/O。
main.pyL371-424执行期间 pause、输出登记与恢复 seeder。

暂停扫描避免它与模型加载/输出写入争用大量磁盘和 CPU;完成后只针对 output roots 增量 enrich,比每次全盘重扫更合适。

输出资产登记

节点 UI 输出在 execution.py 里先经 enrich_output_with_assets(),再写 ui_outputs、cache 和 WebSocket。即使没有连接客户端,资产仍可登记;asset ID 也会进入 history/cache。

这体现一个重要边界:业务持久化不能依赖“消息是否成功发给某个 Socket”。发送路径是易失的,资产登记发生在结果处理路径。

安全边界清单

边界核心已有防护仍需部署方负责
浏览器 → 本地服务Host/Origin、可选 CORS、CSP不随意开放公网、网关认证
Prompt → 节点schema/类型/范围/环验证Python 插件可信度与资源配额
文件名 → 磁盘realpath/commonpath、根目录类别文件权限、磁盘 quota、恶意大文件
文件 → 浏览器MIME、attachment、nosniff、Sec-Fetch-Dest反向代理别覆盖关键 header
user header → profile已知 ID、系统前缀拒绝、目录隔离真正身份认证与 header 防伪
secret → API nodesensitive extra data 分离secret store、域名策略、日志审计
model file → Pythonsafetensors / safe load 路径模型来源、hash、依赖供应链
custom node → 进程allow/deny/manager policyOS/container 隔离与代码审计

威胁建模的几个具体场景

场景 1:../../etc/passwd

最终绝对路径不在 input/output root,commonpath 检查拒绝;存在性接口也返回 false,避免旁路探测。

realpath 展开 symlink 后 target 落在 root 外,被 is_within_directory 拒绝。只做 abspath 会漏掉这一类。

场景 3:上传恶意 SVG 再直接打开

MIME 被判危险,document navigation 获得 attachment + nosniff;仅真正 <img> destination 可走受限 inline 特例,且缓存按目的区分。

场景 4:外部请求冒充 __system

UserManager 在读取 header 时先拒绝保留前缀,并使用同类未知用户错误减少 probing。

场景 5:自定义节点读取任意文件

如果节点自己调用 open('/etc/...'),folder_paths 无法阻止,因为 Python 插件已拥有进程权限。防护必须在插件审计与 OS 沙箱层。

运维建议

  1. 默认只监听 loopback;远程访问放在认证反代后。
  2. 对上传、输出、模型盘分别设置权限、容量和备份策略。
  3. 生产环境使用 custom-node allowlist 和锁定依赖。
  4. 优先 safetensors,并记录模型内容 hash 与来源。
  5. 不把 /history 当审计数据库;外部持久化 prompt ID、参数、结果与错误。
  6. 开启 Assets 前评估模型盘大小;hashing 可能带来明显启动/扫描 I/O。
  7. 保留关键安全响应头,反向代理不要把 attachment 改回 inline。
  8. 多租户需求使用进程/container 隔离,不只依赖 comfy-user。

可迁移的设计方法

  • 内容对象与路径/用户引用分表,支持去重、移动、缺失和多 owner。
  • 文件边界检查必须在 decode、join、realpath 之后做最终判定。
  • 同源用户文件的安全响应同时考虑 MIME、disposition、nosniff 与 cache。
  • 易失执行状态与应用数据库明确分层,不制造“好像持久化”的错觉。
  • 持久化副作用放在结果处理路径,不依赖客户端连接是否存在。
Last updated on