# 認証

認証はクライアントの身元を検証するプロセスです。ほとんどのアプリケーションにおいて必須の機能であり、不正なクライアント接続からサービスを保護するのに役立ちます。

EMQXは複数の認証メカニズムをサポートしており、[TLS X.509](https://en.wikipedia.org/wiki/X.509)証明書認証や[TLS-PSK](https://www.rfc-editor.org/rfc/rfc4279)認証もサポートしています。これにより、クライアントとサーバー間の認証要求に対するオプションが提供されます。

本セクションでは、身元認証の基本概念と設定について説明します。

::: tip

デフォルトでは、EMQXは認証機能を有効にしていないため、すべてのクライアントの接続を許可します。運用環境で使用する場合は、少なくとも1つの認証方式を事前に設定してください。

:::

## 認証メカニズム

EMQXがサポートする認証メカニズムは以下の通りです：

- X.509証明書認証
- JWT認証
- ユーザー名／パスワード認証
- MQTT 5.0の拡張認証
- PSK認証

### X.509証明書認証

EMQXはクライアント認証のために[X.509証明書認証](./x509.md)をサポートしています。EMQXでX.509証明書認証を使用すると、クライアントとサーバーはTLS/SSLを介して信頼できる接続を確立し、通信当事者の真正性と送信データの整合性を保証します。EMQXは片方向認証（クライアントがサーバーのみを認証）と双方向認証（クライアントとサーバーが互いに証明書を検証）をサポートしており、様々なセキュリティ要件や展開シナリオに対応可能です。

### JWT認証

[JSON Web Token (JWT)](https://jwt.io/)はトークンベースの認証メカニズムであり、サーバーがクライアントの認証情報やセッション情報を保持しません。

クライアントは接続要求時にJWTを携帯し、EMQXは事前に設定されたシークレットまたは公開鍵を使ってJWT署名を検証します。JWKSエンドポイントを設定している場合、JWT認証器はJWKSエンドポイントから取得した公開鍵リストを用いてJWT署名を検証します。

### パスワード認証

EMQXは最もシンプルで一般的なパスワード認証をサポートしており、クライアントはユーザー名、クライアントID、対応するパスワードなどの認証情報を提供する必要があります。場合によっては、TLS証明書の一部フィールド（例：証明書のCommon Name）をクライアントの身元認証情報として利用することも可能です。いずれの場合も、これらの認証情報は事前にデータベースに保存されており、パスワードは通常ソルト付きハッシュ形式で保存されます。

EMQXのパスワード認証の動作は以下の通りです：クライアントは接続要求時に認証情報を携帯し、EMQXはクライアントが提供した認証情報に対応するハッシュ化されたパスワードをデータベースから照会し、一致した場合のみ接続を許可します。  
![emqx-authn-flow](./assets/emqx-authn-flow.png)

組み込みデータベースのほか、EMQXはMySQL、PostgreSQL、MongoDB、Redisなどの各種バックエンドデータベースとの連携もサポートしています。

また、EMQXはユーザーが開発したHTTPサーバーなど外部サービスに認証処理を委譲する設定も可能です。

### MQTT 5.0 拡張認証

[MQTT 5.0の拡張認証](https://www.emqx.com/en/blog/mqtt5-enhanced-authentication)は、基本認証にチャレンジ／レスポンス方式の認証を追加したものです。これにより、SCRAM認証（Salted Challenge Response Authentication Mechanism）やKerberos認証など、より安全な認証方式の利用が可能になります。EMQXの拡張認証実装では、組み込みデータベースおよび外部HTTPサービスを通じたSCRAMユーザー管理をサポートしています。

### PSK認証

EMQXの[PSK認証](../../network/psk-authentication.md)は、証明書ベースのTLSに代わるシンプルかつ安全な認証方式です。クライアントとサーバーが共有する秘密鍵に基づき、デジタル証明書を必要としません。リソース制約のある環境で証明書管理のオーバーヘッドを軽減するのに適しています。

## EMQX認証器

EMQXは認証メカニズムおよびバックエンドデータベースに応じて、以下の認証方式（以下「認証器」と呼びます）をサポートしています：

| メカニズム       | データベース         | 説明                                                         |
| -------------- | ----------------- | ------------------------------------------------------------ |
| パスワードベース | 組み込みデータベース | [Mnesiaデータベースを認証情報ストレージとして使用する認証](./mnesia.md) |
| パスワードベース | MySQL             | [MySQLデータベースを認証情報ストレージとして使用する認証](mysql.md) |
| パスワードベース | PostgreSQL        | [PostgreSQLデータベースを認証情報ストレージとして使用する認証](postgresql.md) |
| パスワードベース | MongoDB           | [MongoDBデータベースを認証情報ストレージとして使用する認証](./mongodb.md) |
| パスワードベース | Redis             | [Redisデータベースを認証情報ストレージとして使用する認証](./redis.md) |
| パスワードベース | LDAP              | [LDAPサーバーを認証情報ストレージとして使用する認証](./ldap.md) |
| パスワードベース | HTTPサーバー       | [外部HTTP APIを用いた認証情報検証認証](./http.md)             |
| JWT            |                   | [JWTを用いた認証](./jwt.md)                                   |
| SCRAM          | 組み込みデータベース | [SCRAMを用いた認証](./scram.md)                               |
| SCRAM          | HTTPサーバー       | [RESP APIベースのSCRAM認証](./scram_restapi.md)               |
| GSSAPI         | Kerberos          | [Kerberosを用いたGSSAPI認証](./kerberos.md)                   |
| ルールベース    |                   | [Client-infoを用いた認証](./cinfo.md)                         |

## 認証チェーン

EMQXは認証チェーンの作成をサポートしており、複数の認証器を定義された順序で評価できます。チェーン内の各認証器は異なるタイプである必要があります（例：1つはHTTP、1つはLDAP、1つは組み込みデータベース）。

::: tip

現時点でEMQXはMQTTクライアントに対してのみ認証チェーンの作成をサポートしています。ゲートウェイは認証チェーンをサポートせず、単一の認証器を使用してください。

:::

X.509証明書ベースの認証が適用されている場合、認証チェーンの実行前に常に実行されます。

### 認証チェーンの動作

認証チェーンが設定されている場合、EMQXは最初の認証器から認証情報の取得を試み、失敗した場合は次の認証器に切り替えて処理を続行します。

パスワードベース認証を例に挙げると、動作は以下の通りです：

1. **前提条件の評価（設定されている場合）：**  
   認証器に[前提条件](#authenticator-preconditions)がある場合、EMQXはクライアント属性情報（例：`listener`、`clientid`、`username`）に基づいて式を評価します。  
   - 式が`true`の場合、認証器が呼び出されます。  
   - そうでなければ、認証器はスキップされます。

2. **認証器の実行：**  
   - 認証情報が見つかり有効（例：パスワードが正しい）であれば、クライアントは認証成功とみなされ接続が許可されます。  
   - 認証情報が見つかったが無効であれば、クライアントはアクセス拒否されます。  
   - 認証情報が見つからなければ、EMQXは次の認証器に移ります。

3. **エラーまたは無効時のスキップ：**  
   認証器が無効化されている場合や、実行中に内部エラー（例：データベースが利用不可）が発生した場合もスキップされます。

4. **フォールバック動作：**  
   すべての認証器がスキップされるか、いずれもクライアントを認証できなかった場合、EMQXはデフォルトで接続を拒否します。

![](./assets/authn-chain.png)

### 認証器の前提条件

EMQX 5.9以降、各認証器に前提条件を割り当てることで、特定のクライアントに対して認証器を呼び出すかどうかを制御できます。前提条件は[Variform式](../../configuration/configuration.md#variform-expressions)であり、クライアント属性（`listener`、`username`、`clientid`など）を評価します。式が`true`でなければ認証器はスキップされます。

この機能により認証チェーンに条件分岐ロジックを組み込めます。例えば、異なるリスナー経由のクライアントに対して異なる認証器を適用するなど、細かい認証制御が可能です。EMQXは適切な場合にのみ認証器を呼び出し、外部システムへの不要なリクエストを回避します。

#### 前提条件でサポートされるクライアント属性

前提条件で使用可能なクライアント属性は以下の通りです：

- `username`：クライアントのユーザー名
- `password`：クライアントのパスワード
- `clientid`：クライアントID
- `client_attrs.*`：クライアント属性
- `cert_common_name`：クライアントTLS証明書のSubjectフィールド
- `cert_subject`：クライアントTLS証明書のCommon Name（CN）
- `peersni`：TLSクライアントが送信したSNI（Server Name Indication）
- `listener`：リスナーID（例：`tcp:default`）
- `zone`：関連付けられたコンフィグゾーン

#### 前提条件の例

異なるリスナー経由のクライアントに異なる認証器を適用する例：

- `tcp:default`のクライアントにHTTP認証器を適用：

  ```
  str_eq(listener, 'tcp:default')
  ```

- `ssl:default`のクライアントにPostgreSQL認証器を適用：

  ```
  str_eq(listener, 'ssl:default')
  ```

JWT認証器とパスワードベース認証器を1つの認証チェーンで組み合わせる場合、JWT認証器に以下の前提条件を設定します：

```
is_jwt(password)
```

EMQX 6.2.3以降、この前提条件はパスワードが構造的にJWTである場合にのみ`true`を返します。パスワードがJWTでないか存在しない場合、EMQXはJWT認証器をスキップし、次の認証器に進みます。

## 外部リソースキャッシュ

EMQXはMySQL、MongoDB、Redisなどの外部バックエンドから取得した認証結果をノード単位でキャッシュする仕組みを提供しています。このキャッシュは認証結果の検索性能を向上させ、特に高スループット環境での外部リソースへの繰り返しアクセスを削減します。

::: tip 注意

外部リソースキャッシュは外部データソースにのみ適用されます。組み込みデータベース認証器などローカルソースには適用されません。

:::

### 外部リソースキャッシュの動作

外部リソースキャッシュはノード単位で認証結果を保存し、同一ノード上のすべてのクライアントセッションで共有されます。これにより外部認証バックエンドへの冗長な問い合わせを回避します。

1. クライアントが接続し認証をトリガーする。
2. EMQXはキャッシュに以前の結果があるか確認する：  
   - 有効な結果があれば**キャッシュヒット**となり、外部バックエンドへの問い合わせは行いません。  
   - 結果がなければ**キャッシュミス**となり、外部バックエンドに問い合わせます。

3. バックエンドからの結果はキャッシュに保存され、**キャッシュ挿入**メトリクスが増加します。

この仕組みによりレイテンシ削減、バックエンド負荷軽減、システム応答性維持が可能になります。

### 外部リソースキャッシュの有効化と設定

EMQXダッシュボードから外部リソースキャッシュを有効化および設定できます：

1. **アクセス制御** -> **認証** に移動します。

2. 右上の **外部リソースキャッシュ設定** ボタンをクリックすると、右側からサイドパネルが表示されます。

3. パネル内の **外部リソースキャッシュを有効にする** ボタンでキャッシュ機能をオン／オフできます。有効化後、以下のキャッシュ設定を行います：

   | 項目名                          | 説明                                                         |
   | ------------------------------- | ------------------------------------------------------------ |
   | **最大キャッシュアイテム数**      | ノードあたりの最大キャッシュエントリ数。デフォルト：`1,000,000`。 |
   | **最大メモリ使用量**              | キャッシュのメモリ使用上限。デフォルト：`100 MB`。           |
   | **キャッシュTTL**                | キャッシュエントリの有効期間。デフォルト：`1分`。            |

4. **更新** をクリックして設定を適用します。

これらの設定はクラスター全体に適用され、すべてのノードで一貫した動作を保証します。

### 外部リソースキャッシュの状態監視

<!--@include: ../monitor-cache-status.md-->

## スーパーユーザー

通常、認証はクライアントの身元認証情報の検証のみを行い、クライアントが特定のトピックに対してパブリッシュやサブスクライブの権限を持つかどうかは認可システムが判断します。しかしEMQXはスーパーユーザー役割と権限プリセット機能を提供しており、パブリッシュ／サブスクライブの認可処理を容易にします。

::: tip

権限プリセットはJWT認証およびHTTP認証でサポートされています。現在のクライアントが所有するパブリッシュ／サブスクライブ権限の[アクセス制御リスト（ACL）](./acl.md)はJWTペイロードやHTTPレスポンスボディに含まれ、認証成功後にクライアントにプリセットされます。

:::

ユーザーがスーパーユーザーかどうかは、データベースクエリ、HTTPレスポンス、JWTクレームの`is_superuser`フィールドで確認できます。

## 認証結果からのクライアントID上書き

認証バックエンドは認証成功時に`clientid_override`を返すことができます。値は空でない完全な置換用クライアントIDの文字列でなければなりません。EMQXは認証後、クライアントセッション開始前にこの値を適用します。フィールドが存在しないか空の場合は、従来のクライアントIDを保持します。

認証バックエンドが置換用クライアントIDを決定する場合にこの仕組みを利用します。EMQXが認証前に接続情報から置換クライアントIDを構築できる場合は、代わりに`mqtt.clientid_override`を使用してください。複数テナント環境における仕組みの選択、実行順序、失敗時の挙動については[クライアントID分離](../../multi-tenancy/namespace-global-settings.md#client-id-isolation)を参照してください。

1つの接続に対してクライアントID上書きの仕組みは1つだけ使用してください。両方設定されている場合、認証結果の上書きが後に実行され、`mqtt.clientid_override`で生成されたクライアントIDを置き換えます。

### 認証バックエンドの設定

`clientid_override`は認証結果のフィールドであり、すべての認証器共通のダッシュボード設定ではありません。各認証バックエンドで以下のように設定します：

| 認証バックエンド | `clientid_override`の提供方法                                   |
| ---------------- | ------------------------------------------------------------ |
| [HTTP](./http.md) | 成功時のJSONレスポンスのトップレベル文字列フィールドとして返す。 |
| [JWT](./jwt.md)   | JWTペイロードのトップレベル文字列クレームとして追加する。       |
| [LDAP](./ldap.md) | LDAP属性に格納し、**Client ID Override Attribute**に属性名を設定。デフォルトは`clientIdOverride`。 |
| [MongoDB](./mongodb.md) | ドキュメントフィールドに格納し、**Client ID Override Field**にフィールド名を設定。デフォルトは`clientid_override`。 |
| [MySQL](./mysql.md) | クエリ結果のカラム名`clientid_override`として返す。             |
| [PostgreSQL](./postgresql.md) | クエリ結果のカラム名`clientid_override`として返す。             |
| [Redis](./redis.md) | `clientid_override`フィールドに格納し、クエリコマンドに含める。 |

クライアントID分離に置換クライアントIDを使用する場合は、グローバルに一意であることを保証してください。

## パスワードハッシュ化

パスワードを平文で保存すると、データベースを閲覧した誰もがパスワードを読み取れてしまいます。そのため、パスワードはハッシュ化アルゴリズムを用いて生成されたハッシュ値として保存することが推奨されます。EMQXは様々なセキュリティ要件に対応するため、多様なパスワードハッシュ化アルゴリズムをサポートしています。

また、EMQXはソルトの付加もサポートしており、ソルトを加えたユニークなハッシュ（password_hash）は様々な攻撃に対して保護効果を発揮します。

### ワークフロー

パスワードハッシュ化のワークフローは以下の通りです：

1. EMQX認証器は設定されたクエリ文を使い、ハッシュ化パスワードとソルト値を含む認証情報をデータベースから取得します。
2. クライアントが接続を試みる際、EMQX認証器はクライアントが提供したパスワードを設定されたハッシュアルゴリズムと取得したソルト値でハッシュ化します。
3. EMQX認証器はステップ1で取得したハッシュパスワードとステップ2で計算したハッシュ値を比較し、一致すれば認証を許可します。

以下はEMQXがサポートするハッシュアルゴリズム例です：

```
# シンプルなアルゴリズム
password_hash_algorithm {
  name = sha256             # plain, md5, sha, sha512
  salt_position = suffix    # prefix, disable
}

# bcrypt
password_hash_algorithm {
  name = bcrypt
}

# pbkdf2
password_hash_algorithm {
  name = pbkdf2
  mac_fun = sha256          # md4, md5, ripemd160, sha, sha224, sha384, sha512
  iterations = 4096
  dk_length = 32           # 任意、単位：バイト
}
```

ハッシュアルゴリズムによってパフォーマンスに大きな差があるため、用途に応じて選択してください。参考までに、4コア8GBマシンで各アルゴリズムを100回実行した平均実行時間は以下の通りです：

![](./assets/hash-compare.png)

## 認証プレースホルダー

EMQXはクエリ文やHTTPリクエスト内でプレースホルダーを使用できます。認証時にこれらは実際のクライアント情報に置換され、現在のクライアントにマッチするクエリやHTTPリクエストが構築されます。

有効なプレースホルダーは`${PATH.TO.VALUE}`の形式で、PATH.TO.VALUEはオブジェクト内のドット区切りパスです。使用可能な文字は英数字、ドット（`.`）、アンダースコア（`_`）です。サポートされない文字を含むプレースホルダーはプレーンテキストとして扱われます。

例えば、EMQXのMySQL認証器のデフォルトクエリSQLは`${username}`プレースホルダーを使用しています：

```
SELECT password_hash, salt FROM mqtt_user where username = ${username} LIMIT 1
```

クライアント（名前：`emqx_u`）が接続要求を送ると、構築されるクエリ文は以下のようになります：

```
SELECT password_hash, salt FROM mqtt_user where username = 'emqx_u' LIMIT 1
```

EMQXが現在サポートするプレースホルダーは以下の通りです：

- `${clientid}`：実行時にクライアントIDに置換されます。クライアントIDは通常、`CONNECT`パケットでクライアントが明示的に指定します。`use_username_as_clientid`や`peer_cert_as_clientid`が有効な場合は、ユーザー名や証明書のフィールド、証明書内容で上書きされます。

- `${username}`：実行時にユーザー名に置換されます。ユーザー名は`CONNECT`パケットの`Username`フィールドから取得します。`peer_cert_as_username`が有効な場合は証明書のフィールドや内容で上書きされます。

- `${password}`：実行時にパスワードに置換されます。パスワードは`CONNECT`パケットの`Password`フィールドから取得します。

- `${peerhost}`：実行時にクライアントのIPアドレスに置換されます。EMQXは[Proxy Protocol](http://www.haproxy.org/download/1.8/doc/proxy-protocol.txt)をサポートしており、TCPプロキシやロードバランサーの背後にあっても実際のIPアドレスを取得可能です。

- `${peername}`：実行時にクライアントのIPアドレスとポートに置換され、形式は`IP:PORT`です。

- `${cert_subject}`：実行時にクライアントTLS証明書のSubjectに置換されます。ロードバランサーがTCPリスナーにクライアント証明書情報を送信する場合は、Proxy Protocol v2の使用を確認してください。

- `${cert_common_name}`：実行時にクライアントTLS証明書のCommon Nameに置換されます。ロードバランサーがTCPリスナーにクライアント証明書情報を送信する場合は、Proxy Protocol v2の使用を確認してください。

- `${client_attrs.NAME}`：クライアント属性。`NAME`は事前設定された属性名に置換されます。クライアント属性の詳細は[MQTTクライアント属性](../../../develop/client-attributes/client-attributes.md)を参照してください。

- `${zone}`：実行時にクライアントのゾーンに置換されます。`${zone}`プレースホルダーは認証テンプレートで直接使用可能です。ゾーン設定の詳細は[ゾーンオーバーライド](../../configuration/configuration.md#zone-override)を参照してください。

  例えば、以下のACLルールは`${zone}`を用いてクライアントの割り当てゾーンに基づき動的に権限を適用します：

  ```
  {allow, all, all, ["${zone}/${username}/#"]}
  ```

## 認証の設定

EMQXは認証の設定方法として、ダッシュボード、設定ファイル、HTTP APIの3通りを提供しています。

### ダッシュボードでの認証設定

EMQXダッシュボードは認証器の状態確認や設定カスタマイズが直感的に行えるインターフェースです。下図の例では、組み込みデータベースベースのパスワード認証とJWT認証の2つの認証器が設定されています。

![](./assets/authn-dashboard-2.png)

### 設定ファイルでの認証設定

設定ファイルでもEMQX認証器を設定可能です。

例えば、以下の`authentication`フィールドでは複数の認証器からなる認証チェーンを作成しており、設定ファイル内の順序で認証器が実行されます。

```
# base.hocon

# すべてのMQTTリスナーに対するグローバル認証チェーン
authentication = [
  ...
]

listeners.tcp.default {
  ...
  # 指定したMQTTリスナーに対する認証チェーン
  authentication = [
    ...
  ]
}

gateway.stomp {
  ...
  # すべてのSTOMPリスナーに対するグローバル認証器
  authentication = {
    ...
  }

}
```

認証器のタイプによって必要な設定項目は異なります。詳細は設定章を参照してください。<!--後続で該当章へのリンク挿入予定-->

### HTTP APIでの認証設定

設定ファイルに比べ、HTTP APIはより便利でランタイム更新をサポートし、設定変更をクラスター全体に自動同期できます。

EMQX認証APIを使い、グローバル認証器の作成や特定認証器の設定更新などを管理可能です。

- `/api/v5/authentication`：グローバルMQTT認証管理用APIエンドポイント
- `/api/v5/gateway/{protocol}/authentication`：他アクセスプロトコルのグローバル認証管理用APIエンドポイント
- `/api/v5/gateway/{protocol}/listeners/{listener_id}/authentication`：他アクセスプロトコルのリスナー認証管理用APIエンドポイント

#### 認証器ID

特定認証器を操作するには、上記エンドポイントに認証器IDを付加します。例：`/api/v5/authentication/{id}`。メンテナンスを容易にするため、IDはEMQXが自動生成・APIで返却するのではなく、以下の規則に従います：

```
<mechanism>:<backend>
```

または：

```
<mechanism>
```

例：

1. `password_based:built_in_database`
2. `jwt`
3. `scram:built_in_database`

リスナーIDにも同様の規則があります：

```bash
<transport_protocol>:<name>
```

ゲートウェイリスナーIDはプロトコル名を前に付けます：

```bash
<protocol>:<transport_protocol>:<name>
```

認証器IDおよびリスナーIDはURLで使用する際、URLエンコード規則に従う必要があります。例えば、`:`は`%3A`に置換します：

```bash
PUT /api/v5/authentication/password_based%3Abuilt_in_database
```

#### データ操作API

[組み込みデータベース](./mnesia.md)および[MQTT 5.0拡張認証](./scram.md)を用いた認証では、認証データの作成、更新、削除、一覧取得を行うHTTP APIを提供しています。詳細は[HTTP APIでの認証データ管理](./user_management.md)を参照してください。

詳細なAPIリクエストやパラメータは[HTTP API](../../api.md)を参照してください。
