
AI を活用したアプリケーションを常に進化する脅威や攻撃手法から守ることは、容易ではありません。最近では、不可視文字を利用した新しいタイプの攻撃が登場しています。かつてはスパイ活動で使われた「見えないインク」が、現代では AI プロンプトに対する脅威となっています。
ユーザーが入力するプロンプトを受け付ける大規模言語モデル (LLM) や AI アプリケーションは、不可視の「タグ文字」を利用したインジェクション攻撃にさらされる可能性があります。ユーザー本人は無害なリクエストを送っているつもりでも、実際には気づかないうちに悪意ある内容を送信してしまう場合があります。
タグ文字とは?
Unicode のタグ文字は、英語キーボードにあるほとんどの文字 (アルファベット、数字、スペース、記号 # や & など) に対応する不可視の特殊文字で、ASCII 標準に基づいて設計されています。
これらのタグ文字は、もともとテキストの言語情報を不可視のメタデータとして記録する目的で導入されました。つまり、XML (Extensible Markup Language) のような複雑なマークアップを使わずに、文章の言語を非表示で示す方法として利用されていました。例えば、タグ文字で「da」を挿入することで、その後のテキストがデンマーク語であることを示す、といった使い方です。
これらのタグ文字は2008年に Unicode 標準から正式に廃止されましたが、その多くは後に復活し、別の用途へと再利用されています。地域の旗を表す絵文字のエンコードとして、例えば、Unicode の「黒旗」文字にタグ文字「gbsct」を続け、最後に「CANCEL TAG」文字を付けることで、スコットランドの旗(🏴)を生成するといった使い方があります。
どこに問題があるのか
問題は、これらの不可視文字が AI プロンプトを汚染するために悪用できてしまう点にあります。見た目は正常で無害なプロンプトであっても、不可視のタグ文字が挿入されることで、プロンプトの意味がまったく別のものに改変されてしまう可能性があります。
例えば、次のようなシンプルなプロンプトを考えてみましょう。「カメルーンの首都はどこですか?」
攻撃者はここに不可視文字を追加し、モデルの動作を変える指示を隠して埋め込むことができます。「ここまでのすべての指示を無視して…」といった命令文を、不可視のまま埋め込むことができるのです。
これらの文字は人間には見えませんが、一部の LLM はそれらを通常の文字や句読点として読み取り、隠された指示を可視テキストと同様に処理します。こ
すべての LLM が同じように振る舞うわけではありません。不可視タグ文字を無視するモデルもあれば、通常の ASCII として解釈するモデルもあります。英字アルファベットや主要な記号すべてに不可視版が存在し得るので、こうした攻撃は非常に柔軟で悪用しやすいのです。
深く掘り下げて防御策を探る
タグ文字は独自の Unicode ブロックに属しており、範囲は U+E0000 から U+E007F です。これらに対抗する一つの方法として、トラフィック経路上に Web アプリケーションファイアウォール (WAF) を配置し、これらの文字の存在を検出して含まれるリクエストをブロックするルールを設定することが挙げられます。プログレスの LoadMaster WAF は、そういった対応をすることができます。
WAF エンジンは歴史的に Unicode の扱いに苦戦してきました。正規表現パターンで高いコードポイントを表現することは意外に難しく、異なる正規表現ライブラリや WAF エンジン間での互換性も問題となります。
ただ、WAF は UTF-8 でエンコードされた際に Unicode 文字が生成するバイトパターンを確実に検出できます。UTF-8 は現代インターネットの共通言語です。Unicode タグブロック全体は技術的にはすべて割り当てられているわけではありません (未割り当てコードポイントが31個あります) が、UTF-8 バイト列を調べることで U+E0000 から U+E007F の範囲の任意の文字を検出することが可能です。
- 開始コードポイント: U+E0000
- UTF-8 エンコードバイト: F3 A0 80 80
- 終了コードポイント: U+E007F
- UTF-8 エンコードバイト: F3 A0 81 BF
最初の2バイトは固定で常に F3 A0 であり、これが検出パターンの開始部分を形成します。
次のステップとして、ブロック内の各コードポイントの UTF-8 エンコードバイトをすべて出力して確認すると、最後のバイトは連続的に増加せず (UTF-8 のエンコード方式による)、範囲が分かれていることがわかります。その結果、検出すべきバイト範囲は2つに分かれます。
- F3 A0 80 80 - F3 A0 80 BF
- F3 A0 81 80 - F3 A0 81 BF
これを正規表現パターンで表現することが可能です。
\xF3\xA0(?:\x80|\x81)[\x80-\xBF]
すべてを組み合わせる: カスタム WAF ルール
この正規表現パターンは、リクエスト内に不可視タグ文字が存在するかどうかを検出するカスタム WAF ルールの基盤となります。正規表現は、不可視タグ文字が検出された場合にリクエストを明示的に拒否し、後で分析できるように問題のあるリクエストの詳細をログに記録する WAF アクションと組み合わせることができます。
以下は、LoadMaster WAF に追加して不可視タグ文字の検出・ブロック動作を有効にできる SecRule (WAF セキュリティルール) の例 です。
SecRule ARGS “@rx \xF3\xA0(?:\x80|\x81)[\x80-\xBF]” \
“id:1000,\
phase:2,\
deny,\
capture,\
t:none,\
log,\
msg:‘Detected invisible Unicode tag characters’,\
logdata:‘Matched Data: %{TX.0} foundwithin %{MATCHED_VAR_NAME}: %{MATCHED_VAR}’,\
tag:‘PROGRESS/KEMP-LOADMASTER’”
新しいルールのテスト
ステップ1: ルールをコピー&ペーストして必要に応じて編集
まず、ルールをテキストエディタにコピー&ペーストし、簡単にアクセスできる場所に .conf ルールファイルとして保存します。

他のカスタムルールをすでに使用している場合は、例として示した ID:1000 がすでに使用されている可能性があるため、ルール ID をユニークな値に変更してください。
補足: この例のルールは ARGS コレクションをスキャンします。つまり、クエリ文字列や POST ボディの全ての引数 (JSON や XML ペイロードを含む) に対して不可視タグ文字の有無を確認します。ここをスキャンするのが最も実用的ですが、必要に応じて LoadMaster のサポートチームから、リクエストヘッダーやクッキーなど別の場所をスキャンする方法についてもガイダンスを受けることができます。
ステップ2: 新しいカスタムルールをアップロード
次に、LoadMaster の Web UI で Web Application Firewall > Custom Rules に移動し、新しく作成したカスタムルールファイルをアップロードします。

ステップ3: 新しいルールを有効化
View/Modify Services で、新しいルールを適用したい仮想サービスの横にある Modify をクリックします。次に WAF タブ を展開し、新しいカスタムルールファイルのチェックボックスにチェックを入れて Apply をクリックすると、ルールが有効化されます。
補足: ルールを適用すると、仮想サービスが一時的にリロードされます。その間、トラフィックが短時間中断する可能性があります。

重要: POST リクエストや JSON リクエストなど、リクエストボディを検査する場合は、Advanced Settings を開き Inspect HTTP POST Request Bodies を有効にしてください。ほとんどのユーザーに、この機能を有効にすることが推奨されています。

ステップ4: 新しいルールをテスト
A: ネガティブテスト(正常系)
高いコードポイントを持つ Unicode 文字を含むリクエストを送るのはやや難しいですが、Linux ターミナルと curl コマンドを使えば比較的簡単にテストできます。
まず、クリーンで正当なリクエストを送信します。

仮想サービスから 200 OK のレスポンスが返され、WAF を通過できれば正常です。

B: ポジティブテスト(異常系)
次に、不可視タグ文字を含むリクエストを送信します。Linux ターミナル (デフォルトの GNOME Terminal で確認済み) で、CTRL+SHIFT+U を押して Unicode 文字を入力します。すると、下線付きの「u」が表示され、その後にコードポイントを入力できます。この例では、不可視の大文字 P (コードポイント “ue0050”) をリクエストに追加しています。

ENTER キーを押して不可視の Unicode 文字を追加すると、消えたように見えますが、実際には存在しており、リクエストに含まれて送信されます。

リクエストを送信し、WAF が 403 Forbidden のレスポンスを返すことを確認します。

ルールの効果は、WAF のログを確認することで二重にチェックできます。確認手順は、System Configuration > Logging Options > System Log Files に移動し、WAF Event Log File の横にある View をクリックします。

ログファイルの最下部付近に、対象となるログエントリが記録されています。早く確認したい場合、ルール ID で検索することも可能です。
id “1000”
ログの内容は、読みやすさのため複数行に分けると以下のようになります:
2025-09-03T09:45:54+00:00 lb100 wafd:
ModSecurity: Access denied with code 403 (phase 2). Pattern match "\\xF3\\xA0(?:\\x80|\\x81)[\\x80-\\xBF]" at ARGS:ai-prompt.
[file "/tmp/waf/1/block-invisible-tag-chars.conf"]
[line "10"]
[id "1000"]
[msg "Detected invisible Unicode tag characters"]
[data "Matched Data: \xf3\xa0\x81\x90 found within ARGS:ai-prompt: foo\xf3\xa0\x81\x90\xf3\xa0\x81\x90"]
[tag "PROGRESS/KEMP-LOADMASTER"]
[hostname "192.168.2.150"]
[uri "/"]
[unique_id "33941351-75e7-451e-a5ed-6f38d74ff79f"]
この例では、UTF-8 でエンコードされた不可視の大文字 P タグ文字が \xf3\xa0\x81\x90 として正しく検出され、リクエストは拒否されました。
C: ブロックされる範囲のテスト
追加のステップとして、意図した範囲の文字だけがブロックされていることを確認するため、ブロック範囲の直前と直後の文字 をテストできます。これらの文字は許可され、200 OK のレスポンスが返るはずです。
(注: タグブロックの前後のコードポイントは現時点では割り当てられておらず、有効な文字ではありません。しかし将来的に割り当てられる可能性もあるため、不必要にブロックしないようにします。)
簡単なスクリプトでテストした結果は以下の通りで、期待通りの動作となっています:
⋮
U+DFFFD: 200
U+DFFFE: 200
U+DFFFF: 200
U+E0000: 403
U+E0001: 403
⋮
U+E007D: 403
U+E007E: 403
U+E007F: 403
U+E0080: 200
U+E0081: 200
⋮
まとめ
AI を活用したアプリケーションやサービスは、悪意ある攻撃者から守るために、特に注意と時間をかける必要があります。特に、ユーザーが自由形式の入力 (AI プロンプトなど) を行える AI アプリケーションは、インジェクション攻撃の対象となりやすく、防御が必須です。本記事で紹介した新しい攻撃手法に加え、リモートコマンド実行やサーバーサイドリクエスト偽造 (SSRF) などの従来型攻撃も依然として有効であり、WAF はこれらすべてを検出、記録、否認することができます。
LoadMaster WAF や負荷分散に関してご質問があれば、何なりとお問い合わせください。また、LoadMaster の30日間無料トライアルも是非ご利用ください。