MySQLの新認証方式について
MySQL 5.6 で新たな認証方式 sha256_password が追加され、 MySQL 8 ではその改良版となる caching_sha2_password が追加されました。詳しくは MySQL 8.0.4 : New Default Authentication Plugin : caching_sha2_password を参照してください。
これらは従来の native_password 方式よりも安全とされています。 3つの認証方式について特徴をまとめてみます。
攻撃経路
認証方式を考えるとき、ざっくりと2つの攻撃経路があります。
1つ目は mysql.user
テーブルの authentication_string
カラム (旧 password
カラム) です。このカラムをSELECTできるユーザーが、他のユーザーの authentication_string
を閲覧し、その情報を元になりすましができる可能性があります。
2つ目は通信経路です。具体的には盗聴したりサーバーになりすますことで、認証に必要な情報を取得する可能性があります。
3つの認証方式の詳細
caching_sha2_password は sha256_password の改良版です。特に初回の認証は sha256_password と同じです。なのでまずは native_password と sha256_password を比較してみましょう。
認証方式 | native_password | sha256_password |
---|---|---|
ハッシュ関数 | SHA-1 | SHA-256 |
SALT | なし | あり |
認証プロトコル | 非可逆なチャレンジ&レスポンス | パスワードを可逆な形で送信 |
native_password
authentication_string
カラムの内容は、 native_password が salt なしの純粋な SHA1(SHA1(password)) の先頭に、 MySQL 4.1 以前の形式と区別するための目印として "*" をつけたものです。
mysql> create user t3 identified with 'mysql_native_password' by 'password'; Query OK, 0 rows affected (0.20 sec) mysql> select Host,User,plugin,authentication_string from mysql.user WHERE User='t3'; +------+------+-----------------------+-------------------------------------------+ | Host | User | plugin | authentication_string | +------+------+-----------------------+-------------------------------------------+ | % | t3 | mysql_native_password | *2470C0C06DEE42FD1618BB99005ADCA2EC9D1E19 | +------+------+-----------------------+-------------------------------------------+ 1 row in set (0.00 sec) mysql> select SHA1(unhex(SHA1('password'))); +------------------------------------------+ | SHA1(unhex(SHA1('password'))) | +------------------------------------------+ | 2470c0c06dee42fd1618bb99005adca2ec9d1e19 | +------------------------------------------+ 1 row in set (0.00 sec)
このため、複数のユーザーが同じパスワードを利用していると authentication_string も同じになります。また、 NIST により認証に SHA-1 を使うのをやめるように推奨されています。これが前述の記事で説明された、新認証方式が必要になった理由 (native_password の弱点) です。
Client-Serverプロトコル (以降プロトコルと呼ぶ) では、まずサーバーからクライアントに nonce が送られ、クライアントは次の計算結果をサーバーに返します。 (https://dev.mysql.com/doc/internals/en/secure-password-authentication.html#packet-Authentication::Native41 より引用)
SHA1( password ) XOR SHA1( "20-bytes random data from server" <concat> SHA1( SHA1( password ) ) )
サーバーは最初に送信した nonce と SHA1(SHA1(password)) を知っているので、再度 XOR を取ることで SHA1(password) を知ることができます。それにもう一度 SHA1() をかけて authentication_string と一致するかで認証することができます。
攻撃者がこの通信を盗聴した場合、 password や SHA1(password) を知ることができません。
また、 攻撃者がサーバーになりすました場合も、password や SHA1(password) を知ることができません。ただしリレー攻撃で本物のサーバーに接続することは可能です。
もちろん、攻撃者が authentication_string を知っている場合は、サーバーと同じ手順で SHA1(password) を得ることができます。生のパスワードがなくても SHA1(password) があれば本物のサーバーに対して認証を通ることができます。
sha256_password
authentication_string
はSALT付きで SHA256 を複数ラウンドしたものらしいです。実際、同じパスワードのユーザーを複数作成してみても、全員が異なる authentication_string が異なります。攻撃者が同じMySQLサーバーにログインして mysql.user
テーブルを閲覧可能な場合は、 native_password よりも格段に安全です。
一方で、認証プロトコルは次のようになっています。
- 安全な経路 (SSL あるいは unix domain socket) では password + nonce を送る。
- それ以外の経路では password を RSA で暗号化して送る。
どちらも適切に設定されている場合は native_password と同等以上に安全だと思います。
ただしサーバーになりすまされた場合に攻撃者に生の password を与えてしまう点は気になります。なりすましを許した時点でリレー攻撃により本物のサーバーに接続して SET PASSWORD
を含む任意のクエリを実行されてしまう可能性があるのであくまでも気持ちの問題ですが。
caching_sha2_password
1度目の認証は sha256_password と同じですが、サーバーはそのときに SHA256(password) をキャッシュします。
2度めからの認証は native_password に似た(ただし SHA-256 を利用した)チャレンジ&レスポンス認証になるので、生パスワードがプロトコル上に乗ることもありませんし、SSLを使っていない環境でRSA暗号化するオーバーヘッドが要らなくなります。
RSA公開鍵に関する設定
SSL を利用しない場合にクライアントがパスワードをRSAで暗号化するための公開鍵は、ローカルにあるファイルを指定する (--server-public-key-path
オプション)か、認証時にサーバーから自動取得する (--get-server-public-key
オプション) 事ができます。
自動取得を有効にすると、サーバーになりすました攻撃者が自分の RSA 公開鍵を送りつけることで生パスワードを得ることができるようになります。パフォーマンスを考えてもラウンドトリップが1回ふえることになります。なので個人的には --server-public-key-path
オプションをお勧めします。
なりすましサーバーにSSL接続してしまった場合にも生パスワード送信してしまうので、SSLを使わない場合は --ssl-mode=DISABLED
も設定しておくと良いでしょう。
ただし、繰り返しになりますが、なりすまされてる時点でリレー攻撃可能なので、生パスワードがもれなかったらOKというわけではありません。
@methane