ステータス:ドラフト(2026-10-07)。 暗号化・復号速度、整合性チェック、そして復号したウエイトをメモリボリュームに置いてvLLMサーバーを立ち上げるパス(0.6Bモデル)は筆者が直接測定した値です。機密コンピューティングはハードウェアがないため直接検証できず、論文やベンダーのドキュメントの記述を参照して引用しました。大きなモデルや複数のGPUでのロードは測定していません(未測定)。 執筆ルール:実測データを基本とし、測定できなかったものは「未測定」と表記します。
目次
- 1. 質問を先に決める:何からウエイトを守るのか
- 2. 方法
- 3. コスト:どれくらい遅くなり、どれくらい大きくなるのか
- 4. 整合性:改ざんと言い換え(すり替え)を検知する
- 5. 防げないこと:実行中のウエイトは平文である
- 6. 鍵をどこに置くのか
- 7. 面接で出そうな質問
- 外部の根拠
- 確認できなかったこと
- 参考文献
- 確認したバージョンと日付
1. 質問を先に決める:何からウエイトを守るのか
「モデルを暗号化する」という言葉は、脅威を定義しなければ意味がありません。ウエイトが露出する経路は複数あり、暗号化がそれを防ぐのは一部の経路のみです。
| 脅威 | 例 | 保存時暗号化 | その他に必要なこと |
|---|---|---|---|
| ストレージ・バックアップ・イメージレジストリからの流出 | バケット権限のミス、バックアップテープの紛失、イメージレイヤーの流出 | 防ぐ(鍵が別にある場合) | 鍵をストレージとは別の場所に保管する |
| 転送中の盗聴 | ノードとストレージの間 | TLSが防ぐ、保存時暗号化は二重の防御 | mTLS |
| ファイルの改ざん・すり替え(サプライチェーン) | 悪意あるウエイトへの置き換え、順序の入れ替え | 認証暗号(GCM)が検知(第4節で確認) | 署名と出所の検証 |
| 実行中サーバーのメモリダンプ | ノードのroot、同一ノード内の悪意あるポッド、デバッガー | 防げない(復号されたウエイトがメモリ上に存在する) | アクセス制御、機密コンピューティング |
| サービングAPIへの機能模倣 | 大量のクエリで出力を収集し模倣モデルを学習する | 無関係 | レートリミット、異常検知、契約 |
| サービングAPIからのパラメータ抽出 | ロジットや対数確率を露出するAPIから最終層の実際のパラメータを復元(Carlini他、ICML 2024) | 無関係 | 露出するフィールドの制限(logprobsとlogit biasの組み合わせ禁止) |
この記事では、1行目と3行目(保存時暗号化と整合性)のコストと効果を測定し、4行目がなぜ残るのかを整理します。
2. 方法
| 項目 | 値 |
|---|---|
| 対象 | Qwen3-1.7Bのsafetensorsの2つの分割のうち大きい方(3.44GB) |
| 暗号 | AES-256-GCM(cryptography ライブラリ)、64MiBのチャンクごとに12バイトのランダムnonce、16バイトのタグ |
| 追加認証データ(AAD) | チャンク番号(8バイト)。チャンクの順序がすり替わっても検知するため |
| 保存形式 | チャンクごとに(長さ4バイト、nonce 12バイト、暗号文、タグ16バイト) |
| 環境 | コンテナ(CPU制限6コア、一時ディスク)、計算はCPUのみ使用 |
| 測定 | ファイル暗号化・復号のスループット(ディスクの書き込み・読み込みを含む)、メモリ内の純粋な計算スループット(ディスクなしで64MiBバッファを繰り返し) |
3. コスト:どれくらい遅くなり、どれくらい大きくなるのか
| 項目 | 値 |
|---|---|
| 平文ファイルの読み込み(ページキャッシュを含むため上限に近い) | 2.15 GB/s |
| 暗号化、ファイルからファイルへ | 0.56 GB/s(スレッド数1つと4つで同じ) |
| 復号、ファイルからメモリへ | スレッド1つ 0.78、2つ 0.92、4つ 0.95 GB/s |
| 純粋な計算、AES-256-GCM暗号化 | スレッド1つ 1.81、2つ 2.10、4つ 2.21 GB/s |
| 純粋な計算、AES-256-GCM復号 | スレッド1つ 1.77、2つ 2.10、4つ 2.20 GB/s |
| ファイルサイズの増加 | 3.441GBが3.441GBに(64MiBのチャンクごとに32バイト、約0.00005%) |
- ファイルサイズは実質的に増加しません。 チャンクごとにnonce 12バイトとタグ16バイト、長さ4バイトが付加されるだけです。
- 復号は平文読み込みの36〜44%の速度です(0.78〜0.95対2.15 GB/s)。ただし、 純粋な計算は1.8〜2.2 GB/s であるため、このテストのファイルパスは計算ではなく、入出力やPython側の処理によって低速化していました。この内訳の分解は行っていません(推測)。
- スレッドを増やしてもメリットが小さいです。 計算は1つから4つで22%しか増加せず(1.81から2.21)、ファイルパスでは22%(0.78から0.95)でした。当初はスレッドが同一のメモリ帯域幅を奪い合うためだと推測していましたが、後述の「CPU補強テスト」でこの推測が誤りであることが判明しました。
- ハードウェアアクセラレーション(AES命令)を備えたCPUでした。 アクセラレーションがない場合の速度は、後述の「CPU補強テスト」にて環境変数でアクセラレーションを無効にして測定しました。
コールドスタートに加算される時間
復号のスループット約0.9 GB/sをそのまま適用すると、モデルサイズ別の追加時間は以下のようになります(計算値、本テストのファイルパス値基準)。
| モデルサイズ | 復号の追加時間(0.9 GB/s) | 純粋な計算速度(2 GB/s)だった場合 |
|---|---|---|
| 3.4GB(1.7B bf16) | 約3.8秒 | 約1.7秒 |
| 7GB(12B 4bit) | 約7.8秒 | 約3.5秒 |
| 140GB(70B bf16) | 約156秒 | 約70秒 |
140GBは外挿による計算であり、直接測定していません。70Bモデルは通常複数のGPUに分散してロードされるため、GPUごとに並列で復号を行えば時間を分散できます(プロセスを分けた並列テストは後述の「CPU補強テスト」)。第14回のサーバー準備時間(再起動35〜55秒、そのうちウエイトのロードが4〜6秒)と比較すると、小規模なモデルでは数秒の復号が目立つ割合となり、大規模なモデルではモデルのダウンロードおよびロード時間に埋もれます。
4. 整合性:改ざんと言い換え(すり替え)を検知する
GCMは暗号化とともにタグを用いて改ざんを検証します(NIST SP 800-38D)。2つのケースを実際に試行しました。
| 試行 | 結果 |
|---|---|
| 暗号文チャンクの1バイトを反転させる | InvalidTag により復号が拒否される |
| 1番目のチャンクを0番目の位置に配置する(順序のすり替え) | InvalidTag により復号が拒否される(チャンク番号をAADに含めているため) |
| 正常に復号された結果のSHA-256 | オリジナルと一致 |
- チャンク番号をAADに含めない場合、順序のすり替えは検知されません。 各チャンクは自身のタグに対して正しいため、位置を縛る情報が認証対象に含まれていなければなりません。実際に確認しました。4つのチャンクを暗号化して2つのチャンクの位置を入れ替えた際、AADがない場合は復号が エラーなく成功し(原文とは異なる内容)、チャンク番号をAADに含めた場合は
InvalidTagとなりました。 - ファイルの末尾を切り詰めてチャンクをごっそり削除する攻撃はこのテストでは扱いませんでした。実際に確認しました。最後のチャンクを除外したファイルは、AADがチャンク番号のみである場合、3つのチャンクが エラーなく復号されてしまい、切り詰められた事実をタグが通知できませんでした。総チャンク数をAADに含めた場合、ヘッダーの総数を書き換えていたときはすべてのチャンクが
InvalidTagとなり、ヘッダーをそのままにした場合は読み込んだ数と総数を比較する段階(コードのassert)が切り詰めを検出しました。つまり、総数をAADにバインドすることと、個数の確認の両方が必要です。 - nonceはチャンクごとにランダムな12バイトです。NIST SP 800-38Dセクション8.2.2では、ランダム方式IVのランダムフィールドに96ビット以上を要求し、セクション8.3では同じ鍵を使用する認証暗号化呼び出しの総数を2の32乗以下に制限しています。64MiBのチャンクに分割した場合、この制限は同一の鍵に対して約256PiBとなります(2の32乗×64MiB、筆者計算)。140GBモデルは約2,100個のチャンクであるため、同じ鍵でこのようなモデルを約200万回暗号化するまで制限内であり、1つのファイル内でnonceが重複する確率は誕生日のパラドックスの近似により約2の-75乗です(筆者計算)。制限は暗号化の呼び出しをカウントするため、同じ鍵で再暗号化を繰り返すローテーション手順では呼び出しが累積します。復号はカウントされません。
- AADにファイル識別子も含める必要があります(筆者の推論であり、NISTの要求ではありません)。 本記事のAADにはチャンク番号と総チャンク数のみが含まれています。同じ鍵で複数のファイルを暗号化すると、異なるファイルの同じ番号のチャンクを入れ替えてもタグの検証が通過してしまいます。ファイルごとに個別にラップされた鍵(DEK)を使用するか、ファイル識別子をAADに含める必要があり、これは第6節のDEKの推奨事項と噛み合うポイントです。実際に確認しました。同じ鍵で作成した2つのファイルにおいて、ファイルAの1番目のチャンクをファイルBの1番目の位置に配置した際、AADが(チャンク番号、総数)のみである場合は 復号に成功し、1番目のチャンクはファイルAの内容になっていました。AADにファイル識別子を追加すると
InvalidTagとなりました。
暗号文は平文と区別がつかない
最初の1MiB(ヘッダーを含む)のバイトエントロピーは、平文(safetensors、bf16ウエイト)が6.34ビット/バイト、暗号文が8.00ビット/バイトでした。ウエイトファイルには統計的構造が残っており、圧縮やパターン分析の手がかりを与えてしまう可能性がありますが、暗号文はランダムと同等です。暗号化したファイルは圧縮もできないため、 圧縮は暗号化の前 に行う必要があります。
サービングエンジンのロードパスに組み込んだ場合、どれほど増加するか
復号したウエイトをメモリボリューム(/dev/shm)に配置し、そのパスを経由してvLLMサーバーを起動してみました。モデルはQwen3-0.6B(safetensors 1.50GB)で、8GB級GPU1基、チャンク64MiB、チャンク番号と総チャンク数をAADに含めた構成です。サーバーの準備時間は、サーバープロセスを開始した時点からヘルスチェックのレスポンスが返るまでであり、復号に要した時間はこの値に含まれていません。
| 区分 | 測定値 |
|---|---|
| 暗号化(1.50GB) | 2.43秒、0.62 GB/s |
| 復号3回(メモリボリュームへ) | 2.02、2.10、2.18秒(0.69〜0.75 GB/s)。Pythonの起動とインポートを含む呼び出し全体は2.3〜2.4秒 |
| 平文ディレクトリからのサーバー準備 | 68.0秒(初回起動)、29.4秒、28.3秒 |
| 復号パスからのサーバー準備 | 49.4秒(初回起動)、28.3秒、27.3秒 |
| 改ざん(暗号文の1バイト反転) | 復号ステップで InvalidTag により中断。ウエイトファイルは作成されず(設定ファイルのみコピーされる)、サーバー起動ステップに到達しない |
解釈は以下の通りです。
- 定常状態における追加コストは、復号時間そのものです。 2回目の起動以降、サーバーの準備時間は平文と復号パスで同等レベル(28秒前後)であり、これに約2.3秒の復号が先頭に付加されます。このモデルにおけるコールドスタートは約8%増加しました。
- 初回起動はいずれも長くなりました。 平文での初回起動時における68.0秒は、コンパイル結果がキャッシュになかったためと考えられ、復号パスの初回起動時における49.4秒はモデルパスが初めだったためキャッシュを再作成したものと推測します(ログで原因を確認したわけではありません)。パスや設定が変更されるたびに一度長くなる可能性があるため、復号コストよりもこの影響の方が大きくなる場合があります。
- 改ざんはサーバーが起動する前に拒否されます。 復号ステップが失敗した場合にサーバーを起動しない構成(起動スクリプトが復号の終了コードを確認する)が前提となります。
- このテストは0.6Bの1つのモデルと1枚のGPUでのみ行いました。140GBモデルや複数のGPUでの数値は計測していません(未実測)。
CPU補強テスト:アクセラレーションの無効化、並列化
上記テストの残りの疑問をCPUのみで再度計測しました(使い捨てのPod、22コアサーバーCPUの1つのノード、 cryptography 50.0.2、64MiBチャンク、メモリのみ使用)。このノードでは他のワークロードが同時に動作していたため、同一テストの絶対値が約40%変動しました(1プロセスでの復号は1.20 GB/sとなり、スレッドテストの1つは2.00 GB/sでした)。そのため、比率のみに意味を持たせています。
| テスト | 結果 |
|---|---|
| デフォルト(AES-NI、PCLMULQDQを使用) | 復号 2.01 GB/s、暗号化 2.08 GB/s(シングルスレッド) |
OPENSSL_ia32cap=~0x200000000000000 でAES-NIを無効化 |
0.47 GB/s(約4.3倍遅い) |
| AES-NIとPCLMULQDQの両方を無効化 | 0.29 GB/s(約6.9倍遅い) |
| スレッド数1、2、4、8での復号(1プロセス) | 2.00、2.52、2.41、2.44 GB/s |
| プロセス数1、2、4、8での復号 | 1.20、2.30、4.24、6.07 GB/s(1個と比較して1.9、3.5、5.1倍) |
- AES命令がない場合、復号は4〜7倍遅くなります。 140GBモデルの場合、上記の外挿(約70秒)は5分前後になります(計算値)。クラウドの一般インスタンスには大半でアクセラレーションがありますが、仮想化設定によりCPUフラグが隠されている環境の場合は確認が必要です(
grep -m1 -o aes /proc/cpuinfo). - スレッドは2つで頭打ちになりましたが、プロセスは増やすにつれて増加しました。 最初のメモリ帯域幅の推測は間違っていました。このノードのメモリ帯域幅に比べて2.5 GB/sは非常に小さな値であり、プロセスに分割したところ8つで6 GB/sまで上がりました。Pythonの1つのプロセス内でスレッド並列がブロックされる理由はこのテストでは分解していません(原因は未確認、インタープリター側の制約の可能性)。実用上は、チャンクをプロセス(またはGPUごとに個別のローダープロセス)に分散して復号する構成が適切です。
- 制限事項:1つのノードでの単一テストであり、ファイル入出力は除外しています。複数のGPUに実際に分散してロードする時間は計測していません(未実測)。
5. 防げないこと:実行中の重みは平文である
サーバーがモデルを使用するには、復号した重みをメモリ(GPUメモリを含む)にロードする必要があります。その瞬間から、 ノードのrootや、同じノード上の侵害されたプロセスがメモリを読み取ることができます。 通常のGPUはメモリを暗号化しないため、この脅威は保存時暗号化だけでは防げません。
NVIDIAの技術ブログ(2023-05-31)では、H100 GPUがCPUの信頼実行環境(AMD SEV-SNP、Intel TDX)と連携して動作する機密コンピューティング機能を提供していると説明しています。この記事ではこの機能を直接テストできていません(データセンターGPUがないため)。同社の2023-08-03のブログでは、保護方式を次のように記述しています。CPU TEEが仮想マシンのメモリを暗号化し、GPUはハードウェアによって隔離され、CPUとGPU間のデータは暗号化されたバウンスバッファを経由してやり取りされます。一方、GPUパッケージ上のHBMは日常的な物理攻撃ツールに対して安全とみなされるため、暗号化はしないと記載されています。したがって、機密コンピューティングを「GPUメモリ暗号化」と呼ぶのは不正確であり、防御の対象はホスト側の平文露出とホスト管理者のアクセスです。また、同記事ではCPUとGPU間の帯域幅がCPUの暗号化性能によって制限され、約4GB/秒であると記述されていますが、起動時に重みをロードする本記事のパスでは、この値がロード時間を左右する可能性があります(ベンダーの記述であり、直接計測していません)。鍵をどのように安全に渡すか(証明、すなわちattestationの後に鍵を公開する)も別の設計課題です。
したがって、実務上のレイヤーは次のように積み重なります。
- ストレージ層: 重みを暗号化して保存し、鍵はKMSやシークレット管理ツールに配置します。バケットの権限エラーが即座に流出につながることはありません。
- ロード層: 起動時に鍵を受け取って復号します。復号した平文をディスクに残さないようにメモリベースのボリューム(
emptyDirのmedium: Memory)を使用すると、ノードのディスクに平文は残りません。このボリュームはモデルのサイズ分だけRAMを消費するため、ノードのメモリおよびPodのメモリ制限に含めて計算する必要があります(計算値、直接テストはしていません)。 - 実行層: ノードへのアクセス制御(SSH、デバッグPodの禁止)、Podのセキュリティ設定、必要に応じて機密コンピューティングノード。
- 出力層: レートリミットと異常クエリの検知。重みを盗まなくても、出力を集めて模倣モデルを作成することが可能です。
6. 鍵をどこに置くか
- イメージや重みのストレージと 同じ場所に置かないでください。 そうすると、一度の流出で両方が露出してしまいます。
- 鍵はPodの起動時に外部KMSから取得し(Podのアイデンティティで認証)、環境変数に平文のままで長く放置しません。
- 鍵を変更する手順(再暗号化)が必要です。3.4GBのファイルを0.56 GB/sで再暗号化すると約6秒(計算値)ですが、大きなモデルでは時間がかかるため、 ファイルをラップする鍵(DEK)を別途用意し、その鍵をマスターキーでラップする 方式により、鍵ローテーションのコストを削減できます。
7. 面接で想定される質問
- モデルの重みを暗号化すると、何が保護されるのか? ストレージ・バックアップ・レジストリからの流出と改ざん(GCMの場合)。実行中のメモリダンプは防げない。
- GCMを使用する理由は? 暗号化と改ざん検知を同時に行うため。1バイト反転させると復号が拒否される。
- チャンクに分割して暗号化する際の注意点は? チャンク番号を追加認証データに入れて順序のすり替えを防止し、チャンク数を認証対象に含めて読み込んだ数を確認することで切り詰めを防止し、ファイル識別子も入れて別ファイルのチャンクが混ざることを防止し(3つとも直接確認)、nonceが重複しないようにする。
- 復号はコールドスタートをどれくらい増加させるか? このテストでは約0.7〜0.9 GB/sであったため、7GBモデルでは約8〜10秒となり(外挿)、0.6Bモデルでは実際にサーバーの準備に約28秒、復号に約2.3秒が追加された。計算自体は2 GB/s水準である。
- 実行中のモデルをどのように保護するか? ノードのアクセス制御と機密コンピューティング(CPU TEEとGPUの隔離、PCIe区間の暗号化。HBM自体は暗号化されない)。この記事では論文とベンダーのドキュメントのみを確認した。
外部の根拠
直接計測できなかった、または計測だけでは不十分な主張について、以下の資料を参照して確認しました。「アブストラクトのみ」とは、本文ではなくアブストラクトまでしか読んでいないことを意味します。
| 主張 | 資料 | 確認した内容 |
|---|---|---|
| nonceと呼び出し回数の上限 | NIST SP 800-38D(2007) セクション8.2.2、8.3 | ランダムフィールドは96ビット以上、同一鍵での認証暗号化の呼び出し総数は2の32乗以下。2026年現在、改訂作業中だが効力を持つドキュメントは2007年版 |
| 機密コンピューティングの保護範囲 | NVIDIA技術ブログ(2023-08-03) | HBMは暗号化せず、CPU TEEと隔離および暗号化されたバウンスバッファで保護。CPUとGPU間は約4GB/秒(ベンダーの記述) |
| H100機密コンピューティングの推論オーバーヘッド | Zhu他、arXiv 2409.03992(Confidential Computing on NVIDIA Hopper GPUs: A Performance Benchmark Study) | 大半の一般的なクエリで7%未満、大規模モデルと長いシーケンスではほぼ0。70Bは4ビット量子化で1枚にロード |
| 同テーマに関する別の測定 | Chrapek他、arXiv 2509.18886(アブストラクトのみ) / Ibarra他、arXiv 2505.16501(アブストラクトのみ) | スループット低下4〜8%(Llama2 7B〜70B) / モデルを入れ替える構成において非機密側の方がスループットが45〜70%高い。差異の要因をモデルをGPUにロードする際の暗号化・復号コストに帰している |
| API経由のパラメータ抽出 | Carlini他、ICML 2024(arXiv 2403.06634) | 対数確率やロジットバイアスを公開するAPIから最終層を復元。対策としては、レートリミットよりも露出フィールドの制限が実際の措置であった |
| API経由の機能複製 | Tramèr他、USENIX Security 2016(arXiv 1609.02943、アブストラクトのみ) | 信頼度スコアを隠しても抽出される場合がある |
| 推論サーバーが乗っ取られた場合の持ち出し | Rinberg他、arXiv 2511.02620(アブストラクトのみ) | 正常な応答に重みを隠して持ち出す脅威を定式化し、検証ベースの検知を提案 |
参照できなかった資料もあります。RANDのモデル重み保護に関するレポート(2024)は原文へのアクセスがブロックされて参照できず、そのため流出事例の頻度に関する数値はこの記事に記載していません。
確認できなかったこと
- 機密コンピューティングのパフォーマンスコストと実際の保護範囲。ハードウェアがないため計測できず、以下の外部根拠の範囲(推論はおおむね一桁パーセント、条件によっては約20%まで、モデルを入れ替える構成では45〜70%の差)から推測するのみとなります。
- 大規模モデルおよび複数GPUでのロード時間。 0.6Bモデル1個の数値のみを計測し、並列復号やテンソル並列構成は計測していません。
- GPUによる復号方式、大規模モデルを複数GPUに実際に分散してロードする際の並列復号時間(CPU補強テストはプロセス並列までしか計測していません)。
- 鍵管理システム(KMS)の呼び出し遅延と障害時の動作。
参考文献
- NIST SP 800-38D、GCMおよびGMACの推奨:https://csrc.nist.gov/pubs/sp/800/38/d/final
cryptographyライブラリのAEADドキュメント:https://cryptography.io/en/latest/hazmat/primitives/aead/- NVIDIA技術ブログ、機密コンピューティングによる機密データとAIモデルの保護(2023-05-31):https://developer.nvidia.com/blog/protecting-sensitive-data-and-ai-models-with-confidential-computing/
- NVIDIA技術ブログ、Confidential Computing on NVIDIA H100 GPUs for Secure and Trustworthy AI(2023-08-03)
- Zhu ほか、「Confidential Computing on NVIDIA Hopper GPUs: A Performance Benchmark Study」: https://arxiv.org/abs/2409.03992
- Carlini ほか、「Stealing Part of a Production Language Model」: https://arxiv.org/abs/2403.06634
- 本シリーズの第14回(サーバーの準備時間)、第8回(safetensors形式とモデルのロード)
確認したバージョンと日付
2026年10月7日時点。 cryptography ライブラリ(AESGCM)、コンテナ内のPython 3.12、Qwen3-1.7B safetensors(速度テスト)、Qwen3-0.6B(サーバーロードパスのテスト、vLLM 0.31.0)。