ALBでクライアント証明書をパススルーしたときのバックエンド側の実装
はじめに
ALBのmTLSサポートにより、従来よりも柔軟なクライアント認証の実装が可能になりました。この機能は特に以下のようなユースケースで有用です。
- マイクロサービス間の相互認証
- APIのセキュリティ強化
- 特定のクライアントのみにアクセスを制限
トラストストアモードを利用すれば、ALBだけでクライアント証明書の検証処理を完結させることができます。しかし、特定のターゲットグループに対してのみmTLSを有効にしたい場合もあるかと思います。そのような場合にはパススルーモードを利用し、ALBではクライアント証明書を検証せずにバックエンド側へリクエストを転送することができます。バックエンドには x-amzn-mtls-clientcert
ヘッダーが追加され、その値はクライアント証明書のPEM形式の文字列です。バックエンド側でこの値を利用してクライアント証明書を検証することができます。
⚠️ この記事で作成する自己署名証明書は検証用です。本番環境では信頼できる認証局による証明書を使用してください。
クライアント証明書の作成
今回は検証ですので、いわゆるオレオレ証明書を作成します。CA証明書のCNを selfsigned-ca
とし、クライアント証明書のCNを client
とします。
# CAの秘密鍵を作成
openssl genpkey -algorithm rsa -pkeyopt rsa_keygen_bits:4096 -out ca.key
# 自己署名CA証明書を作成
openssl req -x509 -new -key ca.key -subj "/CN=selfsigned-ca" -days 3650 -out ca.crt
# クライアントの秘密鍵を作成
openssl genpkey -algorithm rsa -pkeyopt rsa_keygen_bits:4096 -out client.key
# CSR(証明書署名要求)を作成
openssl req -new -key client.key -out client.csr -subj "/CN=client"
# CAによる署名済みクライアント証明書を作成
openssl x509 -req -in client.csr -CA ca.crt -CAkey ca.key -CAcreateserial -out client.crt -days 365
証明書作成時の補足事項
- RSAキーサイズは4096ビットを使用していますが、セキュリティ要件に応じて2048ビットも一般的です
- 証明書の有効期限は検証用に設定していますが、本番環境では適切な期間を設定してください
検証の手順
ALBのパススルーモードでは、クライアント証明書が x-amzn-mtls-clientcert
ヘッダーとしてバックエンドに転送されます。このヘッダーの値はURLエンコードされたPEM形式の証明書であることに注意が必要です。
検証においては以下のポイントを確認します:
- クライアント証明書がX.509形式であること
- クライアント証明書のCNが想定した値(今回の例では
client
)であること - クライアント証明書が有効期限内であること
- クライアント証明書の発行者が正しいこと(今回の例では
selfsigned-ca
) - クライアント証明書がCA証明書によって正しく署名されていること
実装例
以下は、PHPでの検証例です。なお、CA証明書はサーバ内に配置してあるものとします。
<?php
// HTTPリクエストヘッダーからx-amzn-mtls-clientcertを取得
// このヘッダーの値はURLエンコードされているので、デコードが必要
$mtlsClientCert = $_SERVER['HTTP_X_AMZN_MTLS_CLIENTCERT'] ?? '';
if (empty($mtlsClientCert)) {
echo 'No client certificate provided';
exit;
}
// URLエンコードされている証明書をデコード
$mtlsClientCert = urldecode($mtlsClientCert);
// クライアント証明書の検証
$cert = openssl_x509_parse($mtlsClientCert);
if ($cert === false) {
echo 'Invalid client certificate';
exit;
}
// クライアント証明書のCNが正しいかを検証
if ($cert['subject']['CN'] !== 'client') {
echo 'Invalid client';
exit;
}
// クライアント証明書の有効期限を検証
$now = time();
if ($now < $cert['validFrom_time_t'] || $now > $cert['validTo_time_t']) {
echo 'Client certificate is expired';
exit;
}
// クライアント証明書の発行者が自己署名CAであることを検証
if ($cert['issuer']['CN'] !== 'selfsigned-ca') {
echo 'Invalid issuer';
exit;
}
// CA証明書を取得
$caCert = file_get_contents('ca.crt');
if ($caCert === false) {
echo "Failed to read CA certificate\n";
exit;
}
// 署名の検証
$result = openssl_x509_verify($mtlsClientCert, $caCert);
if ($result === 1) {
echo "Client certificate is valid\n";
} elseif ($result === 0) {
echo "Client certificate is not valid\n";
} else {
echo "Failed to verify client certificate\n";
while ($msg = openssl_error_string()) {
echo $msg . "\n";
}
}
おわりに
ALBのmTLSパススルーモードは、アプリケーションレベルで特定の条件に基づいてクライアント認証を行いたい場合に特に有用です。この実装により、ALBのパススルーモードを使用してバックエンドアプリケーションでクライアント証明書の検証を行うことが可能になります。