Product

【Product】Dify沙箱介绍

摘要:本文深入解析 DifySandbox 的设计初衷与实现原理。针对工作流中代码执行的安全需求,文章对比了 WebAssembly、Docker 等现有方案,并详细阐述了 DifySandbox 采用多层隔离与系统级防护的架构设计,旨在保障业务灵活性的同时彻底阻断恶意代码风险。

文章分类:Product

Introduction to DifySandbox

大家好,我是 Yeuoly。我是 Dify.AI 的后端工程师,也是 DifySandbox 的开发者。

对于社区用户来说,DifySandbox 服务应该并不陌生。它是运行在 Dify 后台的一个 Docker 服务。我们收到了很多反馈,但大多数用户对其内部机制并不了解。本文将逐步带你揭开 DifySandbox 的内部工作原理。

DifySandbox for Code Execution and Security

在 Dify 中,Workflow 是一项核心功能。用户可以通过拖拽编排逻辑流,实现相对复杂的业务逻辑。在编排过程中,数据处理至关重要。我们经常会遇到以下场景:

  • 经常需要处理 LLM 生成的 JSON 文本,以提取结构化数据。同时,还需处理 HTTP 请求返回的 XML 或 JSON 文本,将其解析为结构化数据并提取信息。
  • 有时需要合并两个知识库检索节点的输出内容,或者将 Google 搜索节点与知识库检索节点的结果进行融合。
  • 更多情况下,具备基础编程能力的用户希望利用 Jinja2 和 Liquid 等模板语法,实现更灵活的 Prompt 编排。

尽管场景多样,但核心都涉及数据处理,需要一个统一的解决方案。最直观的思路自然是编写代码。相比高度定制化的数据处理节点,代码方案更具通用性。毕竟,没人希望某个特定节点专门负责解析 JSON。因此,为什么不提供一个代码编辑框,让用户自行编写数据处理逻辑呢?我们甚至可以用模板语法包裹 LLM 节点,从而实现更灵活的 Prompt 编排。上述问题都能通过这种方式轻松解决。

在 Dify 中执行用户代码,必须直面安全问题。面对恶意用户时,代码执行功能极易变成安全漏洞。

通常情况下,绝大多数请求并无恶意。无论是运行代码还是访问工作区信息,所有请求都必须经过 Dify 路由。涉及代码执行时,系统会在服务器上启动一个新的 Python 或 Node.js 进程,并将用户代码发送至该进程运行。

一旦进程落入恶意用户手中,问题就来了。由于进程直接运行在服务器上,它可以访问文件系统与数据库。恶意代码可能读取服务器上的任意文件,甚至渗透并篡改整个 Dify 数据库。

为此,我们开发了 DifySandbox。这是一款代码沙箱方案,能在有效拦截恶意代码的同时,保障正常业务运行。下文将详细介绍 DifySandbox 的设计与实现。

Design and Implementation of DifySandbox

在设计之初,我们仔细考量了沙箱的几项核心安全要求。

全球开发者的编码偏好各不相同。在 LLM 领域,Python 和 Node.js 无疑是首选。但我们不希望限制用户只能使用其中一种,因此两者都要支持。这意味着沙箱的技术方案不能绑定特定语言,而必须提供一套系统级的通用方案。

沙箱能被绕过吗?绝对的安全并不存在,完全防止绕过是不可能的。因此,我们不能仅依赖沙箱本身来保障安全。正确的思路是:即使沙箱存在漏洞,也要确保攻击者无法触及核心资源。

网络隔离是重中之重。生产环境中,许多沙箱绕过事件都源于网络漏洞。例如 VMWare 的诸多漏洞就是通过网络被利用的。即便有代码级和系统级防护,恶意代码仍可能嗅探内网并非法访问内部资源。因此,沙箱必须严格隔离网络。

同时,我们调研了全球现有的沙箱方案。以下是它们的优势与劣势对比:

  • WebAssembly:一种流行的方案,将 Python 或 Node.js 解释器编译为 WebAssembly 运行时。这使得 Python 代码能在 Node.js 或浏览器中运行。正确使用下,WebAssembly 能提供强大的系统级安全。但存在局限。例如,安装第三方依赖易引发架构不兼容问题。此外,Python 和 Node.js 需分别处理,通用性较差。
  • Docker:部分服务商为每次代码执行请求创建新容器。该方案灵活性高,但执行速度极慢,单次运行需 1 秒以上。若工作流包含 10 个代码节点,耗时将累积至 10 秒以上,性能损耗严重。此外,需管理容器并将 docker daemonsock 挂载至 Dify 的 api 容器,带来显著安全风险。若使用 docker-in-docker 则更慢,均非理想之选。
  • 语言专属沙箱包:如 Node.js 的 vm2 和 Python 的 PyPy。此类包仅支持特定语言,且自带局限。它们并非通用方案,依赖管理需遵循语言特定规范。例如 PyPy 对 Python 版本限制严格,依赖管理困难。虽有 Pyodide 等库,但 Node.js 与 Python 方案无法互通,维护成本极高。
  • 内核扩展:部分成熟方案利用内核扩展限制进程行为。但配置文档复杂,启动流程繁琐。典型代表如 Sandboxiejudge0。尽管属于内核级方案,但 Judge0 曾因配置问题爆发过严重 CVE。此类方案需特权容器支持内核扩展,一旦限制被绕过,Docker 的隔离机制也将失效。

现有方案均无法完全满足我们的业务需求。它们在执行速度、语言特性兼容性或潜在安全风险等方面各有短板。因此,我们决定自研一套方案,并聚焦以下核心特性:

  • 多层隔离:我们基于 Docker 容器实现多层隔离,但采用了独特架构。不同于为每个任务创建新容器,我们仅在启动阶段运行单个沙箱容器。该容器内置 http 服务,用于接收系统下发的代码执行请求。需注意,此架构目前主要面向 Linux,Windows 和 Mac 平台需借助 Docker Desktop 或 Orbstack 等工具。
  • 系统级隔离:在 Linux 环境下,Docker 是常见的系统沙箱方案。但我们已有一层 Docker 隔离,需进一步利用其底层技术:Seccomp(Secure Computing Mode)。Seccomp 充当一个过滤器,用于
文章来源: https://dify.ai/blog/dify-ai-blog-introducing-difysandbox
← 返回文章列表