メモリポイズニングとは
メモリポイズニングは、悪意ある攻撃者が AI エージェントの長期記憶(ベクターストア・会話履歴・スクラッチパッド)に不正なインストラクションを書き込み、将来のセッションでエージェントの行動を操作する攻撃手法だ。従来のプロンプトインジェクションがセッション終了とともにリセットされるのに対し、メモリポイズニングは攻撃の効果がセッションをまたいで持続し、トリガーとなる入力から時間的に切り離された状態で実行される点が特徴的だ。
OWASP は 2026 年に「OWASP Top 10 for Agentic Applications」を策定し、メモリ・コンテキストポイズニングを ASI06 として分類した。エージェントが参照するドキュメント・ウェブページ・RAG インデックスのいずれかに悪意あるコンテンツを混入させるだけで攻撃が成立し、研究では 80〜99.8% の攻撃成功率が報告されている。
なぜ注目されているのか
エージェントが長期記憶を持つことで実現できる「コンテキストの継続性」は、同時に攻撃のターゲットになる。攻撃者はエージェントが処理する外部ドキュメントに一度だけ不正なインストラクションを埋め込めば、その内容がエージェントの記憶に取り込まれ、数日後や別ユーザーとのセッションで実行される可能性がある。Claude Code や Cursor のような自律型コーディングエージェントが普及するほど、この脅威の影響範囲は広がる。
既存のプロンプトインジェクション対策(入力モデレーション・出力フィルタリング・セッション監視)はリセットを前提とした設計であるため、永続的な攻撃に対しては有効性が低い。防御には、メモリ書き込み時の出所追跡・信頼スコアリング・読み出し前の内容検証を組み合わせたレイヤードアプローチが必要とされている。
関連する技術
プロンプトインジェクションと攻撃の起点は共通しているが、持続性の有無で区別される。メタハーネスのような複数エージェントを束ねる構成では、単一エージェントのメモリが汚染されると他のエージェントにも影響が伝播するリスクがあり、アーキテクチャレベルでのメモリ分離設計が重要になる。
ドラゴンボールで例えると
メモリポイズニングはバビディの魔人化魔法そのものだ。
- バビディ = 攻撃者。悪意あるコンテンツをドキュメントやウェブページに仕込む存在
- 魔人化の呪い = エージェントが処理したファイルやウェブページに埋め込まれた不正インストラクション
- ベジータが魔人ベジータになる = エージェントが汚染されたメモリを取り込み、以後のセッションで攻撃者の指示に従うようになる
- 「後から発動する」 = バビディの種は植え付けた瞬間には発動しない——ある条件が揃ったとき(特定の入力が来たとき)に初めて実行される。これが「今のセッションだけ終わる」通常のプロンプトインジェクションとの決定的な違い
- 防御(信頼スコアリング) = 「心の清さ」を事前に確認することでバビディの支配を防ぐ——情報源の信頼度をスコアリングして低信頼ソースのメモリを除外する仕組み
ドラゴンボールで最も厄介だったのは「操られた戦士が仲間に牙を向く」ことだった。エージェントにとっての外部ドキュメントも、一見無害に見えて悪意が潜んでいる場合がある。
実装してみる
- 01 エージェントが使用するメモリストア(ベクターDB やファイル)への書き込みをラップする検証レイヤーを作成する
- 02 書き込み時にコンテンツの出所(URL・ユーザーID・タイムスタンプ)をメタデータとして付与する
- 03 読み出し時に信頼スコアの低いメモリをフィルタリングするガード関数を追加する
- 04 Claude Code に下記プロンプトを渡してサニタイザーを実装させる
# メモリサニタイザーの雛形を Claude Code で生成する claude "エージェント用メモリストアへの読み書きをラップし、書き込み時はソース・タイムスタンプ・信頼スコアをメタデータとして付与し、読み出し時は信頼スコアが閾値以下のエントリをフィルタリングする TypeScript モジュールを実装してください。RAG ベクターストアとファイルベースの両方に対応すること。"
interface MemoryEntry {
content: string;
metadata: {
source: string;
timestamp: string;
trustScore: number; // 0.0 - 1.0
};
}
const TRUST_THRESHOLD = 0.7;
export function writeMemory(store: Map<string, MemoryEntry>, key: string, content: string, source: string) {
const trustScore = source.startsWith('user:') ? 0.5 : 0.9;
store.set(key, {
content,
metadata: { source, timestamp: new Date().toISOString(), trustScore },
});
}
export function readMemory(store: Map<string, MemoryEntry>): MemoryEntry[] {
return [...store.values()].filter(e => e.metadata.trustScore >= TRUST_THRESHOLD);
}
エージェントの長期記憶への書き込み前にコンテンツを検証し、不審なインストラクション(命令形・ロール変更・システムプロンプト上書きを示すパターン)が含まれていればブロックまたはフラグを立てるサニタイザー関数を TypeScript で実装してください。信頼スコアは出所ドメインとコンテンツパターンで算出し、RAG ベクターストアへの書き込みと読み出し両方をラップすること。