【実務・中級編】 TPM 2.0におけるPCR(Platform Configuration Register)の役割と改ざん検知 – 暗号理論・認証基盤 & エンドポイントセキュリティ防御ガイド

泥沼のインシデント現場から学ぶ:TPM 2.0とPCRが守る「信頼の起点(Root of Trust)」

現場でインシデント対応をしていると、OSやアプリケーション層の脆弱性ばかりに目を奪われがちなエンジニアが多いことに危機感を覚える。だが、本当のプロなら知っているはずだ。「OSが起動する前の領域」、あるいは「ハードウェアが改ざんされた瞬間の検知」をサボれば、どれほど強固なWAFや認証基盤も、単なる飾りになるということを。

今日は、TPM 2.0の心臓部であるPCR(Platform Configuration Register)について、実務的な観点から掘り下げる。

—

PCRは「システムのDNA」を記録するレコーダーだ

PCRは、TPMチップ内にある揮発性のレジスタだ。ここには単なる値が入るわけではない。ブートローダー、ファームウェア、OSカーネルといった、起動プロセスの各段階の「ハッシュ値」が、積み上げ式(Extend演算)で格納される。

なぜこれが「改ざん検知」になるのか

攻撃者がルートキットを仕込んでカーネルを書き換えたとする。その瞬間にハッシュ値が変わり、PCRに記録される値も変わる。TPMは、この「PCRが特定の期待値と合致しない限り、秘密鍵を解放しない」という封印(Sealing)ができる。つまり、ハードウェア構成やブートプロセスが1ビットでも改ざんされたら、復号鍵は永遠に手に入らない。これが「信頼の起点」だ。

—

現場で遭遇する「PCR改ざん」の脅威シナリオ

「ファームウェアなんて書き換えるのは国家レベルの攻撃者だけだろう?」と高を括っていないか? 実際には、物理アクセス可能な攻撃者による「Evil Maid Attack(悪魔のメイド攻撃)」や、リモートから管理機能を悪用した設定変更のリスクがある。

特に、PCRの値を固定せずに、鍵をハードコードしたり、ディスク暗号化の解除を自動化(Auto-unlock)しているケースは、攻撃者にとって「どうぞご自由に中身を盗んでください」と言っているようなものだ。

—

実践:PythonでTPM 2.0の封印状態を確認する

実際、アプリケーションレベルで「今のシステムが安全か」をチェックするのは難しいが、TPM 2.0のAPI(tpm2-tss系)を叩くことで、現在のPCR値を確認するスクリプトは書ける。以下は、Pythonで現在のPCR 0(通常はBIOS/UEFIの測定値)を取得し、改ざんをチェックするイメージコードだ。

import subprocess

def get_pcr_value(pcr_index):
    """
    tpm2-toolsを利用して、指定したPCRの現在のハッシュ値を取得する
    本番環境では、事前のクリーンな状態のハッシュ値と比較するロジックを組む
    """
    try:
        # tpm2_pcrreadでPCR値を取得(出力はヘッダーが付くため加工が必要)
        result = subprocess.check_output(['tpm2_pcrread', f'sha256:{pcr_index}'], text=True)
        return result
    except subprocess.CalledProcessError as e:
        print(f"TPMアクセスエラー: {e}")
        return None

# 改ざん検知のロジック(簡易版)
EXPECTED_HASH = "事前取得した信頼できるPCR0の値"
current_hash = get_pcr_value(0)

if current_hash and EXPECTED_HASH not in current_hash:
    # ここでアラートを飛ばす、あるいはシステムを停止させる
    print("【重大警告】PCR値に不整合を検知!システム改ざんの疑いあり")
else:
    print("システム整合性チェックOK")

—

実運用における「封印(Sealing)」の鉄則

暗号鍵をPCRに紐づけて封印する際、多くのエンジニアが陥る罠は「どのPCRを使うか」の選定ミスだ。

  • PCR 0 – 3: ファームウェアやハードウェア構成。これらを紐付けると、BIOSのアップデートのたびに鍵が封印解除できなくなり、復旧作業で泣きを見ることになる。
  • PCR 7: セキュアブートの状態。これこそが、OSより上のレイヤーの整合性を担保する鍵だ。実務では、PCR 7をベースに鍵を封印するのが最も運用とセキュリティのバランスが良い。

Nginx/WAF側の設定ヒント(補足)

TPMで守られたサーバーで鍵を読み込む際、もし鍵が「封印解除」できない状態であれば、Nginxなどのサービスは起動すらさせない設定にするべきだ。

# systemdでサービス依存関係を制御する際のヒント(.serviceファイル)
[Unit]
Description=Secure App Service
# TPMの封印が解除され、キーが利用可能になるまでサービスを開始しない
Requires=tpm2-unseal.service
After=tpm2-unseal.service

[Service]
ExecStart=/usr/bin/nginx -g 'daemon off;'

—

最後に:防御は「スタック」で考える

暗号理論を理解していても、それをハードウェアの信頼性と結びつけなければ、現代の複雑な攻撃の前では無力だ。「Webアプリの脆弱性さえ塞げばいい」という時代は終わった。

1. ハードウェア(TPM)でブートの整合性を保証する。
2. OS/カーネルで適切な権限管理と暗号化を行う。
3. アプリ(PHP/Python等)で境界防御を行う。

このレイヤーをまたいだ「防御の多層化」こそが、我々エンジニアが守るべき最後の砦だ。次にサーバーを構築する際は、tpm2-toolsをインストールし、まずは今のシステムが何をPCRに記録しているかを確認することから始めてほしい。

現場からは以上だ。また次の戦場で会おう。

コメント

タイトルとURLをコピーしました