ユーザ認証とアクセス権の設定をサポートします。INTER-Mediator自身がデータベースを利用して、ユーザーやグループを管理する手法を「ビルトイン認証」、データベースエンジン側に登録したユーザーを利用する認証を「ネイティブ認証」、LDAPサーバのユーザーを利用した認証を「LDAP認証」と呼びます。
認証動作に必要なテーブル
チャレンジ等を記録するテーブル
ユーザ認証ではセキュリティを高めるために、サーバーからクライアントに「チャレンジ」を送り、その値を利用して認証情報を作ります。そのため、サーバー側にチャレンジを残しておく必要があり、テーブルの定義が必要です。そのテーブルは、IM_Entryの第3引数あるいはparams.phpで指定したユーザで読み書きの権限が必要です。チャレンジのテーブル名は、IM_Entryの第2引数で指定するか、あるいは既定値の名前(issuedhash)のテーブルを作成します。テーブルには以下のフィールドが必要です。
フィールド名 | 型の例 | 説明 |
---|---|---|
user_id | INT, | ユーザテーブルのキーフィールドとなるid値(ユーザをデータベース内のテーブルで管理する場合に利用) |
username | VARCHAR(48), | ユーザテーブルのキーフィールドとなるid値(ユーザをデータベースエンジンが提供するもので運用する場合に利用) |
clienthost | VARCHAR(48), | クライアントを識別するもの。Ver.1.0.5の実装では、クライアントのIPアドレスのみを記録している |
hash | VARCHAR(48), | チャレンジに使うハッシュ値。実際には24バイトの16進数文字列。 |
expired | DateTime, | チャレンジの有効期限を示すタイムスタンプ値 |
なお、このテーブルへの検索では、user_idとclienthostあるいはusernameとclienthostの2つのフィールドに対してAND演算での検索条件が与えられます。従って、高速化するためには、この2つのフィールドを合成したインデックスを作っておくなどの措置をとる事ができます。
ユーザーのテーブル
認証のためのユーザは、データベース内にテーブルを作成する方法と、データベースエンジンが管理するユーザによる方法を利用できます。データベース内にテーブルを作る場合、そのテーブルは、IM_Entryの第3引数あるいはparams.phpで指定したユーザで少なくとも読み出しの権限が必要です。チャレンジのテーブル名は、IM_Entryの第2引数で指定するか、あるいは既定値の名前(authuser)のテーブルを作成します。テーブルには以下のフィールドが必要です。
フィールド名 | 型の例 | 説明 |
---|---|---|
id | INT AUTO_INCREMENT, | 連番の数値を入れて、キーフィールドとする |
username | VARCHAR(48) | ユーザ名(特に制限はないが、重複した名前が定義されないようにするのが望ましい) |
hashedpasswd | VARCHAR(48) | パスワードのハッシュ値 |
VARCHAR(48), | 電子メールアドレス。ユーザー名の代わりに使用したり、パスワードのリセットで利用 | |
limitdt | DateTime | LDAP認証ユーザーのキャッシュの期限 |
ユーザの場合は、ユーザ名をキーフィールドに使えるとも考えられますが、大量のユーザになったときなどに処理速度を有利にするために、内部的にはidフィールドによる連番で与えた数値で処理をすることにします。なお、usernameを条件として検索をかけるので、高速化のためにはこのフィールドにインデックスを作成しておきます。パスワードはハッシュを保存しますが、ハッシュ値は、次の手順で計算をします。
- パスワード(pw)と、4バイトのソルト(s)を用意します。
- パスワードとソルトをつなげた文字列のSHA-1を求めます( sha1(pw+s) → H1)
- H1の16進数表現した文字列と、ソルトを16進数表現した文字列をつなげます(hex(H1)+hex(s)→H2)
- H2をフィールドに保存します。
たとえば、OS Xにおいて、pw=user1、s=TESTとすると、以下のコマンドで「d83eefa0a9bd7190c94e7911688503737a99db0154455354」というハッシュ値の16進数表現を求めることができます。
pw=user1
salt=TEST
h1=`/bin/echo -n "${pw}${salt}" | openssl sha1 -sha1`
h2=`/bin/echo -n "${salt}" | xxd -ps`
echo ${h1}${h2}
グループを管理するテーブル
グループそのものを管理するテーブルと、ユーザーやグループの特定のグループへの所属を記録するテーブルの2つがあります。
フィールド名 | 型の例 | 説明 |
---|---|---|
id | INT AUTO_INCREMENT, | グループを識別するための番号 |
groupname | VARCHAR(48), | グループ名 |
フィールド名 | 型の例 | 説明 |
---|---|---|
user_id | INT, | 所属するユーザのidフィールドの値 |
group_id | INT, | 所属するグループのidフィールドの値 |
dest_group_id | INT, | 所属されるグループのidフィールドの値 |
アクセス権に関する設定の評価
認証の設定は基本的にコンテキストに行いますが、IM_Entryの2番目の引数にも行えます。記述可能な内容については、『定義ファイルでの記述方法a>』を参照してください。つまり、2カ所の設定があるので、以下のように、コンテキストに設定する方を「コンテキストの認証設定」、2つ目の引数に設定するのを「オプションの認証設定」とします。また、いずれも、アクセス権の設定も行いますが、「認証とアクセス権」を合わせてここでは「認証」というネーミングをします。
IM_Entry(
array(
array(
"name"=>"context",
"authentication"=>array(
/* コンテキストの認証設定 */
),
),
),
array(
"authentication"=>array(
/* オプションの認証設定 */
),
...);
コンテキストの認証設定なし、オプションの認証設定あり
タイトル用な条件だと、コンテキストの認証設定のuserとgroupがアクセス権の設定に関係します。もし、userもgroupも設定されていない場合は、認証が通ったユーザは何でもできると考えてください。
userあるいはgroupが設定されている場合、認証が通ると同時に、そのユーザあるいはそのユーザを含むグループが、userあるいはgroupの設定に含まれている必要があります。
コンテキストの認証設定あり、オプションの認証設定なし
オプションの設定のうちuserとgroupの設定がないということです。この場合、特定のコンテキストのデータベース処理に対して、コンテキストの認証設定が適用されます。
コンテキストの認証設定あり、オプションの認証設定あり
両方の認証設定があれば、先にオプションの認証設定が評価されて、その後にコンテキストの認証設定が評価されます。
コンテキストの認証設定の適用対象
適用対象として、4つのデータベース処理、load/update/new/deleteに対する処理と、それらすべてに対してまとめて設定を行うallがあります。
コンテキストの認証設定のtableとfleld
"table"キーに対する値がfield-userあるいはfield-groupの場合は、そのテーブルにあるfieldに指定したフィールドに、ログインが成功したユーザあるいはそのユーザが含むグループの名前が設定されちているレコードだけに絞り込まれます。検索条件にANDでユーザないしはグループの値をfieldに指定したフィールドに対する条件を付け加えます。したがって、fieldで指定したフィールドのうち、空欄ものは認証すればいっさいアクセスできないということになります。
"table"キーに対する値がfield-useやfield-groupではない場合、アクセス権はすべてのテーブルに渡って適用されます。レコードごとのアクセス権を記録するフィールドは不要です。このとき、userあるいはgroup属性に含まれていないユーザで認証した場合、いっさいのアクセスはできないようになっています。オプションの認証設定と異なる動作ですので、注意してください。
load/update/deleteに関しては、アクセス権は「検索条件」として集約されます。一方、newの操作に対しては、アクセス権がある場合だけ、レコードの作成が可能です。レコードの作成により、fieldで指定したフィールドに、ユーザないしはグループが設定されます。ユーザの場合は1つに決まりますが、グループの場合は、グループ名を並べ替えたときのいちばん先頭のグループ名を記録するようにします。
ネイティブ認証、LDAP認証で使う鍵データ
ネイティブ認証やLDAP認証では、クライアントで入力したパスワードを、サーバー側でも利用します。そのため、クライアントからサーバーにパスワードそのものを伝達しなければなりませんが、平文で流すことがないように、暗号化してクライアントからサーバーに送ります。そのために、RSAの鍵ペアを生成してサーバーに記録します。クライアントへは公開鍵だけが送られて、暗号にしたデータをサーバーに送ります。鍵ペアは例えば、「openssl rsagen -out gen.key 2048」のようなコマンドを入れてファイルをまず作ります。そして、その内容のテキストを、$params.phpファイルの変数$generatedPrivateKeyに代入します。INTER-Mediatorに含まれているparams.phpファイルで、該当する変数を探して、生成した鍵に置き換えてください。もし、鍵にパスワードが必要な場合には変数$passPhraseに記述できますが、このファイル自体はPHPが稼働していればクライアントから参照できないとはいえ、扱いには注意しましょう。
LDAP認証
LDAPサーバに関する情報を、params.phpファイルに記述をします。以下はその例です。変数ldapServerはLDAPサーバのURL、ldapPortはポート番号です。そして、ユーザーのDN(Distinguished Name)を決定するために、3つの変数を指定します。たとえば、ユーザー名がmsykなら、Open Directoryの場合はDNは「uid=msyk,cn=users,dc=homeserver,dc=msyk,dc=net」となります。「msyk」の前はldapAccountKey、後はldapContainerとldapBaseで指定した記述がつなげられて、DNを構成しています。ldapExpiringSecondsは、1回のLDAP認証後、指定した時間はLDAPサーバではなく、ネイティブ認証のためのテーブルにキャッシュした情報で認証します。これはパフォーマンスを高めるためです。最後に認証してから、変数ldapExpiringSeconds以上の時間が経過すると、改めてLDAPサーバに接続して認証を行います。
$ldapServer = "ldap://homeserver.msyk.net";
$ldapPort = 389;
$ldapBase = "dc=homeserver,dc=msyk,dc=net";
$ldapContainer = "cn=users";
$ldapAccountKey = "uid";
$ldapExpiringSeconds = 1800;