
为什么Monty没有socket、subprocess和eval沙箱模块白名单设计解析【免费下载链接】montyA minimal, secure Python interpreter written in Rust for use by AI项目地址: https://gitcode.com/GitHub_Trending/monty3/montyMonty 是用 Rust 编写的轻量级 Python 沙箱解释器专为运行 AI 生成的代码而设计。它最反直觉的一点是标准库里根本没有socket、subprocess、threading、ctypes也没有eval和exec。这不是没做完而是一套刻意为之的沙箱模块白名单设计。本文将用通俗的方式拆解Monty 为什么砍掉这些功能以及这套白名单设计给安全编程带来的启发。一句话结论能力不存在而不是被拦截很多沙箱的思路是先允许一切再用规则拦截危险操作。Monty 完全相反——它只实现安全的操作危险的能力在字节码虚拟机里压根不存在。官方安全文档 docs/security.md 里有句很精辟的描述沙箱不能读文件、读环境变量、开 socket、派生进程。不是被拦截了——这个能力在字节码虚拟机里根本不存在。这意味着没有容器、没有 seccomp、没有 VM隔离完全由解释器本身保证。攻击面不是缩小了而是从源头消失了。模块白名单只有 16 个模块能 importMonty 内置了一个固定的标准库模块集合import白名单之外的任何模块都会直接抛出ModuleNotFoundError——没有sys.path、没有 site-packages沙箱代码无法加载任何额外模块。完整清单见 limitations/modules.md可导入的模块只有这些类别可用模块常见用途数据处理json、collections、dataclasses、base64、binascii解析/序列化计算math、functools、itertools数值与迭代文本re、unicodedata、typing正则与类型时间datetime日期时间系统os、pathlib、asyncio、sys受控的文件与并发而这份不可用清单读起来像一份安全黑名单非穷举会破坏沙箱的socket、subprocess、multiprocessing、threading、ctypes会绕过隔离的http、urllib、pickle、io尚未实现的enum、operator、random、time等白名单的实现在源码里一目了然crates/monty/src/modules/mod.rs 中的StandardLib枚举就是全部可导入模块的户口本代码按名字查枚举查不到就拒绝——没有任何动态加载的口子。为什么 socket、subprocess、threading 一个都不能要Monty 的定位非常单一执行 AI Agent 写的、没有人审核过的代码。在这个前提下每个危险模块都对应一条逃逸路径socket网络→ 数据外泄通道LLM 生成的代码可能带着提示词注入的后门。有了 socket沙箱里的数据就能直接发到外网。砍掉它网络访问只能走开发者显式提供的主机函数每次调用都可控、可审计。subprocess进程→ 任意命令执行能subprocess.run(rm -rf ...)的沙箱和没有沙箱没区别。Monty 甚至让解释器完全不做文件系统 I/O——所有文件操作都挂起并上报由宿主组件决定怎么做文件系统代码独立放在 crates/monty-fs/部分构建里 worker 根本不链接它。threading/multiprocessing并发→ 击穿崩溃隔离Monty 靠每次会话跑在独立 worker 子进程来做崩溃隔离。沙箱代码如果自己能开线程就能在 worker 内部制造宿主无法预料的并发状态隔离边界立刻失效。ctypesFFI→ 直通 C一旦能调用任意 C 函数前面所有语言级隔离全部作废。所以 Monty 明确无 FFI、无 C 依赖沙箱里的任何东西都调不到原生代码。 更关键的细节是这些危险模块不仅是不可导入连类型标注文件里都没有。Monty 内置了一个精简版 typeshed见 crates/monty-typeshed/里面只包含白名单模块的.pyi。所以使用socket的代码会在类型检查阶段就被拒绝根本走不到运行时报错——问题发现得越早代价越小。为什么连 eval 和 exec 也不存在eval、exec、compile、globals、locals、__import__在 Monty 里全部不存在引用时抛NameError。原因它们是沙箱内的沙箱。一个能eval的动态执行器等于允许沙箱代码在运行时二次编译任意源码、动态构造模块名——任何静态白名单都可能被这种动态性绕开。白名单设计的生命线是可枚举我能列出代码可能用到的全部入口才能谈得上默认拒绝。eval一旦出现入口集合就不再可枚举了。同理Monty 也拒绝第三方面包没有sys.path就没有安装/加载路径from m import *这种通配导入也在解析期直接报错。参见 docs/python-subset.md 中Rejected at parse time一节。那沙箱怎么访问外部两条受控的闸门什么都没有当然没法干活。Monty 提供了仅有的两条外部通道而且都是开发者主动开启的主机函数Host Functions沙箱代码读到一个未定义的名字时执行挂起由宿主按external_lookup提供的函数表执行真实逻辑再把结果返回。解释器永远停下来等答案从不主动越界。详见 docs/host-functions.md。目录挂载 os回调宿主目录被挂载到虚拟路径open()和pathlib只能活动在山点内..、符号链接、绝对路径都会被虚拟路径策略挡在门外。详见 docs/filesystem.md。再叠加资源限额兜底——内存、累计执行时间、递归深度、循环检查点任何一项超限就取消执行防止合法但疯狂的代码把宿主拖垮。完整参数见 docs/resource-limits.md。白名单设计给普通开发者的 3 点启发默认拒绝显式授予与其维护一份不断追补的黑名单不如只实现你确定安全的能力集合其余天然不可达。让错误尽早暴露Monty 连影子都不留——缺的模块在类型检查期就报错而不是等运行时炸。做 API 设计时明确说没有比留个桩更友好。安全边界要结构性而非检查性用能力裁剪没有 FFI、没有动态执行比逐条 if-else 校验更难被绕过这也是能力不存在优于能力被拦截的根本原因。快速上手如何本地体验 Monty如果你想亲手验证import socket 真的不存在三步即可克隆仓库git clone https://gitcode.com/GitHub_Trending/monty3/monty阅读 README.md 中的多语言 QuickstartPython / JavaScript / Rust 三种接入方式用 Python 绑定的方式安装uv add pydantic-monty然后对沙箱feed_run(import socket)你会收到一个干脆的ModuleNotFoundError更多细节支持的语言特性子集见 docs/python-subset.md与 CPython 的全部差异记录在 limitations/ 目录下——Monty 的另一个好习惯是每一处与 CPython 的分歧都被白纸黑字写下来这让白名单永远是可核对、可审计的。【免费下载链接】montyA minimal, secure Python interpreter written in Rust for use by AI项目地址: https://gitcode.com/GitHub_Trending/monty3/monty创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考