PolarDB Always-confidential 機能は、コードを変更することなく Java アプリケーションのシームレスな統合をサポートするために、専用のクライアントドライバーである Encrypted Java Database Connectivity (EncJDBC) を提供します。EncJDBC は暗号文を自動的に復号し、プレーンテキストデータを返します。このプロセスはアプリケーションに対して透過的です。アプリケーションは、数行の設定だけで PolarDB Always-confidential に接続でき、既存のビジネスコードを変更する必要はありません。
ソリューションアーキテクチャ
PolarDB Always-confidential は、データベースプロキシに基づく動的な暗号化および復号ソリューションです。そのコアロジックは、プロキシレイヤーがクエリ結果内の事前に設定された機密フィールドを暗号化してからクライアントに返すというものです。クライアントアプリケーションは、専用の EncJDBC ドライバーを使用して、受信時にデータを透過的に復号する必要があります。
ワークフローとロール権限
クエリリクエスト: クライアントアプリケーションは、EncJDBC ドライバーを介して標準の SQL クエリを PolarDB クラスターエンドポイントに送信します。
プロキシ処理: データベースプロキシはリクエストを受信し、実行のためにバックエンドのデータベースカーネルに転送します。
オンザフライ暗号化: クエリ結果が返されると、プロキシレイヤーは事前に設定された暗号化ポリシーに一致するかどうかを確認します。一致が見つかった場合、結果セット内の機密フィールドは、KMS または自己管理キーから指定したキーを使用して暗号化されます。
データ返却: 暗号化された結果セットがクライアントに返されます。
透過的な復号: クライアント上の EncJDBC ドライバーが自動的に暗号文を復号します。アプリケーションはプレーンテキストデータを受信し、プロセス全体がビジネスコードに対して透過的です。
クエリ結果はデータベースアカウントのロールによって異なります
スーパー管理者: クエリ結果は常にプレーンテキストであり、暗号化ポリシーの影響を受けません。これはデータベースの管理と監査に役立ちます。
通常ユーザー: クエリ結果は暗号文です。クライアントでデータを復号するには、EncJDBC ドライバーと正しいキーを使用する必要があります。
その他のユーザー: クエリ結果は暗号文であり、復号できません。
制限
この機能を実装する前に、以下の制限を確認して、この機能がビジネスおよび技術アーキテクチャの要件を満たしているかどうかを評価してください。
エンドポイント要件: 暗号化ルールは、クラスターエンドポイントまたはカスタムエンドポイントを使用して接続する場合にのみ有効になります。プライマリエンドポイントに直接接続するとプロキシがバイパスされ、暗号化機能が無効になります。
キー管理: 自己管理キーを使用する場合、キーのローテーションはサポートされていません。また、キーの紛失や漏洩のリスクもあります。キーが紛失した場合、対応する暗号化されたデータは復号できません。キーを管理するための厳格なセキュリティ手順を確立する必要があります。
JDK バージョン: JDK 1.8 以降を使用する必要があります。
KMS キーの使用
ステップ 1: KMS アクセス権限の設定
AccessKey ペアの取得: RAM ユーザーの
AccessKey IDとAccessKey Secretを取得します。これにより、アプリケーションは KMS によって管理されるマスター暗号化キー (MEK) を取得できます。KMS にアクセスするための適切な RAM ユーザーが既にある場合は、そのユーザーを使用できます。
適切な RAM ユーザーがいない場合は、RAM コンソールに移動します。左側のナビゲーションウィンドウで、 を選択します。[ユーザーの作成] をクリックし、画面の指示に従ってユーザーを作成します。
RAM ユーザーへの権限付与
RAM コンソールに移動します。 ページで、[アクセスポリシーの作成] をクリックします。スクリプトエディターに切り替え、次の内容をコピーして、[OK] をクリックします。ポリシー名を入力して作成を完了します。
{ "Version": "1", "Statement": [ { "Effect": "Allow", "Action": "KMS:Decrypt", "Resource": "*" } ] }RAM コンソールで、 ページに移動します。対象の RAM ユーザーを見つけ、[操作] 列の [権限の追加] をクリックし、新しく作成したアクセスポリシーを付与します。これにより、PolarDB はデータを動的に復号できるようになります。
ステップ 2: 依存関係のインストール
Maven プロジェクトの pom.xml ファイルに次の依存関係を追加します。
<dependency>
<groupId>com.aliyun</groupId>
<artifactId>aliyun-cls-jdbc</artifactId>
<version>1.0.10-1</version>
</dependency>Maven の依存関係を追加する際、必要に応じて version の値を置き換えてください。aliyun-cls-jdbc の最新バージョンは、公式サイトで確認できます。
ステップ 3: データベースに接続するための MEK の設定
MEK は、JDBC プロパティ、ファイル、または URL を使用して設定できます。JDBC を 2 つ以上の方法で設定する場合、優先順位は次のようになります: JDBC プロパティ 設定 > ファイル設定 > URL 設定。
以下の設定および接続方法では、
MEKはクライアント上でローカルに処理され、安全な方法 (エンベロープ暗号化) を使用してサーバーに送信されるため、MEKが漏洩することはありません。AccessKey ペア (
AccessKey IDとAccessKey Secret) をビジネスコードにハードコーディングしないでください。この例では、システム環境変数を使用して AccessKey ペアを管理します。詳細については、「Linux、macOS、および Windows システムで環境変数を設定する」をご参照ください。一時的な STS アクセス認証情報を使用して KMS で管理されている MEK を取得するには、STS SDK を使用して一時的な認証情報であるセキュリティトークンサービス (STS) トークンを取得できます。STS SDK の例については、「STS SDK の概要」をご参照ください。
JDBC プロパティ設定
標準の JDBC 接続が確立されると、Properties オブジェクトを使用してカスタム属性を設定できます。
例
// エンドポイント (ホスト名)、ポート、データベースインスタンス名 (dbname)、ユーザー名、パスワードなどの接続情報を準備します。
String hostname = "your-hostname";
String port = "your-port";
String dbname = "your-database-name";
String username = "your-username";
String password = "your-password";
// 環境変数から AccessKey ID と AccessKey Secret を取得します。
String accessKeyId = System.getenv("ALIBABA_CLOUD_ACCESS_KEY_ID");
String accessKeySecret = System.getenv("ALIBABA_CLOUD_ACCESS_KEY_SECRET");
// 一時的な STS アクセス認証情報を使用して KMS キーを読み取る場合は、取得したセキュリティトークンも入力する必要があります。
// String stsToken= "yourSecurityToken";
// KMS インスタンスエンドポイントには、パブリックネットワークアクセスの場合はパブリックエンドポイントを、VPC ネットワーク内のアクセスの場合はインスタンス VPC エンドポイントを使用します。
String kmsEndpoint = "your-kms-endpoint";
Properties props = new Properties();
props.setProperty("user", username);
props.setProperty("password", password);
props.setProperty("ALIBABA_CLOUD_ACCESS_KEY_ID", accessKeyId);
props.setProperty("ALIBABA_CLOUD_ACCESS_KEY_SECRET", accessKeySecret);
props.setProperty("ALIBABA_CLOUD_KMS_ENDPOINT", kmsEndpoint);
// props.setProperty("ALIBABA_CLOUD_STS_TOKEN","stsToken");
// 以下は MySQL データベースの接続 URL フォーマットです: "jdbc:mysql:encdb://%s:%s/%s"
String dbUrl = String.format("jdbc:mysql:encdb://%s:%s/%s", hostname, port, dbname);
// 以下は MySQL データベース用の EncJDBC ドライバーをロードします。
Class.forName("com.aliyun.encdb.mysql.jdbc.EncDriver");
// データベース接続を取得します。
Connection connection = DriverManager.getConnection(dbUrl, props);
// ... クエリを開始 ...URL 設定
URL に KMS キーを取得するためのパラメーターを埋め込むことができます。
例
// エンドポイント (ホスト名)、ポート、データベースインスタンス名 (dbname)、ユーザー名、パスワードなどの接続情報を準備します。
String hostname = "your-hostname";
String port = "your-port";
String dbname = "your-database-name";
String username = "your-username";
String password = "your-password";
// 環境変数から AccessKey ID と AccessKey Secret を取得します。
String accessKeyId = System.getenv("ALIBABA_CLOUD_ACCESS_KEY_ID");
String accessKeySecret = System.getenv("ALIBABA_CLOUD_ACCESS_KEY_SECRET");
// 一時的な STS アクセス認証情報を使用して KMS キーを読み取る場合は、取得したセキュリティトークンも入力する必要があります。
// String stsToken= "yourSecurityToken";
// KMS インスタンスエンドポイントには、パブリックネットワークアクセスの場合はパブリックエンドポイントを、VPC ネットワーク内のアクセスの場合はインスタンス VPC エンドポイントを使用します。
String kmsEndpoint = "your-kms-endpoint";
// 以下は MySQL データベースの接続 URL フォーマットです。
String dbUrl = String.format("jdbc:mysql:encdb://%s:%s/%s?ALIBABA_CLOUD_ACCESS_KEY_ID=%s&ALIBABA_CLOUD_ACCESS_KEY_SECRET=%s&ALIBABA_CLOUD_KMS_ENDPOINT=%s", hostname, port, dbname, accessKeyId, accessKeySecret, kmsEndpoint);
// STS トークンを使用します。
// String dbUrl = String.format("jdbc:mysql:encdb://%s:%s/%s?ALIBABA_CLOUD_ACCESS_KEY_ID=%s&ALIBABA_CLOUD_ACCESS_KEY_SECRET=%s&ALIBABA_CLOUD_KMS_ENDPOINT=%s&ALIBABA_CLOUD_STS_TOKEN=%s", hostname, port, dbname, accessKeyId, accessKeySecret, kmsEndpoint, stsToken);
// 以下は MySQL データベース用の EncJDBC ドライバーをロードします。
Class.forName("com.aliyun.encdb.mysql.jdbc.EncDriver");
// データベース接続を取得します。
Connection connection = DriverManager.getConnection(dbUrl, username, password);
// ... クエリを開始 ...ステップ 4: 暗号化された列からプレーンテキストデータをクエリする
データベースへの接続に成功すると、通常の JDBC クエリと同様にデータベース操作を実行できます。EncJDBC は暗号化された列を自動的に復号し、プレーンテキストデータを返します。
例
// クエリ文を作成します。
Statement statement = connection.createStatement();
ResultSet resultSet = statement.executeQuery("SELECT * FROM your_table_name");
// 結果セットを走査します。
while (resultSet.next()) {
for (int i = 0; i < rs.getMetaData().getColumnCount(); i++) {
System.out.print(rs.getString(i + 1));
System.out.print("\t");
}
System.out.print("\n");
}付録: 完全なコード例
この例では、Maven バージョン 3.9.9 と IntelliJ IDEA Community Edition 2024.1.2 開発ツールを使用しています。
import java.sql.*;
import java.util.Properties;
public class EncryptedColumnAccess {
public static void main(String[] args) throws ClassNotFoundException, SQLException {
// 以下の接続情報を、エンドポイント (ホスト名)、ポート、データベースインスタンス名 (dbname)、ユーザー名、パスワードなど、お使いのインスタンスの詳細に更新してください。
String hostname = "your-hostname";
String port = "your-port";
String dbname = "your-database-name";
String username = "your-username";
String password = "your-password";
// 環境変数から AccessKey ID と AccessKey Secret を取得します。
String accessKeyId = System.getenv("ALIBABA_CLOUD_ACCESS_KEY_ID");
String accessKeySecret = System.getenv("ALIBABA_CLOUD_ACCESS_KEY_SECRET");
// 一時的な STS アクセス認証情報を使用して KMS キーを読み取る場合は、取得したセキュリティトークンも入力する必要があります。
// String stsToken= "yourSecurityToken";
// KMS インスタンスエンドポイントには、パブリックネットワークアクセスの場合はパブリックエンドポイントを、VPC ネットワーク内のアクセスの場合はインスタンス VPC エンドポイントを使用します。
String kmsEndpoint = "your-kms-endpoint";
Properties props = new Properties();
props.setProperty("user", username);
props.setProperty("password", password);
props.setProperty("ALIBABA_CLOUD_ACCESS_KEY_ID", accessKeyId);
props.setProperty("ALIBABA_CLOUD_ACCESS_KEY_SECRET", accessKeySecret);
props.setProperty("ALIBABA_CLOUD_KMS_ENDPOINT", kmsEndpoint);
// props.setProperty("ALIBABA_CLOUD_STS_TOKEN","stsToken");
// MySQL データベースの接続 URL フォーマットは "jdbc:mysql:encdb://%s:%s/%s" です。
String dbUrl = String.format("jdbc:mysql:encdb://%s:%s/%s", hostname, port, dbname);
// EncJDBC ドライバーをロードします。
Class.forName("com.aliyun.encdb.mysql.jdbc.EncDriver");
// データベース接続を取得します。
Connection connection = DriverManager.getConnection(dbUrl, props);
// クエリを開始します。
try {
// クエリ文を作成します。
Statement statement = connection.createStatement();
ResultSet resultSet = statement.executeQuery("SELECT * FROM users");
// 結果セットを走査します。
while (resultSet.next()) {
int id = resultSet.getInt("id");
String name = resultSet.getString("username");
String phone = resultSet.getString("phone");
// テーブルスキーマに基づいて他のフィールドを処理します。
System.out.println("ID: " + id + ", Name: " + name + ", Phone: " + phone);
}
// リソースを閉じます。
resultSet.close();
statement.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}ローカルキーの使用
ステップ 1: MEK の生成
マスター暗号化キー (MEK) は、クライアントが暗号化されたデータにアクセスすることを承認するためのルート認証情報です。セキュリティ上の理由から、暗号化されたデータベースは MEK を保持または管理しません。また、MEK の生成やバックアップサービスも提供しません。MEK はご自身で生成する必要があります。MEK の保管と管理は、データベースのセキュリティにとって非常に重要です。MEK を適切にバックアップすることをお勧めします。
MEK: MEK は、安全な非対称暗号化プロトコルを使用してクライアントからデータベースサーバーに送信されます。これにより、サーバーとクライアントは同じキーを共有し、対称暗号化を通じて安全なデータ伝送が可能になります。
有効な値: 16 バイトの 16 進数文字列で、長さは 32 文字です。例: 00112233445566778899aabbccddeeff。
一般的な生成方法: パスワード生成ツールやプログラミング言語のランダム関数を使用できます。
例
Linux では、組み込みの OpenSSL ツールを使用できます。
openssl rand -hex 16を実行してキーを生成します。# OpenSSL を使用して安全なランダムキーを生成 openssl rand -hex 16 # 出力例: 11ce9a9489fab7355cf710837cea5d5bWindows では、OpenSSL パッケージをインストールできます。
ステップ 2: 依存関係のインストール
Maven プロジェクトの pom.xml ファイルに次の依存関係を追加します。
<dependency>
<groupId>com.aliyun</groupId>
<artifactId>aliyun-cls-jdbc</artifactId>
<version>1.0.10-1</version>
</dependency>Maven の依存関係を追加する際、必要に応じて version の値を置き換えてください。aliyun-cls-jdbc の最新バージョンは、公式サイトで確認できます。
ステップ 3: データベースに接続するための MEK の設定
MEK は、JDBC プロパティ、ファイル、または URL を使用して設定できます。JDBC を 2 つ以上の方法で設定する場合、優先順位は次のようになります: JDBC プロパティ 設定 > ファイル設定 > URL 設定。
以下の設定および接続方法では、MEK はクライアント上でローカルに処理され、安全な方法 (エンベロープ暗号化) を使用してサーバーに送信されるため、MEK が漏洩することはありません。
JDBC プロパティ設定
標準の JDBC 接続が確立されると、Properties オブジェクトを使用してカスタム属性を設定できます。
例
// エンドポイント (ホスト名)、ポート、データベースインスタンス名 (dbname)、ユーザー名、パスワードなどの接続情報を準備します。
String hostname = "your-hostname";
String port = "your-port";
String dbname = "your-database-name";
String username = "your-username";
String password = "your-password";
// カスタマーマスターキー。
String mek = "00112233445566778899aabbccddeeff";
Properties props = new Properties();
props.setProperty("user", username);
props.setProperty("password", password);
props.setProperty("MEK", mek);
// MySQL データベースの接続 URL フォーマットは "jdbc:mysql:encdb://%s:%s/%s" です。
String dbUrl = String.format("jdbc:mysql:encdb://%s:%s/%s", hostname, port, dbname);
// MySQL データベース用の EncJDBC ドライバーをロードします。
Class.forName("com.aliyun.encdb.mysql.jdbc.EncDriver");
// データベース接続を取得します。
Connection connection = DriverManager.getConnection(dbUrl, props);
// ... クエリを開始 ...ファイル設定
ファイル設定方法は、ローカル MEK の設定にのみ適用されます。
設定ファイルから必要な MEK やその他のパラメーターをインポートできます。プロジェクトで、encJdbcConfigFile という名前のプロパティを設定し、その値を設定ファイルのパスに設定します。パスを指定しない場合、システムはデフォルトで encjdbc.conf ファイルを使用します。以下のコードは設定ファイルの内容です:
MEK=00112233445566778899aabbccddeeff例
設定ファイルの場所は、次の 2 つの方法のいずれかで指定できます:
次の図に示すように、ファイルをプロジェクトのリソースディレクトリに配置します:

ファイルをプロジェクトのルートディレクトリ (プログラムのランタイムディレクトリ) に配置します。
ファイルを設定した後、次のコードに示すように、プログラムで追加の設定を行う必要はありません:
// エンドポイント (ホスト名)、ポート、データベースインスタンス名 (dbname)、ユーザー名、パスワードなどの接続情報を準備します。 String hostname = "your-hostname"; String port = "your-port"; String dbname = "your-database-name"; String username = "your-username"; String password = "your-password"; // MySQL データベースの接続 URL フォーマットは "jdbc:mysql:encdb://%s:%s/%s" です。 String dbUrl = String.format("jdbc:mysql:encdb://%s:%s/%s", hostname, port, dbname); // MySQL データベース用の EncJDBC ドライバーをロードします。 Class.forName("com.aliyun.encdb.mysql.jdbc.EncDriver"); // データベース接続を取得します。 Connection connection = DriverManager.getConnection(dbUrl, username, password); // ... クエリを開始 ...
URL 設定
URL に MEK やその他のパラメーターを埋め込むことができます。
例
// エンドポイント (ホスト名)、ポート、データベースインスタンス名 (dbname)、ユーザー名、パスワードなどの接続情報を準備します。
String hostname = "your-hostname";
String port = "your-port";
String dbname = "your-database-name";
String username = "your-username";
String password = "your-password";
// カスタマーマスターキー。
String mek = "00112233445566778899aabbccddeeff";
// MySQL データベースの接続 URL フォーマットは "jdbc:mysql:encdb://%s:%s/%s?MEK=%s" です。
String dbUrl = String.format("jdbc:mysql:encdb://%s:%s/%s?MEK=%s", hostname, port, dbname, mek);
// MySQL データベース用の EncJDBC ドライバーを読み込みます。
Class.forName("com.aliyun.encdb.mysql.jdbc.EncDriver");
// データベース接続を取得します。
Connection connection = DriverManager.getConnection(dbUrl, username, password);
// ... クエリを開始 ...ステップ 4: 暗号化された列からプレーンテキストデータをクエリする
データベースへの接続に成功すると、通常の JDBC クエリと同様にデータベース操作を実行できます。EncJDBC は暗号化された列を自動的に復号し、プレーンテキストデータを返します。
例
// クエリ文を作成します。
Statement statement = connection.createStatement();
ResultSet resultSet = statement.executeQuery("SELECT * FROM your_table_name");
// 結果セットを走査します。
while (resultSet.next()) {
for (int i = 0; i < rs.getMetaData().getColumnCount(); i++) {
System.out.print(rs.getString(i + 1));
System.out.print("\t");
}
System.out.print("\n");
}付録: 完全なコード例
この例では、Maven バージョン 3.9.9 と IntelliJ IDEA Community Edition 2024.1.2 開発ツールを使用しています。
import java.sql.*;
import java.util.Properties;
public class EncryptedColumnAccess {
public static void main(String[] args) throws ClassNotFoundException, SQLException {
// 以下の接続情報を、エンドポイント (ホスト名)、ポート、データベースインスタンス名 (dbname)、ユーザー名、パスワードなど、お使いのインスタンスの詳細に更新してください。
String hostname = "your-hostname";
String port = "your-port";
String dbname = "your-database-name";
String username = "your-username";
String password = "your-password";
// これは単なる例です。より複雑なキーを使用することをお勧めします。
String mek="00112233445566778899aabbccddeeff";
Properties props = new Properties();
props.setProperty("user", username);
props.setProperty("password", password);
props.setProperty("MEK", mek);
// MySQL データベースの接続 URL フォーマットは "jdbc:mysql:encdb://%s:%s/%s" です。
String dbUrl = String.format("jdbc:mysql:encdb://%s:%s/%s", hostname, port, dbname);
// EncJDBC ドライバーをロードします。
Class.forName("com.aliyun.encdb.mysql.jdbc.EncDriver");
// データベース接続を取得します。
Connection connection = DriverManager.getConnection(dbUrl, props);
// クエリを開始します。
try {
// クエリ文を作成します。
Statement statement = connection.createStatement();
ResultSet resultSet = statement.executeQuery("SELECT * FROM users");
// 結果セットを走査します。
while (resultSet.next()) {
int id = resultSet.getInt("id");
String name = resultSet.getString("username");
String phone = resultSet.getString("phone");
// テーブルスキーマに基づいて他のフィールドを処理します。
System.out.println("ID: " + id + ", Name: " + name + ", Phone: " + phone);
}
// リソースを閉じます。
resultSet.close();
statement.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}