次の方法で共有


トラステッド プラットフォーム モジュールの構成証明

トラステッド プラットフォーム モジュール (TPM) が搭載されたデバイスは、初期ブート機能の状態を検出するためのメジャー ブート プロセスを使用すると共に構成証明を利用して、ブート整合性が損なわれていないことを証明できます。

デバイスの種類、ブートローダー、ブート スタック攻撃の数が増えると、それに応じて構成証明ソリューションが進化する必要があります。 デバイスの構成証明状態は、プラットフォームの証拠の内容を検証するために使用される構成証明ポリシーに基づきます。

この記事では、TPM 構成証明の概要と、Azure Attestationでサポートされる機能について説明します。

概要

TPM 構成証明は、証明書利用者がブート フローを検証できるポイントまでの TPM 自体の検証から始まります。

一般に、TPM 構成証明は、以下の基本動作に基づいています。

TPM の信頼性を検証する

TPM を検証することで TPM の信頼性を検証します。

  • すべての TPM は、"保証キー" (EK) と呼ばれる一意の非対称キーと共に出荷されます。 このキーは製造元によって焼き付けられます。 このキーの公開部分は EKPub と呼ばれます。 関連付けられている秘密キーは EKPriv と呼ばれます。 一部の TPM チップには、EKPub のために製造元によって発行された EK 証明書もあります。 この証明書は EKCert と呼ばれます。
  • 証明書作成者 (CA) は、EKPub または EKCert のいずれかを使用して TPM 内で信頼を確立します。
  • デバイスは、証明書が要求されているキーが EKPub に暗号的に結び付けられていることと、TPM が EKpriv を所有していることを、CA に対して証明します。
  • CA は、特別な発行ポリシーを持つ証明書を発行して、キーが TPM によって保護されていることの構成証明を示します。

ブート中に行われた測定を検証する

Azure Attestation を使用して、ブート中に行われた測定を検証します。

  • トラステッド ブートおよびメジャー ブートの一環として、すべてのステップが検証され、TPM に測定されます。 プラットフォームごとに異なるイベントが測定されます。 Windowsでの測定ブート プロセスの詳細については、「Windows ブート プロセスのセキュリティ保護」を参照してください。
  • 起動時に構成証明 ID キーが生成されます。 これは、使用中の TPM が EK 検証の実行後に証明書を発行されたことを証明する暗号化サービスを提供するために使用されます。
  • 証明書利用者は、Azure Attestation に対して構成証明を実行できます。これを使用して、ブート プロセス中に行われた測定を検証できます。
  • その後、証明書利用者は、構成証明ステートメントを利用して、リソースまたはその他のアクションへのアクセスを制限できます。

概念的なデバイス構成証明フローを示す図。

概念的には、前の図に示すように TPM 構成証明を視覚化できます。 証明書利用者は、プラットフォームの整合性と約束違反を検証するために Azure Attestation を適用します。 検証プロセスを使用すると、ワークロードを実行したり、リソースへのアクセスを提供したりすることができます。

悪意のあるブート攻撃からの保護

成熟した攻撃手法は、ブート チェーンに感染することを目指しています。 ブート攻撃により、攻撃者はシステム リソースにアクセスでき、攻撃者はマルウェア対策ソフトウェアから非表示にすることができます。 信頼されたブートは、防御の最初の順序として機能します。 信頼されたブートと構成証明を使用すると、証明書利用者に機能が拡張されます。 ほとんどの攻撃者は、セキュアブートをバイパスするか、ブート プロセスで望ましくないバイナリを読み込もうとします。

リモート構成証明を使用すると、証明書利用者は、ブート チェーン全体で約束違反を検証できます。 たとえば、UEFI によって測定されるセキュリティで保護された変数の値を検証する、構成証明サービスによるセキュア ブート評価を検討してください。

メジャー ブート インストルメンテーションを使用すると、暗号的に結び付けられた測定を実行後に変更することはできなくなります。また、信頼できるコンポーネントのみが測定を行うことができます。 この理由から、セキュリティで保護された変数を検証すれば、有効性を十分に確保できます。

Azure Attestation は追加でレポートに署名して、構成証明の整合性が維持されるようにし、Man in the Middle 型の攻撃からも保護されるようにします。

単純なポリシーは、次のように使用できます。

version=1.0;

authorizationrules { 
    => permit();
};


issuancerules
{
[type=="aikValidated", value==true] && 
[type=="secureBootEnabled", value==true] => issue(type="PlatformAttested", value=true);
};

ブート内の 1 つのコンポーネントのみを検証するだけでは不十分な場合があります。 コード整合性やハイパーバイザーで保護されたコード整合性 (HVCI) や System Guard Secure Launch などの補完的な機能を検証すると、デバイスの保護プロファイルが追加されます。 また、違反を評価し、プラットフォームに対する自信を持てるように、ブートをピアリングする機能も必要です。

次の例では、ポリシー バージョン 1.2 を利用して、セキュア ブート、HVCI、System Guard セキュア起動に関する詳細を確認します。 また、ブート中に不要な (malicious.sys) ドライバーが読み込まれていないことも確認します。

version=1.2;

authorizationrules {
    => permit();
};

issuancerules
{

// Verify if secure boot is enabled
c:[type == "events", issuer=="AttestationService"] => add(type = "efiConfigVariables", value = JmesPath(c.value, "Events[?EventTypeString == 'EV_EFI_VARIABLE_DRIVER_CONFIG' && ProcessedData.VariableGuid == '8BE4DF61-93CA-11D2-AA0D-00E098032B8C']"));
c:[type=="efiConfigVariables", issuer=="AttestationPolicy"]=> add(type = "secureBootEnabled", value = JsonToClaimValue(JmesPath(c.value, "[?ProcessedData.UnicodeName == 'SecureBoot'] | length(@) == `1` && @[0].ProcessedData.VariableData == 'AQ'")));
![type=="secureBootEnabled", issuer=="AttestationPolicy"] => add(type="secureBootEnabled", value=false);

// HVCI
c:[type=="events", issuer=="AttestationService"] => add(type="srtmDrtmEventPcr", value=JmesPath(c.value, "Events[? EventTypeString == 'EV_EVENT_TAG' && (PcrIndex == `12` || PcrIndex == `19`)].ProcessedData.EVENT_TRUSTBOUNDARY"));
c:[type=="srtmDrtmEventPcr", issuer=="AttestationPolicy"] => add(type="hvciEnabledSet", value=JsonToClaimValue(JmesPath(c.value, "[*].EVENT_VBS_HVCI_POLICY | @[?String == 'HypervisorEnforcedCodeIntegrityEnable'].Value")));
c:[type=="hvciEnabledSet", issuer=="AttestationPolicy"] => issue(type="hvciEnabled", value=ContainsOnlyValue(c.value, 1));
![type=="hvciEnabled", issuer=="AttestationPolicy"] => issue(type="hvciEnabled", value=false);

// Validating unwanted(malicious.sys) driver is not loaded
c:[type=="events", issuer=="AttestationService"] => add(type="boolProperties", value=JmesPath(c.value, "Events[? EventTypeString == 'EV_EVENT_TAG' && (PcrIndex == `12` || PcrIndex == `13` || PcrIndex == `19` || PcrIndex == `20`)].ProcessedData.EVENT_TRUSTBOUNDARY"));
c:[type=="boolProperties", issuer=="AttestationPolicy"] => issue(type="MaliciousDriverLoaded", value=JsonToClaimValue(JmesPath(c.value, "[*].EVENT_LOADEDMODULE_AGGREGATION[] | [? EVENT_IMAGEVALIDATED == true && (equals_ignore_case(EVENT_FILEPATH, '\\windows\\system32\\drivers\\malicious.sys') || equals_ignore_case(EVENT_FILEPATH, '\\windows\\system32\\drivers\\wd\\malicious.sys'))] | @ != null ")));
![type=="MaliciousDriverLoaded", issuer=="AttestationPolicy"] => issue(type="MaliciousDriverLoaded", value=false);

};

Linux 上の Integrity Measurement Architecture (IMA) を使用した悪意のあるブート攻撃からの保護の拡張

Linux システムは、Windows と同様のブート プロセスに従います。TPM 構成証明を使用すると、保護プロファイルをカーネルの起動を超えて拡張することも、Integrity Measurement Architecture (IMA) を使用することもできます。 IMA サブシステムは、リモートとローカルの両方で、ファイルが偶発的または悪意を持って変更されたかどうかを検出するように設計されており、ランタイム測定リストを維持します。ハードウェアのトラステッド プラットフォーム モジュール (TPM) に固定されている場合、このリストの集計整合性値によって、ソフトウェア攻撃から回復することができます。 IMA サブシステムの最近の機能強化により、ファイルベース以外の属性をリモートで測定および構成証明することもできます。 Azure Attestation では、システムの整合性の全体像を提供するために、リモートで構成証明されるファイル ベース以外の測定値がサポートされています。

次の ima-policy で IMA を有効にすると、ローカル ファイル整合性構成証明を有効にしながら、ファイル以外の属性の測定が有効になります。

次の構成証明ポリシーを使用して、secureboot、カーネル署名、カーネル バージョン、grub によって渡されたカーネル cmdline、IMA でサポートされるその他の主要なセキュリティ属性を検証できるようになりました。

version = 1.2;

configurationrules
{
};

authorizationrules
{
    [type == "aikValidated", value==true]
    => permit();
};

issuancerules {
    // Retrieve all EFI Boot variables with event = 'EV_EFI_VARIABLE_BOOT' 
    c:[type == "events", issuer=="AttestationService"] => add(type ="efiBootVariables", value = JmesPath(c.value, "Events[?EventTypeString == 'EV_EFI_VARIABLE_BOOT']"));

    // Retrieve all EFI Driver Config variables with event = 'EV_EFI_VARIABLE_DRIVER_CONFIG' 
    c:[type == "events", issuer=="AttestationService"] => add(type ="efiConfigVariables", value = JmesPath(c.value, "Events[?EventTypeString == 'EV_EFI_VARIABLE_DRIVER_CONFIG']"));

   // Grab all IMA events
   c:[type=="events", issuer=="AttestationService"] => add(type="imaMeasurementEvents", value=JmesPath(c.value, "Events[?EventTypeString == 'IMA_MEASUREMENT_EVENT']"));

   // Look for "Boot Order" from EFI Boot Data
   c:[type == "efiBootVariables", issuer=="AttestationPolicy"] => add(type = "bootOrderFound", value = JmesPath(c.value, "[?ProcessedData.UnicodeName == 'BootOrder'] | length(@) == `1` && @[0].PcrIndex == `1` && @[0].ProcessedData.VariableData"));
   c:[type=="bootOrderFound", issuer=="AttestationPolicy"] => issue(type="bootOrder", value=JsonToClaimValue(c.value));
   ![type=="bootOrderFound", issuer=="AttestationPolicy"] => issue(type="bootOrder", value=0);

   // Look for "Secure Boot" from EFI Driver Configuration Data
   c:[type == "efiConfigVariables", issuer=="AttestationPolicy"] => issue(type = "secureBootEnabled", value = JsonToClaimValue(JmesPath(c.value, "[?ProcessedData.UnicodeName == 'SecureBoot'] | length(@) == `1` && @[0].PcrIndex == `7` && @[0].ProcessedData.VariableData == 'AQ'")));
   ![type=="secureBootEnabled", issuer=="AttestationPolicy"] => issue(type="secureBootEnabled", value=false);

   // Look for "Platform Key" from EFI Boot Data
   c:[type == "efiConfigVariables", issuer=="AttestationPolicy"] => add(type = "platformKeyFound", value = JmesPath(c.value, "[?ProcessedData.UnicodeName == 'PK'] | length(@) == `1` && @[0].PcrIndex == `7` && @[0].ProcessedData.VariableData"));
   c:[type=="platformKeyFound", issuer=="AttestationPolicy"] => issue(type="platformKey", value=JsonToClaimValue(c.value));
   ![type=="platformKeyFound", issuer=="AttestationPolicy"] => issue(type="platformKey", value=0);
  
   // Look for "Key Exchange Key" from EFI Driver Configuration Data
   c:[type == "efiConfigVariables", issuer=="AttestationPolicy"] => add(type = "keyExchangeKeyFound", value = JmesPath(c.value, "[?ProcessedData.UnicodeName == 'KEK'] | length(@) == `1` && @[0].PcrIndex == `7` && @[0].ProcessedData.VariableData"));
   c:[type=="keyExchangeKeyFound", issuer=="AttestationPolicy"] => issue(type="keyExchangeKey", value=JsonToClaimValue(c.value));
   ![type=="keyExchangeKeyFound", issuer=="AttestationPolicy"] => issue(type="keyExchangeKey", value=0);

   // Look for "Key Database" from EFI Driver Configuration Data
   c:[type == "efiConfigVariables", issuer=="AttestationPolicy"] => add(type = "keyDatabaseFound", value = JmesPath(c.value, "[?ProcessedData.UnicodeName == 'db'] | length(@) == `1` && @[0].PcrIndex == `7` && @[0].ProcessedData.VariableData"));
   c:[type=="keyDatabaseFound", issuer=="AttestationPolicy"] => issue(type="keyDatabase", value=JsonToClaimValue(c.value));
   ![type=="keyDatabaseFound", issuer=="AttestationPolicy"] => issue(type="keyDatabase", value=0);

   // Look for "Forbidden Signatures" from EFI Driver  Configuration Data
   c:[type == "efiConfigVariables", issuer=="AttestationPolicy"] => add(type = "forbiddenSignaturesFound", value = JmesPath(c.value, "[?ProcessedData.UnicodeName == 'dbx'] | length(@) == `1` && @[0].PcrIndex == `7` && @[0].ProcessedData.VariableData"));
   c:[type=="forbiddenSignaturesFound", issuer=="AttestationPolicy"] => issue(type="forbiddenSignatures", value=JsonToClaimValue(c.value));
   ![type=="forbiddenSignaturesFound", issuer=="AttestationPolicy"] => issue(type="forbiddenSignatures", value=0);

   // Look for "Kernel Version" in IMA Measurement events
   c:[type=="imaMeasurementEvents", issuer=="AttestationPolicy"] => add(type="kernelVersionsFound", value=JmesPath(c.value, "[].ProcessedData.KernelVersion"));
   c:[type=="kernelVersionsFound", issuer=="AttestationPolicy"] => issue(type="kernelVersions", value=JsonToClaimValue(c.value));
   ![type=="kernelVersionsFound", issuer=="AttestationPolicy"] => issue(type="kernelVersions", value=0);

   // Look for "Built-In Trusted Keys" in IMA Measurement events
   c:[type=="imaMeasurementEvents", issuer=="AttestationPolicy"] => add(type="builtintrustedkeysFound", value=JmesPath(c.value, "[? ProcessedData.Keyring == '.builtin_trusted_keys'].ProcessedData.CertificateSubject"));
   c:[type=="builtintrustedkeysFound", issuer=="AttestationPolicy"] => issue(type="builtintrustedkeys", value=JsonToClaimValue(c.value));
   ![type=="builtintrustedkeysFound", issuer=="AttestationPolicy"] => issue(type="builtintrustedkeys", value=0);
};

注: ファイルベース以外の測定値のサポートは、Linux カーネル バージョン 5.15 以降でのみ使用できます

TPM キーの構成証明のサポート

多くのアプリケーションは、資格情報の盗難から保護するために、キーと証明書の基本的な資格情報管理に依存しています。資格情報のセキュリティを確保する主な方法の 1 つは、マルウェアや攻撃に対して追加のセキュリティを提供するキー ストレージ プロバイダーに依存することです。 Windows には、ソフトウェアまたはハードウェア ベースの暗号化プロバイダーが実装されています。

最も重要なのが、次の 2 つです。

  • Microsoft Software Key Storage Provider: キーをソフトウェア ベースで保存し、CNG (Crypto-Next Generation) をサポートする標準プロバイダー

  • Microsoft Platform Crypto Provider: TPM (トラステッド プラットフォーム モジュール) にキーを保存し、CNG もサポートするハードウェア ベース

ストレージ プロバイダーを使用する場合は、通常、信頼のルートにチェーンされた pub/priv キー ペアを作成します。 作成時に、より多くのプロパティを使用して、キー ストレージ、エクスポート可能性などの特定の側面を有効にすることもできます。このコンテキストでのキー構成証明は、秘密キーが内部で生成され、内部で管理され、エクスポートできない形式であることを利用者に証明する技術的能力です。 このような構成証明は、他の情報と組み合わせて、資格情報の盗難やリプレイ型の攻撃から保護するのに役立ちます。

また、TPM では、キーが TPM に常駐していることを証明する機能も提供され、エクスポート不可能、ハンマリング防止、キーの分離によって支えられたより高いセキュリティ保証が可能になります。 一般的なユース ケースは、サブスクライバー キーのデジタル署名証明書を発行するアプリケーションで、サブスクライバーの秘密署名キーが承認された TPM で生成および管理されていることを確認することです。 次のようなポリシーを使用して、適切な Nonexportability フラグが設定された有効な TPM にキーが存在することを簡単に証明できます。

version=1.2;

authorizationrules
{
    => permit();
};

issuancerules
{
    // Key Attest Policy
    // -- Validating key types
	c:[type=="x-ms-tpm-request-key", issuer=="AttestationService"] => add(type="requestKeyType", value=JsonToClaimValue(JmesPath(c.value, "jwk.kty")));
	c:[type=="x-ms-tpm-other-keys", issuer=="AttestationService"] => add(type="otherKeysTypes", value=JsonToClaimValue(JmesPath(c.value, "[*].jwk.kty")));
    c:[type=="requestKeyType", issuer=="AttestationPolicy", value=="RSA"] => issue(type="requestKeyType", value="RSA");
    c:[type=="otherKeysTypes", issuer=="AttestationPolicy", value=="RSA"] => issue(type="otherKeysTypes", value="RSA");

    // -- Validating tpm_quote attributes
	c:[type=="x-ms-tpm-request-key", issuer=="AttestationService"] => add(type="requestKeyQuote", value=JmesPath(c.value, "info.tpm_quote"));
	c:[type=="requestKeyQuote", issuer=="AttestationPolicy"] => add(type="requestKeyQuoteHashAlg", value=JsonToClaimValue(JmesPath(c.value, "hash_alg")));
    c:[type=="requestKeyQuoteHashAlg", issuer=="AttestationPolicy", value=="sha-256"] => issue(type="requestKeyQuoteHashAlg", value="sha-256");

    // -- Validating tpm_certify attributes
    c:[type=="x-ms-tpm-request-key", issuer=="AttestationService"] => add(type="requestKeyCertify", value=JmesPath(c.value, "info.tpm_certify"));
    c:[type=="requestKeyCertify", issuer=="AttestationPolicy"] => add(type="requestKeyCertifyNameAlg", value=JsonToClaimValue(JmesPath(c.value, "name_alg")));
    c:[type=="requestKeyCertifyNameAlg", issuer=="AttestationPolicy", value==11] => issue(type="requestKeyCertifyNameAlg", value=11);

    c:[type=="requestKeyCertify", issuer=="AttestationPolicy"] => add(type="requestKeyCertifyObjAttr", value=JsonToClaimValue(JmesPath(c.value, "obj_attr")));
    c:[type=="requestKeyCertifyObjAttr", issuer=="AttestationPolicy", value==50] => issue(type="requestKeyCertifyObjAttr", value=50);

    c:[type=="requestKeyCertify", issuer=="AttestationPolicy"] => add(type="requestKeyCertifyAuthPolicy", value=JsonToClaimValue(JmesPath(c.value, "auth_policy")));
    c:[type=="requestKeyCertifyAuthPolicy", issuer=="AttestationPolicy", value=="AQIDBA"] => issue(type="requestKeyCertifyAuthPolicy", value="AQIDBA");

    c:[type=="x-ms-tpm-other-keys", issuer=="AttestationService"] => add(type="otherKeysCertify", value=JmesPath(c.value, "[*].info.tpm_certify"));
    c:[type=="otherKeysCertify", issuer=="AttestationPolicy"] => add(type="otherKeysCertifyNameAlgs", value=JsonToClaimValue(JmesPath(c.value, "[*].name_alg")));
    c:[type=="otherKeysCertifyNameAlgs", issuer=="AttestationPolicy", value==11] => issue(type="otherKeysCertifyNameAlgs", value=11);

    c:[type=="otherKeysCertify", issuer=="AttestationPolicy"] => add(type="otherKeysCertifyObjAttr", value=JsonToClaimValue(JmesPath(c.value, "[*].obj_attr")));
    c:[type=="otherKeysCertifyObjAttr", issuer=="AttestationPolicy", value==50] => issue(type="otherKeysCertifyObjAttr", value=50);

    c:[type=="otherKeysCertify", issuer=="AttestationPolicy"] => add(type="otherKeysCertifyAuthPolicy", value=JsonToClaimValue(JmesPath(c.value, "[*].auth_policy")));
    c:[type=="otherKeysCertifyAuthPolicy", issuer=="AttestationPolicy", value=="AQIDBA"] => issue(type="otherKeysCertifyAuthPolicy", value="AQIDBA");
};

次のステップ