# X.509証明書認証

X.509は、安全なインターネット通信で広く使われている標準的な公開鍵証明書フォーマットです。EMQXはTLS/SSL接続およびX.509証明書を用いたクライアント／サーバー間の双方向認証をサポートしており、より高いレベルのセキュリティを提供します。

## 動作原理

EMQXはクライアントがX.509証明書を用いてTLS/SSL接続を確立できるようにし、X.509証明書認証のために証明書情報とクライアントのバインディングをサポートします。利用方法とワークフローは以下の通りです。

1. サーバー証明書を発行し、EMQXでTLS/SSLを有効化し、[双方向認証](../../network/emqx-mqtt-tls.md#enable-ssl-tls-with-two-way-authentication)を設定します。
2. クライアント証明書を発行し、証明書と秘密鍵ファイルをデバイスに組み込み、TLS/SSL接続に使用します。
3. クライアントはTLSハンドシェイク時に証明書をサーバーに送信し、自身の正当性を証明します。
4. EMQXはクライアントの証明書を受け取ると、その証明書を検証しクライアントの身元を確認します。
5. 検証が成功すると、サーバーはTLSハンドシェイクを完了し、安全な接続を確立します。

接続成功後、EMQXは証明書情報をクライアント属性にマッピングして証明書とクライアントのバインディングを実現できます。さらに、[JWT認証](./jwt.md)や[パスワード認証](./pwoverview.md)など他のアプリケーション層認証と組み合わせて複数認証方式の併用も可能です。

## 特徴と利点

- **セキュリティ**：X.509はデジタル証明書と公開鍵暗号を用いた安全で信頼性の高い認証機構を提供し、通信の機密性、完全性、認証を保証します。不正なデバイスのネットワークアクセスや悪意ある操作を防止します。
- **相互運用性**：X.509は広くサポートされている普遍的な標準です。多くのIoTデバイスがX.509証明書をサポートしており、デバイス間の認証と安全な通信をより簡単かつ確実にします。
- **スケーラビリティ**：X.509は大規模なIoT展開に対応可能です。柔軟な証明書チェーンと管理機構を備え、複雑なIoT環境に適応し、多数のデバイスやエンティティの認証をサポートします。
- **信頼できる第三者検証**：X.509証明書は厳格なセキュリティ審査と検証を経た信頼できる認証局（CA）によって発行されることが多いです。デバイスは信頼されたCA発行の証明書を用いて自身の身元と証明書の正当性を保証できます。
- **強力な暗号化アルゴリズムのサポート**：X.509は共通の対称・非対称暗号アルゴリズムを含む幅広い暗号化アルゴリズムと鍵長をサポートし、IoTデバイスが強力な暗号アルゴリズムで通信の安全性を確保できます。
- **柔軟な証明書設定と管理**：X.509は柔軟な証明書設定および管理オプションを備えています。デバイスは用途に応じて適切な証明書属性や拡張フィールドを選択でき、特定のIoTアプリケーション要件に対応可能です。さらに、証明書の失効や更新も証明書管理機構で効果的に管理できます。

これらの特徴により、X.509はIoTセキュリティに最適な選択肢となります。EMQXはX.509証明書認証を完全にサポートし、IoTデバイスの安全なアクセスと通信を容易に実現します。

## X.509証明書認証の有効化

X.509証明書認証は基本的にTLS/SSL双方向認証です。設定方法は[双方向認証付きSSL/TLSの有効化](../../network/emqx-mqtt-tls.md#enable-ssl-tls-with-two-way-authentication)を参照してください。

::: tip

X.509証明書認証はパスワード認証やJWT認証よりも先に実行されます。

:::

## 証明書情報のマッピング

EMQXはX.509証明書情報をユーザー名またはクライアントIDとしてマッピングし、証明書とクライアントのバインディングを実現できます。Dashboardまたは設定ファイルから設定可能です。

::: warning mTLSによる証明書フィールドマッピングの必須条件

`peer_cert_as_username`または`peer_cert_as_clientid`を`disabled`以外に設定する場合、SSLリスナーは必ず相互TLS（mTLS）を強制する必要があります。

- `verify = verify_peer`
- `fail_if_no_peer_cert = true`
- `cacertfile`は管理するCAバンドルに設定（任意の名前で証明書を発行可能な公開CAは信頼しないこと）

:::

mTLSがない場合、クライアントは証明書を提示せずに接続したり、任意のCN/DNを持つ自己署名証明書を提示できます。EMQXは検証せずにCN/DNを直接ユーザー名またはクライアントIDにマッピングします。攻撃者が選んだCN/DNにより任意のIDをなりすますことが可能です。空のCN/DNはHTTP認証など下流の認証機構で予期せぬ動作を引き起こすこともあります。mTLSを強制すると、マッピング適用前にEMQXがハンドシェイクを拒否します。

::: tip 注意

空のユーザー名ケースに対する追加の安全策として、`listeners.{type}.{name}.enable_authn = quick_deny_anonymous`を設定し、派生ユーザー名が空のクライアントを認証チェーンに入れず即座に拒否することが推奨されます。これはmTLSの補完であり代替ではありません。偽造CN/DNに対する防御にはなりません。

:::

ピア証明書のフィールド（CN、DN、または証明書全体の内容など）をユーザー名として使用可能です。現在サポートされている証明書情報の解釈は以下の通りです。

- `cn`：証明書のCNフィールド
- `dn`：証明書のDN情報（`CN=xxx,OU=xxx,O=xxx,L=xxx,ST=xxx,C=xxx`形式）
- `crt`：証明書のDER形式コンテンツ
- `pem`：DER証明書をPEM形式に変換したコンテンツ
- `md5`：DERまたはPEM証明書コンテンツのMD5値

### PROXYプロトコルの考慮事項

TLSを上流のロードバランサーで終端し、TCPリスナーで`proxy_protocol = true`を有効にする場合、同様のIDなりすましリスクがあります。EMQXはロードバランサーがPROXY v2フレームで注入するCN/DNを信頼します。この構成で証明書フィールドマッピングを安全に使うには：

- ロードバランサー自身がmTLSを実施し、検証済みのCN/DNのみを転送すること。
- PROXYプロトコル対応リスナーは信頼できるロードバランサーからのみアクセス可能にすること。`listeners.{type}.{name}.access_rules = ["allow <trusted-LB-CIDR>", "deny all"]`を設定し、ファイアウォールやプライベートネットワーク、Unixソケットなどのネットワークレベル制御と組み合わせて多層防御を行うこと。

PROXYプロトコルポートに直接アクセス可能な攻撃者は任意の証明書フィールドを含むPROXY v2フレームを作成し、任意のクライアントをなりすますことが可能です。

### Dashboardによる設定

1. Dashboardを開き、**管理** -> **MQTT設定**ページに移動し、**一般**タブを選択します。
2. MQTT設定内で以下の項目を見つけて変更します。
   - **ピア証明書をユーザー名として使用**：X.509証明書情報をユーザー名としてマッピングします。
   - **ピア証明書をクライアントIDとして使用**：X.509証明書情報をクライアントIDとしてマッピングします。
3. **変更を保存**ボタンをクリックして設定を完了します。新規接続クライアントは設定に従ってマッピングされます。

### 設定ファイルによる設定

1. インストール環境に応じて`./etc`または`/etc/emqx/etc`ディレクトリ内の設定ファイルを開きます。
2. MQTT設定は明示的に定義されていないため、以下を追加してデフォルト値を上書きします。

```hocon
mqtt {
# 証明書情報をクライアントIDとして使用
peer_cert_as_clientid = "disabled" # "disabled" | "cn" | "dn" | "crt" | "pem" | "md5"
# 証明書情報をユーザー名として使用
peer_cert_as_username = "cn" # "disabled" | "cn" | "dn" | "crt" | "pem" | "md5"
}
```

## 証明書のSubject Alternative Namesからクライアント属性を初期化

EMQX Enterprise 6.3.0以降、TLS接続がEMQXで終端される場合にクライアント証明書のSubject Alternative Names（SANs）からクライアント属性を初期化できます。サポートされるSANタイプはDNS名、IPv4およびIPv6アドレス、メールアドレス、URIです。

`mqtt.client_attrs_init`式で`cert_san.dns`、`cert_san.ip`、`cert_san.email`、`cert_san.uri`を使用します。これらは配列変数のため、`nth()`や`join_to_string()`などのVariform関数を使ってクライアント属性値を生成してください。設定詳細と例は[証明書のSubject Alternative Namesからクライアント属性を初期化](../../../develop/client-attributes/client-attributes.md#initialize-client-attributes-from-certificate-subject-alternative-names)を参照してください。

`peer_cert_as_username`および`peer_cert_as_clientid`はSAN抽出をサポートしていません。SANを認証、認可、またはクライアント属性をサポートする他の機能で使用する場合は、まずクライアント属性として値を保存してください。
