MySQLサーバーに接続すると、まずサーバー接続の許可判定が行われる。もし身元が一致しない場合、接続を拒否し、接続許可した場合、サーバーはリクエストを待つ。
身元は接続しようとしているホスト、MySQL ユーザー名に基づいて確認される
身元の確認は、 user テーブルのスコープフィールド(Host, User, Password)
を使用して行われる。
userテーブルのスコープフィールドの登録は以下のとおり
- Host の値はホスト名か IP アドレスか 「localhost」。
- Host にはワイルドカード文字のパーセント(%) とアンダーバー(_) が使用可能。
- Host に % を設定すると、全てのホストにマッチする。 Host を空にすると、%
と同じになる。これらの値は、どんなホストもサーバーに接続できるということになる。
- ワイルドカード文字は User 項目に設定できないが、 User
項目を空(ブランク)にすることはでる。空文字は全ての名前にマッチする。これはユーザー名がない状態で接続してきたものに適用され、クライアントがユーザー名を明記しない限り、匿名ユーザー(名前がブランク)として扱われる。全てのアクセスのチェックに空文字のユーザー名が使用される事を意味する。
- Password 項目は空にできる。その場合、パスワードなして接続できることになる。
非ブランクの Password 値はパスワードを暗号化したものだ。 MySQL
はだれもが見れるようにパスワードをプレーンテキストでは保存しない。接続を試みようとしているユーザーのパスワードも(PASSWORD()
関数で) 暗号化され、user テーブルに保存されている暗号化パスワードと比較される。
以下の表は、接続要求に対して与える、 user テーブルの Host と User の設定例:
Host に IP のワイルドカード(例えば '144.155.166.%'
はサブネットの全てのホストにマッチ) を使用することができる。が、この場合、
144.155.166.somewhere.com
というホスト名でだれかが接続しようとしてくるかもしれない。このような攻撃に対し、MySQL
は数字やドットで始まるホスト名を拒否している。もし 1.2.foo.com
のような名前のホストを持っている場合、許可テーブルの Host には絶対にマッチしない。 IPアドレスのみ、IP
のワイルドカードにマッチする事になる。
サーバーに来る接続は、user テーブル内の登録に1つ以上マッチするかもしれない。例えば, rfs.jp
の sv
からの接続は、上に示された登録のうちのいくつかにマッチする。サーバーは、複数の登録にマッチした場合、どのようにしてその中から使用する登録を選ぶのでしょう?サーバーは起動後に
user
テーブルをソートし、並び換えられた順に登録を検索することにより、この問題を解決する。最初にマッチした登録が使用される。
user テーブルが以下のようにソートされていた場合:
+-----------+----------+-
| Host | User | ...
+-----------+----------+-
| % | root | ...
| % | staff | ...
| localhost | root | ...
| localhost | | ...
+-----------+----------+-
サーバーがこのテーブルを読むと、Host
に値が最も確実に特定できるホストを指定しているエントリを、最初に参照する。
Host の値が同じエントリがあった場合、もっとも明確に User
の値がユーザーを指定しているエントリを最初に参照する。 この結果、user テーブルは以下のようにソートされる。
+-----------+----------+-
| Host | User | ...
+-----------+----------+-
| localhost | root | ...
| localhost | | ...
| % | staff | ...
| % | root | ...
+-----------+----------+-
接続が試みられた場合、サーバーは並び換えられた登録を探し、最初に見つけたものを使用する。 "localhost"
の jeffrey からの接続は、まず最初に Host に localhost
を設定しているエントリにマッチする。ユーザー名が空のエントリは、ホスト名とユーザー名の両方を指定した接続にもマッチする(
'%'/'staff' エントリもマッチするが、最初にはマッチしない)。
よくある過ちは、ユーザー名を与えた場合、サーバーが接続にマッチするものを探す際に、そのユーザーが登録されている全てのルールが、最初に使用されるだろうと考えることだ。これは正しくない。"rfs.jp"
の "staff" からの接続が最初にマッチするのは、 User フィールドの値が "staff"
になっているエントリではなく、ユーザー名なし(=だれでも) のエントリの方が先にマッチする。
もしサーバーへの接続がうまく行かない場合、 user
テーブルを表示し、マニュアルでソートしてみて、どのエントリに最初にマッチするか試すべきだ。
一度接続か確立されると、サーバーはこの接続から来るそれぞれの要求が許可されているかどうかをチェックする。チェックは実行しようとしている操作のタイプにより行う。その操作が許可テーブルのどの特権フィールドに当てはまるかを見る。
user
テーブルは全てに対して基本となる特権をユーザーに割り当てる。たとえカレントのデータベースが許可を与えていなくても、user
テーブルの設定が優先される。例えば、user テーブルで delete
を許可した場合、サーバーにあるどんなデータベースの行も削除できる。この特権はスーパーユーザー(サーバーやデーターベース管理者)のみに与えておく事が賢明だ。ユーザーは、user
テーブルの特権の設定を 'N' のままにしておくべきだし、また、db テーブルと host
テーブルを利用して、データベースを指定した上でユーザーに特権を許可すべきだ。
db テーブルと host テーブルは特定のデータベースに対する特権許可を行う。
- ワイルドカード文字 `%' と `_' は Db テーブルと Host
フィールドだけに使用できます。
- '%' Host 値は ``あらゆるホスト'' を意味します。 db テーブルの Host
に空を設定すると、``さらに host テーブルに許可情報を探しにいく'' となります。
- '%' か 空値を Host テーブルに設定すると、それは ``あらゆるホスト'' となります。
- '%' か 空値を host テーブルの Db フィールドに設定すると、それは
``あらゆるデータベース'' となります。
- User を空値にすると、匿名ユーザーにマッチします。
サーバー起動時に、db テーブルと host テーブルはサーバーに読み込まれる。 (user
テーブルもこの時に同時に読まれる) db テーブルは Host, Db, User のフィールドでソートされ、
host テーブルは Host, Db フィールドでソートされる。 user
テーブルは、一番特定できるエントリを最初に、一番特定できないものを最後にソートする。サーバーはソートされたものの中から、最初にマッチしたものを使用する。
tables_priv と columns_priv
テーブルは、特定のテーブルと項目に対する特権を許可する。スコープフィールドの値は、以下にそって記述される:
- ワイルドカード文字 `%' と `_' はどちらかのテーブルの Host 項目に使用できる。
- どちらかのテーブルの Host 値を '%' かブランクにすると、 "any host."
を意味する。
- Db, Table_name, Column_name
項目はどのテーブルにもワイルドカードやブランクは使用できない。
tables_priv と columns_priv テーブルは Host, Db, User
項目で並び換えらる。これは db テーブルのソートに似ているが、 Host
項目だけがワイルドカードを含むので、ソートはより単純なものになる。
この要求の承認は次のようにして行う。もしアクセス承認を決定する部分のソースコードを理解できるなら、ちょっと変わったアルゴリズムで承認の決定を行っている事に気づくだろう。
管理者の要求(shutdown, reload, etc.)については、サーバーは user
テーブルだけを参照する。(user
テーブルだけが管理者特権の項目を持つ)。エントリに許可登録されている操作は受け入れられ、それ以外は拒否される。例えば、mysqladmin
shutdown を実行しようとしても、user テーブルの shutdown
特権が許されていなければ実行できない。この時、db と host
テーブルはチェックされない。(これらのテーブルには Shutdown_priv カラムが無いからだ)
データベースへの要求 (insert, update, etc.)
において、サーバーはまず最初に、ユーザーのグローバルな特権(スーパーユーザー)を user
の中から探しだす。もし許可が与えられていれば、アクセスは成功する。
user
テーブルのグローバルな特権の設定が不十分であるなら、サーバーはユーザーのデータベースに対する特権を db
テーブルと host テーブルから決定する:
- サーバーは db テーブルの Host,Db,Userフィールドを参照します。 Host と
User はユーザーの接続時のホスト名と MySQL ユーザー名にマッチします。 Db
フィールドはユーザーがアクセスしたいデータベース名にマッチします。 Host と User
にマッチするものが無かった場合、アクセスは拒否されます。
- db テーブル内の Host
フィールドが空でないエントリにマッチした場合、ユーザーの指定されているデータベースに対する特権が定義されます。
- Host フィールドが空値の db
テーブルのエントリにマッチした場合、どのホストがそのデータベースへアクセスできるかを host
テーブルから探し出します。この場合、host テーブル の Host, Db
フィールドとマッチするものを探し出します。 host
テーブルにエントリがなかった場合、アクセスは拒否されます。もしマッチすると、ユーザーの特定データベースに対する特権は、
host テーブルと db テーブル両方にまたがった特権から割り出されます。いうならば両方とも 'Y'
である特権。 (この方法を使用すると、まず db
テーブルのエントリに大まかな特権を設定しておき、それから host
テーブルのエントリを使用して、ホスト情報もとに特権を限定していくという事ができます)
特定データベースに対する特権が db テーブルと host
テーブルのエントリから決定された後、サーバーはその割り出された特権に対し、user
テーブルて設定されている特権を加える。この結果から得られた特権にマッチした要求は受け入れらる。そうでなければ、サーバーはユーザーのテーブル、カラムに対する許可を、
tables_priv と columns_priv 内に探す。アクセスはこの結果により、許可、拒否される。
前のユーザの特権を計算する方法に関する記述は、 boolean で表すとするなら、以下のように表せる:
global privileges
OR (database privileges AND host privileges)
OR table privileges
OR column privileges
もし最初にグローバルな user
エントリ特権がオペレーション要求に対して不十分である事がわかった場合に、サーバはあとから、それらの特権をデータベース特定、テーブル特定、コラム特定
特権に加えるのか、これはちょっとわからないかもしれない。
その理由は、ある要求が 1 つ以上のタイプの特権を必要とするかもしれないからだ。例えば、INSERT
... SELECT を実行するばあい、 insert , select
特権が必要だ。もしあなたの特権が、一つだけ user テーブルで許可されており、もう片方が db
テーブルで許可されているする。この場合、そのリクエストを実行できる特権を持っているが、
サーバー自身がどちらのテーブルからか見分けることが出来ない。両方のエントリで与えられる特権は結合される。
host テーブルは"安全な" ホストのリストを維持するために使用できる。
逆に host table で安全ではないホストを指定することもできる。 public.your.domain
というマシンが安全ではない、公開されている場所にあるとする。その場合以下のようにして、その公開マシン以外のネットワーク上のホストに対して、アクセスを許可することができる:
+--------------------+----+-
| Host | Db | ...
+--------------------+----+-
| public.your.domain | % | ... (all privileges set to 'N')
| %.your.domain | % | ... (all privileges set to 'Y')
+--------------------+----+-
特権のテーブル設定は、あなたの思い通りに許可が得られるのか、常に(mysqlaccess等を使用して)チェックすべきだ。
mysqld の起動時、全ての許可テーブルはメモリーに読み込まれ、この時点で有効になる。
GRANT, REVOKE, SET PASSWORD
を使用して許可テーブルを変更した場合、直にサーバに通知される。
もし手動で許可テーブルを変更した場合(INSERT, UPDATE などで)、 FLUSH
PRIVILEGES 構文か mysqladmin flush-privileges
コマンドを実行して、サーバーに許可テーブルの読み込みを指示しなければならない。そうしなければ、サーバーを再起動させるまで、変更は反映されない。
サーバーが許可テーブルの変更を通知した場合、既に接続しているクライアントは、以下のような影響を受ける:
- テーブルと項目の許可の変更は、次のクライアントの要求から反映される。
- データベースに対する許可の変更は次の USE db_name コマンド以降から有効になる。
グローバル特権とパスワードの変更は、次のクライアントの接続時から反映される。