
未パッチのMCP STDIOコマンドインジェクションの脆弱性(CVE-2026-30623ファミリー)に対するドロップイン修正
未パッチのMCP STDIOコマンドインジェクション脆弱性(CVE-2026-30623ファミリー、2026年4月にOX Securityによって「仕様」として開示、SDKパッチはなし)に対するドロップイン修正です。1行インポートするだけで、Pythonアプリが起動するすべてのstdio MCPサーバーは、OSがプロセスを生成する前にコマンド/引数/環境変数が検証されます。
初めての方は、まずスコープを読み、次にインストールとはじめにを読めば、2分以内に保護されます。
Pre-1.0、活発に開発中。
check/launch/rules)は実装済みで、テストマシンにインストールされている実際のバイナリ(python、node、npx)に対して実行される自動テストスイートでカバーされています(モックではありません)。実際の生成されたサーバーフィクスチャを通じた本物のエンドツーエンドMCPハンドシェイクと、launchの実際のサブプロセスレベルテストを含みます。対象範囲: stdio MCPサーバーの起動(コマンド + 引数 + 環境変数)をOSのプロセス生成レイヤーに到達する前に検証し、特にSECURITY.mdで説明されているコマンド/引数インジェクションパスを閉じること。
明示的に範囲外: サーバーが宣言したツールのリスク能力のスキャン(それは別の問題です。AgentGuardを参照)、生成されたプロセスのサンドボックス化、および非stdio(SSE/HTTP)MCPトランスポート。
git clone <this-repo>
cd mcpshield
pip install -e . # コアCLI: click + rich のみ
pip install -e ".[mcp]" # Python自動パッチも必要な場合(`mcp` SDKが必要)
動作確認:
mcpshield --version
mcpshield --help
アプリがPythonで書かれており、StdioServerParametersを構築したり、自身でmcp.client.stdio.stdio_clientを呼び出している場合、エントリーポイントの最上部(他の何かがmcp.client.stdioをインポートする前)に1行のインポートを追加します:
import mcpshield.autopatch # 副作用インポート; 最初に置く必要がある
from mcp import ClientSession, StdioServerParameters
from mcp.client.stdio import stdio_client
# ... 今まで通りstdio_clientを使用 – これで検証されるようになりました
安全でない起動は、プロセスを生成する代わりにmcpshield.core.errors.UnsafeConfigurationError(ValueErrorのサブクラス)を発生させます。
何も実行せずにmcpServers形式の設定ファイルを監査します:
mcpshield check claude_desktop_config.json
+---------------------------------------------------------------+
| Server | Status | Command | Detail |
|------------------+---------+---------+------------------------|
| filesystem | OK | npx | - |
| evil-server | BLOCKED | npx | Argument '...' contains|
| | | | shell metacharacter |
+---------------------------------------------------------------+
1 ok, 0 warned, 1 blocked
何かがBLOCKEDの場合、非ゼロ終了コードで終了します(--strictを追加するとWARNでも失敗します)– CIにそのまま組み込めます。
Python自動パッチを使用できないMCPクライアント(Node、Java、Rustなど)の場合、設定を実際のコマンドの代わりにmcpshieldに向けます:
{
"command": "mcpshield",
"args": ["launch", "--", "npx", "-y", "some-mcp-server"]
}
launchは検証を行い、その後実際のコマンドをMCPクライアントが期待するのと同じstdioで実行します(透過的パススルー)。安全でない起動の場合は明確なエラーで拒否します。
| チェック | ネイティブバイナリ(例: python.exe) | シェル解釈可能(.cmd/.bat/シバンスクリプト) |
|---|---|---|
引数内のシェルメタ文字(&, |, ;, バックティック, $(...), ...) | 許可 | ブロック |
| 引数内のNULバイト/改行 | ブロック | ブロック |
相対パストラバーサル(..)によるコマンド解決 | ブロック | ブロック |
| コマンドが実在するファイルに解決されない | ブロック | ブロック |
環境変数内のLD_PRELOAD / NODE_OPTIONS / など | 削除(警告) | 削除(警告) |
環境変数内のPYTHONPATH | フラグ付き(警告)、削除しない | フラグ付き(警告)、削除しない |
ネイティブバイナリは引数チェックが緩和されます。なぜなら直接execするため、引数リストを再解釈するシェルが存在しないからです。シェル解釈可能なコマンド(Windowsでは多くの場合npx.cmd/npx.bat)は厳格なチェックを受けます。なぜならそれがまさに基盤となるCVEが悪用するメカニズムだからです。
どちらも意図的で、値ごとのオプトインです。「すべてのチェックを無効にする」フラグではありません:
allow_raw_args=["--some-value-with-a-pipe"](ライブラリ)は、レビュー済みで信頼できる特定の引数値を例外として許可します。allow_env=["SOME_VAR"]は、通常は削除される環境変数を変更せずに通過させることを許可します。| コマンド | 機能 |
|---|---|
mcpshield check <config> [--format table|json] [--strict] | mcpServers設定の静的監査。何も実行しません。BLOCKEDがあると非ゼロ終了(--strictではWARNでも同様)。 |
mcpshield launch -- <command> [args...] | 検証後、実際のコマンドをstdioパススルーで実行。 |
mcpshield rules list | アクティブなシェルメタ文字ブロックリスト、環境変数リスト、既知の安全なランチャーバイナリを表示。 |
git cloneが必要です。mcp.client.stdio.stdio_clientのみをパッチします。import mcpshield.autopatchよりも前にfrom mcp.client.stdio import stdio_clientを実行して自身の参照を保持しているコードは、これをバイパスします – 常に最初にmcpshield.autopatchをインポートしてください。checkは、実行されているマシンを使用してコマンドを解決します。実際にデプロイされるマシン(異なるPATH、異なるインストールツール)で異なる解決結果になる設定では、異なる報告結果になる可能性があります。mcpshield/
autopatch.py # Python MCPホスト向け1行インポート修正
core/
validate.py # 検証エンジン(コマンド/引数/環境変数チェック)
rules.py # ブロックリスト/許可リストデータ
errors.py # UnsafeConfigurationError
cli/
main.py
commands/ (check.py, launch.py, rules.py)
tests/
fixtures/ # 実際の良性MCPサーバー + サンプル/悪意のある設定
pip install -e ".[dev,mcp]"
pytest
テストスイートは、実行されているマシンにインストールされている実際のpython/node/npxバイナリに対して検証を行い(エンジン自身が解決するのと同じ方法で解決)、実際の生成されたサーバーフィクスチャを通じた本物のエンドツーエンドMCPハンドシェイクを含みます(モックではありません)。