wtorek, 15 września 2015

Apache mod_authn_dbd: uwierzytelnianie HTTP z wykorzystaniem bazy danych MySql

W niniejszym artykule opiszę jak skonfigurować i wymusić uwierzytelnianie HTTP z poziomu serwera Apache, z użytkownikami i hasłami zapisanymi w tabeli bazy danych MySql. Pomimo iż zadanie wygląda na dość skomplikowane, poradzenie sobie z nim nie wymaga dużej ilości pracy.

Opisane tutaj zagadnienia opracowałem, przetestowałem i zademonstruję w następującym środowisku Linux:

System operacyjny Scientific Linux 7;
Kernel 3.10.0-229.11.1.el7.x86_64;
Serwer www Apache v. 2.4.6;
Server MySql mariadb v. 5.5.44;
apr-util-mysql v. 1.5.2 – pakiet sterownika MySql dla abstrakcyjnego interfejsu DBD bazy danych

Aby zrozumieć i prawidłowo zastosować opisywane tutaj rozwiązania, wymagana jest od czytelników następująca wiedza:

dobra znajomość podstawowych zagadnień Linux;
dobra znajomość konfiguracji serwera Apache;
dobra znajomość serwera i klienta MySql.

Należy zwrócić uwagę, iż w celu przeprowadzenia opisanej tutaj konfiguracji, serwer Apache wymaga obecności modułów mod_auth_basic, mod_authn_core, mod_authn_file, mod_authn_socache, mod_dbd oraz mod_authn_dbd. W celu wdrożenia uwierzytelniania HTTP z wykorzystaniem tabeli MySql pakiet apr-util-mysql powinien zostać zainstalowany.

Jeżeli nie mamy pewności co do obecności wymaganych modułów należy się upewnić czy są one dostępne dla serwera, np. za pomocą narzędzia httpd z parametrem -M.

Aby wyjaśnić zagadnienie w dość przejrzysty i łatwy do zrozumienia sposób, podzielę całą procedurę na dwa etapy. W pierwszym kroku skonfiguruję proste uwierzytelnianie HTTP oparte na funkcjonalności modułu Apache mod_auth_basic, w drugim zaś kroku zmodyfikuję nieco pierwotną konfigurację, dodając do niej funkcjonalność odczytu informacji o użytkownikach z tabeli MySql.

Apache Web Server
Apache Web Server

Krok 1 - Apache mod_auth_basic: podstawowe uwierzytelnianie HTTP Apache


W tym kroku zademonstruję jak przykładowo skonfigurować podstawowe uwierzytelnianie HTTP z poziomu serwera Apache, za pomocą modułu mod_auth_basic. Należy zwrócić uwagę, że zasady bezpieczeństwa wymagałyby jednoczesnego stosowania szyfrowania SSL, co w połączeniu z uwierzytelnianiem mod_auth_basic przyniosłoby naprawdę wysoki poziom bezpieczeństwa jeśli chodzi o przesyłanie nazw użytkowników i haseł w sieci. Zależy zwrócić uwagę, że stosowanie modułu mod_auth_basic wraz z szyfrowaniem SSL jest o wiele bezpieczniejsze niż stosowanie uwierzytelniania opartego o sam moduł mod_auth_digest, który niegdyś był jednak o wiele bezpieczniejszy niż mod_auth_basic, ale dziś już tak nie jest.

Przytoczę przykład podstawowego uwierzytelniania HTTP dla ścieżki /var/www/html.

Aby rozpocząć należy dodać do pliku httpd.conf następujące linie:
<Directory "/var/www/html/zabezpieczony">
    AllowOverride None
    AuthType Basic
    AuthName "Strefa zabezpieczona"
    AuthBasicProvider file
    AuthUserFile /etc/httpd/users_zabezpieczony
    Require user michael
</Directory>
Co oznaczają te wszystkie dyrektywy? Otóż ich znaczenie jest następujące:

Directory: w sekcji <Directory>, która powinna być ograniczona znacznikami <Directory "katalog"></Directory>, umieszcza się ustawienia dotyczące określonego katalogu. 

AllowOverride: ta ważna dyrektywa z parametrem None zabezpiecza konfigurację katalogu przed nadpisaniem jej ustawień wpisami w pliku .htaccess, umieszczonym bezpośrednio w danym katalogu. Warto tutaj zwrócić uwagę, że w przypadku konieczności stosowania ustawień dotyczących uwierzytelniania bezpośrednio w pliku .htaccess, na przykład z powodu ograniczonego dostępu do plików konfiguracyjnych serwera Apache, wówczas w pliku httpd.conf, w sekcji dotyczącej danego katalogu, powinna się znajdować dyrektywa AllowOverride z parametrem AuthConfig.

AuthType Basic: określa metodę uwierzytelniania HTTP. W tym przypadku jest to podstawowe uwierzytelnianie.

AuthName: dyrektywa określa nazwę, która ma się wyświetlać w okienku uwierzytelniania. W naszej przykładowej konfiguracji jest to „Strefa zabezpieczona”.

AuthBasicProvider: dyrektywa określa metodę przechowywania nazw użytkowników i haseł dla skonfigurowanego dla danego katalogu uwierzytelniania HTTP. W naszej przykładowej konfiguracji ustawiamy metodę "file", co oznacza, że nazwy użytkowników i ich hasła mają być przechowywane w specjalnym pliku, który za chwilę utworzymy.

AuthUserFile: określa lokalizację pliku z nazwami użytkowników i ich zaszyfrowanymi hasłami. W naszej przykładowej konfiguracji jest to /etc/httpd/users_zabezpieczony

Require: dyrektywa określająca uprawnionych użytkowników, którzy mają mieć dostęp do katalogu. W naszej przykładowej konfiguracji dostęp do katalogu ma mieć wyłącznie użytkownik o nazwie michael.

Jeżeli katalog /var/www/html/zabezpieczony jeszcze nie istnieje to należy go teraz utworzyć. Własność i uprawnienia do katalogu powinny wyglądać następująco:
drwxr-xr-x 2 apache apache    6 Sep 14 09:00 zabezpieczony
Następnie należy utworzyć plik przechowujący nazwy użytkowników i ich zaszyfrowane hasła. Należy to zrobić za pomocą polecenia htpasswd:
[michael@sphera3]$ htpasswd -c /etc/httpd/users_zabezpieczony michael
New password:
Re-type new password:
Adding password for user michael
Dla wygenerowanego pliku należy następnie ustawić odpowiedniego właściciela, grupę, oraz uprawnienia:
-rw-r-----   1 apache apache   46 Sep 14 09:04 users_zabezpieczony
Nowe ustawienia, zapisane w pliku konfiguracyjnym serwera Apache, zaczną obowiązywać po ponownym uruchomieniu serwera, a więc:
[michael@sphera3]$ systemctl restart httpd
Zgodnie z przeprowadzoną konfiguracją próba dostępu do zabezpieczonego katalogu www uruchamia proces uwierzytelniania HTTP. Aby się o tym przekonać wystarczy wpisać do paska adresów przeglądarki adres serwera i ścieżkę prowadzącą do zabezpieczonego katalogu.

Krok 2 – Apache mod_authn_dbd: uwierzytelnianie HTTP Apache z użytkownikami w tabeli MySql


W tym kroku nauczymy się jak skonfigurować serwer Apache do uwierzytelniania HTTP, tak aby nazwy i zaszyfrowane hasła użytkowników mogły być przechowywane w odpowiedniej tabeli bazy danych MySql. Tego typu rozwiązanie jest nieco bardziej złożone niż to, które zostało opisane w poprzednim kroku.

Uwaga: w sieci można znaleźć obfitą ilość instruktarzy dotyczących tego zagadnienia. Wiele z nich, jak udało mi się ustalić, prowadzi do różnego rodzaju błędów, które mogą powodować. Przykładowo, najczęściej powodują one generowanie następujących komunikatach o błędach w logach serwera Apache:
[dbd:crit] [pid 9253] (20014)Internal error: AH00636: child init failed! [dbd:error] [pid 9250] (20014)Internal error: AH00629: Can't connect to mysql: Access denied for user 'apache'@'localhost' (using password: NO)
Aby prawidłowo skonfigurować uwierzytelnianie HTTP Apache z wykorzystaniem bazy danych MySql, poprzednią konfigurację uwierzytelniania HTTP dotyczącą naszego katalogu należy zmienić w następujący sposób:
DBDriver mysql
DBDParams "host=localhost,dbname=apache,user=apache,pass=tajne_haslo_apacha"
DBDMin  1
DBDKeep 1
DBDMax 10
DBDExptime 300
DBDPersist On

<Directory "/var/www/html/zabezpieczony">
  AuthType Basic
  AuthName "Strefa zabezpieczona"
  AuthBasicProvider socache dbd
  AuthnCacheProvideFor dbd
  AuthnCacheContext strefa_zabezpieczona
  Require valid-user
  AuthDBDUserPWQuery "SELECT password FROM authn WHERE user = %s"
</Directory>
Powyżej przedstawione dyrektywy DBD powinny znajdować się w pliku httpd.conf i nie mogą być zamknięte w jakichkolwiek konkretnych sekcjach, np. sekcjach <Directory>. Dyrektywy DBD określają sterownik bazy danych, nazwę użytkownika i hasło oraz kilka innych parametrów związanych z serwerem bazodanowym.

Co oznaczają dyrektywy z powyższej sekcji <Directory>, zwłaszcza, że jest ich o wiele więcej niż w przykładzie z pierwszego kroku?

AuthBasicProvider: dyrektywa ta, wraz z parametrami socache i dbd instruuje serwer Apache do odczytywania nazw użytkowników i ich haseł z serwera bazy danych oraz, w miarę możliwości, ze specjalnej pamięci podręcznej (mod_authn_socache) w celu zmniejszenia częstotliwości odczytu tabeli bazy danych.

AuthnCacheProvideFor oraz  AuthnCacheContext: te dyrektywy związane są z konfiguracją specjalnej pamięci podręcznej dla zabezpieczanego katalogu, jak wyjaśniłem powyżej.

AuthDBDUserPWQuery: ta dyrektywa określa zapytanie SQL stosowane do odczytania hasła dla nazwy użytkownika wprowadzonej w trakcie procesu uwierzytelniania.

Znaczenie pozostałych dyrektyw konfiguracji opisywanej w tym kroku przedstawiłem w wyjaśnieniach do pierwszego kroku naszej konfiguracji.

Po zapisaniu powyżej przedstawionej konfiguracji należy ponownie uruchomić serwer Apache.

Należy teraz utworzyć bazę danych i tabelę MySql, które będą wykorzystywane w procesie uwierzytelniania HTTP, zgodnie z ustawionymi w powyższej konfiguracji parametrami dyrektyw DBDParams oraz AuthDBDUserPWQuery. W tym celu należy uruchomić klienta MySql jako użytkownik root i wydać następujące polecenia:
MariaDB [(none)]> create database apache;
Query OK, 1 row affected (0.00 sec)

MariaDB [(none)]> use apache;
Database changed
MariaDB [apache]> create table authn (
    -> user varchar(20) not null,
    -> password char(38) not null ) engine = InnoDB charset utf8;
Query OK, 0 rows affected (0.06 sec)
W tym przykładzie zastosuje szyfrowanie haseł metodą MD5. Należy zwrócić szczególną uwagę na fakt, iż przechowywane w tabeli bazy danych hasła użytkowników muszą być zakodowane w jednym z rozpoznawanych przez Apache formatów haseł! Dlatego pole password ma mieć długość 38 znaków, zamiast typowej dla MD5 długości 32 znaków. Jeśli hasła mają być zakodowane algorytmem MD5, nie wystarczy zakodować ich tym algorytmem z wiersza poleceń MySql! Apache obsługuje swój specyficzny format MD5. Aby dowiedzieć się więcej na ten temat warto przeczytać ten dokument.

Dla celów niniejszego przykładu zakoduję hasła w specyficznym dla Apache formacie algorytmu MD5, posługując się w tym celu narzędziem htpasswd:
[michael@sphera3]$ htpasswd -nbm HTTPUser1 HTTPPassword1
HTTPUser1:$apr1$IWNLcKto$RkkBvlQ.cAS85wjHi8mUu/

[michael@sphera3]$ htpasswd -nbm HTTPUser2 HTTPPassword2
HTTPUser2:$apr1$mYBsnAnx$of8v/VS.ja1i0I2dh0j8n1
Następnie należy wprowadzić kilka przykładowych użytkowników z zaszyfrowanymi w formacie Apache-md5 hasłami do nowo utworzonej tabeli:
MariaDB [apache]> insert into authn values('HTTPUser1',”$apr1$IWNLcKto$RkkBvlQ.cAS85wjHi8mUu/”),('HTTPUser2',”$apr1$mYBsnAnx$of8v/VS.ja1i0I2dh0j8n1”);
Query OK, 2 rows affected (0.01 sec)
Records: 2  Duplicates: 0  Warnings: 0
Nasza przykładowa tabela powinna teraz zawierać 2 wiersze danych:
MariaDB [apache]> select * from authn;
+---------------+---------------------------------------------------------------+
| user               | password                                                                     |
+---------------+---------------------------------------------------------------+
| HTTPUser1  | $apr1$IWNLcKto$RkkBvlQ.cAS85wjHi8mUu/      |
| HTTPUser2  | $apr1$mYBsnAnx$of8v/VS.ja1i0I2dh0j8n1             |
+---------------+---------------------------------------------------------------+
2 rows in set (0.00 sec)
Po pomyślnym wprowadzeniu nazw użytkowników i ich zaszyfrowanych haseł do tabeli authn, należy utworzyć użytkownika apache@localhost dla serwera MySql, nadać mu hasło zgodnie z dyrektywą DBDParams naszej przykładowej konfiguracji i uprawnić użytkownika apache@localhost do odczytywania danych z tabeli authn:
MariaDB [apache]> grant usage on *.* to apache@localhost identified by "tajne_haslo_apacha";
Query OK, 0 rows affected (0.00 sec)

MariaDB [apache]> grant select on apache.authn to apache@localhost;
Query OK, 0 rows affected (0.00 sec)
Wszystko jest już gotowe i można sprawdzić czy rzeczywiście konfiguracja działa. Utwórzmy testowy plik index.html zawierający jakąkolwiek treść, np. „HELLO” i wrzućmy go do zabezpieczonego katalogu:
-rw-r--r-- 1 apache apache  7 Sep 15 12:19 index.html
Po wywołaniu pliku w przeglądarce, powinno się pojawić okienko uwierzytelniania, do którego należy wprowadzić jednego z utworzonych użytkowników oraz jego niezaszyfrowane hasło, czyli np. HTTPUser1 i HTTPPassword1:

Apache - Uwierzytelnianie HTTP z wykorzystaniem bazy danych MySql
Apache - Uwierzytelnianie HTTP z wykorzystaniem bazy danych MySql


W przypadku jakichkolwiek pytań służę pomocą. Proszę o komentarze!














1 komentarz:

  1. Witam. Śliczny i bardzo kreatywny blog osoby z pasją! <3
    Zaprosiłabym Cię na swojego bloga, ale nie prowadzę bloga
    tylko instagrama na którego chciałabym Cię z miłą chęcią zaprosić! :)
    Chętnie się również odwdzięczam.
    mój ig: janaanastazja
    link: https://www.instagram.com/janaanastazja/

    OdpowiedzUsuń