08 Mar 2020

mysql8 系からデフォルトになった caching_sha2_password に mysqljs/mysql はまだ対応していない

mysqljs/mysql (pure js の mysql クライアント実装) は caching_sha2_password (mysql8 系でデフォルトになった authentication plugin) に現時点では未対応。

homebrew で雑に mysql をセットアップすると、現時点では 8.0.19 が入るため、mysqljs/mysql で接続しようとすると ER_NOT_SUPPORTED_AUTH_MODE: Client does not support authentication protocol requested by server; consider upgrading MySQL client というエラーが出る。

> const mysql = require('mysql');
undefined
> const connection = mysql.createConnection({
...   host: 'localhost',
...   user: 'root',
...   password: 'xxx',
...   database: 'yyy'
... });
undefined
> connection.connect();
undefined
> Uncaught:
<ref *2> Error: ER_NOT_SUPPORTED_AUTH_MODE: Client does not support authentication protocol requested by server; consider upgrading MySQL client
    at Handshake.Sequence._packetToError (/Users/cou929/Desktop/bcrypter/node_modules/mysql/lib/protocol/sequences/Sequence.js:47:14)
    at Handshake.ErrorPacket (/Users/cou929/Desktop/bcrypter/node_modules/mysql/lib/protocol/sequences/Handshake.js:123:18)
    at Protocol._parsePacket (/Users/cou929/Desktop/bcrypter/node_modules/mysql/lib/protocol/Protocol.js:291:23)
    at Parser._parsePacket (/Users/cou929/Desktop/bcrypter/node_modules/mysql/lib/protocol/Parser.js:433:10)
    at Parser.write (/Users/cou929/Desktop/bcrypter/node_modules/mysql/lib/protocol/Parser.js:43:10)
    at Protocol.write (/Users/cou929/Desktop/bcrypter/node_modules/mysql/lib/protocol/Protocol.js:38:16)
    at Socket.<anonymous> (/Users/cou929/Desktop/bcrypter/node_modules/mysql/lib/Connection.js:88:28)
    at Socket.<anonymous> (/Users/cou929/Desktop/bcrypter/node_modules/mysql/lib/Connection.js:523:10)
    at Socket.emit (events.js:321:20)
    at Socket.EventEmitter.emit (domain.js:547:15)
...

対処方法

対応しているクライアントを使う。

MySQL :: MySQL 8.0 Reference Manual :: 2.11.4 Changes in MySQL 8.0

あるいは、検証用環境などであれば、とりあえず auth plugin を以前の mysql_native_password に戻すのが簡単だと思われる。

USE mysql;
UPDATE user SET plugin = 'mysql_native_password' WHERE user = 'root';  -- root の場合の例
SELECT Host, User, plugin, authentication_string FROM user; -- 確認
FLUSH PRIVILEGES;

caching_sha2_password

MySQL :: MySQL 8.0 Reference Manual :: 2.11.4 Changes in MySQL 8.0

mysql にクライアントから接続する際の認証方式。いままでは mysql_native_password という名前のものだったが、よりセキュアな sha256_password caching_sha2_password が追加されている。(具体的な内容までは追っていない)

クライアント側は libmysqlclient を使うとよいらしいが、mysqljs/mysql は pure js 実装なので、おそらくこの部分を自前で実装しなければならず、対応が追いついていないのかなと思われる。

default_authentication_plugin ディレクティブでデフォルトを mysql_native_password に設定しておけば過去の挙動を維持できそうだった。なおこれは新規ユーザーを追加する際の mysql.User.plugin のデフォルト値に影響する設定なので、すでに caching_sha2_password などで作成済みのユーザーは自分で更新する必要がある。

[mysqld]
default-authentication-plugin=mysql_native_password

設定反映後に:

mysql> CREATE USER 'newuser'@'localhost' IDENTIFIED BY 'newpass';
Query OK, 0 rows affected (0.01 sec)

mysql> SELECT Host, User, plugin, authentication_string FROM user; -- 確認
+-----------+------------------+-----------------------+------------------------------------------------------------------------+
| Host      | User             | plugin                | authentication_string                                                  |
+-----------+------------------+-----------------------+------------------------------------------------------------------------+
...
| localhost | newuser          | mysql_native_password | xxx                                                                    |
...

この問題にあたった背景

auth0 という認証機能を提供する SaaS の検証をしていた。 auth0 はよくできていて、既存のデータベースのユーザーデータと接続し、逐次データマイグレーションをしながら既存のユーザーデータ資産を活かすという使い方ができる。 既存のデータベースとの接続 のための グルーコードは Node.js 環境で mysqljs/mysql を利用して書く必要 がある。 この状況で手元の mysql8.0 に auth0 から接続しようとして ER_NOT_SUPPORTED_AUTH_MODE が発生、という経緯だった。

参考

実践ハイパフォーマンスMySQL 第3版
Baron Schwartz Peter Zaitsev Vadim Tkachenko
オライリージャパン
売り上げランキング: 167,250