Parquet モジュラー暗号化の操作

IBM Analytics Engine Serverless は、Parquet モジュラー暗号化をサポートします。Parquet 標準に新たに追加され、Parquet ファイルの書き込み時に機密性の高い列を暗号化したり、暗号化されたファイルの読み取り時にこれらの列を暗号化解除したりできるようになりました。 Parquet モジュラー暗号化(Parquet modular encryption)を参照。

Parquet 暗号化は、機密の保護だけでなく、保管データの整合性も保護します。 ファイル内容の改ざんが検出されると、リーダー側の例外がトリガーされます。

主要機能は以下のとおりです:

  1. Parquet の暗号化および復号は、Spark ワーカーで実行されます。 したがって、機密データと暗号鍵は、ストレージには見えません。

  2. Parquet モジュラー暗号化形式のファイルにも、エンコード、圧縮、列の投影、述部プッシュダウンなどの標準の Parquet 機能を通常どおり使用できます。

  3. Parquet 仕様に規定されている 2 つの暗号化アルゴリズムから選択することができます。 どちらのアルゴリズムも、列の暗号化をサポートします。ただし、次の点に注意してください。

    • デフォルトのアルゴリズム AES-GCM は、Parquet ファイルのデータ部分とメタデータ部分に対する全体的な改ざん防止を提供します。
    • もう一方のアルゴリズム AES-GCM-CTR は、Parquet ファイルの整合性を部分的に保護します。 改ざんが防止されるのはメタデータ部分のみで、データ部分は防止されません。 このアルゴリズムの利点は、スループットのオーバーヘッドが AES-GCM アルゴリズムよりも低いことです。
  4. 暗号化する列を選択できます。 他の列は暗号化されないため、スループットのオーバーヘッドを削減できます。

  5. 列ごとに別の鍵で暗号化することができます。

  6. デフォルトでは、メインの Parquet メタデータ・モジュール (ファイルのフッター) は、ファイルのスキーマおよび機密列のリストを隠すために暗号化されます。 ただし、レガシー・リーダー (Parquet 暗号化をまだサポートしていない他の Spark ディストリビューションなど) が、暗号化されたファイルに含まれている暗号化されていない列を読み取れるように、ファイルのフッターを暗号化しないことを選択することもできます。

  7. 暗号鍵は、次の 2 つの方法のいずれかで管理できます。

    • アプリケーションで直接行います。 アプリケーションによる鍵管理を参照してください。
    • IBM® Key Protect for IBM Cloud® で管理する。これは、IBM Analytics Engine サービスで使用する暗号鍵の生成、管理、および破棄に利用できる、一元化された鍵管理システム (KMS) です。 Key Protectによる鍵管理{: {: external}} を参照してください。

    IBM® Key Protect for IBM Cloud® では、暗号化された鍵を IBM Cloud の Identity and Access Management (IAM) の役割に応じた方法で管理できます。

    : マスター暗号鍵 (MEK) のみを (アプリケーションまたは Key Protect のどちらかを使用して) 管理する必要があります。 データ暗号鍵 (DEK) は、Spark/Parquet によって自動的に処理されます。 Parquet 暗号化の内部で行われる鍵処理について詳しくは、暗号鍵の処理の内部詳細を参照してください。

    機密列ごとに、暗号化に使用するマスター鍵を指定する必要があります。 また、暗号化された各ファイル(データ・フレーム)のフッターに対してマスター・キーを指定する必要があります。フッターは、スキーマや機密列のリストなどのメタデータを保持するため、機密である可能性もあります。 デフォルトでは、そのフッター鍵はフッターの暗号化に使用されます。 ただし、プレーン・テキストのフッター・モードを選択した場合は、フッターは暗号化されないので、鍵はフッターの整合性検査にのみ使用されます。

    標準の Spark Hadoop 構成を使用して暗号化パラメーターを渡すことができます。例えば、次のようにアプリケーションの SparkContext の Hadoop 構成に構成値を設定できます。

    sc.hadoopConfiguration.set("<parameter name>" , "<parameter value>")
    

    また、書き込みオプションを使用してパラメーター値を渡すこともできます。

    <data frame name>.write
    .option("<parameter name>" , "<parameter value>")
    .parquet("<write path>")
    

Parquet 暗号化を使用して実行する場合の必須パラメーター

暗号化データを書き込むには、以下のパラメーターが必要です。

  • マスター暗号鍵を伴う、暗号化する列のリスト。

    parameter name: "parquet.encryption.column.keys"
    parameter value: "<master key ID>:<column>,<column>;<master key ID>:<column>,.."
    
  • フッター鍵:

    parameter name: "parquet.encryption.footer.key"
    parameter value: "<master key ID>"
    

    以下に例を示します。

    dataFrame.write
    .option("parquet.encryption.footer.key" , "k1")
    .option("parquet.encryption.column.keys" , "k2:SSN,Address;k3:CreditCard")
    .parquet("<path to encrypted files>")
    

    重要:"parquet.encryption.column.keys"パラメーターも"parquet.encryption.footer.key"パラメーターも設定されていない場合、ファイルは暗号化されません。 これらのパラメーターは暗号化されたファイルに必須であるため、これらのパラメーターのいずれか 1 つのみが設定されている場合、例外がスローされます。

  • EncryptionPropertiesFactory を実装するクラス:

    parameter name: "parquet.crypto.factory.class"
    parameter value: "com.ibm.parquet.key.management.IBMKeyToolsFactory"
    

    重要: "parquet.crypto.factory.class" が設定されていない場合、ファイルは暗号ファクトリーによって暗号化されません。

オプション・パラメーター

暗号化データを書き込むときに、次のオプション・パラメーターを使用できます。

  • 暗号化アルゴリズム AES-GCM-CTR

    デフォルトでは、Parquet の暗号化には、Parquet ファイルのデータ部分とメタデータ部分に対する全体的な改ざん防止を提供する AES-GCM アルゴリズムが使用されます。 しかし、Spark 2.3.0 は、(Java 9 で初めて追加された) CPU ハードウェアの AES アクセラレーションをサポートしない Java 8 で実行されるので、状況によっては、データの整合性検査のオーバーヘッドがワークロードのスループットに影響を及ぼす可能性があります。

    これを補うために、データの整合性検査のサポートをオフに切り替え、代替アルゴリズム AES-GCM-CTR を使用して、暗号化されたファイルを書き込むことができます。このアルゴリズムは、メタデータ部分の整合性のみを検査してデータ部分の整合性は検査しないので、AES-GCM アルゴリズムよりもスループット・オーバーヘッドが低くなります。

    parameter name: "parquet.encryption.algorithm"
    parameter value: "AES_GCM_CTR_V1"
    
  • レガシー・リーダー向けのプレーン・テキスト・フッター・モード

    デフォルトでは、メインの Parquet メタデータ・モジュール (ファイルのフッター) は、ファイルのスキーマおよび機密列のリストを隠すために暗号化されます。 ただし、他の Spark や Parquet のリーダー (Parquet 暗号化をまだサポートしていないもの) が、暗号化されたファイルに含まれている暗号化されていない列を読み取れるように、ファイルのフッターを暗号化しないことを選択することもできます。 フッター暗号化をオフにするには、次のパラメーターを設定します。

    parameter name: "parquet.encryption.plaintext.footer"
    parameter value: "true"
    

    重要:"parquet.encryption.footer.key"パラメーターは、プレーン・テキスト・フッター・モードでも指定する必要があります。 フッターは暗号化されませんが、フッターの内容に署名するために鍵が使用されます。つまり、新しいリーダーはその整合性を検証できます。 レガシー・リーダーは、フッター・シグニチャーの追加による影響を受けません。

使用例

以下の Python および Scala のサンプル・コード・スニペットは、データ・フレームを作成し、暗号化された Parquet ファイルを書き込み、暗号化された Parquet ファイルを読み取る方法を示しています。

  • Python: 暗号化データの書き込み

    from pyspark.sql import
    
    RowsquaresDF = spark.createDataFrame(
        sc.parallelize(range(1, 6))
        .map(lambda i: Row(int_column=i,  square_int_column=i ** 2)))
    
    sc._jsc.hadoopConfiguration().set("parquet.crypto.factory.class",
        "com.ibm.parquet.key.management.IBMKeyToolsFactory")
    sc._jsc.hadoopConfiguration().set("encryption.key.list",
        "key1: AAECAwQFBgcICQoLDA0ODw==, key2: AAECAAECAAECAAECAAECAA==")
    
    encryptedParquetPath = "squares.parquet.encrypted"squaresDF.write\
       .option("parquet.encryption.column.keys", "key1:square_int_column")\
       .option("parquet.encryption.footer.key", "key2")\
       .parquet(encryptedParquetPath)
    
  • Python: 暗号化データの読み取り

    sc._jsc.hadoopConfiguration().set("parquet.crypto.factory.class",
        "com.ibm.parquet.key.management.IBMKeyToolsFactory")
    sc._jsc.hadoopConfiguration().set("parquet.encryption.key.list",
         "key1: AAECAwQFBgcICQoLDA0ODw==, key2: AAECAAECAAECAAECAAECAA==")
    
    encryptedParquetPath = "squares.parquet.encrypted"
    parquetFile = spark.read.parquet(encryptedParquetPath)
    parquetFile.show()
    
  • Scala: 暗号化データの書き込み

    case class SquareItem(int_column: Int, square_int_column: Double)
    val dataRange = (1 to 6).toList
    val squares = sc.parallelize(dataRange.map(i => new SquareItem(i, scala.math.pow(i,2))))
    sc.hadoopConfiguration.set("parquet.crypto.factory.class","com.ibm.parquet.key.management.IBMKeyToolsFactory")
    sc.hadoopConfiguration.set("parquet.encryption.key.list","key1: AAECAwQFBgcICQoLDA0ODw==, key2: AAECAAECAAECAAECAAECAA==")
    val encryptedParquetPath = "squares.parquet.encrypted"
    squares.toDS().write
      .option("parquet.encryption.column.keys", "key1:square_int_column")
      .option("parquet.encryption.footer.key", "key2")
      .parquet(encryptedParquetPath)
    
  • Scala: 暗号化データの読み取り

    sc.hadoopConfiguration.set("parquet.crypto.factory.class","com.ibm.parquet.key.management.IBMKeyToolsFactory")
    sc.hadoopConfiguration.set("parquet.encryption.key.list","key1: AAECAwQFBgcICQoLDA0ODw==, key2: AAECAAECAAECAAECAAECAA==")
    val encryptedParquetPath = "squares.parquet.encrypted"
    val parquetFile = spark.read.parquet(encryptedParquetPath)
    parquetFile.show()
    

暗号鍵の処理の内部詳細

Parquet ファイルを書き込むときには、暗号化される各列およびフッターに対してランダムなデータ暗号鍵 (DEK) が生成されます。 それらの鍵を使用して、Parquet ファイルのデータ・モジュールおよびメタデータ・モジュールが暗号化されます。

その後、データ暗号鍵が、鍵暗号鍵 (KEK) を使用して暗号化されます。この KEK も、マスター鍵ごとに Spark/Parquet の内部で生成されるものです。 鍵暗号鍵は、マスター暗号鍵 (MEK) を使用して暗号化されます。この暗号化は、マスター鍵をアプリケーションで管理する場合はローカルで実行され、マスター鍵を IBM® Key Protect for IBM Cloud® で管理する場合は Key Protect サービスで実行されます。

暗号化されたデータ暗号鍵と鍵暗号鍵が、マスター鍵 ID と一緒に Parquet ファイルのメタデータに保管されます。 各鍵暗号鍵には固有の ID (セキュアでランダムな 16 バイトの値としてローカルに生成されます) があり、これもファイルのメタデータに保管されます。 鍵暗号鍵はアクセス・トークンによってキャッシュされるため、暗号化された列またはファイルが同じマスター暗号鍵を使用する場合は、暗号化された列またはファイルごとでKeyProtectサービスと対話する必要はありません。

Parquet ファイルを読み取るときには、マスター暗号鍵 (MEK)、暗号化された鍵暗号鍵 (KEK) とその ID、および暗号化されたデータ暗号鍵 (DEK) が、ファイルのメタデータから抽出されます。

鍵暗号鍵がマスター暗号鍵で復号されます。この復号は、マスター鍵をアプリケーションで管理する場合はローカルで実行され、マスター鍵を IBM® Key Protect for IBM Cloud® で管理する場合は Key Protect サービスで実行されます。 鍵暗号鍵はアクセス・トークンによってキャッシュされるため、暗号化解除された列またはファイルが同じ鍵暗号鍵を使用する場合、それらの列またはファイルごとでKeyProtectサービスと対話する必要はありません。 次に、データ暗号鍵 (DEK) が、キー暗号鍵 (KEK) を使用してローカルで復号されます。

鍵のローテーション(オプション・フィーチャー)

エンベロープ暗号化の手法には、回転(変更)マスター鍵の拡張オプションがあり、漏えいしたMEKによる機密データ漏えいのリスクを最小限に抑えます。 DEKは変更できません。これは、Parquetファイル全体を再度暗号化することをになるためです。 新しいMEK(および透過的に再生成されたKEK)は、DEKを再暗号化するために使用されます。

Parquetファイルで鍵のローテーションを有効にするには、Parquetファイルの書き込み時にHadoop 構成で"parquet.encryption.key.material.store.internally"パラメーターを"false"に設定する必要があります。

このパラメーターを設定すると、ParquetはParquetファイル内ではなく、別個の小さいファイルに鍵(MEKでラップされたもの)を保管します。 鍵のローテーションが実行されると、小さい鍵素材ファイルのみが置き換えられます。 Parquetファイルを変更する必要はありません(変更できないことが多い)。 ほとんどのKMSシステムは、保管されたマスター鍵の内容を置き換え、MEK IDをそのまま保持する(ただし、鍵バージョンを更新する)ことによって、鍵のローテーションをサポートします。 ユーザーまたは管理者がKMS固有のAPIを使用してKMSインスタンス内でマスター鍵のローテーションを実行した後、以下を呼び出すことにより、(例えば、同じ管理者によって)Parquet鍵ローテーション・メカニズムをトリガーできます:

public static void KeyToolkit.rotateMasterKeys(String folderPath, Configuration hadoopConfig)

鍵のローテーションが実行されると、folderPathのすべての鍵素材ファイル内のすべてのKEKが古いMEKバージョンでアンラップされ、DEKの暗号化解除が可能になります。 マスター鍵IDごとに新しいKEKが生成され、新しいMEKバージョンでラップされます。 新しいKEKを作成すると、古いKEKを再使用する代わりに、セキュリティーが向上するだけでなく、後続のデータ読み取り操作のパフォーマンスも向上します。 これは、管理者によって実行される単一の鍵ローテーション・プロセスが、異なるプロセスによって作成された複数のファイルにわたって動作するためです。このことは、同じMEK IDに対して多数の異なるKEKが存在することを意味します。 鍵のローテーション・プロセスでは、古いKEKが単一の新しいKEKに置き換えられます。これにより、リーダーは、マスター鍵ごとにKMSとの単一のアンラップ対話を実行できます。

鍵のローテーションの使用方法の例については、以下を参照してください:

詳細情報

Parquet暗号化の使用方法については、以下のサンプル・ノートブックを参照してください: