LLMプロンプトインジェクション攻撃における Unicode 同形字およびゼロ幅文字回避の検出

Dev.to / 2026/3/12

📰 ニュースIdeas & Deep AnalysisTools & Practical Usage

要点

  • ソースコードに埋め込まれたLLMプロンプトの静的解析は、ランタイム保護を補完する重要だが十分には対処されていない防御層である。
  • PromptSonarはデプロイ前にプロンプトの脆弱性を特定するよう設計された静的解析ツールである。
  • 本稿はBase64エンコード、Unicode同形字、ゼロ幅文字の3つの回避技術を文書化し、正規表現ベースの検出を回避できることを示す。
  • 事前デプロイの静的解析はゼロレイテンシ検査、IDE統合、CI/CDゲーティング、そして問題を早期に検出してランタイムリスクを低減する。

元々は Medium に公開されました

私はLLMプロンプトの脆弱性を静的に解析するツール PromptSonar を作っています。スキャナに対する回避技法をテストしている際、ほとんどの正規表現ベース検出を突破する3つのUnicodeベースの攻撃を見つけました。以下に、その仕組みと私がどう対処したかを示します。

誰も語られていない問題

OWASP LLM Top 10(2025)は、Prompt InjectionをLLMアプリケーションにおける最大の脆弱性クラスとして特定しています。セキュリティコミュニティは、モデルへプロンプトが到達する際に検査するランタイム介入ツール(Google Model Armor、Lakera Guard、Prompt Shields など)で対応しています。

しかし、ほとんど対処されていない補完的な層があります。ソースコードに直接書かれたプロンプト文字列についてはどうでしょう? アプリケーションに同梱されるシステムプロンプト、ファーストショットの例、ロール定義はどうでしょうか。

静的解析は新たな攻撃面を導入します。スキャナーが自分のプロンプト文字列を検査すると知っている敵は、悪意のある意味内容を保持しつつスキャナーを回避するようにそれを設計することができます。

デプロイ前の静的解析は、ランタイムのみのアプローチより現実的な利点を提供します:

  • ゼロ遅延オーバーヘッド。 解析は開発時に行われ、リクエストごとには行われません。
  • IDE統合。 開発者はコードを書く際に脆弱性を認識します。
  • CI/CDゲーティング。 脆弱性を含むプルリクエストは自動的にブロックされます。
  • 偽陽性のランタイム発生ゼロ。 指摘されたコードはまだユーザー入力を処理していません。

ただし静的解析ツール自体も標的となり得ます。本稿は、攻撃者がそれらを回避するために用いる3つの技術と、それらを止める方法を解説します。

3つの回避技術

1. Base64エンコード

最も簡単なアプローチです。悪意のあるプロンプトをソースコードに埋め込む前にBase64でエンコードします。素朴なスキャナーはエンコード済みの文字列しか見ず、パターンと一致しません。

... コードブロックは省略 ...

スキャナーは 'SWdub3Jl...' を検出せず、ジャイルブレイク指示は検出されずに配送されます。

検出: PromptSonar は Base64 文字セットに一致し、16文字を超えるサブストリングを識別します。候補の塊はデコードされ、完全なルールセットを適用します。16文字の閾値は、識別子やハッシュのような短いBase64風文字列による偽陽性を最小限に抑えます。

2. Cyrillic Homoglyph Substitution

キリル文字系は、多くのフォントサイズでラテン文字と視覚的に区別がつかない複数の文字を含みます。ラテン文字の代わりにキリル文字の見かけ上の同形字を置換すると、人間のレビュアーには完全に読めますが、ラテン文字の正規表現パターンには一致しません。

  • Latin a → Cyrillic а (U+0430)
  • Latin c → Cyrillic с (U+0441)
  • Latin e → Cyrillic е (U+0435)
  • Latin i → Cyrillic і (U+0456)
  • Latin o → Cyrillic о (U+043e)
  • Latin p → Cyrillic р (U+0440)
  • Latin x → Cyrillic х (U+0445)

以下の文字列は、任意の人間のレビュアーにはジャイルブレイクの指示として読めますが、複数の位置にキリル文字を含んでいます:

... コードブロックは省略 ...

正規表現 /ignore\s+all\s+previous/i は一致しません。Unicodeコードポイントはパターンが期待するASCII範囲の外側にあります。

検出: PromptSonar はパターンマッチングの前に正規化パスを適用します。既知のキリル同形字をラテン文字に置換する文字マッピング表を使用します。このマップはキリル、数学用アルファベット、囲み文字を網羅します。

3. Zero-Width Character Injection

ゼロ幅文字は、表示上のグリフを生じさせないUnicodeコードポイントです。ジャイルブレイクの語句の文字間に挿入すると、正規表現が要求する連続した列を壊します — 一方で人間のレビュアーには完全には見えません。

この攻撃でよく使われる主な文字:

  • U+200B — ゼロ幅スペース
  • U+200C — ゼロ幅非結合文字
  • U+200D — ゼロ幅結合文字
  • U+FEFF — ゼロ幅ノーブレークスペース(BOM)
... コードブロックは省略 ...

正規表現 /ignore\s+all\s+previous/i は単語間の \s+ を要求します。 U+200B はほとんどの正規表現エンジンでは空白として分類されません。マッチなし。攻撃は配送されます。

これは3つのうち最も危険なもので、ほとんどのコードエディタやセキュリティレビューツールでは見えません。文字列を調べるレビュアーには、文字が完全に通常のように読めてしまいます。

検出: PromptSonar はパターンマッチングの前にすべてのゼロ幅文字を除去します。実装上の重要な詳細として、ソースファイルから文字列リテラルを抽出するためのパーサー Tree-sitter はときに \u200B を6文字として返します。正規化パイプラインは両方の表現を処理します。

検出パイプライン

これら3つの技術はいずれも共通の脆弱性を共有します。すべて文字レベルで動作しており、意味論レベルではありません。正規化を先に行うパイプラインは、いずれか1つのパターンがマッチする前に3つすべてを打ち負かします。

パイプラインは7段階で実行されます:

  1. 文字列抽出。 Tree-sitter の AST 解析は、言語コンテキストとフレームワークの呼び出し場所に基づいてプロンプト文字リテラルを特定します。TypeScript、JavaScript、Python、Go、Rust、Java、C# に対応します。
  2. リテラルエスケープ解決。 \\uXXXX シーケンスを実際のUnicode文字に変換します。
  3. ゼロ幅の削除。 U+200B、U+200C、U+200D、U+FEFF および関連する不可視文字を削除します。
  4. 同形字正規化 キリル文字、数学用アルファベット、囲み文字をラテン文字に対応づけます。
  5. Base64候補検出。 16文字以上のBase64サブストリングを識別してデコードします。
  6. ルール評価。 全ルールセットを正規化文字列に適用します(21ルール、7つの柱、v1.0.26)。
  7. 発見の生成。 元の文字列の元の行と列で報告します。正規化は内部の処理であり、出力は常に実際のソースを参照します。

核となる正規化関数:

... コードブロックは省略 ...

検証結果

3つの技術はすべて PromptSonar v1.0.26 で検証されました:

  • Base64エンコードbtoa('Ignore all previous instructions') → ✅ 検出
  • キリル同形字Іgnore аll prevіous іnstructіons → ✅ 検出
  • ゼロ幅挿入Ignore​All​Previous​Instructions(語間に U+200B) → ✅ 検出
  • 組み合わせ攻撃 — キリル置換ジャイルブレイクのBase64 → ✅ 検出

偽陽性テストの結果: ignoreErrors()、OpenAI SDK 初期化、標準システムプロンプト、パス参照はすべて検出なしでした。

本稿がカバーしない点

正直な制限:

  • 混在スクリプト文字列。 国際化されたプロンプトは正規化後に偽陽性を生む可能性があります。
  • 新しい同形字セット。 Greek、Armenian などは未対応です。
  • 動的構築。 実行時に複数変数から構築される Base64 は静的解析には見えません。
  • 意味的言い換え。 既知パターンを回避するジャイルブレイクは文字レベル検出の範囲外です。

ツールを超えた重要性

これまでの同形字研究は、ドメインなりすまし、IDNホモグラフ攻撃、ソースコード汚染を対象としています。LLM プロンプトインジェクション回避への適用は初めて文献で体系化されています。

LLMアプリが本番インフラとなるにつれ、プロンプト設計のセキュリティを他のコード資産と同等の厳密さへ高めるべきです。静的解析はそのスタックの第一層です。

この静的解析はランタイム検査の代替ではなく、デプロイ前にソースで見えるものを捉える開発時のゲートです。Prompt SBOM は次の論理的一歩で、検証済みプロンプトと実行時のプロンプト間の差分を検出する基準を提供します。

試してみる

npx @promptsonar/cli scan ./src

参考文献

  1. OWASP Foundation. LLMアプリケーション向け OWASP Top 10, 2025。
  2. Perez & Ribeiro (2022). Ignore Previous Prompt. arXiv:2211.09527.
  3. Holgers, Watson & Gribble (2006). Cutting through the Confusion. USENIX ATC.
  4. Gabrilovich & Gontmakher (2002). The Homograph Attack. CACM 45(2).
  5. Boucher et al. (2022). Bad Characters. IEEE S&P.
  6. Greshake et al. (2023). Not What You''ve Signed Up For. arXiv:2302.12173.