mod_pythonでRDBMSベースのBasic認証
このエントリは2007/07/21の再掲です
Apacheで任意のURIにBasic認証セキュリティをかけるさい、 多くの場合ファイルベースのパスワードソースが使われます。httpd.confや.htaccessに、こんな設定をする例のアレです。
AuthType Basic AuthName "My Private Secure Zone" AuthUserFile path-to-passwd-file Require valid-user
基本的なApache-Basic認証は、臨機応変にリソースを隠蔽するのに便利なのですが、こればかりだと困ってしまう状況も多々あります。
- htpasswdコマンドが実行できる端末を必要とする
- パスワードファイルがネットワーク越しに見えてはいけないため、リモート管理が困難
- 管理担当者に上記のスキルがない場合に、ユーザ対応が後手後手になる
- ユーザ数に比例してパフォーマンスが低下するので大規模化できない
- 他のWeb-DBサービスで管理しているユーザのパスワードと同期できない
もしApacheの認証ソースにRDBMSを使うことができれば、Webアプリケーションのフロントエンドからパスワード管理でき、ユーザ数増加時でもオーバーヘッドを一定にすることができます。そこで、mod_pythonでデータベースに接続する認証ハンドラを書けないかを検証してみましょう。
mod_pythonの機能とインストールについては、このページで十分ですね。また、今回の実験には、RDBMSとDBAPIにそれぞれ、PostgreSQLとpsycopg2を使います。psycopg2に限らず、サードパーティ製のドライバ選定には注意が必要です。PostgreSQLについてはメジャーバージョン、 Pythonについては2.xのxの部分が違うと、まったくバイナリ互換性がありません。makeしないかぎりは、バイナリバージョンに十分注意してインストールしましょう。
まず何はともあれ、ユーザ名からパスワードを引き当てるためのテーブルを設けます。
CREATE TABLE auth ( username varchar PRIMARY KEY, passwd varchar )
なにか適当なデータを格納しておき、ちゃんと問い合わせることができるかを、Python対話インタプリタで試しましょう。
import psycopg2 conn = psycopg2.connect( "host='localhost' dbname='apacheuser' user='postgres' password='postgres'") curs = conn.cursor() user = 'myaccount' curs.execute( "select passwd from auth where username=%s", (user,)) r = curs.fetchone() print r # password for myaccount shown? curs.close() conn.close()
うまくいけば、user変数に格納したユーザのパスワードが表示されるはずです。さて本題はここからで、Apacheがコンテンツを送出するまでに、このユーザ名とパスワードを、HTTPリクエストのヘッダデータと比較することが可能かどうかです。
どうやら、mod_pythonにはPythonAccessHandler / PythonAuthenHandler / PythonAuthzHandlerと、アクセス制限ハンドラを割り込ませるポイントが準備されていて、マニュアルに登場する二つ目のチュートリアルには、もう簡単な認証のデモが掲載されています。これを見れば、答えは「できるにちがいない」ですね。
AuthUserFile ディレクティブを削除し、PythonAuthenHandlerディレクティブに、認証ハンドラを持つモジュール(authenhandler関数を持つスクリプトファイルの.pyを除いたファイル名)を追加しましょう。Pythonのモジュール検索パスを変更できるPythonPathディレクティブ、それと、ブラウザにmod_python自身のエラーを出力してくれるPythonDebugディレクティブも活用できます。
AuthType Basic AuthName "My Private Secure Zone" # AuthUserFile path-to-passwd-file -- no needs yet Require valid-user PythonAuthenHandler private_access_control PythonPath sys.path+['/your/script/path'] PythonDebug On
スクリプト実装例 private_access_control.py
private_access_control.py をダウンロードして調整したら、/your/script/pathの部分を、このファイルを置いたディレクトリに変更し、Apacheを再起動します。うまくいけば、データベースにエントリを追加するだけで、この方法で保護されたリソースにアクセスできるユーザを作成できます。
データベースのエントリを管理するのなら、それこそWebフロントエンドを作って、スキルや環境を問わずにユーザ管理できるWebサーバを構築できます。さらに、管理がWebアプリケーションになるのなら、もう一歩進んで、パスワードフレーズの入力にサインアップと承認のモデルが使えます。ユーザ個別にパスワードを持てて、それを自分で入力して決めることができれば、ユーザと管理者がパスワードフレーズを何度もメールでやり取りすることや、ユーザが共用パスワードを付箋メモに残すような、人間的なセキュリティホールをごく自然に防ぐことが可能になります。
課題