amamanamam

データベースと仲良くなりたいです

caching_sha2_passwordの仕様の話

MySQLの認証プラグインの1つにcaching_sha2_passwordがある。 MySQL5.7ではmysql_native_passwordがデフォルトではあったが、MySQL8.0ではこやつがデフォルトとなっている(参考:リリースノート)。実際、手元で以下のように確認ができる

mysql> show variables like 'default_authentication_plugin';
+-------------------------------+-----------------------+
| Variable_name                 | Value                 |
+-------------------------------+-----------------------+
| default_authentication_plugin | caching_sha2_password |
+-------------------------------+-----------------------+
1 row in set (0.42 sec)

さてこのデフォルトに躍り出たcaching_sha2_passwordの仕様を他のプラグインと比較しながら理解していく

ざっくり認証までの過程

amamanamam.hatenablog.com 以前にも記載したように、クライアントがサーバーへ接続するときにLoginRequestというフェーズでユーザ情報を送信する。そして、サーバー側では送られたユーザがmysql.userテーブルにあるか検索し、どの認証プラグインを使用するか決定する。認証プラグインが決定した後に、サーバーはそのプラグインを呼び出してユーザー認証を開始し、その結果をクライアント側に送信する。このようにMySQLでは認証がプラガブル(様々なタイプのプラグインを付け外しできる)になっている。選べる認証プラグイン公式ページに記載されている。

因みに、使用する認証プラグインはもちろんサーバーが勝手に決めるわけではない。クライアント側と幾ばくかのやりとりをする。LoginRequest以前のServerGreetingの段階でサーバー側はクライアント側にdefault_authentication_pluginで設定されている認証プラグインを送信する。それに対してLoginRequestでクライアント側は使用する認証プラグイン(サーバー側から送られてきたものと必ずしも一致しない)をサーバー側に送信する。そして、サーバー側でユーザで設定されている認証プラグインと期待されている認証プラグインの一致を確かめてから認証を行う。なお、不一致があればAuthSwitchRequestをクライアント側に送信して別の認証方法を選択するように依頼する。

※ServerGreetingの段階でパケット内を除くと後述するNonce(サーバーからクライアント側に送るランダムデータ)のような要素が見つかる。おそらく、サーバークライアント間のやりとりの節約のために、使用する認証プラグインの予測をつけて予め認証に必要な情報を交換していると思われる。

mysql_native_passwordの認証過程

まずはmysql_native_passwordについて。Source Code Documentを適宜参照しながら解説する。

mysql_native_passwordはパスワードハッシュ方式に基づいた認証を実装している。ハッシュ関数SHA1が用いられている。以下のようにユーザ作成を行うと、mysql.userのauthentication_stringカラムにSHA1(SHA1(passwors))の結果が格納される。よって同じパスワードであれば同じauthentication_stringになる。

mysql> create user kubo identified with 'mysql_native_password' by 'password';
Query OK, 0 rows affected (0.27 sec)

mysql>  select Host,User,plugin,authentication_string from mysql.user where User='kubo';
+------+------+-----------------------+-------------------------------------------+
| Host | User | plugin                | authentication_string                     |
+------+------+-----------------------+-------------------------------------------+
| %    | kubo | mysql_native_password | *2470C0C06DEE42FD1618BB99005ADCA2EC9D1E19 |
+------+------+-----------------------+-------------------------------------------+
1 row in set (0.03 sec)

クライアント・サーバー間ではチャレンジ&レスポンス認証が行われる。 サーバ側からクライアント側に20バイトのランダムデータ(Nonce)が送信され、クライアント側で以下の計算をしてサーバーに送信する。

SHA1( password ) XOR SHA1( "20-bytes random data from server" <concat> SHA1( SHA1( password ) ) )

サーバーはauthentication_stringの値、すなわちSHA1( SHA1( password )とランダムデータの値を知っているので、それらとクライアントから送られてきた計算結果にXORとSHA1を作用させることで、パスワードハッシュを照合することができる。言い換えれば、ユーザが入力したパスワードに2回SHA1を作用させた値(クライアント側)とauthentication_stringの値(サーバー側)の照合をすることができる。

sha256_passwordの認証過程

sha256_passwordはSHA-256 ハッシングを実装する認証プラグインである。こちらを適宜参照して解説する。

今度はsalt付きでハッシュ化されるため、以下のように同じパスワードのユーザ作成でも異なるauthentication_stringの値となる。

mysql> create user kubo1 identified with 'sha256_password' by 'password';
Query OK, 0 rows affected (0.03 sec)

mysql> create user kubo2 identified with 'sha256_password' by 'password';
Query OK, 0 rows affected (0.03 sec)

mysql> select Host,User,plugin,SUBSTR(HEX(authentication_string), -10) from mysql.user where User in ('kubo1','kubo2');
+------+-------+-----------------+-----------------------------------------+
| Host | User  | plugin          | SUBSTR(HEX(authentication_string), -10) |
+------+-------+-----------------+-----------------------------------------+
| %    | kubo1 | sha256_password | 736D4A6132                              |
| %    | kubo2 | sha256_password | 7631786841                              |
+------+-------+-----------------+-----------------------------------------+

クライアント・サーバー間ではmysql_native_passwordと同様のやりとりをすることはできない。それはサーバー側ではsalt付きのパスワードハッシュ値を知っているが、クライアント側はそのsaltの値を知らないことにある。そのため、クライアント側で先ほどの計算式のような計算結果をサーバー側に送ったとしても、salt無しでハッシュ化されたパスワードが渡るのみで、サーバー側ではauthentication_stringとの照合ができない。

とはいえ、そのまま生パスワードの送信には問題がある。そこでSSL/TLS接続もしくはRSA暗号化通信がsha256_passwordでは必須となる。このように安全な通信経路や暗号化通信が確立された上で、プレーンテキストのパスワードがレスポンス時に送信され、無事に照合ができるようになる。

caching_sha2_passwordの認証過程

caching_sha2_passwordもまたSHA-256 ハッシングを実装する認証プラグインである。こちらもSource Code Document公式ブログを適宜参照しながら解説する。

クライアント・サーバー間のやり取りには以下の2つのフェーズがある。

  • Fast authentication
  • Complete authentication

Fast authenticationでは、mysql_native_passwordのようにまずサーバーからクライアントへランダムデータ(Nonce)を送る。その後クライアントでは以下の計算結果をサーバーに送る

XOR(SHA256(password), SHA256(SHA256(SHA256(password)), Nonce))

ここでサーバー側では該当ユーザのパスワードハッシュの値がキャッシュ内にあるか確認する。そこでもし見つかれば、クライアントから送られてきた計算結果にその値とランダムデータの値をXORとSHA1を作用させることで、パスワードハッシュの照合ができる。言い換えれば、ユーザが入力したパスワードに2回SHA2を作用させた値(クライアント側)とキャッシュ内の値(サーバー側)の照合をすることができる。

もしもキャッシュ内に何も見つからなければComplete authenticationのフェーズに入る。 Complete authenticationでは、sha256_passwordのようにSSL/TLS接続もしくはRSA暗号化通信がなされた状況下でパスワードをそのまま受け取って照合を行う。

このようにcaching_sha2_passwordはmysql_native_passwordとsha256_passwordを組み合わせたような認証方法となっている。