以下の内容はhttps://blog.flatt.tech/entry/llm_application_securityより取得しました。


LLM / 生成AIを活用するアプリケーション開発におけるセキュリティリスクと対策

はじめに

こんにちは、GMO Flatt Security株式会社セキュリティエンジニアの佐藤(@Nick_nick310)です。

近年、大規模言語モデル(LLM、 Large Language Models)の進化と普及は目覚ましく、多くのサービスや業務プロセスで生成AIとして活用されています。LLMは多大なメリットをもたらす一方で、その特性に起因する新たなセキュリティリスクも指摘されており、安全な活用のためには十分な理解と対策が不可欠です。LLMを自社のサービスや業務に組み込む際、どのようなセキュリティ上の課題に直面する可能性があるでしょうか。

本稿では、LLMを活用したアプリケーションを開発・運用する上で考慮すべき主要なセキュリティリスクについて、国際的な指標である「OWASP Top 10 for LLM Applications」を用いながら解説します。併せて、これらのリスクに対し、ソースコードレベルでの精査を通じて実装上の具体的な問題点まで評価する、GMO Flatt Security独自の診断アプローチについても少しご紹介します。

※ 本稿では「OWASP Top 10 for LLM Applications 2025」で示される項目を主に説明しますが、LLMに関する全てのセキュリティ問題を網羅するものではない点にご留意ください。

LLMアプリケーション診断のリリースの背景

この度、GMO Flatt Securityは「LLMアプリケーション診断」を正式リリースしました。

既にLLMを活用したアプリケーションにおけるLLM特有の診断を複数件実施しており、事業者からの診断要望も増えつつあります。また、業界的なトレンドからもLLMに関する診断サービスの需要が今後益々求められる社会になることを見据えてのリリースとなります。

さらに、なんといっても弊社は2025年3月に提供を開始した、日本初のセキュリティ診断AIエージェント「Takumi」も開発しています。Takumiは、高度なセキュリティレビューや巨大なコードベース内調査をAIにおまかせでき、社内でもLLMを活用した診断の取り組みやセキュリティ観点の整備が活発化しています。

2025年4月現在、ウェイトリストにご登録いただいた方に順次ご案内をしています。

Takumi ウェイトリスト登録受付中 先着順でご案内

これらの背景より、GMO Flatt SecurityはAIエージェントを開発している組織だからこその専門的な深い知見も活かしながら、最先端なLLMアプリケーション診断を提供します。

広がるLLM活用とセキュリティ課題

今日、LLMは多くのサービスや業務プロセスで生成AIとして取り入れられ、その汎用性の高さから様々な用途での活用が進んでいます。

例えば、以下のような用途での活用事例がよくあります。

  • チャットボット
    • FAQ応答, カスタマーサポート, 社内ヘルプデスク
  • コンテンツの生成
    • ブログ記事の作成, 画像生成
  • テキストの分析
    • 用語チェックやレビュー, 要約や分析, 翻訳

多くの場合、社外向けのサービスや社内向けのツールを問わず、作業の自動化や効率化、支援ツールなどのサービスや機能として取り入れられています。

しかし、LLMの導入は新たなセキュリティ課題ももたらします。特に、プロンプトの内部的な組み立て方、LLM出力の具体的な処理・利用方法、外部連携時の権限設定といった実装の詳細には、注意すべきセキュリティリスクが潜んでいます。

LLMによって注意すべきセキュリティリスクの例としては、以下が挙げられます。

  • 機密情報の漏洩
  • レピュテーションの低下
  • 意図しないデータの更新・削除
  • 認証・認可制御の回避
  • サービスの停止
  • 任意コードの実行

弊社では、アプリケーションのソースコードを精査することにより、これらの実装固有のリスク要因まで特定し、より包括的なセキュリティ評価を提供します。

診断時にホワイトボックス手法が有効な理由

弊社のLLMアプリケーション診断は、原則としてアプリケーションのソースコードをご提供いただき、ホワイトボックスな状態で実施します。その理由としては、LLM固有の特性と、診断の網羅性にあります。

LLMの動作はTemperatureパラメータにも寄りますが、一般に同じ入力でも応答が変動しうるため、外部からの挙動を観察するブラックボックス診断だけでは、機能や潜在的な問題を網羅的に検証することが困難です。また、ブラックボックス診断では、他の脆弱性を探るためにまずプロンプトインジェクションを成功させる必要が生じがちです。

しかし、限られた期間内でこれが成功しない場合、その奥に潜む本質的なリスクが見逃される可能性があります。

弊社はソースコードやシステムプロンプトを直接分析するため、プロンプトインジェクションの成否に依存せず、コードレベルに潜む脆弱性や設計上の不備といった潜在的なリスクまで明らかにすることが可能です。

さらに、LangChain等のフレームワーク固有のリスクも、その実装コードから直接検出可能です。

このようなアプローチにより、より深く網羅性の高い診断を実現します。具体的な診断観点は次項でご説明します。

OWASP Top 10 for LLM Applications 2025

Webアプリケーションのセキュリティ向上を目的とした国際的な非営利団体である「OWASP (The Open Web Application Security Project)」は、LLMアプリケーション特有のセキュリティリスクをまとめたランキング形式のリスト「OWASP Top 10 for LLM Applications」を公開しています。

弊社のLLMアプリケーション診断は、「OWASP Top 10 for LLM Applications」を参考とし、独自観点を加えた項目で診断します。

2025年4月時点で最新版である「OWASP Top 10 for LLM Applications 2025」は以下の通りです。

  1. プロンプトインジェクション
  2. 機密情報の開示
  3. サプライチェーン攻撃
  4. データやモデルの汚染
  5. 安全でない出力処理
  6. 過剰な代理行為
  7. システムプロンプトの漏洩
  8. ベクトル化と埋め込みの脆弱性
  9. 不正確な情報
  10. 無制限の消費

「OWASP Top 10 for LLM Applications」の詳細については、以下をご覧ください。

一般的なLLMアプリケーションにおけるセキュリティ観点

ここでは、「OWASP Top 10 for LLM Applications 2025」をもとに、一般的なLLMアプリケーションのセキュリティ観点を深ぼって紹介します。

プロンプトインジェクション (LLM01)

「プロンプトインジェクション(Prompt Injection)」とは、ユーザーの指示により本来の意図しないLLMの動作を引き起こしたりする指摘事項です。

例えば、LLMによってある程度の役割を持たせたチャットボットに対し、悪意のあるユーザーが敵対的なプロンプトを入力することで、LLMに規約に反した動作を実行したり、機密情報を出力したりする可能性があります。

ちなみに、プロンプトインジェクションは大きく2種類の手法があります。

  • 直接的なプロンプトインジェクション
    • ユーザーが直接的な悪意のある命令を含むプロンプトを入力することで、LLMに意図しない動作を引き起こさせる方法です。
  • 間接的なプロンプトインジェクション
    • LLMがGoogle検索の結果やWebサイト、ファイルなどの外部リソースを読み込む場合、ユーザーが悪意のあるリソースを用意して間接的にLLMに読み込ませることで、LLMに意図しない動作を引き起こさせる方法です。

また、細かい手法としては、英語などの他言語やUnicode文字、絵文字による命令で対策を回避できる場合もあります。

プロンプトインジェクションの事例としては、以下のような事例が存在します。

「プロンプトインジェクション」観点の診断では悪意あるプロンプトによってどういった悪影響が起きうるかを評価します。悪影響の例としては以下が挙げられます。

  • LLMに与えられた権限を使用して、本来想定していない操作をさせられる。
  • LLMが保持する機密情報やアクセス可能な内部情報を出力させられる。
  • LLMに過負荷な処理を行わせたり、無限ループに陥らせたりすることで、サービスに負荷を与える。

「プロンプトインジェクション」の対策・緩和策としては、以下のようなことが考えられます。

  • LLMの動作や役割を明確にして限定的な操作のみ可能なように制御する。
  • 可能な限り入力内容を検証し、敵対的なプロンプトを拒否する。
  • 可能な限り出力形式を設け、検証する。または構造化出力機構を用いて出力形式を厳密に制限する。

LLMのプロンプトは自然言語を使用する関係上、プロンプトインジェクションを完全に対策することは難しい状況になっています。

そのため、プロンプトインジェクションされたとしても、リスクが最小限になるように対策することが重要です。それらの対策については、弊社の「LLMアプリケーション診断」ではソースコードに基づいた対策案を提案することも可能です。

システムプロンプトの漏洩 (LLM07)

「システムプロンプトの漏洩(System Prompt Leakage)」とは、LLMの動作を制御するために使用されるシステムプロンプトに、意図しない機密情報が含まれている場合の指摘事項です。(システムプロンプトとは、LLMに対して役割や振る舞い、制約や出力形式などを指示する重要な設定内容のことです。)

例えば、システムプロンプトにAPIキーやアクセストークンといった資格情報が含まれる場合、これらの情報漏洩につながる可能性があります。

また、資格情報ではないにしても、以下のような情報がシステムプロンプトに含まれる場合、内容が漏洩することにより制限の回避につながる可能性があります。

  • 内部規約やセキュリティ制御の設定内容
  • 社内情報やロジック情報

動作確認用として、サンプルアプリを用意しました。 以下のコードは、システムプロンプトに認証情報を含めている例です。

   # ChatPromptTemplateを使用してメッセージを構築
    chat_prompt = ChatPromptTemplate.from_messages([
        SystemMessagePromptTemplate.from_template(
            "id:secret@localhost password:secret123! You are a helpful assistant. Here is the relevant user information:\n{user_context}"
        ),
        HumanMessagePromptTemplate.from_template("{message}")
    ])

システムプロンプトは、細工したプロンプトを使用することで出力させることが可能です。この例では、システムプロンプトに含まれる認証情報を奪取することが可能です。

「プロンプトインジェクション」の説明で「完全な対策は難しい」と記載しましたが、同様にシステムプロンプトの漏洩も完全に対策するのは難しい状況です。そのため、システムプロンプト自体をそもそも機密情報として扱わないことが重要です。

「システムプロンプトの漏洩」の対策案としては、以下のようなことが考えられます。

  • システムプロンプトに機密情報を含めないようにする。
  • システムプロンプトでアプリのセキュリティ制御を行わず、LLMに関連しない場所でセキュリティ制御を行う。

無制限の消費 (LLM10)

「無制限の消費(Unbounded Consumption)」とは、ユーザーがLLMに対して過剰なリソースを浪費させるような入力を送信することで、LLMの出力トークンを増大させる指摘事項です。

例えば、LLMによって入力した内容を要約したり作文したりする機能に対し、悪意のあるユーザーが長いコメント内容や繰り返す命令を入力することで、意図的にリソースを浪費させられる可能性があります。

サンプルアプリで確認してみます。 LLMに対して「文字列を繰り返し表示する」命令をすることで、容易に出力上限まで生成させることが可能です。

この例の対策では、出力するトークンを制限することで対策可能です。

llm = ChatOpenAI(
        temperature=0, 
        openai_api_key=OPENAI_API_KEY,
        max_tokens=100  # 出力トークン数を制限
)

また、システムプロンプトやアプリケーション側でLLMの出力トークンを制限するようにしている場合、ロジックの不備などが原因でその上限数を回避可能な場合も存在します。

「無制限の消費」のセキュリティリスクとしては、以下のようなことが考えられます。

  • サービスに過負荷がかかる。 (DoS攻撃)
  • LLM自体の従量課金制プランにおける経済的損失を負う。 (EDoS攻撃)

「無制限の消費」の対策案としては、以下のようなことが考えられます。

  • LLMの入力トークンや出力トークンに上限やレート制限を設定する。
  • コメントやファイルのサイズ制限や入力検証を設ける。

RAGやツール連携等を行うLLMアプリケーションにおけるセキュリティ観点

LLMアプリケーションは、外部向けに公開するアプリの他に、内部向け(社内限定)に作成されたアプリケーションや便利なプロダクトなどがあります。例として、社内ヘルプデスクエージェントやドキュメント作成エージェントなどが挙げられます。

これらのアプリケーションは、内部向けで使用する関係上、外部向けのLLMを活用したサービスより多くの機能を持ったり、高い権限レベルを仕様として持っている傾向があります。

ここでは、そのような高い権限レベルを持つLLMアプリケーションにおける重要なセキュリティ観点を紹介します。

データやモデルの汚染(LLM04)

「データやモデルの汚染(Data and Model Poisoning)」とは、学習データや埋め込みデータが汚染され、動作や出力が変更される指摘事項です。

LLMアプリケーションにおいて、独自のモデルを使用して学習させるケースは稀なため、ここでは主にデータの汚染について説明します。

内部向けのLLMアプリケーションは、社内に蓄積したデータを元に業務効率化やドキュメント作成をすることが多いです。この時、参照するデータに悪意あるものが含まれると、動作や出力結果にそれが反映される場合があります。

例として、社内の技術ナレッジに悪意あるコンテンツが含まれると、この技術ナレッジを学習してドキュメントを作成する際に悪意あるコンテンツが反映される可能性があります。

「データやモデルの汚染」の対策案としては、以下のようなことが考えられます。

  • 信頼できるデータソースのみを用いる
    • 例 : 信頼できるデータ作成者を定義し、その作成者によるデータかどうかを検証する
  • 信頼できるコンテンツのみを用いる。
    • 例 : データに信頼性を表すメタデータを一定条件(手動レビューや機械的なバリデーション等)で付与し、これを付与されたデータのみを用いる

過剰な代理行為(LLM06)

「過剰な代理行為(Excessive Agency)」とは、LLMに与える権限が強すぎることにより、想定していない更新・削除などが発生する指摘事項です。

内部向けのLLMアプリケーションは、その用途から外部サービスの権限を与えることが多いです。この時、与える権限が大きすぎると、想定していない操作をLLMが行う場合があります。

例として、Google Driveのデータを読み取って学習するLLMアプリのケースを考えます。Google Driveのアクセス権限を渡す際に、読み取り権限だけでなく書き込み権限も付与すると、LLMはGoogle Driveに対して書き込みを行うことができます。

LLMの動作は絶対的なものではないため、プロンプトインジェクションを介して、想定していないGoogle Driveに対する書き込みが行われる可能性があります。

「過剰な代理行為」の対策案としては、以下のようなことが考えられます。

  • LLMアプリケーションに与える権限は、アプリケーションの仕様を満たす最低限のものにする
    • 例:LLMアプリケーションにクラウドストレージのデータを参照する機能を実装する場合は、クラウドストレージの読み取り権限のみを与えるようにする。

LLMアプリケーションに強い権限を与える必要がある場合は、以下のような緩和策が考えられます。

  • LLMアプリケーションが実施した操作のログを取得する。可能であればモニタリングし、想定外の操作(多数の既存のドキュメントに破壊的変更を加える等)があった場合はアラートを出すようにする。
  • LLMアプリケーションの操作による変更履歴を取得し、想定外の操作があった場合にロールバックできるようにする。
  • LLMアプリケーションが強い権限が必要な操作を行う場合は、操作の内容を人間がレビュー・承認した上で反映するようにする。

ベクトル化と埋め込みの脆弱性(LLM08)

「ベクトル化と埋め込みの脆弱性(Vector and Embedding Weaknesses)」とは、Retrieval Augmented Generation (RAG)を使用する際に、意図しないデータをcontextに含めてしまう場合の指摘事項です。 (Retrieval Augmented Generation (RAG)とは、外部データから情報を検索し、それを元にLLMが回答・要約・文章生成などを行う手法です)

contextに含まれる情報も、プロンプトインジェクションによって意図せず漏洩する可能性があるため、含める情報は制御する必要があります。アクセス制御が不十分な場合、他ユーザーの情報や内部情報がコンテキストに含まれ、結果として情報漏洩に繋がる可能性があります。

サンプルアプリで挙動を見てみます。 以下のコードはベクトル化による埋め込みを作成し、入力されたプロンプトをもとに検索を行うものです。

    # ユーザー情報を取得してドキュメントに変換
    users = db.query(User).all()
    docs = [
        Document(
            page_content=f"User: {user.username}, Email: {user.email}",
            metadata={"user_id": user.id}
        ) for user in users
    ]
    
    # embeddingを作成
    embedding = OpenAIEmbeddings(openai_api_key=OPENAI_API_KEY)
    
    # ベクトルストアを作成
    vectorstore = InMemoryVectorStore.from_documents(
        documents=docs,
        embedding=embedding
    )

    # メッセージに関連するユーザー情報を検索
    relevant_docs = vectorstore.similarity_search(query=params.message, k=3)
    
    # 検索結果をコンテキストとして構築
    user_context = "\n".join([doc.page_content for doc in relevant_docs])
    
    # ChatPromptTemplateを使用してメッセージを構築
   chat_prompt = ChatPromptTemplate.from_messages([
        SystemMessagePromptTemplate.from_template(
            """
            あなたは親切なアシスタントです。
            以下に、関連するユーザー情報を記載します。
            {user_context}
            """
        ),
        HumanMessagePromptTemplate.from_template("{message}")
    ])
    
    # プロンプトを生成
    messages = chat_prompt.format_messages(
        user_context=user_context,
        message=params.message
    )
    
    llm = ChatOpenAI(temperature=0, openai_api_key=OPENAI_API_KEY)
    reply = llm.invoke(messages)
    return {"reply": reply}

この例では、データのアクセス制御をしていないため、簡易なプロンプトでシステムに存在するユーザーのメールアドレスを列挙することが可能になります。

この例の対策としては、similarity_searchを実行する際にfilterを指定し、取得するユーザー情報をログインユーザーのもののみに制限することで対策可能です。

   # フィルタリング関数を定義
    def filter_by_user_id(doc):
        return doc.metadata.get("user_id") == current_user['user_id']
    
    # メッセージに関連するユーザー情報を検索
    relevant_docs = vectorstore.similarity_search(
        query=params.message, 
        k=3,
        filter=filter_by_user_id
    )

「ベクトル化と埋め込みの脆弱性」の対策案としては、以下のようなことが考えられます。

  • contextに含めるデータは、そのユーザーの権限で閲覧できる範囲に限定する。

不正確な情報(LLM09)

「不正確な情報(Misinformation)」とは、LLMの出力結果の信頼性が低い場合の指摘事項です。

LLMは、その学習データやモデルの特性上、事実に基づかない情報(ハルシネーション)を生成することがあり、回答が現実にそぐわない場合があります。現実にそぐわない回答を意思決定など重要なシーンで使用することで、重大な損失が発生する可能性があります。

「不正確な情報」の対策案としては、以下のようなことが考えられます。

  • RAGなどを使用して内部に合わせた出力をする可能性を高める。
  • LLMの出力を表示する際に、「参考として使用してください」など再確認を促すメッセージも合わせて表示する。

LLM フレームワークにおけるセキュリティ観点

LLMアプリケーションを作成する際、多数の機能を持たせる場合はRAGの管理やファイル操作といったLLMができない機能を実装する必要があります。

この実装を円滑にするためのフレームワークがあります。フレームワークを使用してもLLMアプリ特有のセキュリティ観点は考慮する必要があり、さらにフレームワーク特有の観点も考慮する必要があります。

ここでは特に有名な「LangChain」を例にとりつつ、フレームワークにおける重要なセキュリティ観点を紹介します。

過剰な代理行為(LLM06)

この観点は内部向けLLMアプリケーションでも取り上げましたが、フレームワークにおいても重要な観点となります。 フレームワークは様々な機能と連携する機能があります。例としては、DB連携が挙げられます。 DB連携をする際、DBにアクセスするための認証情報をフレームワークに渡す必要がありますが、この認証情報が強い権限だった場合、LLMは強い権限でDBに対してアクセスすることが可能になります。

そのため、ユーザーに対して本来許可していない操作がLLM経由で実行される可能性があります。

対策案としては、内部向けLLMアプリケーションと同様になります。

テンプレートインジェクション

「テンプレートインジェクション」はプロンプトテンプレートに使用されているテンプレートエンジンを悪用した任意コード実行が可能な場合の指摘事項です。

フレームワークでは、プロンプトの構築を円滑に行うためにプロンプトテンプレート機能が提供されていることが多いです。 LangChainでは「f-string」や「mustache」といったフォーマットの他に、「jinja2」を使用することができます。

jinja2のテンプレート構文はPythonコードを実行することが可能なため、信頼できないソースを使用してテンプレートを構築すると、テンプレートインジェクションにつながる可能性があります。

LangChainの公式ドキュメントにおいても、jinja2の代わりにf-stringを使用することが推奨されています。

サンプルアプリで挙動を確認します。 以下のコードはLangChainのtemplate_formatにjinja2を使用し、プロンプトに対してf-stringでユーザー入力を埋め込んでいる例です。

    prompt = ChatPromptTemplate.from_messages(
        messages=[
            ("system", "You are a helpful assistant."),
            ("human", f"{params.message}"),
        ],
        template_format="jinja2",
    )
    prompt_value = prompt.invoke({"params": params})
    print(prompt_value)
    reply = llm.invoke(prompt_value)
    return {"reply": reply}

jinja2のテンプレートとして解釈される文字列を送信すると、LLMの生成結果としては解釈されていないように見えますが、ログを確認するとテンプレートが解釈されていることが確認できます。

この例の対策としては、template_formatにf-stringを指定し、プロンプトに対して直接f-stringを使用しないことで対策可能です。

    prompt = ChatPromptTemplate.from_messages(
        messages=[
            ("system", "You are a helpful assistant."),
            ("human", "{params}"),
        ],
        template_format="f-string",
    )
    prompt_value = prompt.invoke({"params": params})

セキュリティリスクとしては、以下が考えられます。

  • (sandbox内で実行される場合)DoSや制限の回避
  • (sandbox外で実行される場合)サーバ上で任意コードの実行

対策案としては、以下のようなことが考えられます。

  • 任意コード実行につながるテンプレートエンジンを使用する場合は、ユーザー入力をサニタイズする。

安全でない構成での関数使用

「安全でない構成での関数使用」は、フレームワークが提供する関数が安全でない構成で使用されている場合の観点です。実際指摘する場合は、関数に応じた指摘になります。

フレームワークが提供する関数は、様々なユースケースに対応するためにセキュリティ面が弱くなる構成やオプションを許可していることが多いです。

LangChainで例を挙げると、LLMSymbolicMathChain関数は任意コードの実行につながるallow_dangerous_requestsオプションを提供しています。

File System toolsにおいては、root_dirパラメータが未設定な場合、または過剰な範囲を指定した場合に情報漏洩や不正なファイルの書き込みにつながる可能性があります。

フレームワークが提供する関数を使用する場合であっても、構成が安全であるかを検証して使用することが大切です。

サンプルアプリで挙動を確認します。 以下のコードは、FIle System toolsにおいて、root_dirパラメータを未指定にした例です。

   toolkit = FileManagementToolkit(
        selected_tools=["read_file"]
    )

動作確認用として、「/etc/」ディレクトリにflattファイルを作成しています。

細工したプロンプトを使用することで、「/etc」ディレクトリのファイルを閲覧することが可能です。

この例の対策としては、root_dirパラメータに適切なディレクトリを設定することで対策可能です。

    toolkit = FileManagementToolkit(
        root_dir="/app/img/",  # 操作を許可するディレクトリを制限
        selected_tools=["read_file"]
    )

セキュリティリスクとしては、上記の例においては以下が考えられます。

  • sandbox外で任意コードの実行
  • 想定していないデータの読み取り・書き込み・削除

対策案としては、以下のようなことが考えられます。

  • 公式ドキュメントのセキュリティに関する注意や警告に従って実装する

終わりに

本稿では、LLMアプリケーションを開発・運用する上で考慮すべき主要なセキュリティリスクや、GMO Flatt Security独自の診断アプローチについてもご紹介しました。

GMO Flatt Securityの「LLMアプリケーション診断」では、「一般的なLLMアプリケーション」「RAGやツール連携等を行うLLMアプリケーション」「LLMフレームワーク」など診断対象の特性ごとにセキュリティ観点を考慮し、対象の仕様や構成に合わせて柔軟に診断を実施いたします。ぜひ、お問合せください。

他にも様々なサービスを提供し、ソフトウェアプロダクトの開発組織のセキュリティをサポートしています。ご興味のある方はお気軽にお問い合わせください。

また、GMO Flatt Security はセキュリティに関する様々な発信を行っています。 最新情報を見逃さないよう、公式Xのフォローをぜひお願いします!

ここまでお読みいただきありがとうございました。




以上の内容はhttps://blog.flatt.tech/entry/llm_application_securityより取得しました。
このページはhttp://font.textar.tv/のウェブフォントを使用してます

不具合報告/要望等はこちらへお願いします。
モバイルやる夫Viewer Ver0.14