amamanamam

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

Connection LifecycleのConnection Phaseの話

MySQL: Connection Phaseを読んでフムフムと思ったことを実験を交えて書き記すことにした。

MySQL側ではクライアントからTCP接続が確立してから以下のようなフェーズを辿る

https://dev.mysql.com/doc/dev/mysql-server/latest/page_protocol_connection_lifecycle.html

これらのフェーズではMySQLプロトコルというMySQL独自のプロトコル(アプリケーションレイヤー)によってクライアントとやり取りされる。初めに接続フェーズを介して認証情報の評価や送信を行い、その後コマンドフェーズで実際のコマンドパケットの処理を行う。今回はこのうちの接続フェーズの部分の話をする。

※実際、クエリを叩く際のパケットキャプチャでは以下のようにMySQLプロトコルのやりとりが確認できる。この内のRequestQuery以前の過程が接続フェーズとなる。

※簡単のため、SSL/TLS接続は考慮しない

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についてはまた今度