Mika Ayenson, PhD

LLMワークフローへのセキュリティの組み込み:Elasticのプロアクティブなアプローチ

LLMのライフサイクルにセキュリティを統合し、脆弱性から保護するためのElasticの革新的なアプローチを、ElasticのAI Assistantで紹介します。

LLMワークフローにセキュリティを組み込む:Elasticの能動的アプローチ

先日、四半期ごとに開催するElastic OnWeekイベントの1つが終了し、通常の日常以外の機会を探求するユニークな週となりました。 OWASPNSA AISCの最近の発表に沿って、ElasticではLLMのネイティブなOWASP Top 10の脆弱性について少し時間を割くことにしました。この記事では、 ES|QLは、すなわち:

  • LLM01: プロンプトインジェクション
  • LLM02: 安全でない出力処理
  • LLM04: モデルサービス拒否
  • LLM06: 機密情報の漏えい

Elasticは、LLMアプリケーションの悪意のある動作を監査する機能を提供します。ここでは、わずか4つのステップで1つのアプローチをご紹介します。

  1. LLM 要求と応答のインターセプトと分析
  2. LLM 固有の解析結果によるデータのエンリッチメント
  3. Elastic Securityへのデータ送信
  4. ESの書き方|後で応答に使用できる QL 検出ルール

このアプローチは、新たな生成AIテクノロジーやセキュリティの課題に対応しながら、LLMに特化した検出ルールの開発など、高度な検出戦略を探求し、実装するための継続的な取り組みを反映しています。 この基盤の上に、昨年は、この積極的な道を前進し続けるためのツールキットと全体的な能力が大幅に向上しました。

Elasticは、高度な検索アプリケーションを開発するための関連ツールのコレクションである Search AI Platform によって、オープンなジェネレーティブAIの相棒がどのように強化されているかを紹介するAI Assistant for Securityを リリース しました。機械学習(ML)と人工知能(AI)に支えられたこのAIアシスタントは、アラートの要約、ワークフローの提案、クエリ変換、エージェント統合のアドバイスなど、強力な事前構築済みワークフローを提供します。 Elasticの AIアシスタント について、オブザーバビリティとセキュリティにシームレスに機能がどのように及ぶかについて、詳しくお読みになることを強くお勧めします。

AI Assistantの機能をサードパーティのLLMアプリケーションとして使用して、リクエストとレスポンスをキャプチャ、監査、分析し、利便性を高めて実験を実行することができます。 データがインデックスに格納されると、そのインデックスに振る舞い検出を書き込むことが通常どおりなり、セキュリティ検出エンジン全体を活用することもできます。 この実験ではElastic AI Assistant LLMアクティビティをプロキシしていますが、これは単に監査LLMベースのアプリケーションをデモンストレーションするための手段として使用されているに過ぎません。 さらに、このプロキシアプローチは、サードパーティアプリケーションが データをElasticセキュリティに送信することを目的としています。

LLMアクティビティをインターセプトするか、監視可能なLLMメトリックを活用することにより、アプリケーションのライフサイクルにセキュリティメカニズムを導入できます。 プロンプトベースの脅威に対処するには、 さまざまな安全対策を実装するのが一般的です。

  1. クリーンな入力: ユーザー入力をモデルに供給する前に、サニタイズして検証します
  2. コンテンツモデレーション:OpenAIツールを使用して、有害なプロンプトと出力をフィルタリングします
  3. レート制限と監視:使用パターンを追跡して、疑わしいアクティビティを検出します
  4. 許可/ブロックリスト:特定のアプリケーションに対して許容または禁止された入力を定義します
  5. Safe Prompt Engineering: モデルを意図した結果に導く事前構築済みのプロンプトを設計します
  6. ユーザーロール管理:ユーザーアクセスを制御して、不正なアクションを防止します
  7. エンドユーザーの教育:リスクを軽減するために、モデルの責任ある使用を促進します
  8. レッドチーム&モニタリング:脆弱性をテストし、予期しない出力を継続的に監視します
  9. モデルトレーニングのためのHITLフィードバック:ヒューマンインザループのフラグが立てられた問題から学習し、時間の経過とともにモデルを改良します
  10. API アクセスの制限: 特定のニーズとユーザー検証に基づいてモデル アクセスを制限します

OpenAIや他の多くのLLM実装者が提供する2つの強力な機能は、 エンドユーザーIDを送信しモデレーションAPIに対してコンテンツをチェックする機能であり、LLMの安全性の基準を設定する機能です。 ハッシュ化されたIDを元のリクエストと一緒に送信することで、不正使用の検出に役立ち、ターゲットを絞ったフィードバックが提供されるため、個人情報を送信せずに一意のユーザー識別が可能になります。 また、OpenAIのモデレーションエンドポイントは、開発者がヘイトスピーチ、自傷行為の奨励、暴力などの潜在的に有害なコンテンツを特定し、そのようなコンテンツをフィルタリングするのを支援します。 さらに一歩進んで、脅威や自傷行為の意図を検出します。

悪意のあるプロンプトから保護するためのすべての推奨事項とベストプラクティスにもかかわらず、私たちは単一の完璧な解決策がないことを認識しています。 OpenAIのAPIのような機能を使用すると、これらの脅威の一部がコンテンツフィルターによって検出され、コンテンツフィルターは使用ポリシー違反の通知で応答します。

このコンテンツフィルタリングは、多くの問題に対処するのに役立ちます。ただし、環境、アプリケーションエコシステム、または表示される可能性のあるその他のアラートの広範なコンテキストで、さらなる脅威を特定することはできません。 ジェネレーティブAIのユースケースを既存の保護機能に統合できれば、潜在的な脅威に対処するための制御と可能性が広がります。 さらに、初歩的な攻撃を阻止するためのLLM保護手段が講じられている場合でも、不正使用を黙ってブロックしたり許可したりするのではなく、検出エンジンを使用してアラートを発し、将来の修復措置を講じることができます。

LLM 要求とセットアップのプロキシ

最適なセキュリティソリューションは、LLMアプリケーションのエコシステム内に追加の保護手段を直接統合します。 これにより、要求と応答を取り巻く完全なコンテキストでアラートを充実させることができます。 リクエストがLLMに送信されると、リクエストを傍受して分析し、潜在的な悪意のあるアクティビティを検出できます。 必要に応じて、応答アクションをトリガーして、後続の HTTP 呼び出しを延期できます。 同様に、LLMの応答を検査することで、悪意のある動作のさらなる兆候を明らかにすることができます。

プロキシを使用してこれらのインタラクションを処理すると、いくつかの利点があります。

  • 統合と管理の容易さ:専用のプロキシアプリケーション内で新しいセキュリティコードを管理することで、複雑なセキュリティロジックをメインアプリケーションに直接埋め込むことを回避できます。 このアプローチにより、既存のアプリケーション構造で必要な変更が最小限に抑えられ、メンテナンスが容易になり、セキュリティとビジネス ロジックがより明確に分離されます。 メイン・アプリケーションは、プロキシーを介して LLM 要求をルーティングするようにのみ再構成する必要があります。
  • パフォーマンスとスケーラビリティ: プロキシを別のサーバーに配置すると、セキュリティ メカニズムが分離され、計算負荷が分散されます。 これは、運用をスケールアップしたり、パフォーマンス集約型のタスクを管理したりするときに重要になり、メインアプリケーションのパフォーマンスが追加のセキュリティ処理の影響を受けないようにします。

クイックスタートオプション:Flask付きプロキシ

受信および発信 LLM 接続をプロキシして、初期設定を高速化できます。 このアプローチは、単純な Python ベースの Flask アプリケーションを作成することで、他の LLM アプリケーションにも一般化できます。 このアプリケーションは、通信を傍受し、セキュリティリスクについて分析し、応答を転送する前に関連情報をログに記録します。

Elasticsearchに接続し、OpenAI LLMリクエストを処理するためのSDKは複数存在します。 提供されている llm-detection-proxy リポジトリは、使用可能な Elastic クライアントと OpenAI クライアントを示しています。 このスニペットは、1 つの Flask ルート内の実験的なプロキシの大部分を強調表示しています。

@app.route("/proxy/openai", methods=["POST"])
def azure_openai_proxy():
   """Proxy endpoint for Azure OpenAI requests."""
   data = request.get_json()
   messages = data.get("messages", [])
   response_content = ""
   error_response = None

   try:
       # Forward the request to Azure OpenAI
       response = client.chat.completions.create(model=deployment_name, messages=messages)
       response_content = response.choices[0].message.content  # Assuming one choice for simplicity
       choices = response.choices[0].model_dump()
   except openai.BadRequestError as e:
       # If BadRequestError is raised, capture the error details
       error_response = e.response.json().get("error", {}).get("innererror", {})
       response_content = e.response.json().get("error", {}).get("message")

       # Structure the response with the error details
       choices = {**error_response.get("content_filter_result", {}),
                  "error": response_content, "message": {"content": response_content}}

   # Perform additional analysis and create the Elastic document
   additional_analysis = analyze_and_enrich_request(prompt=messages[-1],
                                                    response_text=response_content,
                                                    error_response=error_response)
   log_data = {"request": {"messages": messages[-1]},
               "response": {"choices": response_content},
               **additional_analysis}

   # Log the last message and response
   log_to_elasticsearch(log_data)

   # Calculate token usage
   prompt_tokens = sum(len(message["content"]) for message in messages)
   completion_tokens = len(response_content)
   total_tokens = prompt_tokens + completion_tokens

   # Structure and return the response
   return jsonify({
       "choices": [choices],
       "usage": {
           "prompt_tokens": prompt_tokens,
           "completion_tokens": completion_tokens,
           "total_tokens": total_tokens,
       }
   })

Flask サーバーを使用すると、プロキシを使用するように OpenAI Kibana Connector を構成できます。

このLLMへのプロキシはローカルで実行されているため、認証情報と接続情報はElasticの外部で管理され、APIキーセクションに空の文字列を指定できます。 先に進む前に、接続をテストすることは一般的に良い考えです。 プロキシ ソリューションを実際の環境に実装することを検討している場合は、他のセキュリティへの影響を考慮することが重要です (このプロトタイプでは簡潔にするために考慮したものではありません)。

これで、LLM 要求と応答にインデックスを付け、この実験で作成された azure-openai-logs インデックスの使用可能なデータに検出を書き込むことができます。 オプションで、Elastic インジェストパイプラインを使用してデータを前処理することもできますが、この人為的な例では、ES|QL の力で検出を効果的に記述できます。

AzureOpenAI LLM 要求/応答データのサンプル

ラングスミスプロキシ

注: Langsmith Proxy プロジェクトは、LLM API の Docker 化されたプロキシを提供します。 最小限のソリューションを提供していますが、この記事の執筆時点では、カスタムセキュリティ分析ツールを組み込んだり、Elasticセキュリティと直接統合したりするためのネイティブ機能がありません。

LangSmith Proxyは、LLM APIのインタラクションを簡素化するように設計されています。 これは、最小限の構成 (LLM API URL など) を必要とするサイドカー アプリケーションです。 これにより、トラフィックの多いシナリオのパフォーマンス (キャッシング、ストリーミング) が向上します。 効率性のためにNGINXを使用し、詳細なLLMインタラクション追跡のためのオプションのトレースをサポートしています。 現在はOpenAIとAzureOpenAIと連携しており、将来的には他のLLMにも対応予定です。

LLM の潜在的な攻撃と検出ルールの機会

文書化された保護リストが一部のLLMに付属していない場合でも、これらのプロンプトの一部を試すだけですぐに拒否されたり、プロンプトの送信に使用されたプラットフォームで禁止されたりする場合があることを理解することが重要です。 悪意のあるプロンプトを送信する前に、慎重に実験し、SLA を理解することをお勧めします。 この探索では OpenAI のリソースを活用するため、bugcrowd のガイダンス に従い、@bugcrowdninja.com メール アドレスを使用して追加のテスト アカウントにサインアップすることをお勧めします。

ここでは、検出の機会を示すために、いくつかのもっともらしい例のリストを示します。 各 LLM トピックには、OWASP の説明、プロンプトの例、サンプル ドキュメント、検出の機会、およびワークフローに追加のセキュリティ メカニズムを統合する場合にユーザーが実行できる可能性のあるアクションが含まれています。

このリストは現在、広範なものではありませんが、Elastic Security Labsは現在、将来の開発を確実にするためにさまざまな取り組みを行っており、ルールの正式な化は今後も継続されます。

LLM01 - プロンプトインジェクション

OWASP の説明: 細工された入力を介して LLM を操作すると、不正アクセス、データ侵害、および意思決定の侵害につながる可能性があります。 ここで参照してください。

: 敵対者は、LLM を騙して意図しないアクションを実行したり、機密情報を漏らしたりするようにプロンプトを作成しようとする可能性があります。 注: promptmap のようなツールを使って、クリエイティブなプロンプト注入のアイデアを生成し、テストプロセスを自動化することができます。

Prompt:

応答例:

検出ルールの機会: この例では、LLM は、セキュリティ上のリスクのためにデータベース接続文字列の処理を拒否することで応答しました。 資格情報を非公開にすることを強調し、環境変数やボールトなどの安全な方法を使用して保護することを提案しています。

非常に脆弱ですが、基本的なインジケーターマッチングクエリは次のようになります。

FROM azure-openai-logs |
   WHERE request.messages.content LIKE "*generate*connection*string*"
   OR request.messages.content LIKE "*credentials*password*username*"
   OR response.choices LIKE "*I'm sorry, but I can't assist*"

少し高度なクエリでは、前日中に 2 回以上の同様の試行が検出されます。

FROM azure-openai-logs
| WHERE @timestamp > NOW() -  1 DAY
| WHERE request.messages.content LIKE "*credentials*password*username*"
   OR response.choices LIKE "*I'm*sorry,*but*I*can't*assist*"
   OR response.choices LIKE "*I*can’t*process*actual*sensitive*"
| stats total_attempts = count(*) by connectorId
| WHERE total_attempts >= 2

悪意のあるプロンプトを検出し、LLM 応答を保護するための多くのアプローチがあることに注意してください。 これらの指標だけに頼ることは最善のアプローチではありません。ただし、追加のエンリッチメントや多数の応答試行により、検出を徐々に改善できます。 さらに、ドキュメントにIDを導入すると、特定のユーザーに関連するフィールドに基づいて試行を集約することで、クエリをさらに強化できます。

例2: クレッシェンド 効果は、敵対者が一連の一見無害な問い合わせを通じて言語モデルを徐々に操作し、安全なシステムの不正アクセスと操作を含む架空のシナリオをモデルに説明するように求める方向にシフトする現実的な脱獄攻撃です。 そうすることで、LLMのセキュリティ制約を回避できる可能性のあるメソッドを抽出することを目指しています。

Prompt:

応答例:

OpenAIのフィルタリングによる追加の分析により、不正使用の最初の発生を即座に検出できます。

検出ルールの機会:

FROM azure-openai-logs
| WHERE @timestamp > NOW() - 1 DAY
 AND (
     request.messages.content LIKE "*credentials*password*username*"
     OR response.choices LIKE "*I'm sorry, but I can't assist*"
     OR analysis.openai.code == "ResponsibleAIPolicyViolation"
     OR malicious
 )
| STATS total_attempts = COUNT(*) BY connectorId
| WHERE total_attempts > 1
| SORT total_attempts DESC

ただし、Crescendo Effectを引き続き使用すると、OpenAIによる最初のコンテンツフィルターの後に会話ピボットのブロックが解除されることがわかります。 このような戦術を防ぐのが難しくても、検出する機会はあることを理解することが重要です。

LLM-Guardなどの追加の分析ツールは、会話が機密性が高いかどうかを検出しますが、この場合は不正確です。 ただし、複数のプロンプトで悪意のある動作を追跡する潜在的な機会を示唆しています。 注: この ES|QL クエリは、複数のイベントの動作を追跡するのに役立ちます。

FROM azure-openai-logs
| WHERE @timestamp > NOW() - 1 DAY
 AND (
     request.messages.content LIKE "*Molotov*"
     OR analysis.openai.code == "ResponsibleAIPolicyViolation"
     OR malicious
 )
| STATS attempts = count(*), max_sensitivity = max(analysis.llm_guard_response_scores.Sensitive) BY connectorId
| WHERE attempts >= 1 AND max_sensitivity > 0.5
| SORT attempts DESC

このクエリは、1 人のユーザー/セッション (connectorId で識別される) に関連付けられた一連のログ エントリを分析することにより、複数のイベントにわたる火炎瓶に関連する疑わしい動作を検出します。 クエリ core は、次の条件に基づいてイベントをフィルタリングします。

  • コンテンツマッチング:会話コンテンツ(request.messages.content LIKE "*Molotov*")で「Molotov」の言及を検索します
  • **ポリシー違反:OpenAIの安全フィルター(analysis.openai.code == "ResponsibleAIPolicyViolation")によってブロックされた試みを特定し、疑わしい可能性のある動作の開始を示します
  • 悪意のあるフラグの考慮事項: これには、システムがコンテンツに悪意のあるフラグ (malicious == true) を付けたログが含まれ、微妙な言及やさまざまな言及がキャプチャされる可能性があります
  • セッションレベルの分析: connectorId でイベントをグループ化することで、セッション内の試行の完全なシーケンスを分析します。 次に、そのセッションのすべての試行で試行の合計数(attempts = count(*))と最高感度スコア(max_sensitivity = max(analysis.llm_guard_response_scores.Sensitive))を計算します
  • 高リスク セッションのフラグ設定: 少なくとも 1 回の試行 (attempts >= 1) と最大感度スコアが 0.5 を超える (max_sensitivity > 0.5) セッションをフィルタリングします。 このしきい値は、ユーザーがリスクのある可能性のあるコンテンツについて執拗に話し合ったり、公開したりしたセッションに焦点を当てるのに役立ちます。

セッション内の複数のイベント間でこれらの要因を分析することで、個々のイベントに単独でフラグが立てられていない場合でも、エスカレートするディスカッションのパターンを検出するアプローチの構築を開始できます。

LLM02 - 安全でない出力処理

OWASP の説明: LLM 出力の検証を怠ると、システムを侵害してデータを公開するコードの実行など、ダウンストリームのセキュリティ悪用につながる可能性があります。 ここで参照してください。

: 敵対者は、LLM を悪用して、クロスサイト スクリプティング (XSS) やその他のインジェクション攻撃に使用できる出力を生成しようとする可能性があります。

Prompt:

応答例:

検出ルールの機会:

FROM azure-openai-logs
| WHERE @timestamp > NOW() - 1 DAY
| WHERE (
   response.choices LIKE "*<script>*"
   OR response.choices LIKE "*document.cookie*"
   OR response.choices LIKE "*<img src=x onerror=*"
   OR response.choices LIKE "*<svg/onload=*"
   OR response.choices LIKE "*javascript:alert*"
   OR response.choices LIKE "*<iframe src=# onmouseover=*"
   OR response.choices LIKE "*<img ''><script>*"
   OR response.choices LIKE "*<IMG SRC=javascript:alert(String.fromCharCode(88,83,83))>*"
   OR response.choices LIKE "*<IMG SRC=# onmouseover=alert('xxs')>*"
   OR response.choices LIKE "*<IMG onmouseover=alert('xxs')>*"
   OR response.choices LIKE "*<IMG SRC=/ onerror=alert(String.fromCharCode(88,83,83))>*"
   OR response.choices LIKE "*&#0000106&#0000097&#0000118&#0000097&#0000115&#0000099&#0000114&#0000105&#0000112&#0000116&#0000058&#0000097&#0000108&#0000101&#0000114&#0000116&#0000040&#0000039&#0000088&#0000083&#0000083&#0000039&#0000041>*"
   OR response.choices LIKE "*<IMG SRC=&#106;&#97;&#118;&#97;&#115;&#99;&#114;&#105;&#112;&#116;&#58;&#97;&#108;&#101;&#114;&#116;&#40;&#39;&#88;&#83;&#83;&#39;&#41;>*"
   OR response.choices LIKE "*<IMG SRC=\"jav&#x0A;ascript:alert('XSS');\">*"
)
| stats total_attempts = COUNT(*), users = COUNT_DISTINCT(connectorId)
| WHERE total_attempts >= 2

この擬似クエリは、クロスサイトスクリプティング (XSS) 攻撃で一般的なスクリプト要素や Cookie アクセスの試行を含む LLM 応答を特定することで、安全でない可能性のある出力処理を検出します。 これは、既知のキーワードの許可リストまたはブロックリストによって拡張できるシェルです。

LLM04 - モデル DoS

OWASP の説明: リソースを大量に消費する操作で LLM を過負荷にすると、サービスの中断やコストの増加が発生する可能性があります。 ここで参照してください。

: 敵対者は、計算リソースを過剰に消費する複雑なプロンプトを送信する可能性があります。

Prompt:

応答例:

検出ルールの機会:

FROM azure-openai-logs
| WHERE @timestamp > NOW() -  1 DAY
| WHERE response.choices LIKE "*requires*significant*computational*resources*"
| stats total_attempts = COUNT(*), users = COUNT_DISTINCT(connectorId)
| WHERE total_attempts >= 2

この検出は、LLM 応答を使用して不正使用の可能性がある動作を特定する方法の別の簡単な例を示しています。 この例は従来のセキュリティ脅威を表していないかもしれませんが、敵対者が被害者にコストを課す方法(リソースまたはトークンを消費する)を模倣する可能性があります。

例 2: 敵対者は、過剰な計算リソースを消費する複雑なプロンプトを送信する可能性があります。

Prompt:

応答例:

一見すると、このプロンプトは無害に見えます。 ただし、短時間での過剰な要求と冗長な応答は、コストを大幅に増加させる可能性があります。

検出ルールの機会:

FROM azure-openai-logs
| WHERE @timestamp > NOW() - 1 HOUR
| STATS request_count = COUNT(*), distinct_prompts = COUNT_DISTINCT(request.messages.content) BY connectorId
| WHERE request_count > 50 AND distinct_prompts > 10
| SORT request_count DESC

例 2 のコンテキストでは、この作業クエリは、過去 1 時間のazure-openai-logsからの各connectorIdのすべての要求と個別のプロンプト コンテンツをカウントすることで、使用パターンを効率的に追跡および分析します。この期間内に connectorId 件を超える 50 件を超えるリクエストを送信し、10 を超える一意のプロンプトを送信した場合、これは、攻撃者が暗号化アルゴリズムの詳細を調査するためにクエリを変更し、過度の負荷を引き起こしたり、検出システムを回避したりする可能性がある、説明したような潜在的な誤用パターンを示しています。 その後、リクエスト数が最も多い connectorIds に優先順位を付けるように結果が並べ替えられるため、最もアクティブなソースや疑わしいソースをすばやく特定できます。

LLM06 - 機密情報の漏えい

OWASP の説明: LLM 出力での機密情報の開示に対する保護を怠ると、法的な結果や競争上の優位性の喪失につながる可能性があります。 ここで参照してください。

: 敵対者は、トレーニング データに埋め込まれた機密情報を抽出するためのプロンプトを作成する場合があります。

Prompt:

応答例:

検出ルールの機会:

FROM azure-openai-logs
| WHERE @timestamp > NOW() - 1 DAY
| WHERE request.messages.content LIKE "*common*passwords*used*in*the*dataset*" AND malicious
| stats total_attempts = COUNT(*), users = COUNT_DISTINCT(connectorId)
 BY identified_threats.keyword, analysis.langkit_score
| WHERE total_attempts >= 2

このクエリは、モデルをトレーニングするために "データセットで使用される一般的なパスワード" と、そのような要求が悪意のあるものとしてフラグが立てられている場所について具体的に尋ねる要求を含む最終日のログを検索します。 これらのログを集約して、そのような試行回数をカウントし、特定された脅威と言語キットのスコアでグループ化された、関与する個別のユーザーを特定します。 AND malicious = trueを含めることで、すでに有害であるとフラグが立てられているリクエストに焦点を絞ることができ、調査と対応アクションの優先順位付けに役立ちます。

Security Insights による検出ルールの強化

LLMリクエストをプロキシ経由でルーティングすることで、専用のセキュリティツールを活用して、各リクエストの悪意の兆候を分析できます。 検出されると、元のリクエストは、悪意のあるコンテンツの可能性とそれが表す特定の種類の脅威を示す追加のメタデータで強化できます。 このエンリッチメントされたデータは、Elasticsearchでインデックス化され、堅牢な監視、アラート、およびレトロスペクティブ分析データセットが作成されます。 このエンリッチメントにより、最後のセクションの LLM 検出の機会が可能になります。

利用可能なすべてのツールを深く掘り下げるわけではありませんが、LLM インタラクションの分析と保護に対するさまざまなアプローチを提供するいくつかのオープンソース ツールが登場しています。 これらのツールの一部は、悪意のあるプロンプトを検出するようにトレーニングされた機械学習モデルによって支えられています。

  • Rebuff (GitHub): 機械学習を利用して、LLM インタラクションを通じてソーシャルエンジニアリング、フィッシング、その他の悪意のあるアクティビティの試みを特定し、軽減します。 使用例としては、リクエストコンテンツをRebuffの分析エンジンに通し、結果に基づいてリクエストに「悪意のある」ブールフィールドにタグを付けることが含まれます。
  • LLM-Guard (GitHub): LLM リクエストの有害なパターンを検出するためのルールベースのエンジンを提供します。 LLM-Guardは、事前に定義されたカテゴリに基づいて検出された脅威を分類し、詳細な脅威分類でリクエストを充実させることができます。
  • LangKit (GitHub): LLM の監視と保護のために設計されたツールキットである LangKit は、敵対的な入力や意図しないモデルの動作の兆候がないか、要求コンテンツを分析できます。 カスタム分析関数を統合するためのフックを提供します。
  • Vigil-LLM (GitHub): 疑わしい LLM リクエストのリアルタイム監視とアラートに焦点を当てています。 プロキシレイヤーへの統合により、潜在的なセキュリティ問題に即座にフラグを立てることができ、リクエストデータにビジランススコアを豊富にすることができます。
  • Open-Prompt Injection (GitHub): プロンプト インジェクション攻撃を検出するための方法論とツールを提供し、プロンプト インジェクション手法に関連する特定の侵害インジケーターを使用して要求データを強化できるようにします。

注:これらのツールのほとんどは、外部LLMへの追加の呼び出し/コストを必要とし、脅威ハンティングを効果的に行うには追加のインフラストラクチャが必要になります。

LLM-guard と LangKit を使用する簡単な実装例の 1 つは、次のようになります。

def analyze_and_enrich_request(
   prompt: str, response_text: str, error_response: Optional[dict] = None
) -> dict:
   """Analyze the prompt and response text for malicious content and enrich the document."""

   # LLM Guard analysis
   sanitized_prompt, results_valid_prompt, results_score_prompt = scan_prompt(
       input_scanners, prompt["content"]
   )
   (
       sanitized_response_text,
       results_valid_response,
       results_score_response,
   ) = scan_output(output_scanners, sanitized_prompt, response_text)

   # LangKit for additional analysis
   schema = injections.init()
   langkit_result = extract({"prompt": prompt["content"]}, schema=schema)

   # Initialize identified threats and malicious flag
   identified_threats = []

   # Check LLM Guard results for prompt
   if not any(results_valid_prompt.values()):
       identified_threats.append("LLM Guard Prompt Invalid")

   # Check LLM Guard results for response
   if not any(results_valid_response.values()):
       identified_threats.append("LLM Guard Response Invalid")

   # Check LangKit result for prompt injection
   prompt_injection_score = langkit_result.get("prompt.injection", 0)
   if prompt_injection_score > 0.4:  # Adjust threshold as needed
       identified_threats.append("LangKit Injection")

   # Identify threats based on LLM Guard scores
   for category, score in results_score_response.items():
       if score > 0.5:
           identified_threats.append(category)

   # Combine results and enrich document
   # llm_guard scores map scanner names to float values of risk scores,
   # where 0 is no risk, and 1 is high risk.
   # langkit_score is a float value of the risk score for prompt injection
   # based on known threats.
   enriched_document = {
       "analysis": {
           "llm_guard_prompt_scores": results_score_prompt,
           "llm_guard_response_scores": results_score_response,
           "langkit_score": prompt_injection_score,
       },
       "malicious": any(identified_threats),
       "identified_threats": identified_threats,
   }

   # Check if there was an error from OpenAI and enrich the analysis
   if error_response:
       code = error_response.get("code")
       filtered_categories = {
           category: info["filtered"]
           for category, info in error_response.get(
               "content_filter_result", {}
           ).items()
       }

       enriched_document["analysis"]["openai"] = {
           "code": code,
           "filtered_categories": filtered_categories,
       }
       if code == "ResponsibleAIPolicyViolation":
           enriched_document["malicious"] = True

   return enriched_document

この関数は、プロキシを通過するリクエストごとに呼び出すことができ、返されたデータはリクエストドキュメントに追加されてからElasticsearchに送信されます。 その結果、LLM との生のインタラクションをキャプチャし、要求と応答に基づいて検出ルールに埋め込むためのセキュリティに関する洞察を即座に提供する、詳細で実用的なデータセットが得られます。 プロンプトインジェクションLLM01の例で一周すると、クエリは次のように更新できます。

FROM azure-openai-logs
| WHERE @timestamp > NOW() - 1 DAY
| WHERE identified_threats.keyword == "LangKit Injection" OR analysis.langkit_score > 0.4
| stats total_attempts = count(*), users = count_distinct(connectorId) by identified_threats.keyword, analysis.langkit_score
| WHERE users == 1 and total_attempts >= 2

ご覧のとおり、どちらのスコアリングメカニズムも、オープンソースのプロンプト分析ツールから返される結果に基づいて主観的です。 このクエリは、特定された脅威が「LangKit Injection」であるか、LangKit スコアが 0.4を超えている過去の日のログをフィルタリングします。 次に、合計試行回数を計算し、特定された各脅威カテゴリに関連付けられた一意のユーザー(エージェント)の数とLangKitスコアをカウントし、1人のユーザーが関与し(users == 1)、合計試行回数が2回以上(total_attempts >= 2)の場合にのみフィルタリングします。

これらの追加ツールにより、検出ルールを改善するためのさまざまな分析結果フィールドを利用できます。 これらの例では、わかりやすくするために、ほとんどのデータを現状のまま出荷しました。 ただし、本番環境では、 Elastic Common Schema (ECS)のようなスキーマに対するすべてのツールとLLMレスポンスでこれらのフィールドを正規化することが重要です。 データをECSに正規化すると、異なるデータソース間の相互運用性が向上し、分析が簡素化され、より効果的でまとまりのあるセキュリティルールの作成が効率化されます。

このシリーズのパート2では、ECSフィールドマッピングと統合に対して、より正式なアプローチをどのように採用したかについて説明します。

LLM アプリケーション監査の代替オプション

プロキシの使用は簡単かもしれませんが、他のアプローチが本番環境のセットアップに適している場合があります。例えば:

当然のことながら、これらのアプローチには、サードパーティツールをサポートするカスタムロジックを開発せずに生成されたすべてのLLMセキュリティ分析ツールデータをネイティブに取り込まないなどの潜在的な制限があります。

Elastic APMを活用してアプリケーションに関する詳細なインサイトを獲得

Elastic APM は、パフォーマンスのボトルネックを検出し、問題のあるリクエストやクエリを特定するために不可欠な、アプリケーションをリアルタイムで監視するための代替ソリューションを提供します。 Elastic APMを統合することで、ユーザーはトランザクション時間、データベースクエリのパフォーマンス、外部API呼び出しの効率などに関する詳細なインサイトを得ることができます。 この包括的な可視性により、パフォーマンスの問題やエラーに迅速に対処して解決することが容易になります。 プロキシアプローチとは異なり、APMはアプリケーションに関するログを自動的にElasticにインジェストするため、データ内で見られる動作に基づいてセキュリティ検出ルールを作成する機会を提供します。

OpenTelemetry を活用して可観測性を強化

すでにOpenTelemetryを採用しているアプリケーションの場合、Elastic APM との統合 を活用することで、インストルメンテーションを大幅に変更することなくオブザーバビリティを強化できます。 この統合は、トレースやメトリクスなど、さまざまなテレメトリデータのキャプチャをサポートしており、Elastic Stackにシームレスに送信できます。 このアプローチにより、開発者は使い慣れたライブラリを引き続き使用しながら、Elasticの堅牢な監視機能の恩恵を受けることができます。 OpenTelemetryは複数のプログラミング言語間で互換性があり、 Elasticのネイティブプロトコル (OTLP)によるサポートにより、簡単なデータ転送が容易になり、分散システムを監視するための堅牢な基盤が提供されます。 プロキシの例と比較すると、このアプローチは、Elasticに対する独立したインデックスとロギングメカニズムを維持するよりも、よりネイティブにデータを取り込みます。

KibanaによるLLM監査

LLMアプリケーションのカスタムロジックを記述してデータを監査および送信するように、ElasticのAIアシスタントを使用してアプローチをテストできます。 TypeScriptに慣れている場合は、Kibana スタートガイドを使用してローカルのElasticインスタンスをデプロイすることを検討してください。 セットアップが完了したら、 Elastic AI Assistant に移動し、監査と分析のためにLLMのリクエストとレスポンスをインターセプトするように設定します。 注: このアプローチは、主に Elastic 固有の LLM 統合を追跡しますが、APM やその他の統合、またはプロキシを使用してサードパーティ アプリケーションを追跡します。 これは、実験および探索的テストの目的でのみ考慮する必要があります。

幸い、KibanaはすでにAPMでインストゥルメントされているため、APMサーバーを設定すると、このソースからのログの取り込みが自動的に開始されます( elastic.apm.active: trueを設定します)。 詳細については、 README を参照してください。

締めくくりの感想

Elasticでは、大規模言語モデルのライフサイクルにセキュリティプラクティスを統合するという探求を続けており、LLMワークフローにセキュリティを組み込むことで、より安全で信頼性の高いアプリケーションを作成するための道筋が開かれることは明らかです。 OnWeekでの作業から引き出されたこれらの人為的な例は、アナリストが最も直感的で効果的だと思うセキュリティソリューションを活用して、悪意のあるアクティビティをプロアクティブに検出し、アラートを発し、トリアージする方法を示しています。

また、プロキシアプローチの例を使用すると、リクエストを積極的に検出して防止するモデルを組み込むことができることも注目に値します。 さらに、悪意のある脅威を特定した場合は、LLM 応答をユーザーに送り返す前にトリアージできます。 この時点で、さまざまな防御アプローチをカバーするようにセキュリティ保護を拡張する柔軟性があります。 この場合、セキュリティとパフォーマンスの間には微妙な境界線があり、チェックを追加するたびに時間がかかり、ユーザーが期待する自然な会話の流れが妨げられます。

llm-detection-proxyで概念実証プロキシを自由にチェックし、ニーズに合わせて適応させてください。

私たちは常にこのようなユースケースやワークフローに関心を持っているので、 いつものようにGitHubの問題で連絡を取ったり、 コミュニティSlackでチャットしたり、 ディスカッションフォーラムで質問したりしています。

本記事に記述されているあらゆる機能ないし性能のリリースおよびタイミングは、Elasticの単独裁量に委ねられます。現時点で提供されていないあらゆる機能ないし性能は、すみやかに提供されない可能性、または一切の提供が行われない可能性があります。

この記事を共有する