Certyfikat chroniący wp-login.php i wp-admin/

Spis treści

Jednym z bardziej wyrafinowanych sposobów ochrony zasobów (katalogów) na serwerze www opartym o oprogramowanie Apache2 jest wykorzystanie certyfikatów klienckich. Tego typu zabezpieczenie można jednak zastosować tylko i wyłącznie w przypadku stron, które korzystają z szyfrowania SSL/TLS. Dla przykładu weźmy sobie blog WordPress'a, gdzie mamy katalog wp-admin/ i plik wp-login.php . Formularz logowania oraz panel admina zwykle są szyfrowane. Zatem każdy taki blog powinien robić już użytek z tunelu SSL/TLS w mniejszym lub większym stopniu. Jeśli teraz mamy dość niestandardową instalację WordPress'a, to przy pomocy certyfikatów możemy weryfikować użytkowników, którzy chcą uzyskać dostęp do tych w/w lokalizacji. Jest to nieco inne podejście w stosunku do tego, które zostało opisane w artykule o ukrywaniu wp-login.php oraz wp-admin, gdzie był wykorzystywany moduł mod_rewrite oraz dyrektywy Files i MatchFiles . Takie certyfikaty klienckie dają nam jednak większe pole manewru, bo identyfikują konkretnego użytkownika chcącego uzyskać dostęp do zasobów serwera. Ten wpis ma na celu pokazanie w jaki sposób zaimplementować obsługę certyfikatów klienckich w Apache2.

Centrum certyfikacji i certyfikaty klienckie

W celu zaimplementowania obsługi certyfikatów klienckich w Apache2, musimy przygotować sobie pierw centrum certyfikacji (Certificate Authority). CA możemy utworzyć na kilka sposobów. Najprościej jest wgrać sobie pakiet easy-rsa i wydać w terminalu poniższe polecenie:

# make-cadir /etc/CA/

Edytujemy teraz plik /etc/CA/vars , dostosowujemy w nim poszczególne parametry i tworzymy CA:

# cd /etc/CA/
# source ./vars
# ./clean-all
# ./build-ca

CA mamy już przygotowane. Musimy jeszcze wygenerować certyfikat dla każdego klienta, który ma mieć dostęp do określonych zasobów na serwerze:

# ./build-key-pkcs12 client

Nakładanie restrykcji na wp-login.php i wp-admin/ w Apache2

W katalogu /etc/CA/keys/ zostało utworzonych trochę plików. W konfiguracji Apache2, tj. w pliku /etc/apache2/sites-enabled/default-ssl.conf , musimy podać ścieżkę do ca.crt . Otwieramy zatem ten plik i szukamy linijki z SSLCACertificateFile :

SSLCACertificateFile /etc/CA/keys/ca.crt

Teraz możemy przejść do konfiguracji dostępu do zasobów na serwerze. W tym celu niżej w pliku /etc/apache2/sites-enabled/default-ssl.conf dodajemy poniższy kod:

<Directory "/apache2/wordpress/wp-admin">
#   SSLOptions +StdEnvVars
    SSLVerifyClient optional
    SSLVerifyDepth 1
</Directory>

<Files "wp-login.php">
#   SSLOptions +StdEnvVars
    SSLVerifyClient optional
    SSLVerifyDepth 1
</Files>

Dyrektywa Directory wskazuje nam katalog, na który mają być założone restrykcje. Podobnie sprawa ma się w przypadku dyrektywy Files , z tym, że ta dotyczy plików. Pozycja SSLVerifyClient może przyjąć poniższe wartości:

  • none -- nie jest wymagany żaden certyfikat.
  • optional -- klient może okazać ważny certyfikat.
  • require -- klient musi okazać ważny certyfikat.
  • optional_no_ca -- klient może okazać ważny certyfikat, z tym, że nie musi on być z powodzeniem zweryfikowany przez serwer.

Różnica między optional i require nie jest tak oczywista jak mogłoby się wydawać na pierwszy rzut oka. Można by pomyśleć, że nas będzie interesować jedynie require , no bo przecie chcemy wymusić przedstawienie certyfikatu przez klienta. Okazuje się jednak, że ustawienie którejkolwiek z tych dwóch wartości będzie skutkowało żądaniem od klienta ważnego certyfikatu, który będzie podlegał weryfikacji przez serwer. W przypadku, gdy cert nie zostanie zweryfikowany, to klient nie zostanie dopuszczony do chronionego zasobu. Po co nam zatem dwie wartości skoro robią w grubsza to samo? W przypadku ustawienia optional serwer będzie w stanie przekierować klienta na dowolną stronę zgodnie z regułami w module mod_rewrite . O tym będzie w dalszej części artykułu. Zapisujemy plik i restartujemy Apache2.

Konfiguracja certyfikatu na klientach

Gdybyśmy w tym momencie otworzyli przeglądarkę internetową i spróbowali przejść na blogu do panelu admina (katalog wp-admin/) lub zalogować się (plik wp-login.php ), to naszym oczom powinien ukazać się poniższy komunikat:

wp-admin-wp-login-php-apache2-brak-certyfikat

Serwis jako taki będzie działać bez problemu po SSL/TLS. Niemniej jednak, na obecną chwilę nie uzyskamy dostępu do tych powyższych zasobów. Musimy posiadać certyfikat kliencki, który zostanie zweryfikowany przez serwer. Ten certyfikat kliencki już sobie utworzyliśmy i jest to plik /etc/CA/keys/client.p12 . Musimy go teraz zaimportować w przeglądarce. W tym przypadku zrobimy to na przykładzie Firefox'a. Przechodzimy zatem kolejno do Preferences > Advanced. Następnie na zakładce Certificates klikamy View Certificates. W okienku, które się pojawi, przechodzimy na zakładkę Your Certificates i klikamy w przycisk Import, gdzie podajemy ścieżkę do pliku client.p12 :

firefox-dodawanie-certyfikat

Ponownie odwiedzamy stronę logowania lub panel administracyjny na naszym blogu. Tym razem powinno nam wyskoczyć takie oto okienko:

firefox-potwierdzenie-certyfikat

Klikamy OK i po chwili powinniśmy uzyskać dostęp do chronionych zasobów serwera.

Przekierowanie klientów bez certyfikatów

W przypadku, gdy jakiś nieuprawniony klient będzie próbował uzyskać dostęp do chronionych zasobów, to zostanie mu zwrócony komunikat błędu, który widzieliśmy wyżej. Nie jest to zbytnio eleganckie rozwiązanie i przydałoby się zadbać, aby tacy użytkownicy zostali przekierowani, np. na stronę główną bloga. Musimy zatem nieco przerobić dyrektywy Directory oraz Files :

<Directory "/apache/wordpress/wp-admin">
    #SSLOptions +StdEnvVars
    SSLVerifyClient optional
    SSLVerifyDepth 1

    <IfModule mod_rewrite.c>
        RewriteEngine   on
        RewriteCond %{SSL:SSL_CLIENT_VERIFY} !^SUCCESS$
        RewriteRule ^(.*) https://%{HTTP_HOST}/ [R=301,L]
    </IfModule>

</Directory>

<Files "wp-login.php">
    # SSLOptions +StdEnvVars
    SSLVerifyClient optional
    SSLVerifyDepth 1

    <IfModule mod_rewrite.c>
        RewriteEngine   on
        RewriteCond %{SSL:SSL_CLIENT_VERIFY} !^SUCCESS$
        RewriteRule ^(.*) https://%{HTTP_HOST}/ [R=301,L]
    </IfModule>

</Files>

W obu dyrektywach pojawił się kod z regułami dla modułu mod_rewrite . Mamy w nim jeden warunek, który odnosi się do weryfikacji certyfikatu klienta przez serwer. Jeśli wynik jest inny od SUCCESS , oznacza to, że klient nie został zweryfikowany prawidłowo. W takim przypadku zostanie dopasowania reguła, która przekieruje żądanie na stronę główną.

Mikhail Morfikov avatar
Mikhail Morfikov
Po ponad 10 latach spędzonych z różnej maści linux'ami (Debian/Ubuntu, OpenWRT, Android) mogę śmiało powiedzieć, że nie ma rzeczy niemożliwych i problemów, których nie da się rozwiązać. Jedną umiejętność, którą ludzki umysł musi posiąść, by wybrnąć nawet z tej najbardziej nieprzyjemniej sytuacji, to zdolność logicznego rozumowania.