PHP から Active Directory を用いた認証を行う方法
PHP での LDAP サポート
PHP ではデフォルトでは LDAP 関数サポートは有効ではありませんが、LDAP 拡張を有効に設定することにより LDAP 関数を利用可能です。
私の手元の環境 (Windows) では、php.ini にて php_ldap.dll を有効にする (コメントアウトをやめる) ことで、 LDAP 関数が利用可能になりました。
LDAP 関数呼び出しの流れ
Active Directory では LDAP を実装しているので、 LDAP のバインド操作 (Bind Operation) を利用して認証を行うことができます。
RFC1777 をみると LDAP のバインドは次のような形式です。
BindRequest ::= [APPLICATION 0] SEQUENCE { version INTEGER (1 .. 127), name LDAPDN, authentication CHOICE { simple [0] OCTET STRING, krbv42LDAP [1] OCTET STRING, krbv42DSA [2] OCTET STRING } }
name の箇所にユーザー名、authentication の箇所にパスワードが入りますので、 実際には次のような形式になります (表示は Wireshark での解釈後)。
LDAPMessage bindRequest(1) "keisuke.oyama@foo.com" simple messageID: 1 protocolOp: bindRequest (0) bindRequest version: 2 name: keisuke.oyama@foo.com authentication: simple (0) simple: 70617373776f7264
PHP からは ldap_connect でドメインコントローラに接続してから、ldap_bind を呼ぶことによって bindRequest を実施できます。
セキュリティ上の注意点としては、simple 認証ではパスワードがクリアテキストで流れていることに注意してください。
認証が成功すると、LDAP のバインドレスポンス (BindResponse) にて、結果コード (resultCode) として成功を意味する 0 が返ります。
LDAPMessage bindResponse(1) success messageID: 1 protocolOp: bindResponse (1) bindResponse resultCode: success (0) matchedDN: errorMessage:
ldap_connect 関数が返すリンク識別子 (link identifier) がスコープを外れるところで、 unbindRequest を送ることを確認済み。
LDAPMessage unbindRequest (2) messageID: 2 protocolOp: unbindRequest (2) unbindRequest
それでは、PHP から AD を用いて認証を行うためのコードを書いてみましょう。
PHP から AD を用いて認証を行う実装例
ldap_connect で LDAP サーバに接続し、ldap_bind でユーザ名とパスワードを渡すことによって、 bindRequest による認証が可能です。つまり・・・
- ldap_connect で LDAP サーバー (ドメインコントローラ) に接続
この戻り値としてリンク識別子 (link identifier) を取得する。 - ldap_bind にユーザー名とパスワードを渡して認証する
という流れになります。
これを実装したのが以下になります。
<?php $ldaphost = 'DOMAIN1DC'; $ldapport = 389; $ldapconn = ldap_connect($ldaphost, $ldapport) or die('Unabled to connect to the server'); if($ldapconn){ $ldapbind = ldap_bind($ldapconn, 'keisuke.oyama@foo.com', 'password'); if($ldapbind){ echo 'LDAP bind succeeded'; } else{ echo 'LDAP bind failed.'; } } else{ echo 'LDAP connect failed.'; } ?>
ldap_connect 関数に渡す値は上記のように、ホスト名とポート番号を分けても良いですし、 次のように指定しても良いです。
$ldapconn = ldap_connect('ldap://domain1dc.foo.com/');
ちなみに、ドメインコントローラのホスト名については Windows のドメイン環境であれば、ドメインコントローラ名の取得コマンド で説明した方法でドメインコントローラのホスト名を取得可能です。(\\ を取った部分)
尚、リンク識別子を保持する変数がスコープから外れるタイミングで、LDAP サーバーと切断されるようです。 (セッションが切断されます) 切断する前に unbindRequest を送信していることも確認しました。 従って、複数の操作を同じリンクに対して行う場合はリンク識別子のスコープを外さないようにしないと、 ldap_bind を行うたびに再度接続しなければならないことになり無駄が増えます。
かといって、スコープを広めた場合は自動的なクリーンアップを期待できなくなり、 下手すると無駄にリソースを食っている時間を増やすだけなので、リンクが不要になったところで意識的に ldap_unbind を呼び出すべきと思います。