実は、AIの評価環境ほど外部接続を狭くした方がいいです。
本番につながっていないから大丈夫、では足りません。評価では普段より強い権限や緩い制約を一時的に使うことがあります。その環境にインターネット、秘密情報、書き込み可能なホスト側フォルダが同居すると、検証用のつもりが境界をまたぐ入口になります。
7月21日、OpenAIとHugging Faceは、サイバー能力の内部評価中に起きたセキュリティ事案を公表しました。公式説明では、評価用に制約を弱めたモデルが、パッケージ用プロキシの脆弱性から外部ネットワークへ到達し、複数の経路をたどったとされています。ここから小さなチームが持ち帰るべきなのは、モデルの性能比較よりも、評価用設定を本番と同じ感覚で置かないことです。
今回は、評価用コンテナの設定をJSONで持ち、危ない項目をPythonで止めます。開発担当に丸投げせず、企画側でも「この検証、外へ出られる状態になっていないか」を確認できる形です。
先に見るのは5項目
評価環境の設定では、次を固定しておきます。
| 項目 | 安全側の値 | なぜ見るか |
|---|---|---|
| outbound | package_proxy_only |
任意の外部サイトへ出さない |
| allowed_hosts | 社内パッケージプロキシだけ | 接続先を増やした変更に気づく |
| privileged | false |
コンテナの権限を広げない |
| mounts | 読み取り専用のテストデータだけ | ホストの鍵や作業フォルダを渡さない |
| environment | APIキーを置かない | 設定ファイルから秘密情報を遠ざける |
この5項目は、設定を渡す前に見る順番にすると抜けにくくなります。
評価でパッケージを入れる必要がある場合でも、全面的なインターネット接続までは要りません。社内のキャッシュやプロキシだけを許可する形に寄せます。少し面倒ですが、ここを雑にすると評価環境だけ例外だらけになります。
Pythonで評価用設定を点検する
まずは eval-config.json を用意します。これはあえて危険な例です。監査スクリプトが何を止めるかを見るために使います。
{
"outbound": "internet",
"allowed_hosts": ["pypi.org"],
"privileged": true,
"mounts": [
{"source": "/Users/demo/.ssh", "target": "/root/.ssh", "read_only": false}
],
"environment": {"OPENAI_API_KEY": "example"}
}
次の audit_eval_config.py は標準ライブラリだけで動きます。JSONを読むだけなので、実際のコンテナを起動する前に使えます。
import json
import sys
from pathlib import Path
APPROVED_HOSTS = {"packages.example.internal"}
SENSITIVE_NAMES = {
"OPENAI_API_KEY", "ANTHROPIC_API_KEY",
"AWS_SECRET_ACCESS_KEY", "GITHUB_TOKEN",
}
SENSITIVE_PREFIXES = ("/", "~", ".ssh", ".aws", ".git")
def audit(config):
errors = []
if config.get("outbound") != "package_proxy_only":
errors.append("outbound: package_proxy_only ではありません")
for host in config.get("allowed_hosts", []):
if host not in APPROVED_HOSTS:
errors.append(f"allowed_hosts: 未承認の接続先 {host}")
if config.get("privileged") is not False:
errors.append("privileged: false ではありません")
for mount in config.get("mounts", []):
source = mount.get("source", "")
if source.startswith(SENSITIVE_PREFIXES) or source == ".":
errors.append(f"mounts: 危険なホスト側パス {source}")
if mount.get("read_only") is not True:
errors.append(f"mounts: read_only ではありません ({source})")
for name in config.get("environment", {}):
if name in SENSITIVE_NAMES:
errors.append(f"environment: 秘密情報らしい変数 {name}")
return errors
config = json.loads(Path(sys.argv[1]).read_text(encoding="utf-8"))
errors = audit(config)
if errors:
print("NG")
for error in errors:
print("- " + error)
raise SystemExit(1)
print("OK: 評価用設定を通過しました")
手元では、上のJSONに対して次の出力になりました。
$ python3 audit_eval_config.py eval-config.json
NG
- outbound: package_proxy_only ではありません
- allowed_hosts: 未承認の接続先 pypi.org
- privileged: false ではありません
- mounts: 危険なホスト側パス /Users/demo/.ssh
- mounts: read_only ではありません (/Users/demo/.ssh)
- environment: 秘密情報らしい変数 OPENAI_API_KEY
outbound だけを止めれば済むと思っていたのですが、鍵のマウントと環境変数も同時に見ないと意味が薄い。ここで最初に詰まりました。通信を閉じても、評価用プロセスへ秘密情報を渡していれば、出口が開いた瞬間に困るからです。
運用では「例外」を設定に残す
安全な設定では、たとえば次の状態を目指します。
{
"outbound": "package_proxy_only",
"allowed_hosts": ["packages.example.internal"],
"privileged": false,
"mounts": [
{"source": "./fixtures", "target": "/work/fixtures", "read_only": true}
],
"environment": {}
}
このJSONなら同じスクリプトは OK: 評価用設定を通過しました を返します。実案件では、許可するホスト名と禁止するパスを自社用に差し替えます。例外が必要なら、口頭で通さず allowed_hosts に追加してレビュー対象にするのが大事です。たとえば一時的に外部APIを使う評価でも、接続先、期限、担当者を別の申請表に残しておく。設定だけ増えていく状態を避けられます。
これは完全な防御ではありません。脆弱性対応、監視、認証情報の保管は別途必要です。ただ、評価ジョブを走らせる直前に設定を機械的に見るだけで、ありがちな接続漏れはかなり拾えます。ここ地味に効きます。
で、現場でどう使うか
AIエージェントの評価は、自由に動かした方が精度を測りやすい場面があります。その自由さを、本番の資産へ届く権限まで広げる必要はありません。
まずは次の評価案件で、この5項目をレビュー表に入れる。設定ファイルをPRに添え、上のスクリプトをCIで1回動かす。担当が変わっても「何を許可したか」が残ります。評価を速く回すためにも、接続の境界は先に決めておくのが得です。