金融業界などの特に高いAPIセキュリティレベルを要求されるシステムへのガイドラインであるFAPI(Financial-grade API)。高いセキュリティレベルの要求事項を満たすためには多くの障壁が存在します。特にアプリ・基盤両面で整備が必要なMTLS(相互TLS認証)を利用したクライアント認証や証明書バインディングの必須化には頭を悩ませている方も多いのではないでしょうか。
そこで、本記事ではFAPIのMTLS要件に対応した認証認可基盤の構成例や設定のポイント等をご紹介します。
FAPIの技術仕様の全体像については、前回記事をご確認ください。
▼参考記事
FAPIとは?|高度なセキュリティレベルのAPIガイドラインを徹底解説
特権ID管理ソリューション「SecureCube Access Check」紹介セミナー
2024年4月17日(水) 12:00開催ウェビナー
はじめに
FAPI(Financial-grade API)は、ユーザーの認証や認可を安全に行うためのオープンスタンダードであるOAuth 2.0 および OpenID Connectを金融業界などのより高いセキュリティ要件に対応できるようにした拡張仕様です。しかし、そのセキュリティ要件の高さ故、FAPIの要求事項に対応するためには、アプリ・基盤の両面において多くの考慮すべき事項が存在します。
特に頭を悩ませるのは、FAPI Part2で定められているMTLS(相互TLS認証)ではないでしょうか。
一般的にMTLSというと、TLS通信の際に、サーバ側だけでなくクライアント側も証明書を利用して相互に相手を認証することを指します。FAPI1.0の文脈ではRFC 8705 OAuth 2.0 Mutual-TLS Client Authentication and Certificate-Bound Access Tokens[i]にて定義されている「MTLSによるOAuthクライアント認証」と「クライアント証明書に紐づいたアクセストークンとその検証処理」を指します。アプリケーションの機能だけでなく、基盤構成にも考慮が必要であり、難易度が高い要件です。
そこで、本記事では、FAPIのMTLS要件に対応した認証認可基盤をAWS上に構築する方法をご紹介します。なお、本記事では「FAPI」と記載した場合には、「FAPI 1.0 Final」[ii]のことを指します。
FAPI 1.0におけるMTLS
2-1. MTLSによるOAuthクライアント認証
FAPIのシーケンスについておさらいします。以下はWebアプリケーションがクライアントとなり、ブラウザ(UserAgent)からアクセスした場合のシーケンス例です。
シーケンス図
FAPIのシーケンスにおいてMTLSによるOAuthクライアント認証が要求されるのは、トークン要求の箇所です。
FAPIではクライアント認証方式として、クライアント証明書を利用したtls_client_authおよびself_signed_tls_client_authが定められています。
クライアント認証方式
大項目 |
小項目 |
説明 |
OAuth2.0/OIDC |
FAPI1.0 Part1 |
FAPI1.0 Part2 |
クライアント認証方式 |
client_secret_basic |
クライアントIDとシークレットをBasic認証の形式でAuthorizationヘッダにセットする |
〇 |
× |
× |
client_secret_post |
クライアントIDとシークレットをPOSTボディに含める |
〇 |
× |
× |
|
client_secret_jwt |
クライアントシークレットで JWT に署名をしてそれをサーバーに提示し、認可サーバーは事前に登録済みのクライアントシークレット(共通鍵)で署名を検証する |
〇 |
〇 |
× |
|
private_key_jwt |
クライアントの秘密鍵でJWT に署名をしてそれをサーバーに提示し、認可サーバーはクライアントの公開鍵でその署名を検証する |
〇 |
〇 |
〇 |
|
tls_client_auth |
TLS 通信で用いられた PKI のクライアント証明書を用いて、事前登録済みの情報と一致するか確認する |
〇 |
〇 |
〇 |
|
self_signed_tls_client_auth |
TLS 通信で用いられた自己署名クライアント証明書を用いて、事前登録済みの情報と一致するか確認する |
〇 |
〇 |
〇 |
tls_client_authおよびself_signed_tls_client_authでは、TLS接続時のクライアント証明書によるクライアント認証に加えて、TLS通信で利用されたクライアント証明書が認可サーバで登録済みの情報と一致するかを確認します。
tls_client_authおよびself_signed_tls_client_authにおける検証内容は、「ポイント③(認可サーバへのクライアント証明書の連携)」で紹介します。
2-2. クライアント証明書に紐づいたアクセストークンとその検証処理
OAuth2.0におけるMTLSでは、認可サーバが発行したアクセストークンをクライアント証明書と紐づけて管理することも求められています。
クライアント証明書と紐づけられたアクセストークンはsender-constrained access tokensと呼ばれ、アクセストークンの保有者の正当性を確認するために利用されます。sender-constrained access tokens を規定したRFC8705では、リソースサーバにおけるsender-constrained access tokensの検証方法として、JWT型のアクセストークンによる検証とトークンイントロスペクションエンドポイントによる検証の2パターンを定義しています。
当社のコンシューマ向けID管理製品「Uni-ID Libra」では、後者の方式を採用し、トークンイントロスペクションエンドポイント応答として、sender-constrained access tokensに紐づいているクライアント証明書のサムプリント(cnfパラメータ)を返却します。
Uni-ID Libraでのトークンイントロスペクション応答の例
リソースサーバは、クライアントが提示したクライアント証明書のハッシュ値(X.509 Certificate SHA-256 Thumbprint)を計算し、cnfパラメータと値が一致するか検証することで、クライアントが正当なアクセストークンの所有者かどうか確認することができます。
基盤構成の紹介
ここからは、クライアントから認可サーバへのトークン要求を例に具体的なMTLS基盤の構築方法を解説します。
今回はAWS環境における、NGINXをリバースプロキシサーバとしたMTLS基盤を紹介します。認可サーバとしては、当社のコンシューマ向けID管理製品「Uni-ID Libra」を利用しました。
以下に、今回紹介する基盤構成を示します。
基盤構成(NGINX)
本構成においてNGINXが果たす役割は、以下の2点です。
- クライアントとNGINX間のMTLS通信を確立する
- クライアントから提示されたクライアント証明書を認可サーバに連携する
ここでの注意事項は、NGINXのロードバランサとしてNLBを利用している点です。本稿執筆時点(2024年1月)では、ロードバランサとしてALBを利用した場合、TLS通信の終端がALBとなりクライアント証明書を認可サーバに連携することができません。
MTLS通信の終端をNGINXとするためには、ロードバランサとしてNLBを利用し、リスナーとしてTCPリスナーを登録する必要があります。
本構成におけるNLBの主な設定項目としては、以下の通りです。
NLBの設定項目
分類 | 項目 | 設定値 |
NLB |
タイプ |
network |
スキーム |
internet-facing |
|
リスナー |
プロトコル |
TCP |
ポート |
443 |
|
ターゲットグループ |
プロトコル |
TCP |
ポート |
443 |
|
ターゲットタイプ |
IP |
なお、MTLS基盤の構成として、API Gatewayを利用した構成も考えられると思います。以下に、API Gatewayを利用した場合の構成例を示します。
基盤構成(APIGW)
しかし、本稿執筆時点(2024年1月)ではAPI Gatewayを利用したMTLS基盤で、FAPIのMTLS要件を満たすことはできませんでした。
API Gatewayを利用した場合に課題となったのが、クライアント証明書の暗号スイートの制限です。FAPIではTLS1.2を利用する場合、セキュリティレベルが高い一部の暗号スイートのみが許容されます。
しかし、API Gatewayを利用した場合、セキュリティポリシーによる暗号スイートの制限では、FAPIで許容していない暗号スイートも許容してしまいます。クライアント証明書の暗号スイートを細かく制御するために、本記事ではNGINXをリバースプロキシサーバとしてMTLS基盤を構築します。
NGINXの設定のポイント
本基盤構成において、中心的な役割を担うのがNGINXです。以下に、NGINXの設定例を示します。※ 設定例はバージョン1.23.3
NGINXの設定例
※ NGINXの設定に関する補足事項
本設定例はFAPIのMTLS要件に必要な設定項目を記載した設定例です。上記の要件に関連する項目以外は、別途検討が必要となります(タイムアウトやセキュリティに関連する設定など)。
ポイント①クライアント証明書の有効性の検証
設定ポイントの1点目は、クライアント証明書の有効性の検証です。
NGINXはMTLS通信の終端となるため、自身のサーバ証明書の他にクライアント証明書の検証を行うためのCA証明書(自己署名証明書の場合は自身の証明書)を設定する必要があります。設定項目としては以下の通りです。
クライアント証明書検証のためのNGINXでの設定項目
項目 |
説明 |
設定例 |
ssl_client_certificate |
クライアントのCA証明書のパスを指定する(自己証明書の場合はクライアント証明書自身) |
/mnt/nginx/client-ca.pem |
ssl_trusted_certificate |
クライアント証明書を検証する際に信頼する証明書のパスを指定する(ルートCAの証明書等) |
/mnt/nginx/client-cert/root_ca_cert.pem |
ssl_verify_client |
クライアントの証明書の検証を有効化 |
optional |
ssl_verify_depth |
クライアント証明書チェーンの検証の深さを設定する |
3 |
クライアント証明書としてPKIが発行した証明書を利用するか、自己署名証明書を利用するかは大きな検討ポイントになります。この際注意すべきポイントは、複数クライアント間におけるクライアント証明書(または同じDNを持つ証明書)の共有リスクです。
FAPI Part2 8.10で言及されているように、複数クライアント間で同じクライアント証明書を利用すると、クライアント証明書が漏洩した場合に、同じクライアント証明書を共有する全クライアントが危険にさらされます。
組織内で複数クライアントを利用する場合は、複数のクライアント証明書の管理が必要であることを考慮した上で、クライアント証明書の発行方法を決める必要があります。
ポイント➁証明書の暗号スイートの制限
設定ポイントの2点目は、証明書の暗号スイートの制限です。FAPI Part2に対応する場合、TLS1.2の暗号スイートは以下のいずれかに制限する必要があります。
FAPI Part2で利用可能な暗号スイート
暗号スイート |
OpenSSLでの暗号スイート名 |
TLS_DHE_RSA_WITH_AES_128_GCM_SHA256 |
DHE-RSA-AES128-GCM-SHA256 |
TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256 |
ECDHE-RSA-AES128-GCM-SHA256 |
TLS_DHE_RSA_WITH_AES_256_GCM_SHA384 |
DHE-RSA-AES256-GCM-SHA384 |
TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 |
ECDHE-RSA-AES256-GCM-SHA384 |
NGINXでは送信されたクライアント証明書の暗号スイートが、上記のいずれかに含まれることを検証する必要があります。なお、NGINXで暗号スイートを指定する際には、OpenSSLの暗号スイート名で記載します。設定項目としては以下の通りです。
暗号スイート指定のためのNGINXの設定項目
項目 |
説明 |
設定例 |
ssl_ciphers |
サポートする暗号スイートのリストを指定する(OpenSSL形式) |
DHE-RSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384 |
ssl_dhparam |
Diffie-Helmanの鍵交換アルゴリズムで利用するパラメータファイル ※ NGINXのバージョンが1.11.0以上でTLS_DHE_RSA_WITH_AES_128_GCM_SHA256またはTLS_DHE_RSA_WITH_AES_256_GCM_SHA384を利用する場合 |
/mnt/nginx/dhparam.pem |
暗号スイートについてはいずれを選択しても、OpenSSL等の標準的なツールで証明書を発行可能であるため、証明書発行のしやすさでは大きな差異はないと考えられます。
ただし、Diffie-Helmanの鍵交換アルゴリズムを利用する暗号スイート(TLS_DHE_RSA_WITH_AES_128_GCM_SHA256またはTLS_DHE_RSA_WITH_AES_256_GCM_SHA384)を利用する場合は、NGINXで追加の設定が必要となります。
NGINXのバージョン1.11.0以上では、Diffie-Helmanの鍵交換アルゴリズムで利用するパラメータが設定されていません。OpenSSLのコマンド(openssl dhparam)でパラメータファイルを作成の上、パラメータファイルへのパスを設定値($ssl_dhparam)として登録する必要があります。
ポイント③認可サーバへのクライアント証明書の連携
設定ポイントの3点目は、クライアント証明書の有効性の検証です。認可サーバでは、クライアントが送信したクライアント証明書が事前に登録した内容と一致するか検証する必要があります。
NGINXの設定例
クライアント証明書の検証と認可サーバへの流れは下記の通りです。
- NGINXはクライアントから送信されたクライアント証明書が有効であること($ssl_client_verify)を検証する
- Uni-ID Libra独自のヘッダ情報(X-FAPI-CLIENT-CERT)にクライアント証明書を含め、認可サーバに転送する(Uni-ID Libra製品仕様)
- 認可サーバは、連携されたクライアント証明書が事前に登録した内容と一致するか検証する
上記の2番目の処理で、認可サーバにクライアント証明書を連携している理由は、以下の2点です。
- TLS通信で利用されたクライアント証明書が認可サーバで事前登録済みの内容と一致するか検証する
- 認可サーバでクライアント証明書とアクセストークンを紐づけて管理する(sender-constrained access tokens)
認可サーバへのクライアント証明書の連携方法は、FAPIの仕様外のため各認可サーバの仕様に従います。
Uni-ID Libraでは、ヘッダ情報にクライアント証明書を含めて連携しています。
さて、認可サーバにおけるクライアント証明書の検証は、連携されたクライアント証明書の発行方法によって2パターンに分類されます。
1つ目は、PKIで発行したクライアント証明書を利用するパターン(クライアント認証方式 : tls_client_auth)です。認可サーバはクライアント証明書に含まれる主体者識別子(Subject Distinguished Name)またはSAN(Subject Alternative Name)のエントリーのいずれかが認可サーバに登録された値と一致するかを検証します。
クライアント証明書の例
2つ目は、自己署名証明書をクライアント証明書として利用するパターン(クライアント認証方式 : self_signed_tls_client_auth)です。認可サーバは、提示されたクライアント証明書が登録済みのJWKのX.509 証明書チェーンのパラメータ(x5c)と一致するかを検証します。
自己署名証明書を含むJWKの例
認可サーバにおけるクライアント証明書の検証は、FAPI1.0 Prat1/Part2 ではなく、RFC8705で定義されています。より詳細な仕様を確認する際には、原文のRFC8705を確認してください。
おわりに
本記事では、NGINXとUni-ID Libraを利用したFAPIのMTLS要件に対応した認証認可基盤のAWS上での構築方法を紹介しました。最終的な設定値だけでなく、実際に構築した際に課題となった点も盛り込んでおりますので、ご参考になれば幸いです。
FAPIではMTLS要件以外にも、守らなければならない細かい要件が数多くありますので、実際にFAPIに準拠したサービスを展開する際には必ず原文をご確認ください。
NRIセキュアには、FAPIを含めたOAuth2.0/OpenID Connect等デジタルアイデンティティの標準仕様の専門的な知識を持つメンバや認証認可基盤構築経験の豊富なメンバが多く在籍しております。FAPI導入検討等の上流工程から、当社のコンシューマ向けID管理製品「Uni-ID Libra」によるFAPI対応機能のご提供、今回のような基盤構成のご支援まで幅広く対応可能です。ぜひお問い合わせください。
BtoCサイトのID統合・SSOソリューション Uni-ID Libra
[i] RFC 8705:OAuth 2.0 Mutual-TLS Client Authentication and Certificate-Bound Access Tokens(https://www.rfc-editor.org/rfc/rfc8705.html)
Financial-grade API Security Profile 1.0 - Part 1: Baseline(https://openid.net/specs/openid-financial-api-part-1-1_0.html)
Financial-grade API Security Profile 1.0 - Part 2: Advanced (https://openid.net/specs/openid-financial-api-part-2-1_0.html)