Schritt für Schritt zur sicheren Web-App: So funktioniert Transportverschlüsselung mit TLS
Wer mit persönlichen Daten hantiert, und sei es nur eine Liste von E-Mail-Adressen für den Newsletter, ist angehalten, diese vor fremdem Zugriff zu schützen. Das verlangt auch das Bundesdatenschutzgesetz. Dabei ist zu unterscheiden, um welche Art von Daten es sich handelt. Zum einen gibt es Daten, die Kontaktpartner innerhalb persönlicher Nachrichten austauschen, zum anderen Nutzerdaten bei Cloud-Diensten.
Point-to-Point contra End-to-End
Egal ob E-Mail oder Instant-Messaging, persönliche Nachrichten dürfen von niemandem außer ihrem Sender und ihrem Empfänger eingesehen werden. Hier eignet sich eine sogenannte Ende-zu-Ende-Verschlüsselung. Das bedeutet, dass Nachrichten vor dem Versand auf dem eigenen Gerät verschlüsselt werden. So ist sichergestellt, dass weder ein Angreifer, der die Internetleitung anzapft, noch der Serverprovider selbst die Nachrichten lesen kann.
Bei Cloud-Diensten stellt der Nutzer im Gegensatz zu persönlichen Nachrichten dem Cloudsoftware-Anbieter seine Daten ganz bewusst zur Verfügung, damit dieser sie verarbeiten kann. Beispiele sind Projektmanagement, Buchhaltung oder Dokumentenverarbeitung in der Cloud – aber auch klassisches Online-Banking. Der Nutzer vertraut hierbei dem Anbieter, dass dieser seine Daten vor dem Zugriff Dritter schützt. Dazu ist es notwendig, die Verbindung zwischen Nutzer und Server über eine sogenannte Transportverschlüsselung (auch Punkt-zu-Punkt-Verschlüsselung genannt) zu sichern.
Sowohl bei der Transport- als auch bei der Ende-zu-Ende-Verschlüsselung kommt ein Public-Key-Verfahren zum Einsatz, bei dem es einen öffentlichen Schlüssel zum Verschlüsseln und einen privaten Schlüssel zum Entschlüsseln gibt. Nur der Empfänger kann die Nachricht mit seinem privaten Schlüssel entschlüsseln. Meist wird RSA als Algorithmus für Publiy-Key-Krypto verwendet. Im Folgenden geht es um die Transportverschlüsselung und deren Implementation.
In der Praxis lässt sich eine Transportverschlüsselung sehr viel leichter einrichten als eine Ende-zu-Ende-Verschlüsselung. Das liegt daran, dass sowohl Sender (Client) als auch Empfänger (Server) beim Aufbau der Verbindung gleichzeitig online sind und die Daten nur einmalig nach dem Transport entschlüsselt werden müssen. So ist das Schlüssel-Management simpel und Schlüssel können nach einer Fehlkonfiguration oder Kompromittierung problemlos ausgetauscht werden.
Auch funktioniert „Perfect Forward Secrecy“ bei Transportverschlüsselungen. Unter Perfect Forward Secrecy versteht man ein kryptographisches Verfahren, das bei jedem Verbindungsaufbau einen neuen, zufälligen Sitzungsschlüssel generiert. So können verschlüsselte Daten, die heute abgefangen werden, auch dann nicht entschlüsselt werden, wenn morgen ein Angreifer den privaten Schlüssel stiehlt.
Transportverschlüsselung bei HTTP
Spricht man von Transportverschlüsselung, ist in der Praxis meist TLS (früher SSL) gemeint. Die weit verbreitete Abkürzung HTTPS meint das Versenden von HTTP-Daten über eine TLS-Verbindung. Vorteile einer TLS-Verbindung: Der Webentwickler muss seine Webanwendung nicht anpassen, wenn diese mittels TLS kommuniziert, der Systemadministrator braucht sich beim Einrichten der TLS-Verbindung keine Gedanken darüber machen, was später einmal versendet werden soll. Allerdings weiß der Server beim Aufbau der TLS-Verbindung nicht, welche Domain aufgerufen werden soll, sodass man in der Regel pro Domain eine eigene IP-Adresse braucht.
Das TLS-Zertifikat
Ein TLS-Zertifikat bestätigt dem Nutzer des Webservices example.com, dass er tatsächlich mit dem Besitzer der Domain example.com kommuniziert und kein sogenannter Man-In-The-Middle-Angriff stattgefunden hat. Erst wenn der Client dies verifiziert hat, findet der weitere Aufbau der TLS-Verbindung statt. Technisch gesehen ist ein Zertifikat der öffentliche Schlüssel des Serverbetreibers mit einer digitalen Unterschrift einer als vertrauenswürdig angesehenen Zertifizierungsstelle (certificate authority, CA).
Um ein Zertifikat zu erstellen, generiert man mit OpenSSL einen privaten RSA-Schlüssel mit 2048 bit Stärke sowie eine Zertifikatsregistrierungsanforderung (CSR).
RSA-Schlüssel generieren
$ mkdir ~/mysslfiles $ cd ~/mysslfiles $ openssl genrsa -out example.com.key 2048
Listing 1
Der Einfachheit halber wird der private Schlüssel im Klartext gespeichert. Besser wäre ein Passwortschutz, den man über den zusätzlichen Parameter „-aes256“ erhält.
CSR erstellen
$ openssl req -new -key example.com.key -out example.com.csr
Listing 2
Es folgt eine Liste an Fragen, deren Antworten im späteren Zertifikat einsehbar sind. Idealerweise decken sich die Angaben mit dem WHOIS-Eintrag der Domain – das schafft Vertrauen. Bei „State or Province Name“ nimmt man in Deutschland das Bundesland und der „Organizational Unit Name“ ist im Zweifel „IT“. In „Common Name“ muss die genaue Domain enthalten sein, die von der Webanwendung benutzt wird. Viele Zertifizierungsstellen generieren dann ein Zertifikat, das für die Domain sowohl mit als auch ohne „www“ funktioniert. Die „extra attributes“ können leer bleiben. Wer das ganze noch einmal überprüfen will, bedient sich folgenden Befehls:
CSR ansehen
$ openssl req -text -in example.com.csr
Listing 3
Mit dieser CSR erhält man bei einer CA seiner Wahl das eigentliche Zertifikat. Für den Anfang reicht oft das kostenfreie StartSSL Free, das von allen gängigen Browsern akzeptiert wird und ein Jahr gültig ist. Die CA spuckt zwei Dateien aus: zum einen das Zertifikat im PEM-Textformat, das man unter „example.com.crt“ speichert, und zum anderen das „Intermediate CA“-Zertifikat von der CA. Bei StartSSL nennt es sich „sub.class1.server.ca.pem“.
Webserver einrichten
Sind alle Schritte durchgeführt, kopiert der Administrator mit dem Befehl rsync alle Dateien über SSH auf den Webserver. Da der Verlust des privaten Schlüssels das Aus der Sicherheit bedeutet, darf an dieser Stelle auf keinen Fall eine unverschlüsselte FTP-Verbindung zum Einsatz kommen.
Dateien übertragen
$ rsync example.com.key user@example.com:/etc/ssl-example.com/ $ rsync example.com.crt user@example.com:/etc/ssl-example.com/ $ rsync sub.class1.server.ca.pem user@example.com:/etc/ssl-example.com/
Listing 4
Apache
Zunächst aktiviert der Admin auf dem Server mit Root-Rechten das mitgelieferte TLS-Modul von Apache. Unter Debian/Ubuntu geht das folgendermaßen:
mod_ssl aktivieren
$ a2enmod ssl
Listing 5
In der Apache-Konfiguration (Debian/Ubuntu: /etc/apache2/ports.conf) sollte ein „Listen 443“ dafür sorgen, dass der Apache auf Port 443 lauscht.
Anschließend sucht er sich seine bestehende VirtualHost-Konfiguration heraus und ergänzt diese um eine analoge TLS-Variante:
VirtualHost-Konfiguration
<VirtualHost *:80> ServerName example.com ServerAlias www.example.com DocumentRoot /var/www/example.com/ # ... </VirtualHost> <VirtualHost *:443> ServerName example.com ServerAlias www.example.com DocumentRoot /var/www/example.com/ # ... SSLEngine on SSLCertificateFile /etc/ssl-example.com/example.com.crt SSLCertificateKeyFile /etc/ssl-example.com/example.com.key SSLCertificateChainFile /etc/ssl-example.com/sub.class1.server.ca.pem </VirtualHost>
Listing 6
Zum Schluss wird der Apache neu gestartet.
Node.js
Bei Node.js ist der Webserver direkt in der Anwendung integriert und wird auch hier konfiguriert. Analog zum Apache sieht das dann folgendermaßen aus:
Node.js
var https = require('https'); var fs = require('fs'); var options = { key: fs.readFileSync('/etc/ssl-example.com/example.com.key'), cert: fs.readFileSync('/etc/ssl-example.com/example.com-combined.crt') }; https.createServer(options, function (req, res) { res.writeHead(200); res.end("hello world\n"); }).listen(443);
Listing 7
Im Gegensatz zu Apache hat Node.js keinen Parameter für das Intermediate-CA-Zertifikat. Daher kopiert der Server-Administrator dieses und das eigene Zertifikat zusammen in eine Datei:
Kombiniertes Zertifikat erstellen
$ cat /etc/ssl-example.com/example.com.crt \ /etc/ssl-example.com/sub.class1.server.ca.pem \sie > /etc/ssl-example.com/example.com-combined.crt
Listing 8
Krypto-Algorithmen spezifizieren
Während des Verbindungsaufbaus handeln Client und Server einen Verschlüsselungsalgorithmus aus, mit dem beide arbeiten können. Doch nicht alle innerhalb von TLS beziehungsweise SSL verwendbaren Algorithmen sind heute noch sicher. Insbesondere das symmetrische Verschlüsselungsverfahren RC4 gilt in Fachkreisen als schwach und es besteht die Vermutung, dass amerikanische Geheimdienste bereits in der Lage sind, RC4 in Echtzeit zu knacken. Auch SSLv2 ist nicht mehr sicher. Außerdem sollte Perfect Forward Secrecy bevorzugt verwendet werden.
Folgende Liste entspricht der Empfehlung „Configuration B“ von BetterCrypto und kann dort kopiert werden.
Krypto-Algorithmen
<VirtualHost *:443> # ... SSLProtocol All -SSLv2 -SSLv3 SSLHonorCipherOrder On SSLCompression off SSLCipherSuite 'EDH+CAMELLIA:EDH+aRSA:EECDH+aRSA+AESGCM:EECDH+aRSA+SHA384:EECDH+aRSA+SHA256:EECDH:+CAMELLIA256:+AES256:+CAMELLIA128:+AES128:+SSLv3:!aNULL:!eNULL:!LOW:!3DES:!MD5:!EXP:!PSK:!DSS:!RC4:!SEED:!ECDSA:CAMELLIA256-SHA:AES256-SHA:CAMELLIA128-SHA:AES128-SHA' </VirtualHost>
Listing 9
Diese Konfiguration wird mit älteren Clients wie dem IE unter Windows XP nicht mehr funktionieren. Ist dies erforderlich, findet sich bei BetterCrypto ein Workaround. Unter Node.js lassen sich die empfohlenen Algorithmen analog vorgeben:
Ciphers in Node.js
var https = require('https'); var fs = require('fs'); var constants = require('constants'); var options = { key: fs.readFileSync('/etc/ssl-example.com/example.com.key'), cert: fs.readFileSync('/etc/ssl-example.com/example.com-combined.crt'), honorCipherOrder: true, ciphers: 'EDH+CAMELLIA:EDH+aRSA:EECDH+aRSA+AESGCM:EECDH+aRSA+SHA384:EECDH+' + 'aRSA+SHA256:EECDH:+CAMELLIA256:+AES256:+CAMELLIA128:+AES128:+SSLv3:!aNULL:!' + 'eNULL:!LOW:!3DES:!MD5:!EXP:!PSK:!DSS:!RC4:!SEED:!ECDSA:CAMELLIA256-SHA:AES256' + '-SHA:CAMELLIA128-SHA:AES128-SHA', secureOptions: constants.SSL_OP_NO_SSLv2 | constants.SSL_OP_NO_SSLv3 };
Listing 10
Perfect Forward Secrecy ist hier erst ab Version 0.11 möglich, wobei Node.js wegen Heartbleed keinesfalls unter Version 0.10.27 beziehungsweise 0.11.13 verwendet werden sollte.
Testen mit dem Tool SSLScan
Der Aufruf der Webanwendung lässt sich nun über https://www.example.com testen. Gegebenenfalls muss in der Firewall zusätzlich noch Port 443 freigeschaltet werden. Je nach CA kann es einige Stunden dauern, bis das Zertifikat über das Online Certificate Status Protocol (OCSP) validiert werden kann. Nicht wundern: Bis das der Fall ist, liefert etwa die Standardkonfiguration des Firefox einen Fehler.
Steht der TLS-Server, kann man mit dem Tool SSLScan alle gängigen Verfahren testen und die Liste mit dem Befehl „grep“ filtern, um herauszufinden, welche vom Server akzeptiert werden.
Akzeptierte Verfahren prüfen
$ sslscan example.com:443 | grep "Accepted"
Listing 11
Detailliertere Informationen zur TLS-Verbindung liefert OpenSSL.
TLS testen
$ openssl s_client -crlf -connect example.com:443
Listing 12
Fazit: Mit Transportverschlüsselung die Web-App sicherer machen
Eine Transportverschlüsselung für die eigenen Web-Applikationen umzusetzen ist kein Hexenwerk und gestaltet sich wesentlich weniger aufwändig als die Implementation einer deutlich komplizierteren Ende-zu-Ende-Verschlüsselung. Das Mehr an Sicherheit macht sich in einem Vertrauensgewinn bei den Nutzern bemerkbar. Wer alle genannten Details beim Einrichten des TLS-Zertifikats für den eigenen Server beherzigt, macht es zudem auch Angreifern wie Geheimdiensten und Co deutlich schwerer, Nutzerdaten abzugreifen und auszuwerten.