Why not login to Qiita and try out its useful features?

We'll deliver articles that match you.

You can read useful information later.

54
42

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

ローカルLLMの脆弱性があるんか?〜CVE-2024-50050から学ぶ「自分だけで使ってるから安全」の落とし穴〜

54
Posted at

この記事の対象読者

  • OllamaLM StudioでローカルLLMを動かしている方
  • 「ローカルで動かしてるから脆弱性は関係ないでしょ?」と思っている方
  • セキュリティ対策が必要かどうか判断したいエンジニアの方

この記事で得られること

  • ローカルLLM基盤を狙う主要な脆弱性の全体像: CVE-2024-50050をはじめとする直近の重大CVEを体系的に理解できる
  • 「自分に対応が必要か」の明確な判断基準: 利用形態ごとのリスクレベルを整理し、自分がどこに該当するか即座にわかる
  • 具体的な防御策とチェックスクリプト: コピペで使える環境診断ツールと設定ファイルで今すぐ対応できる

この記事で扱わないこと

  • プロンプトインジェクションやジェイルブレイク等の「AIモデル自体」への攻撃
  • クラウドAPI(OpenAI、Anthropic等)利用時のセキュリティ
  • モデルの学習データ汚染(データポイズニング)の詳細

1. ローカルLLMセキュリティとの出会い

「ローカルで動かしてるんだから、外部に公開してるわけじゃないし、セキュリティとか気にしなくていいでしょ?」

正直に言おう。私もそう思っていた。Ollamaをインストールして、GGUFモデルをダウンロードして、localhostでチャットする。データは外に出ない。完璧じゃないか。

ところが2024年後半から、ローカルLLM基盤を標的にした脆弱性が次々と公開され始めた。CVE-2024-50050(Meta Llama Stack)、CVE-2024-37032(Ollama)、CVE-2024-34359(llama-cpp-python)...。しかもCVSSスコアが9.0超えの「Critical」がゴロゴロ転がっている。

これらの脆弱性の恐ろしいところは、「ローカルで使っている"つもり"でも攻撃を受ける経路が存在する」ということだ。

この記事では、ローカルLLM界隈で実際に報告された脆弱性を整理し、「結局、自分は対応が必要なのか?」という疑問にズバッと答えていく。

ここまでで、なぜローカルLLMにもセキュリティ対策が必要なのか、その問題意識が共有できたと思う。次は、この記事で使う用語を整理しておこう。


2. 前提知識の確認

本題に入る前に、この記事で登場する用語を確認する。

2.1 CVE(Common Vulnerabilities and Exposures)とは

ソフトウェアの脆弱性に割り当てられる世界共通の識別番号。「CVE-2024-50050」のように「CVE-年-通番」の形式で表記される。番号が振られている=「公式に認知された脆弱性」ということだ。

2.2 CVSS(Common Vulnerability Scoring System)とは

脆弱性の深刻度を0.0〜10.0で数値化するスコアリングシステム。料理で言えばミシュランの星のようなもの。ただし、こちらは星が多いほど「ヤバい」。

スコア範囲 深刻度 例えるなら
0.0 なし 平和そのもの
0.1〜3.9 Low 軽い風邪
4.0〜6.9 Medium インフルエンザ
7.0〜8.9 High 入院レベル
9.0〜10.0 Critical 緊急搬送

2.3 RCE(Remote Code Execution)とは

攻撃者がリモートから任意のコードを実行できる脆弱性のこと。これが成立すると、あなたのマシンで攻撃者が好き放題にコマンドを叩ける。ローカルLLMの脆弱性の多くはこのRCEに分類される。

2.4 デシリアライズ(Deserialization)とは

プログラムが保存・送信用に変換(シリアライズ)したデータを、元のオブジェクトに復元する処理のこと。Pythonのpickle.loads()が代表例。この復元処理に悪意あるデータを食わせると、任意のコードが実行される——これが今回の脆弱性の多くに共通する根本原因だ。

これらの用語が押さえられたら、実際にどんな脆弱性が報告されているのか見ていこう。


3. ローカルLLM脆弱性が生まれた背景

3.1 「速度優先」で生まれたエコシステム

ローカルLLMのエコシステムは、2023年のLlama 2公開を皮切りに爆発的に成長した。OllamaはGitHubで15万スター超え、Hugging Faceには数十万のモデルが公開されている。

しかし、この急成長には代償があった。多くのツールが「まずは動かすこと」を最優先にして開発されたため、セキュリティは後回しにされがちだった。

3.2 従来のソフトウェアとの決定的な違い

従来のWebアプリケーションなら、認証・認可・入力検証は基本中の基本。しかしローカルLLM基盤には独特の事情がある。

従来のWebアプリ ローカルLLM基盤
認証は当たり前 認証機構がそもそもないことが多い
入力は信頼しない モデルファイルを丸ごと信頼してロードする
デプロイ手順が確立 「とりあえずollama run」で動く手軽さ
セキュリティレビュー文化がある AI/MLコミュニティではセキュリティ意識が発展途上

3.3 Pickle:便利だけど危険な「信頼ベース」のフォーマット

多くの脆弱性の根本原因となっているのが、Pythonのpickleモジュール。PyTorchのモデル保存形式は本質的にpickleファイルであり、デシリアライズ時に任意のPythonコードを実行できてしまう。

これは、知らない人から届いた荷物を「中身を確認せずに開封して、書いてある指示を全部実行する」ようなものだ。正規のモデルなら問題ないが、悪意あるコードが仕込まれていたら...結果は想像がつくだろう。

背景がわかったところで、具体的にどんな脆弱性が報告されているのか見ていこう。


4. 基本概念と仕組み:主要な脆弱性の全体像

ローカルLLM関連の脆弱性を攻撃経路別に分類すると、大きく3つのカテゴリーに整理できる。

4.1 カテゴリ1:ネットワーク経由の攻撃(APIの脆弱性)

LLM推論サーバーのAPIを経由して攻撃されるパターン。

CVE 対象 CVSS 概要
CVE-2024-50050 Meta Llama Stack 9.3〜9.8 pyzmqのrecv_pyobj()で未検証データをpickleデシリアライズ→RCE
CVE-2024-37032 Ollama Critical 細工されたHTTPリクエストでRCE(通称「Probllama」)
CVE-2025-62164 vLLM 8.8 Completions APIへの悪意あるテンソルでメモリ破壊→RCE
CVE-2025-63389 Ollama Critical API認証バイパスによる不正なモデル管理操作
CVE-2024-28224 Ollama High DNSリバインディングによるAPI不正アクセス→ファイル窃取

CVE-2024-50050の技術的詳細:

Meta Llama Stackのデフォルト推論実装(Meta Reference)が、pyzmqライブラリのrecv_pyobj()を使用してネットワークソケットからデータを受信していた。この関数は内部的にpickle.loads()を呼び出すため、ネットワーク越しに送り込まれた悪意あるPythonオブジェクトがそのままデシリアライズ・実行される。

# 脆弱なコード(概念的な再現)
# recv_pyobj()は内部でpickle.loads()を呼ぶ
data = socket.recv_pyobj()  # ← ネットワークから受信したデータを無検証でデシリアライズ

# 修正後(v0.0.41以降)
# Pydantic JSONによる型安全な実装に置き換え
data = receive_and_validate(socket)  # ← JSON + 型検証

出典: Oligo Security - CVE-2024-50050: Critical Vulnerability in meta-llama/llama-stack

4.2 カテゴリ2:悪意あるモデルファイル経由の攻撃(サプライチェーン攻撃)

ダウンロードしたモデルファイル自体に悪意あるコードが仕込まれているパターン。

CVE 対象 CVSS 概要
CVE-2024-34359 llama-cpp-python 9.7 Jinja2テンプレートエンジン経由で任意コード実行
CVE-2025-66448 vLLM Critical モデルconfigのauto_map経由でtrust_remote_code=Falseを無視してRCE
(Ollama OOB Write) Ollama Critical 悪意あるGGUFファイルのメタデータでメモリ破壊→RCE
(nullifAI) Hugging Face上のモデル - Pickle内の悪意あるコードがPicklescanを回避

これが一番怖い。「ローカルでしか使ってない」人にも直撃する攻撃経路だ。

Hugging Faceやその他のリポジトリからモデルをダウンロードする行為自体がリスクとなる。2025年にはReversingLabsがHugging Face上で、セキュリティスキャン(Picklescan)を回避する新手法を使った悪意あるモデルを発見している。

出典: ReversingLabs - Malicious ML models discovered on Hugging Face platform

4.3 カテゴリ3:フロントエンド/UI経由の攻撃

Open WebUIなどのGUIツール経由で攻撃されるパターン。

CVE 対象 CVSS 概要
CVE-2024-6707 Open WebUI High パストラバーサルによる任意ファイルアップロード
CVE-2025-64496 Open WebUI 7.3〜8.0 Direct Connections機能でSSE経由のXSS→JWT窃取→アカウント乗っ取り

CVE-2025-64496は特に巧妙だ。「同僚からSlackで共有されたローカルLlamaモデルのエンドポイント」に接続しただけで、テストプロンプトを1つ送った瞬間にJWTトークンが窃取される。

出典: Cato Networks - Vulnerability Discovered in Open WebUI (CVE-2025-64496)

基本概念が理解できたところで、いよいよ核心に入ろう。「自分は対応が必要なのか?」を判断するフローチャートだ。


5. 実践:あなたに対応は必要か? 利用形態別リスク判定

5.1 リスク判定フローチャート

あなたのローカルLLM利用形態は?
│
├─ A. 完全ローカル(localhost限定、外部接続なし)
│   └─ リスクレベル: ★★☆☆☆(中〜低)
│      ※ ただしモデルの入手元次第でサプライチェーン攻撃のリスクあり
│
├─ B. ローカルネットワークに公開(社内LAN、家庭内LAN)
│   └─ リスクレベル: ★★★☆☆(中)
│      ※ DNS Rebinding攻撃の対象になりうる
│
├─ C. インターネットに公開(ポート開放、リバースプロキシ等)
│   └─ リスクレベル: ★★★★★(極めて高)
│      ※ 全カテゴリの脆弱性に晒される
│
└─ D. Docker等でコンテナデプロイ
    └─ リスクレベル: ★★★★☆(高)
       ※ Dockerのデフォルト設定はAPIを公開する

5.2 「完全ローカルなら安全」は本当か?

結論から言うと、「完全ローカルでも安全とは言い切れない」。

確かに、ネットワーク経由の攻撃(カテゴリ1)についてはlocalhostバインドで大幅にリスクを低減できる。Ollamaのデフォルト設定(127.0.0.1:11434)であれば、外部からの直接的なAPI攻撃は成立しない。

しかし、以下の経路は「完全ローカル」でも攻撃が成立する。

攻撃経路 完全ローカルで成立するか 説明
ネットワーク経由のAPI攻撃 No localhostバインドなら外部から到達不可
DNS Rebinding Yes ブラウザ経由でlocalhostのAPIにアクセス可能
悪意あるモデルファイル Yes ダウンロードしたモデル自体が攻撃コードを含む
悪意あるGGUFテンプレート Yes 推論時にJinja2テンプレート経由でコード実行
Open WebUI経由の攻撃 Yes 悪意あるエンドポイントに接続するだけで成立

つまり、ネットワークに公開していなくても「信頼できないモデルをロードする」だけで攻撃される可能性があるのだ。

5.3 利用形態別:具体的に何をすべきか

パターンA: 完全ローカル利用者(最低限やるべきこと)

# 1. Ollamaを最新版にアップデート
# Windows
winget upgrade Ollama.Ollama

# macOS
brew upgrade ollama

# Linux
curl -fsSL https://ollama.com/install.sh | sh
# 2. バインドアドレスがlocalhostであることを確認
echo $OLLAMA_HOST
# 何も表示されないか、"127.0.0.1" なら OK
# "0.0.0.0" ならすぐに修正が必要
# 3. モデルは信頼できるソースからのみ取得
# NG: 見知らぬユーザーのリポジトリからダウンロード
ollama pull random-user/suspicious-model  # ← 危険

# OK: 公式またはverifiedなモデルを使用
ollama pull llama3.2                      # ← 安全
ollama pull mistral                        # ← 安全

パターンB: LAN内公開者(追加で必要な対策)

パターンAの対策に加えて、以下を実施する。

# ファイアウォールで11434ポートを制限(Linux)
sudo ufw allow from 192.168.1.0/24 to any port 11434
sudo ufw deny 11434
# ファイアウォールで11434ポートを制限(Windows)
New-NetFireWallRule -DisplayName 'Ollama - Allow LAN' `
  -Direction Inbound -LocalPort 11434 `
  -RemoteAddress 192.168.1.0/24 -Action Allow -Protocol TCP

New-NetFireWallRule -DisplayName 'Ollama - Block External' `
  -Direction Inbound -LocalPort 11434 `
  -Action Block -Protocol TCP

パターンC: インターネット公開者(最も厳重な対策が必要)

大前提: できればインターネットに直接公開しないこと。

どうしても必要な場合は、以下をすべて実施する。

# /etc/nginx/sites-available/ollama.conf
server {
    listen 443 ssl http2;
    server_name your-domain.com;

    # TLS設定
    ssl_certificate /etc/letsencrypt/live/your-domain.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/your-domain.com/privkey.pem;
    ssl_protocols TLSv1.2 TLSv1.3;

    # Basic認証
    auth_basic "Ollama Service";
    auth_basic_user_file /etc/nginx/.htpasswd;

    # リバースプロキシ
    location / {
        proxy_pass http://127.0.0.1:11434;
        proxy_set_header Host localhost:11434;
        proxy_set_header X-Real-IP $remote_addr;
    }

    # 管理系APIへのアクセスを制限
    location ~ ^/api/(push|pull|delete|create|copy) {
        deny all;
        return 403;
    }
}

5.4 環境別の設定ファイル

開発環境用(config.development.env)

# config.development.env - 開発環境(個人利用・最小構成)
OLLAMA_HOST=127.0.0.1:11434
OLLAMA_ORIGINS=http://localhost:3000,http://localhost:8080
OLLAMA_MODELS=/home/user/.ollama/models
OLLAMA_DEBUG=true
# セキュリティ: localhostのみ、CORS制限あり

ステージング環境用(config.staging.env)

# config.staging.env - ステージング環境(チーム内共有)
OLLAMA_HOST=192.168.1.100:11434
OLLAMA_ORIGINS=http://192.168.1.0/24
OLLAMA_MODELS=/srv/ollama/models
OLLAMA_DEBUG=false
OLLAMA_KEEP_ALIVE=5m
# セキュリティ: LAN限定、ファイアウォール必須

本番環境用(config.production.env)

# config.production.env - 本番環境(リバースプロキシ経由)
OLLAMA_HOST=127.0.0.1:11434
OLLAMA_ORIGINS=https://your-domain.com
OLLAMA_MODELS=/srv/ollama/models
OLLAMA_DEBUG=false
OLLAMA_KEEP_ALIVE=10m
OLLAMA_MAX_LOADED_MODELS=2
# セキュリティ: localhost限定 + Nginx認証 + TLS必須
# 直接外部に公開しないこと

5.5 よくあるエラーと対処法

エラー・症状 原因 対処法
OLLAMA_HOSTを変更したのに反映されない 環境変数がサービスに読み込まれていない systemctl daemon-reload && systemctl restart ollama を実行
ブラウザからlocalhost:11434にアクセスできない Ollamaサービスが起動していない ollama serve または systemctl start ollama で起動
LAN内の他PCからアクセスできない ファイアウォールが11434をブロック 上記のファイアウォール設定を適用
CORS errorが表示される OLLAMA_ORIGINSに接続元が未登録 環境変数に接続元URLを追加
Docker環境でAPIが外部に公開されている Dockerのデフォルトが0.0.0.0バインド ports: "127.0.0.1:11434:11434" に変更

5.6 環境診断スクリプト

問題が発生した場合や、自分の環境のセキュリティ状態を確認したい場合は、以下のスクリプトで診断できる。

#!/usr/bin/env python3
"""
ローカルLLMセキュリティ診断スクリプト
実行方法: python check_llm_security.py
"""

import subprocess
import socket
import json
import sys
import os
from pathlib import Path


def check_section(title: str):
    """セクションヘッダーを表示"""
    print(f"\n{'='*60}")
    print(f"  {title}")
    print(f"{'='*60}")


def check_ollama_binding():
    """Ollamaのバインドアドレスを確認"""
    issues = []

    # 環境変数チェック
    ollama_host = os.environ.get("OLLAMA_HOST", "")
    if ollama_host:
        if "0.0.0.0" in ollama_host:
            issues.append(
                "CRITICAL: OLLAMA_HOST が 0.0.0.0 に設定されています。"
                "全ネットワークインターフェースに公開されます。"
            )
        elif "127.0.0.1" in ollama_host or "localhost" in ollama_host:
            print("  [OK] OLLAMA_HOST はlocalhostにバインドされています")
        else:
            issues.append(
                f"WARNING: OLLAMA_HOST が {ollama_host} に設定されています。"
                "意図した設定か確認してください。"
            )
    else:
        print("  [OK] OLLAMA_HOST 未設定(デフォルト: 127.0.0.1)")

    # ポート開放チェック
    try:
        sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        sock.settimeout(2)
        result = sock.connect_ex(("0.0.0.0", 11434))
        sock.close()
        if result == 0:
            issues.append(
                "WARNING: ポート11434が0.0.0.0でリッスンしている可能性があります"
            )
    except Exception:
        pass

    return issues


def check_ollama_version():
    """Ollamaのバージョンを確認"""
    issues = []
    try:
        result = subprocess.run(
            ["ollama", "--version"],
            capture_output=True, text=True, timeout=5
        )
        version_str = result.stdout.strip()
        print(f"  Ollamaバージョン: {version_str}")

        # バージョン番号を抽出(簡易チェック)
        if "0.1." in version_str:
            minor = version_str.split("0.1.")[1].split(".")[0] if "0.1." in version_str else "0"
            try:
                if int(minor.split()[0]) < 46:
                    issues.append(
                        "CRITICAL: Ollama 0.1.46未満は既知の脆弱性があります。"
                        "すぐにアップデートしてください。"
                    )
            except ValueError:
                pass
    except FileNotFoundError:
        print("  [INFO] Ollamaはインストールされていません")
    except Exception as e:
        print(f"  [INFO] Ollamaバージョン確認をスキップ: {e}")

    return issues


def check_docker_exposure():
    """Docker環境でのOllama公開状態を確認"""
    issues = []
    try:
        result = subprocess.run(
            ["docker", "ps", "--format", "{{.Ports}} {{.Image}}"],
            capture_output=True, text=True, timeout=10
        )
        for line in result.stdout.strip().split("\n"):
            if "ollama" in line.lower() and "0.0.0.0" in line:
                issues.append(
                    f"CRITICAL: Dockerコンテナが外部に公開されています: {line.strip()}"
                )
    except FileNotFoundError:
        print("  [INFO] Dockerはインストールされていません")
    except Exception:
        pass

    return issues


def check_model_sources():
    """ダウンロード済みモデルの確認"""
    issues = []
    ollama_dir = Path.home() / ".ollama" / "models" / "manifests"

    if ollama_dir.exists():
        registries = list(ollama_dir.iterdir())
        for registry in registries:
            if registry.is_dir():
                print(f"  モデルレジストリ: {registry.name}")
                if registry.name != "registry.ollama.ai":
                    issues.append(
                        f"WARNING: 非公式レジストリからのモデルがあります: {registry.name}"
                    )
    else:
        print("  [INFO] Ollamaモデルディレクトリが見つかりません")

    return issues


def check_open_webui():
    """Open WebUIの状態を確認"""
    issues = []
    try:
        sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        sock.settimeout(2)
        result = sock.connect_ex(("127.0.0.1", 8080))
        sock.close()
        if result == 0:
            print("  [INFO] Open WebUIがポート8080で動作中")
            issues.append(
                "INFO: Open WebUI検出。v0.6.35以上であることを確認してください"
                "(CVE-2025-64496対策)"
            )
    except Exception:
        pass

    return issues


def main():
    """メイン診断処理"""
    print("=" * 60)
    print("  ローカルLLMセキュリティ診断ツール v1.0")
    print("=" * 60)

    all_issues = []

    check_section("1. Ollamaバージョン確認")
    all_issues.extend(check_ollama_version())

    check_section("2. ネットワークバインド確認")
    all_issues.extend(check_ollama_binding())

    check_section("3. Docker公開状態確認")
    all_issues.extend(check_docker_exposure())

    check_section("4. モデルソース確認")
    all_issues.extend(check_model_sources())

    check_section("5. Open WebUI確認")
    all_issues.extend(check_open_webui())

    # 結果サマリー
    check_section("診断結果サマリー")
    critical = [i for i in all_issues if i.startswith("CRITICAL")]
    warnings = [i for i in all_issues if i.startswith("WARNING")]
    infos = [i for i in all_issues if i.startswith("INFO")]

    if critical:
        print(f"\n  CRITICAL: {len(critical)}")
        for issue in critical:
            print(f"    [!] {issue}")

    if warnings:
        print(f"\n  WARNING: {len(warnings)}")
        for issue in warnings:
            print(f"    [!] {issue}")

    if infos:
        print(f"\n  INFO: {len(infos)}")
        for issue in infos:
            print(f"    [i] {issue}")

    if not all_issues:
        print("\n  [OK] 重大な問題は検出されませんでした")
        print("  ただし、モデルの入手元には常に注意してください")
    else:
        print(f"\n  合計: {len(all_issues)}件の検出事項")

    return 1 if critical else 0


if __name__ == "__main__":
    sys.exit(main())

5.7 Docker設定(安全なデプロイ例)

# docker-compose.yml - セキュアなOllamaデプロイ設定
version: '3.8'
services:
  ollama:
    image: ollama/ollama:latest
    # localhostのみにバインド(これが最重要)
    ports:
      - "127.0.0.1:11434:11434"
    volumes:
      - ollama_data:/root/.ollama
    environment:
      - OLLAMA_HOST=0.0.0.0:11434  # コンテナ内部は0.0.0.0でOK
      - OLLAMA_KEEP_ALIVE=5m
    deploy:
      resources:
        reservations:
          devices:
            - driver: nvidia
              count: all
              capabilities: [gpu]
    restart: unless-stopped

  # Open WebUIを使う場合(オプション)
  open-webui:
    image: ghcr.io/open-webui/open-webui:main
    ports:
      - "127.0.0.1:8080:8080"  # こちらもlocalhost限定
    volumes:
      - webui_data:/app/backend/data
    environment:
      - OLLAMA_BASE_URL=http://ollama:11434
    depends_on:
      - ollama
    restart: unless-stopped

volumes:
  ollama_data:
  webui_data:

最重要ポイント: ports127.0.0.1:プレフィックスを必ずつけること。

"11434:11434"と書くと0.0.0.0:11434にバインドされ、全世界に公開される。Ollamaの公式Dockerイメージのデフォルトがまさにこれなので、要注意だ。

実装方法がわかったので、次は具体的なユースケース別の対応を見ていく。


6. ユースケース別ガイド

6.1 ユースケース1: 個人でAIチャットを楽しむ人

想定読者: 自宅PCでOllama + LM Studio等を使ってAIと会話している個人ユーザー

リスクレベル: ★★☆☆☆(低〜中)

推奨対策:

  1. ソフトウェアを最新版に保つ(最重要)
  2. モデルは公式レジストリからのみ取得
  3. OLLAMA_HOSTがデフォルトのままであることを確認

チェックコマンド:

# Ollamaが最新か確認
ollama --version

# バインドアドレス確認(Linuxの場合)
ss -tlnp | grep 11434
# "127.0.0.1:11434" と表示されればOK
# "0.0.0.0:11434" と表示されたら要修正

# Windowsの場合
netstat -an | findstr 11434

6.2 ユースケース2: 社内でAIサービスを共有する企業エンジニア

想定読者: 社内ネットワークでOllamaサーバーを立て、チームで共有している方

リスクレベル: ★★★★☆(高)

推奨対策:

  1. パターンAの全対策 + ファイアウォール設定
  2. リバースプロキシで認証を追加
  3. APIアクセスログの取得
  4. モデル管理APIの無効化

アクセスログ設定:

# /etc/nginx/sites-available/ollama-internal.conf
server {
    listen 443 ssl;
    server_name ollama.internal.company.com;

    ssl_certificate /path/to/cert.pem;
    ssl_certificate_key /path/to/key.pem;

    # アクセスログ(監査用)
    access_log /var/log/nginx/ollama_access.log combined;

    # Basic認証
    auth_basic "AI Service";
    auth_basic_user_file /etc/nginx/.htpasswd_ollama;

    # 推論APIのみ許可
    location /api/generate {
        proxy_pass http://127.0.0.1:11434;
    }
    location /api/chat {
        proxy_pass http://127.0.0.1:11434;
    }

    # 管理APIはブロック
    location / {
        return 403 "Access denied";
    }
}

6.3 ユースケース3: AIアプリケーションを開発している開発者

想定読者: vLLM、Llama Stack等を使ってAIアプリケーションを構築している方

リスクレベル: ★★★★★(極めて高)

推奨対策:

  1. 全パターンの対策を実施
  2. 依存パッケージのバージョン管理を厳格化
  3. モデルロード時のtrust_remote_code=Falseを徹底
  4. 入力検証の実装

依存関係チェック:

"""
AI開発プロジェクトのセキュリティチェック
"""
import importlib.metadata
import sys


# 脆弱なバージョンの定義
VULNERABLE_PACKAGES = {
    "llama-stack": {
        "fixed": "0.0.41",
        "cve": "CVE-2024-50050",
    },
    "llama-cpp-python": {
        "fixed": "0.2.72",
        "cve": "CVE-2024-34359",
    },
    "vllm": {
        "fixed": "0.11.1",
        "cve": "CVE-2025-66448, CVE-2025-62164",
    },
    "open-webui": {
        "fixed": "0.6.35",
        "cve": "CVE-2025-64496",
    },
}


def check_package_versions():
    """インストール済みパッケージの脆弱性チェック"""
    print("パッケージセキュリティチェック")
    print("-" * 50)
    found_issues = False

    for pkg_name, info in VULNERABLE_PACKAGES.items():
        try:
            version = importlib.metadata.version(pkg_name)
            # 簡易バージョン比較
            if version < info["fixed"]:
                print(f"  [!] {pkg_name} {version}")
                print(f"      脆弱性: {info['cve']}")
                print(f"      修正版: {info['fixed']}以上にアップデートしてください")
                found_issues = True
            else:
                print(f"  [OK] {pkg_name} {version} (修正済み)")
        except importlib.metadata.PackageNotFoundError:
            pass  # 未インストールならスキップ

    if not found_issues:
        print("  脆弱なパッケージは検出されませんでした")


if __name__ == "__main__":
    check_package_versions()

ユースケースを把握できたところで、この先の学習パスを確認しよう。


7. 学習ロードマップ

この記事を読んだ後、次のステップとして以下をおすすめする。

初級者向け(まずはここから)

  1. Ollama公式FAQ - セキュリティ設定 でデフォルト設定を確認する
  2. 上記の環境診断スクリプトを実行して、自分の環境を確認する
  3. safetensorsフォーマットについて学ぶ — pickleに代わる安全なモデル保存形式

中級者向け(実践に進む)

  1. OWASP ML Security Top 10 で機械学習セキュリティの全体像を把握する
  2. Nginx + 認証の構成を自分の環境に導入する
  3. NVD(National Vulnerability Database) で使用中のツールのCVEをウォッチする

上級者向け(さらに深く)

  1. Oligo Security Blog で最新のAIインフラ脆弱性研究をフォローする
  2. Pwn2Own Berlin 2025のAIカテゴリ の発表内容を確認する(AIシステムが公式競技カテゴリに追加された)
  3. ProtectAIのModelScanを導入してモデルファイルのセキュリティスキャンを自動化する

8. まとめ

この記事では、ローカルLLMの脆弱性について以下を解説した。

  1. CVE-2024-50050を含む主要な脆弱性の全体像 — ネットワーク経由、モデルファイル経由、UI経由の3カテゴリで整理
  2. 「自分に対応が必要か」の判断基準 — 完全ローカルでもサプライチェーン攻撃のリスクがあること
  3. 具体的な防御策 — 環境別の設定ファイル、診断スクリプト、Docker設定

私の所感

正直、この記事を書くために調査を進める中で、自分自身の認識の甘さを痛感した。

「ローカルで使ってるから安全」——これは2024年までなら通用したかもしれない。しかし、ローカルLLMの普及に伴ってセキュリティ研究者の目がAIインフラに向き始め、次々と脆弱性が発見されている。2025年のPwn2OwnではAIシステムが正式な競技カテゴリに追加されたほどだ。

最も重要な教訓は「信頼できないモデルをロードしない」ということ。ネットワーク設定を完璧にしても、悪意あるモデルファイルをダウンロードして実行してしまったら意味がない。

ローカルLLMの世界は日々進化している。セキュリティも同様に進化させていこう。


参考文献


この記事が役に立ったら、いいね・ストックしていただけると励みになります。
ローカルLLMのセキュリティ関連で他にも気になるトピックがあれば、コメントで教えてください。

他にもローカルLLM関連の記事を書いています:


X(Twitter)でもAI/ML系の情報を発信中 → @geneLab_999

54
42
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up

Comments

No comments

Let's comment your feelings that are more than good

54
42

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

Login to continue?

Login or Sign up with social account

Login or Sign up with your email address