Настройка аутентификации в Apache на основе данных с БД MySQL
- Подробности
- Просмотров: 5814
Как уже упоминалось в прошлой записке, модули аутентификации пользователей средствами Apache могут оказаться весьма полезными. Но основное неудобство в этом деле как ни странно будет создание файликов с паролями. Порой бывает, что уже есть хранилище такой информации, только в другой форме. Например есть форум, где каждый пользователь создаёт свой аккаунт. И есть какие-то файлы, которые должны быть доступны только для пользователей форума. Перегонять их через CGI сценарий - дело неблагодарное и никак не может сказаться на скорости работы положительно. Статический файл стянуть куда быстрее и проще, нужно только нужным образом задать к нему доступ.
В Apache такое сделать возможно, для этого есть средства. И тут мы рассморим как использовать БД MySQL как хранилище паролей.
Способ 1. mod_auth_mysql
Это есть специализированный модуль для подчитывания логинов и паролей с таблицы в MySQL. Прежде чем его использовать рекомендую пройти на оффициальный сайт и посмотреть дату последнего обновления (версия 3.0.0 от 22.06.2005), после чего хорошо подумать. Нет, я не утверждаю, что старый софт никуда не годится, но с этим модулем всё походу именно так. Ни один с вариантов конфигурации, описанных в оффициальной документации не прокатил, поскольку в них не было указанного AuthUserFile, а Apache 2.2.22 очень его хотел. В принципе опытным путём подобрал что-то рабочее. Выгладело именно так:
AuthType Basic # использование базовой аутентификации
AuthName "Restricted Area" # сообщение, которое сопровождает запрос пароля
AuthBasicAuthoritative Off # не требовать аутентификацию через Basic
AuthMYSQLEnable on # включить MySQL аутентификацию
AuthMySQLAuthoritative on # требовать прохождение MySQL аунентификации
AuthMySQLHost 127.0.0.1 # адрес MySQL сервера
AuthMySQLPort 3306 # порт MySQL сервера
AuthMySQLUser db_auth # имя пользователя сервера БД
AuthMySQLPassword db_auth_pass # его пароль
AuthMySQLDB users_base # название самой БД
AuthMySQLUserTable users # название таблицы
AuthMySQLPwEncryption none # указание того, что в БД пароли находятся в чистом виде
AuthMySQLNameField username # название поля с логинами пользователей
AuthMySQLPasswordField password # название поля с паролями
Require valid-user # впускать любого правильного пользователя
Такая конфигурация подразумеват, что в БД user_base есть таблица users с полями username и password под логин и пароль соответсвенно. Модуль там ищет пару погин-пароль и если такая находится, вход считается выполненным. Пароли в БД могут быть и хешированными, для этого нужно менять значение директивы AuthMySQLPwEncryption в значения crypt, scrambled (шифрование MySQL), md5, aes или sha1 (нужное подчеркнуть).
Оно даже как-то работало, но периодически переспрашивало пароль. Потом выяснил, что модуть периодически сбоил и терял соединение с БД в процессе выполения запроса, после чего всё же учёл тот факт, что модуль всё-таки древний. Далее мои развлечения с ним закончились.
Потом я посмотрел в репозиторий Gentoo (да, именно она установлена сервере) и кроме рассмотренного www-apache/mod_auth_mysql нашёл там www-apache/mod-auth-mysql уже версии 4.3.9. Он конечно был отмечен как нестабильный, но исследования ради надо было попробовать. Эти два друга вместе стоять не могут впринципе, поэтому пришлось просто заменить.
Походу это то же самое, только потянутое с Debian. В конфигурацию Apache добавляется аналогично своему предшественнику. Включил и результат меня не впечетлил - Apache оказался запускаться, он не смог найти в apache2_mod_auth_mysql.so символ apache2_mysql_auth_module. Неясно куда смотрел пишущий конфиги, оно же с коробки не работает. Может потому на нём нет кейворда стабильности. Ну и фиг с ним, випилил.
Способ 2. mod_authnz_external
Ну поскольку модуля для аутентификации кончились, пришлось копать немного в другую сторону. И нашлось универсальное решение - аутентификация внешней программой. Всё, что нужно - это внешний аутентификатор, который осуществит запрос к БД и скажет, правильные ли даны реквизиты.
Для начала нужно обьявить аутентификатор в конфигурации Apache:
<IfModule authnz_external_module>
DefineExternalAuth cl_auth pipe /var/www/localhost/bin/authscript
</IfModule>
Тут проблема состоит в том, что прописать это в .htaccess просто нельзя, поэтому нужно буть админом сервера. Это можно писать либо в глобальную конфигурацию сервера либо в конфигурацию виртуального хоста. Но не глубже.
Далее защищаем нужную папку:
AuthType Basic # использование базовой аутентификации
AuthName "Restricted Area" # сообщение, которое сопровождает запрос пароля
AuthBasicProvider external # использовать mod_authnz_external
AuthExternal cl_auth # какой именно аутентификатор использовать
Require valid-user # впускать любого правильного пользователя
Это уже можно писать куда угодно. Следует отметить, что можно использовать лишь базовую аутентификацию, поскольку mod_authnz_external с дайджестами скорее всего работать не будет. Его разработчики обясняют это тем, что для дайджестов нужно иметь лишь базу с чистыми паролями или с уже готовыми дайджестами, что в реальной жизни далеко не всегда возможно. Поэтому походу они не парились над поддержкой. Хотя нужно как-то попробовать ;-).
Теперь перейдём к аутентификатору. Нужную нам задачу с лихвой выполнит вот такой скрипт:
#!/usr/bin/php
<?php
$db = new mysqli("127.0.0.1", "db_auth", "db_auth_pass", "users_base", 3306);
if(!$db)
exit(1);
$fp=fopen("php://stdin","r");
$username = stream_get_line($fp, 1024,"\n");
$password = stream_get_line($fp, 1024,"\n");
fclose($fp);
$result = $db->query("SELECT `id` FROM `users` WHERE `username` = '".$db->real_escape_string($username)."' AND `password` = '".$db->real_escape_string($password)."'");
if(!$result)
exit(1);
if($result->num_rows == 1)
exit(0);
exit(1);
Всё просто: если в базе наши одну пару логин и пароль, введённую юзером, то возвращаем 0, чем говорим о том, что все окай. Иначе просто возвращаем 1. Сохраняем его как /var/www/localhost/bin/authscript, даём права на исполнение.
Вроде бы всё нормально. Но не совсем. Тут происходит так же, как и mod_auth_mysql, только реже. А пароль-то всё равно спрашивает повторно и это раздражает. Очень раздражает. Судя по логам этот скрипт возвращает код -5 раньше чем серевер даст ему реквизиты на проверку. Почему - ещё не знаю, но подозреваю, что связано это с какими то системными ограничениями. На PHP списывать не буду, так как заменял его не один раз. Заменил вот этим:
#!/usr/bin/perl
use DBI;
$username= <STDIN>;
chomp $username;
$passname= <STDIN>;
chomp $passname;
my $dbh = DBI->connect('dbi:mysql:users_base:127.0.0.1:3306', 'db_auth', 'db_auth_pass') or exit 1;
my $sth = $dbh->prepare('SELECT id FROM users WHERE username = ? AND password = ?');
$sth->execute($username, $passname);
if($sth->rows == 1) {
exit 0;
}
exit 1;
Этот скрипт вылетает, но уже не так часто. Так что проблема целиком не устранена. Надо будет как-то занятся.
Способ 3. Автоматическая файлогенерация
Но преследуя цель аутентифицировать юзеров на основе данных с БД MySQL не обязательно делать в неё запросы каждый раз, когда нужно проверить правильность реквизитов. Можно просто на основе данных с БД создать файлик с паролями и аутентифицировать при помощи mod_auth_basic или mod_auth_digest в связке с mod_authn_file. Как это делать я уже рассказывал в предыдущей записке. Тут мы только рассмотрим примерную методику генерации файлов.
Итак, если будем юзать mod_auth_basic, то скрит дожен быть примерно следующим:
#!/usr/bin/php
<?php
$db = new mysqli("127.0.0.1", "db_auth", "db_auth_pass", "users_base", 3306);
if(!$db) exit(1);
function getPasswd($login, $pass) {
return crypt($pass, base64_encode($pass));
}
function getPasswdLine($login, $pass) {
return "{$login}:".getPasswd($login, $pass);
}
$file = "/var/www/localhost/etc/db.htpasswd"; // путь, где будет лежать файл с паролями
$tmp = "/tmp/db.htpasswd";
$result = $db->query('SELECT `username`, `password` FROM `users`');
if(!$result) exit(1);
$out = fopen($tmp, 'w');
while($row = $result->fetch_row()) {
$line = getPasswdLine($row[0], $row[1])."\n";
fwrite($out, $line);
}
fclose($out);
system("mv -f $tmp $file");
Вот примерно где-то так. Скрипт можно кинуть в cron на исполенение через некоторое время. Но лучше будет модифицировать программу так, чтобы при смене пароля пользователем или регистрации нового вызывался этот код. Так лучше, потому что изменения будут вступать в силу сразу же, и не будет необходимости ждать, когда же файл будет обновлен.
Если хотим использовать mod_auth_digest, то скрипт немного другой:
#!/usr/bin/php
<?php
$realm = "Restricted Area";
$db = new mysqli("127.0.0.1", "db_auth", "db_auth_pass", "users_base", 3306);
if(!$db)
exit(1);
function getDigest($login, $realm, $pass) {
return md5("{$login}:{$realm}:{$pass}");
}
function getDigestLine($login, $realm, $pass) {
return "{$login}:{$realm}:".getDigest($login, $realm, $pass);
}
$file = "/var/www/claroline/etc/dbo.htdigest";
$tmp = "/tmp/dbo.htdigest";
$result = $db->query('SELECT `username`, `password` FROM `users`');
if(!$result)
exit(1);
$out = fopen($tmp, 'w');
while($row = $result->fetch_row()) {
$line = getDigestLine($row[0], $realm, $row[1])."\n";
fwrite($out, $line);
}
fclose($out);
system("mv -f $tmp $file");
Вот впринципе и всё. Естесственно оба скрипта требуют, чтобы пароли в БД были в чистом виде. Поэтому стоит задуматся о том, чтобы модифицировать программу так, чтобы она сама шифровала пароль в нужный формат ещё при смене, если есть необходимость скрывать пароли пользователей в самой БД. В любом случае из приведённых скриптов ясно и понятно как именно их нужно шифровать.
Скачать скипты:
Аутентификатор для mod_authnz_external (php)
Аутентификатор для mod_authnz_external (perl)
Генератор файла с паролями для mod_auth_basic (php)
Генератор файла с паролями для mod_auth_digest (php)