Client-Cert Authentifizierung

Um die Inhalte einer Website vor unauthorisierten Benutzern zu schützen, installiert man gerne eine Passwortabfrage. In den meisten Fällen reicht das auch aus. In manchen Fällen ist die Website aber derart schützenswert, dass eine weitere Stufe des Zugriffsschutzes hinzugenommen werden muss.

Dazu eignet sich die Client-Zertifikatsauthentifizierung ziemlich gut. Für den Zugang zum Webserver benötigt man also nicht nur Benutzername und Passwort, sondern muss dazu auch noch ein Clientzertifikat haben. Ohne Zertifikat nützen BN/PW herzlich wenig, denn man gelangt nicht einmal so weit sie einzusetzen.

 

Wie funktioniert das?

Der SSL/TLS-verschlüsselte Webserver aktiviert mit einer weiteren Direktive einen Mechanismus, der dafür sorgt, dass bei Kontakt zum Webserver der Client sich per Zertifikat authentifizieren muss. Dafür muss ein Clientzertifikat im Zertifikatspeicher des Clients bzw. in dessen Browser (bpsw. Firefox) installiert werden. Bei Abfrage durch den Webserver (Apache2) wird der Client sein Zertifikat zum Server senden. Dieser nimmt einen Vergleich vor und erlaubt oder verweigert dem Client den Zugriff auf die Website. Somit schafft der Website-User es nicht mal auf die Login-Seite, da im Vorfeld jede Kommunikation verweigert wird.

 

Voraussetzungen

Es muss ein Zertifikat auf dem Webserver installiert sein und ein Clientzertifikat, basierend auf dem Serverzertifikat, ausgestellt und auf dem Client installiert werden.

 

Serverzertifikat erstellen

Beginnen wir damit den Private-Key für den Server zu erzeugen. Das erreichen wir mit folgender Zeile. Der Key hat eine Schlüssellänge von 4096 Bit und wird mit dem Dateinamen server.priv.key gespeichert.

openssl genrsa -out server.priv.key 4096

Danach erstellen wir das Certificate Signing Request.

openssl req -new -key server.priv.key -out server.csr

Es ist völlig ausreichen, wenn man am Ende den Common Name (FQDN) korrekt ausfüllt. Danach kann man ganz einfach den Key selbst signieren, um ein gültiges Serverzertifikat zu erhalten. Das geht so:

openssl x509 -in server.csr -out server.crt  -req -signkey \
server.priv.key -days 365

Das Zertifikat ist 365 Tage gültig und wird als server.crt abgespeichert. Die Dateien server.crt und server.priv.key in ein für den Apache2 zugänglichen Pfad abgelegt werden. Ich habe mich für /etc/apache2/ssl/ entschieden und lege sie dort ab. In die Konfiguration für den vHost trage ich nun den Anteil für SSL ein.

<VirtualHost *:443>
    ...
         ServerName mytest.lokal
    SSLEngine on
    SSLCertificateKeyFile /etc/apache2/ssl/server.priv.key
    SSLCertificateFile /etc/apache2/ssl/server.crt
    ...
</VirtualHost>

Nun den Apache mit  systemctl restart apache2.service neu starten und die Website sollte per HTTPS erreichbar sein.

 

Benutzer- bzw. Clientzertifikat erstellen

Jetzt brauchen wir noch das Zertifikat für den Client. Damit wir uns das Zertifikat gescheit ausstellen können, konfigurieren wir uns openSSL ein bisschen. Dazu führen wir folgende Zeile aus:

sed -i 's/= .\/demoCA/= \/root\/ca/g' /etc/ssl/openssl.cnf

Alternativ kann man auch in der /etc/ssl/openssl.cnf in Zeile 42 den Pfad per Hand ändern. Danach legen wir noch die notwendigen Verzeichnisse und Dateien an.

mkdir /root/ca
cd /root/ca
mkdir certs crl csr newcerts private
touch index.txt
echo 1000 > serial

 

Ab jetzt können wir endlich mit dem Benutzer-Zertifikat beginnen. Einfach die folgenden Kommandos der Reihe nach ausführen:

openssl genrsa -des3 -out user_1_cert.key
openssl req -new -key user_1_cert.key -out user_1_cert.req
openssl ca -cert /etc/apache2/ssl/server.crt -keyfile \
/etc/apache2/ssl/server.priv.key -out user_1_cert.crt \
-in user_1_cert.req

Jetzt haben wir das Benutzerzertifikat erzeugt, aber es geht so noch nicht zu verwenden. Wir müssen es noch in ein Format bringen, welches die Browser verstehen und importieren können. Dazu nutzt man üblicherweise das p12-Format. Folgende Zeile erzeugt uns was wir brauchen. Außerdem wird ein Export-Passwort abgefragt. Wir wählen natürlich ein vernünftiges nach dem üblichen Regeln der Kunst, um kein zu lasches Passwort zu erhalten. Dazu gehören 12 Zeichen Länge, inbegriffen Sonderzeichen, Groß- und Kleinschreibung sowie Zahlen.

openssl pkcs12 -export -inkey user_1_cert.key \
-name "User_1" -in user_1_cert.crt \
-certfile /etc/apache2/ssl/server.crt -out user_1_cert.p12

Die Datei user_1_cert_p12 muss nun dem entsprechenden Benutzer, der die Website besuchen können soll, zur Verfügung gestellt werden samt dem dazugehörigen Export-Passwort. Ein Import ohne Passwort ist nicht möglich!

 

Apache2 Cert-Auth aktivieren

Damit der Apache2 die Zertifikatsauthentifikation nutzt, ist eine Direktive in der .conf des vHosts zu setzen. Direkt unter dem Teil wo das SSL-Zertifikat eingebunden ist kann auch gleich folgendes hinzugefügt werden (blau markiert):

<VirtualHost *:443>
    ...
         ServerName mytest.lokal
    SSLEngine on
    SSLCertificateKeyFile /etc/apache2/ssl/server.priv.key
    SSLCertificateFile /etc/apache2/ssl/server.crt
    SSLCACertificateFile /etc/apache2/ssl/server.crt
    SSLVerifyClient require
    SSLVerifyDepth 1
    SSLOptions +StdEnvVars
    ...
</VirtualHost>

Nun den Apache neu starten. Beim Versuch die Website zu erreichen erhält man nun folgende Fehlermeldung mit dem Firefox:

 

Zertifikat importieren

Firefox Browser

Unter Firefox funktioniert der Import ganz leicht unter -> Einstellungen -> Datenschutz & Sicherheit -> Zertifikate anzeigen. Im folgenden Fenster auf den Kartenreiter "Ihre Zertifikate" und danach "Importieren..." klicken. Danach die Website erneut aufrufen nun erhält man aber eine Identifikationsanfrage. Diese bestätigt man dann mit "ok" und schon kann man die Website sehen.

 

Windowszertifikatspeicher

Für den Internetexplorer sowie Google Chrome muss man das Zertifikat in den Zertifikatsspeicher von Windows laden. Der einfachste Weg ist das Zertifikat mit rechter Maustaste anzuklicken und "PFX installieren" zu wählen. Dann öffnet sich ein Fenster. Die restlichen Schritte sind selbsterklärend.

 

Fehlermeldung für Clients ohne Zertifikat

Wie bereits oben im Abbild gezeigt und auch erklärt, erhält ein Client ohne Zertifikat keinen Zugriff auf die Website. Stattdessen gibt es diese Fehlermeldung wie oben gezeigt. Dem einfachen Benutzer sagt sowas nichts und weiß sich dann auch nicht zu helfen. Es wäre nicht schlecht, wenn  diese Clients dann eine Information bekämen mit der sie mehr anfangen können. Zum Beispiel ein Hinweis, dass Sie vom Administrator bzw. Website-Betreiber ein Zertifikat benötigen und sich an ihn wenden sollen.

Ist das eine wirklich so schlaue Idee?

Das liegt im Auge des Betrachters. Mir persönlich würde es missfallen. Warum? Schauen wir uns die Kommunikation mal genauer an:

HTTPS ist nichts weiter als HTTP getunnelt. Mehr nicht, aber auch nicht weniger. Die zertifikatsbasierte Clientauthentifizierung prüft beim Aufbau der Verbindung, ob der Client ein gültiges Zertifikat hat. Hat er das nicht, so gibt es erst gar keine Kommunikation. Es wird kein Tunnel aufgebaut und folglich auch kein HTTP.

Möchte ich aber eine Infopage haben, dann muss ich irgendwie ja doch den Zugriff erlauben und sei es nur für diese eine html-Seite. Das bedeutet also: Ich weiche das Konzept auf, um eine Seite anzuzeigen, die ich eigentlich nicht brauche.

 

Konfiguration der Fehlerseite

Innerhalb des vHosts, also zwischen <VirtualHost *:443> und </VirtualHost> muss noch eine die Directory-Direktive stehen und SSLVerifyClient muss von required zu optional geändert werden. Und natürlich muss nun eine .html-Datei hinterlegt werden worin die Fehlermeldung hinterlegt ist, um den Benutzer ohne Zertifikat zu informieren. In diesem Beispiel habe ich sie error.html genannt.

Alles in allem müsste die vHost-Konfiguration dann so ausschauen:

<VirtualHost *:443>
  ServerName pki.silvesterlangen.de
  DocumentRoot /var/www/html/

  SSLEngine on
  SSLCertificateFile /etc/apache2/ssl/server.crt
  SSLCertificateKeyFile /etc/apache2/ssl/server.priv.key
  SSLCACertificateFile /etc/apache2/ssl/server.crt
  SSLVerifyClient optional
  SSLVerifyDepth 1
  SSLOptions +StdEnvVars

  <directory "/var/www/html">
        RewriteEngine on
        RewriteCond %{SSL:SSL_CLIENT_VERIFY} !^SUCCESS$
        RewriteRule .* error.html [L]
  </directory>
</VirtualHost>

<VirtualHost *:80>
   ServerName pki.silvesterlangen.de
   Redirect permanent / https://192.168.2.67/
</VirtualHost>

Den Redirect für Port 80 auf HTTPS habe ich hier mit dazugepackt.