Apache2.2 mod_authn_mysql 2.2.3
Basic認証、Digest認証での認証機構であり、MySQLデータベース上のパスワード管理テーブルとの照合を行う。
ダウンロード
Current unofficial CVS Apache webserver binaries and module binaries
http://www.gknw.net/development/apache/httpd-2.2/win32/modules/
mod_authn_mysql-2.2.3-w32.zip 17-Aug-2006 15:26 138K ZIP compressed archive
Windows版のライブラリが同梱されているが、MySQLのライブラリのバージョンが異なっている(場合が多い)ので、自分でビルドすることとなる。データベース中のパスワードが平文であることが気にならなければ、ソースコードの修正の必要はない。
パスワードのhash方法
データベース中に格納されているパスワードは平文であり、セキュリティ上好ましくないため、MD5でhashしたものを格納することとする。
Basic認証の場合
パスワードをMD5でhashしてそのままデータベースに格納する。
ブラウザからはパスワードが平文で送られてくる。
(セキュリティ上好ましくはないが気になる場合はDigest認証となるが、利用できるブラウザの制約がある。)
Digest認証の場合
(ユーザ名)+”:”+(レルム)+”:”+(パスワード)を連結した文字列をMD5でhashして格納する。
Digest認証の場合、ブラウザで上記のようにHashされた値が渡されてくる。
モジュールの修正
Basic認証の場合
認証モジュール(mod_authn_mysql)の「check_mysql_pw」関数でパスワードの比較をする前に入力されたパスワードをMD5 Hashする処理を追加する。
static authn_status check_mysql_pw(request_rec *r, const char *user,
const char *password)
{
authn_status ARV = AUTH_DENIED;
int blah = 0;
mysql_config *conf;
mysql_dconfig *dconf = ap_get_module_config(r->per_dir_config,
&authn_mysql_module);
char* mysql_password;
char* query;
MYSQL_ROW sql_row;
MYSQL_RES *result = NULL;
mysql_res *mysql_res;
const char* esc_user = mysql_escape(user, r->pool);
char* passwdhash; // Added
conf = apr_hash_get(authn_mysql_config, dconf->id, APR_HASH_KEY_STRING);
if(conf == NULL) {
ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
"[mod_authn_mysql.c] - Server Config for \"%s\" was not found", dconf->id);
return ARV;
}
apr_reslist_acquire(conf->pool, (void **)&mysql_res);
/* make the query to get the user's password */
if (conf->rec.isactive_field) {
query = apr_psprintf(r->pool, "SELECT %s FROM %s WHERE %s='%s' AND %s!=0 LIMIT 0,1",
conf->rec.password_field, conf->rec.mysql_table,
conf->rec.username_field, esc_user, conf->rec.isactive_field);
}
else {
query = apr_psprintf(r->pool, "SELECT %s FROM %s WHERE %s='%s' AND %s!=0 LIMIT 0,1",
conf->rec.password_field, conf->rec.mysql_table,
conf->rec.username_field, esc_user);
}
/* perform the query */
if (safe_mysql_query(mysql_res, &result, r, query) == 0){
/* store the query result */
if (result && mysql_num_rows(result) == 1) {
sql_row = mysql_fetch_row(result);
/* ensure we have a row, and non NULL value */
if (!sql_row || !sql_row[0]) {
ARV = AUTH_USER_NOT_FOUND;
}
else {
passwdhash = (char *) ap_md5(r->pool, (const unsigned char*)password); // Added
mysql_password = (char *) apr_pstrcat(r->pool, sql_row[0], NULL);
// Replaced if (strcmp(mysql_password,password) != 0) {
if (strcmp(mysql_password,passwdhash) != 0) {
ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
"[mod_authn_mysql.c] - Password Unmatch(%s:%s", mysql_password, password);
ARV = AUTH_DENIED;
}
else {
ARV = AUTH_GRANTED;
}
}
}
}
mysql_free_result(result);
safe_mysql_rel_server(conf->pool, mysql_res, r);
return ARV;
}
Digest認証の場合
認証モジュール(mod_authn_mysql)の「authn_status get_mysql_realm_hash」関数で、入力されたパスワードをDigest認証用にMD5 Hashしている箇所を削除する。(データベース中のデータが既にHashされているから不要となる。)
static authn_status get_mysql_realm_hash(request_rec *r, const char *user,
const char *realm, char **rethash)
{
int blah = 0;
mysql_config *conf;
authn_status ARV = AUTH_DENIED;
mysql_dconfig *dconf = ap_get_module_config(r->per_dir_config,
&authn_mysql_module);
char* mysql_hash;
char* mysql_pass;
char* query;
const char* esc_user = mysql_escape(user, r->pool);
MYSQL_ROW sql_row;
MYSQL_RES *result;
mysql_res *mysql_res;
conf = apr_hash_get(authn_mysql_config, dconf->id, APR_HASH_KEY_STRING);
if(conf == NULL) {
ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
"[mod_authn_mysql.c] - Server Config for \"%s\" was not found", dconf->id);
return ARV;
}
apr_reslist_acquire(conf->pool, (void **)&mysql_res);
/* make the query to get the user's password */
if (conf->rec.isactive_field) {
query = apr_psprintf(r->pool, "SELECT %s FROM %s WHERE %s='%s' AND %s!=0 LIMIT 0,1",
conf->rec.password_field, conf->rec.mysql_table,
conf->rec.username_field, esc_user, conf->rec.isactive_field);
}
else {
query = apr_psprintf(r->pool, "SELECT %s FROM %s WHERE %s='%s' LIMIT 0,1",
conf->rec.password_field, conf->rec.mysql_table,
conf->rec.username_field, esc_user);
}
/* perform the query */
if (safe_mysql_query(mysql_res, &result, r, query) == 0) {
if (mysql_num_rows(result) > 0) {
sql_row = mysql_fetch_row(result);
/* ensure we have a row, and non NULL value */
if (!sql_row || !sql_row[0]) {
ARV = AUTH_USER_NOT_FOUND;
}
else {
mysql_pass = (char *) apr_pstrcat(r->pool, sql_row[0], NULL);
// mysql_hash = (char *) ap_md5(r->pool, (const unsigned char*)apr_pstrcat(r->pool, mysql_pass, NULL));
// *rethash = mysql_hash;
*rethash = mysql_pass; // Modified
ARV = AUTH_USER_FOUND;
}
}
}
mysql_free_result(result);
safe_mysql_rel_server(conf->pool, mysql_res, r);
return ARV;
}
ビルド(Visual Studio 2005)
コンパイルオプション指定例
/O2 /Ob1 /I "C:\Develop\ref\httpd-2.2.9\include" /I "C:\Develop\ref\httpd-2.2.9\os\win32" /I "C:\Develop\ref\httpd-2.2.9\srclib\apr\include" /I "C:\Develop\ref\httpd-2.2.9\srclib\apr-util\include" /I "C:\Develop\ref\httpd-2.2.9\modules\aaa" /I "C:\Develop\ref\mysql-5.1.25-rc-win32\include" /D "NDEBUG" /D "WIN32" /D "_WINDOWS" /D "_VC80_UPGRADE=0x0600" /D "_WINDLL" /GF /FD /EHsc /MD /Gy /Fp".\Release/mod_authn_mysql.pch" /Fo".\Release/" /Fd"Release\mod_authn_mysql" /W3 /nologo /c /TC /errorReport:promptリンカオプション指定例
/OUT:"Release/mod_authn_mysql.so" /INCREMENTAL:NO /NOLOGO /LIBPATH:"C:\Develop\ref\Apache2.2\lib" /LIBPATH:"C:\Develop\ref\mysql-5.1.25-rc-win32\lib\opt" /DLL /MANIFEST /MANIFESTFILE:".\Release\mod_authn_mysql.so.intermediate.manifest" /NODEFAULTLIB:"msvcrt.lib" /PDB:".\Release/mod_authn_mysql.pdb" /MAP:".\Release/mod_authn_mysql.map" /SUBSYSTEM:WINDOWS /BASE:"@C:\Develop\ref\httpd-2.2.9\os\win32\BaseAddr.ref,mod_authn_mysql" /IMPLIB:".\Release/mod_authn_mysql.lib" /MACHINE:X86 /ERRORREPORT:PROMPT libhttpd.lib libapr-1.lib libaprutil-1.lib wsock32.lib mysqlclient.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib
Apache2.2の設定
conf/httpd.confを修正して設定する。
Basic認証の例
LoadModule auth_basic_module modules/mod_auth_basic.so
LoadModule authn_mysql_module modules/mod_authn_mysql.so
<IfModule mod_authn_mysql.c>
AuthnMySQLDBHost SRV1 localhost
AuthnMySQLDBUsername SRV1 (MySQLユーザ)
AuthnMySQLDBPassword SRV1 (MySQLパスワード)
AuthnMySQLDB SRV1 (MySQLデータベース)
AuthnMySQLTable SRV1 (テーブル名)
AuthnMySQLUsernameField SRV1 (ユーザID列名)
AuthnMySQLPasswordField SRV1 (パスワード列名)
AuthnMySQLIsActiveField SRV1 active_flag
AuthnMySQLConnMin SRV1 3
AuthnMySQLConnSoftMax SRV1 12
AuthnMySQLConnHardMax SRV1 20
AuthnMySQLConnTTL SRV1 600
<Location />
AuthName "名称"
AuthType Basic
AuthBasicProvider mysql
AuthnMySQLServerConfig SRV1
require valid-user
</Location>
</IfModule>
Digest認証の例
LoadModule auth_digest_module modules/mod_auth_digest.so
LoadModule authn_mysql_module modules/mod_authn_mysql.so
<IfModule mod_authn_mysql.c>
AuthnMySQLDBHost SRV1 localhost
AuthnMySQLDBUsername SRV1 (MySQLユーザ)
AuthnMySQLDBPassword SRV1 (MySQLパスワード)
AuthnMySQLDB SRV1 (MySQLデータベース)
AuthnMySQLTable SRV1 (テーブル名)
AuthnMySQLUsernameField SRV1 (ユーザID列名)
AuthnMySQLPasswordField SRV1 (パスワード列名)
AuthnMySQLIsActiveField SRV1 active_flag
AuthnMySQLConnMin SRV1 3
AuthnMySQLConnSoftMax SRV1 12
AuthnMySQLConnHardMax SRV1 20
AuthnMySQLConnTTL SRV1 600
<Location />
AuthName "名称"
AuthType Digest
AuthDigestProvider mysql
AuthBasicProvider off
AuthnMySQLServerConfig SRV1
require valid-user
</Location>
</IfModule>