Connection LifecycleのConnection Phaseの話
MySQL: Connection Phaseを読んでフムフムと思ったことを実験を交えて書き記すことにした。
MySQL側ではクライアントからTCP接続が確立してから以下のようなフェーズを辿る
これらのフェーズではMySQLプロトコルというMySQL独自のプロトコル(アプリケーションレイヤー)によってクライアントとやり取りされる。初めに接続フェーズを介して認証情報の評価や送信を行い、その後コマンドフェーズで実際のコマンドパケットの処理を行う。今回はこのうちの接続フェーズの部分の話をする。
※実際、クエリを叩く際のパケットキャプチャでは以下のようにMySQLプロトコルのやりとりが確認できる。この内のRequestQuery以前の過程が接続フェーズとなる。
ServerGreeting
まず初めにサーバーからクライアントへInitial Handshake packetを送信する。 これはServerGreetingと呼ばれ、以下のように各種バージョンや認証プラグイン、文字セットや照合順序、Server Capabilitiesなどの情報を含む。
MySQL Protocol Packet Length: 80 Packet Number: 0 Server Greeting Protocol: 10 Version: 8.0.28-debug Thread ID: 72 Salt: a^p\017(cQ9 Server Capabilities: 0xffff Server Language: utf8mb4 COLLATE utf8mb4_0900_ai_ci (255) Server Status: 0x0002 Extended Server Capabilities: 0xdfff Authentication Plugin Length: 21 Unused: 00000000000000000000 Salt: WY\031p[k@\nZ@`m Authentication Plugin: mysql_native_password
なお、ペイロードの詳細はProtocol::HandshakeV10 に記載がある。今回の例のプロトコルバージョンが10のため、Protocol::HandshakeV10のリンクを載せている。
また、Server CapabilitiesやExtended Server Capabilitiesにはサーバー機能のビットマスクであり、各種フラグについては同様にCapabilities Flags に記載がある。これらの情報を交換することで、クライアント/サーバー間で期待されていないフォーマットでデータを送るような事を防げる。
Login Request
Initial Handshake packetの応答としてクライアントからサーバーへLogin Requestを送る。 以下のようにクライアント側の要求している認証プラグイン、ユーザ名、パスワード、文字セットや照合順序、Client Capabilitiesなどの情報を含む。
MySQL Protocol Packet Length: 249 Packet Number: 1 Login Request Client Capabilities: 0xa28f Extended Client Capabilities: 0x80be MAX Packet: 1073741824 Charset: utf8mb4 COLLATE utf8mb4_general_ci (45) Unused: 0000000000000000000000000000000000000000000000 Username: app Password: 55b1a7f3d9159712803de97bb0e15400ef37d4b2 Schema: world Client Auth Plugin: mysql_native_password Prefix: 163 Length: 163 Connection Attributes
ペイロードの詳細はProtocol::HandshakeResponseに記載がある。 サーバー側ではこれらを受け取って、Capabilitiesと使用する認証方法の確認を行う。
認証方法の確認について
実はServerGreeting時点ですでにdefault_authentication_pluginをもとに期待している認証プラグインをクライアント側に送信している。 それに応じてLogin Requestでクライアントからサーバーへ使用する認証プラグインを送信する。 そして、その後にサーバー側ではクライアントから受け取った認証情報をもとにmysql.user テーブルを確認しに行く。ここでもし期待されている認証プラグインと異なれば、サーバーはクライアントへAuthSwitchRequestを送信して、使用するべき認証プラグインを通知する。
実際、以下のような設定でappというユーザで接続すると
※クライアントサイドはlibmysqlclientを使用
mysql> select @@default_authentication_plugin;
+---------------------------------+
| @@default_authentication_plugin |
+---------------------------------+
| caching_sha2_password |
+---------------------------------+
1 row in set, 1 warning (0.00 sec)
mysql> select User, plugin from mysql.user where User = 'app'\G
*************************** 1. row ***************************
User: app
plugin: mysql_native_password
1 row in set (0.00 sec)
以下のようにAuthSwitchRequestが確認できる。また、mysql_native_passwordを要求していることがパケットの中身から確認できる。なお、ServerGreetingとLogin requestではAuthentication Pluginはcaching_sha2_passwordの内容で送信していることも確認できる。
認証が問題なければサーバーはクライアントへResponse OKを返す。詳細はOK_Packetに記載されている。
Command Phaseについてはまた今度