1月6日、mavdolが「Notes on sandboxing untrusted code - why Python can't be sandboxed, comparing Firecracker/gVisor/WASM approaches · GitHub」と題した記事を公開した。この記事では、信頼できないコードを実行する際のサンドボックス化、特にPythonにおける課題と、Firecracker、gVisor、WASMといった異なるアプローチの比較について詳しく紹介されている。
以下に、その内容を紹介する。
Python自体のサンドボックス化が困難な理由
Pythonには、信頼できないコードを安全に実行するための組み込み機能が存在しない。これはPythonが高度なイントロスペクション(内省)機能を備えたオブジェクト指向言語であり、実行時(ランタイム)が変更可能であることに起因する。オブジェクトグラフ、フレーム、トレースバックを介してインタプリタのコア要素にアクセスできるため、ランタイムの分離が極めて困難である。
例えば、危険な組み込み関数を削除したとしても、以下のような手法で回避が可能だ。
# 危険な組み込み関数の削除試行
del __builtins__.eval
del __builtins__.__import__
# 1. イントロスペクションによる回避
().__class__.__bases__[0].__subclasses__()
# 2. 例外とフレームによる回避
try:
raise Exception
except Exception as e:
e.__traceback__.tb_frame.f_globals['__builtins__']
過去には sandbox-2 のような選択肢も存在したが、これらは言語レベルではなくOSレベルに近い分離を提供するものであった。そのため、Python自体をサンドボックス化するのではなく、Pythonを「サンドボックスの中で」実行するアプローチが現実的であるとの結論に至っている。
AIエージェント時代におけるサンドボックスの重要性
2025年は、AIエージェントの隔離が単なるリソース管理の問題ではなく、セキュリティ上の重要課題であることを浮き彫りにした。LLMには、システムプロンプトと悪意のある外部命令を区別できない「プロンプトインジェクション」という構造的な欠陥がある。
解決策はプロンプトの改良ではなく、インフラレベルでの「隔離」と「最小権限の原則」の適用である。
具体的には、以下の要素を隔離する必要がある。
- ファイルシステム:
/tmp/agent_sandboxなど、特定のディレクトリ以外へのアクセスを遮断する。 - ネットワーク: 許可リストにあるAPI以外への接続を制限する。
- 資格情報: 最小権限のトークンを使用し、データベース等へのルートアクセスを禁止する。
- ランタイム: 隔離された環境内での実行。
主要なサンドボックス技術の比較
業界では、エージェント全体を隔離する手法と、個々のタスク単位で隔離する手法の2つのパラダイムが存在する。
- Firecracker: AWS Lambdaでも採用されているマイクロVM。KVMを必要とするためLinux限定だが、強力な分離を提供する。ただし、タスク単位の細かい分離にはオーバーヘッドが大きい。
- Docker: 広く利用されているが、セキュリティの観点からはFirecrackerやgVisorが推奨される。
- gVisor: コンテナとVMの中間に位置し、システムコールをインターセプトすることで隔離を実現する。Kubernetes環境に適しているが、Linux限定であり、一定のオーバーヘッドが発生する。
WebAssembly (WASM) によるタスクレベルの隔離
WebAssemblyは、低オーバーヘッドでタスク単位の隔離を実現する有力な代替手段である。デフォルトでファイルシステムやネットワークへのアクセス権限を持たず、ホスト側が明示的に許可したリソースにしかアクセスできない点が最大の利点である。
しかし、現時点では「Pythonランタイムのエコシステム」に制約がある。Python単体で記述されたコード(Pure Python)は良好に動作するが、NumPyやPandas、TensorFlowといった「C言語拡張(C Extensions)」を含むライブラリは、WASMへのコンパイルやリンクが複雑であり、完全な対応には至っていない。
この制約はあるものの、軽量な隔離手段としての将来性は高く、筆者はタスク単位で隔離を行うオープンソースプロジェクト「capsule」を開発している。
from capsule import task
@task(name="analyze_data", compute="MEDIUM", ram="512MB", timeout="30s", max_retries=1)
def analyze_data(dataset: list) -> dict:
# コードはWasmサンドボックス内で安全に実行される
return {"processed": len(dataset), "status": "complete"}
結論
エージェントシステムを設計する際は、最初から「失敗」を前提にすべきである。エージェントが信頼できないコードを実行したり、悪意のある指示を処理したりすることを想定し、それらを封じ込めるアーキテクチャを構築することが不可欠である。
詳細はNotes on sandboxing untrusted code - why Python can't be sandboxed, comparing Firecracker/gVisor/WASM approaches · GitHubを参照していただきたい。