TLS/SSL für Heimwerker
Gastartikel von Oliver Sperke.
Ich bin 34 Jahre alt und seit 10 Jahren selbständiger Webentwickler. Mein Fokus liegt dabei auf der Erstellung, Beratung und Optimierung in den Bereichen High Performance, Usability und Sicherheit in den gängigsten Internetsprachen: PHP, HTML, Javascript und CSS.
In den vorhergehenden Beiträgen habe ich ja schon locker über Passwörter und wie man mit Ihnen umgehen sollte erzählt. Dabei wurde auch immer wieder mal angemerkt, dass meistens nicht der Login, sondern die Übertragung dieser Daten das eigentliche Problem wäre. „Man in the middle“, „Sniffer“ und der ganze … Mist. Das beste Mittel gegen viele Sicherheitsprobleme ist sicher SSL oder neu TLS, nur leider ist das für manche Webmaster immer noch nicht einsetzbar. Warum weiß ich persönlich nicht, denn eine zusätzliche IP, die evtl. benötigt wird, liegt bei etwa 1 Euro/Monat und einfache Shared Zertifikate, die meistens vollkommen ausreichen gibt es kostenlos.
Wer aus welchen Gründen auch immer kein TLS einsetzen kann oder will, sollte sich mit anderen Mitteln helfen. Das Ziel muss sein, alle wirklich sensiblen Daten (E-Mail, Passwörter, etc) so zu übertragen, dass sie auf dem Transport nicht gelesen werden können. Das ist schwierig, denn spätestens unser Server sollte es lesen können. Eine Idee ist, die Passwörter mit Einweghashes zu bearbeiten. Da unsere Passwörter aber natürlich sicher verstaut sind, können wir mit dem ankommendem Passwort nichts anfangen. Selbst wenn wir diesen Mechanismus in Javascript komplett nachbauen, wie kriegen wir den geheimen systemweiten Salt rein? Ausserdem gäben wir einem potentiellem „schlimmen Finger“ wichtige Informationen, was im Jahr 2011 keine gute Idee ist. Eine symmetrische Verschlüsselung kommt ebenfalls nicht in Frage, weil wir müssten mindestens einmal den Schlüssel übertragen, was das Verfahren überflüssig macht.
Die einzige sichere Möglichkeit ist das Verfahren, dass auch TLS benutzt. Asymmetrische Verschlüsselung! Da uns per Aufgabenstellung kein TLS zur Verfügung steht, ist unser Ziel, dieses Verfahren möglichst sicher zu kopieren – TLS für Heimwerker. Auch dieses Verfahren ist nicht ganz einfach, denn man muss zusätzliche Software installieren (können). Ich versuche den Aufwand und die Erklärungen aber so einfach wie möglich zu halten. Ein wirklicher Vorteil: Einmal eingerichtet ist es auf unbegrenzt viele Domains auf einem Server anwendbar, völlig unabhängig von der IP. Ein Anwendungszweck, der mir spontan einfällt sind Blog- oder Forenanbieter, wo die Anschaffung von Wildcardzertifikaten zu teuer wäre.
DEMO | DOWNLOAD |
Diese Anleitung ist sehr lang, aber keine Panik. Es sind sehr viele Ausgaben dabei. Auch wenig erfahrene Benutzer sollten alles hin bekommen. Ihr braucht etwa 30 Minuten Zeit und ein paar Grundkenntnisse auf der Linuxkonsole. Falls Euch „Programme installieren“ und „Textdateien bearbeiten“ nicht überfordern, könnt Ihr sofort loslegen. Trotzdem empfehle ich, den Text einmal ganz zu lesen und erst dann anzufangen und was immer Ihr kaputt macht, ist nicht eine Schuld!
Wie funktioniert TLS?
Zunächst einmal müssen wir das Prinzip von Öffentlichen und Privaten Schlüsseln verstehen. Da stellen ma uns ma janz dumm Das ganze System kann man sich vorstellen wie ein Briefkasten. Jeder, der die Adresse (also den öffentlichen Schlüssel/Publickey) kennt, kann etwas einwerfen, aber nur der Empfänger kann den Briefkasten mit seinem Briefkastenschlüssel (privater Schlüssel/Privatekey) öffnen und die Post lesen. Nach diesem Prinzip funktioniert z. B. das sichere Versenden von E-Mails seit Jahren. Jeder, der meinen öffentlichen Schlüssel kennt, kann mir „hübsche Kryptopäckchen“ packen, die aber nur ich auspacken kann.
Beim Verschlüsseln von Webseiten über TLS kommt noch eine weitere wichtige Komponente hinzu und das ist die digitale Signatur eines Schlüssels. Eine dritte unabhängige Stelle „signiert“ den öffentlichen Schlüssel und bestätigt damit „meine Adresse“. Auf mein Beispiel übertragen: Vor meinem Briefkasten ruft man als Besucher erst noch mal bei der Hotline an und fragt nach, ob die Adresse auch noch richtig ist oder ob der Inhaber den Schlüssel als Verloren gemeldet hat oder ob es andere Gründe gibt, dort nichts mehr einzuwerfen.
Um eine verschlüsselte Verbindung herzustellen sendet ein Besucher Informationen mit meinem (bestätigtem) Publickey zum Server, der antwortet und bestätigen damit zusätzlich, dass er es selbst sind. Der Browser des Besuchers kann jetzt einen zufälligen Schlüssel erzeugen und dem Webserver zusenden. Der Webserver bestätigt diesen Schlüssel (Handshake) und wir können dann sicher (symmetrisch) kommunizieren. Das ist zwar aufwendig, aber es bietet Sicherheit, ohne am eigentlichen Protokoll rum schrauben zu müssen.
Wie wir das nutzen können
In unserem Fall geht es nur darum, Informationen vom Besucher zum Webserver zu schicken. Ein Handshake und eine dauerhafte verschlüsselte Verbindung ist also nicht nötig (wenn auch möglich). Dementsprechend würde es in unserem Fall ausreichen, statt eines Handshakes direkt die Daten mit dem öffentlichem Schlüssel geschützt zum Webserver zu übertragen. Da nur der Webserver über den privaten Schlüssel verfügt, kann niemand, der die Verbindung belauscht mit den Informationen etwas anfangen.
Die Basisarbeiten
Um eine Verschlüsselung mit Schlüsselpaaren zu realisieren, wäre es natürlich sinnig, erst mal so ein Schlüsselpaar zu haben. Bei der Umsetzung habe ich mich hier für gnupg entschieden. Dieses Verfahren ist schon lange im Einsatz und nebenbei, wenn man seinen Webserver entsprechend eingerichtet hat, kann man auch problemlos verschlüsselte E-Mails senden und empfangen. Eine Möglichkeit, die ich bei aktuellen Webprojekten bisher leider sehr selten sehe.
Wir melden uns also zunächst einmal auf unserem Server an und zwar als der Benutzer, der auch die PHP Prozesse bearbeitet und in der gleichen Umgebung. Da ich Webserver und PHP gerne in einer chroot laufen habe, melde ich mich also am Server an und wechsele auf das Rootverzeichnis und den Benutzer. Wenn Ihr keine chroot habt und root den Apache steuert, dann meldet Ihr Euch einfach als root an und sagt es niemandem.
Ich gehe hier einfach mal von Debian/Ubuntu aus. Andere Betriebssysteme funktionieren ähnlich. Lediglich die Pfade und Programme könnten anders heißen.
chroot /var/jail su webserver
Als nächstes benötigen wir gnupg, falls es noch nicht installiert ist.
sudo apt-get install gnupg
Danach erzeugen wir unser Schlüsselpaar und hangeln uns durch den Einstellungsassistenten.
$ gpg --gen-key
Ich habe hier RSA mit 4096 Bit gewählt, weil es die beste Sicherheit bietet. Der Name und die E-Mail spielen keine Rolle. Die Berechnung kann einige Sekunden bis Minuten dauern.
gpg (GnuPG) 1.4.10; Copyright (C) 2008 Free Software Foundation, Inc. This is free software: you are free to change and redistribute it. There is NO WARRANTY, to the extent permitted by law. Bitte wählen Sie, welche Art von Schlüssel Sie möchten: (1) RSA und RSA (voreingestellt) (2) DSA und Elgamal (3) DSA (nur unterschreiben/beglaubigen) (4) RSA (nur signieren/beglaubigen) Ihre Auswahl? 1 RSA-Schlüssel können zwischen 1024 und 4096 Bit lang sein. Welche Schlüssellänge wünschen Sie? (2048) 4096 Die verlangte Schlüssellänge beträgt 4096 Bit Bitte wählen Sie, wie lange der Schlüssel gültig bleiben soll. 0 = Schlüssel verfällt nie = Schlüssel verfällt nach n Tagen w = Schlüssel verfällt nach n Wochen m = Schlüssel verfällt nach n Monaten y = Schlüssel verfällt nach n Jahren Wie lange bleibt der Schlüssel gültig? (0) Schlüssel verfällt nie Ist dies richtig? (j/N) j Sie benötigen eine User-ID, um Ihren Schlüssel eindeutig zu machen; das Programm baut diese User-ID aus Ihrem echten Namen, einem Kommentar und Ihrer Email-Adresse in dieser Form auf: "Heinrich Heine (Der Dichter) " Ihr Name ("Vorname Nachname"): MeinWebserver Email-Adresse: Kommentar: Sie haben diese User-ID gewählt: "MeinWebserver" Ändern: (N)ame, (K)ommentar, (E)-Mail oder (F)ertig/(B)eenden? f Sie benötigen eine Passphrase, um den geheimen Schlüssel zu schützen. Wir müssen eine ganze Menge Zufallswerte erzeugen. Sie können dies unterstützen, indem Sie z.B. in einem anderen Fenster/Konsole irgendetwas tippen, die Maus verwenden oder irgendwelche anderen Programme benutzen. gpg: Schlüssel 09C9929C ist als uneingeschränkt vertrauenswürdig gekennzeichnet Öffentlichen und geheimen Schlüssel erzeugt und signiert. gpg: "Trust-DB" wird überprüft gpg: 3 marginal-needed, 1 complete-needed, PGP Vertrauensmodell gpg: Tiefe: 0 gültig: 1 unterschrieben: 0 Vertrauen: 0-, 0q, 0n, 0m, 0f, 1u pub 4096R/09C9929C 2011-07-19 Schl.-Fingerabdruck = EB51 FBD8 E2A1 081D 40B9 4D74 448C C464 09C9 929C uid MeinWebserver sub 4096R/E606E242 2011-07-19
Für die weitere Bearbeitung sollten wir uns zwei Zeilen merken. In der ersten steht die KeyID des Schlüssels (09C9929C) und in der nächsten der Fingerabdruck. Die beiden Werte können wir in eine leere Textdatei kopieren (oder anders sichern), weil wir sie später noch brauchen.
pub 4096R/09C9929C 2011-07-19 Schl.-Fingerabdruck = EB51 FBD8 E2A1 081D 40B9 4D74 448C C464 09C9 929C
Als nächstes benötigen wir unseren öffentlichen Schlüssel, mit dem der Besucher später die Daten verschlüsseln kann.
$ gpg -a --export 09C9929C > key.pub
Es wird eine Datei erzeugt mit Inhalt, der diesem ähnlich sein sollte.
-----BEGIN PGP PUBLIC KEY BLOCK----- Version: GnuPG v1.4.10 (GNU/Linux) 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 SJKoLAOV684mC+q9NwmGHqxncljH7r7Gbz/+sg== =UM3f -----END PGP PUBLIC KEY BLOCK-----
PHP spricht „gnupgäisch“
Unser Server kann jetzt gnupg, also wird es Zeit, das auch PHP beizubringen. Dafür gibt es eine spezielle Erweiterung, die uns qualvolle Hacks mit system() oder Schlimmeres (gibt es das?) erspart. Um diese Erweiterung zu installieren, müssen wir zunächst einmal PEAR und einige Basistools installieren. Danach installieren wir unsere PHP Erweiterung mit PEAR, das für uns das heruntergeladen, kompilieren und installieren übernimmt.
sudo apt-get install build-essentials gnupg php-pear sudo pecl install gnupg
Im letzten Schritt müssen wir dann noch PHP selbst sagen, dass es da eine neue Erweiterung gibt. Dafür können wir entweder die php.ini selbst bearbeiten und die Zeile extension=gnupg.so
hinzufügen oder entsprechende ini Dateien anlegen. Kopiert bitte nur die Befehle, die Ihr wirklich braucht. Ich habe jetzt einfach mal die üblichsten Möglichkeiten aufgeschrieben.
echo "extension=gnupg.so" > /etc/php5/cli/conf.d/gnupg.ini echo "extension=gnupg.so" > /etc/php5/cgi/conf.d/gnupg.ini echo "extension=gnupg.so" > /etc/php5/fpm/conf.d/gnupg.ini echo "extension=gnupg.so" > /etc/php5/conf.d/gnupg.ini
Damit die Änderungen wirksam werden, müssen wir jetzt entweder den Apache/PHP-FPM neu starten oder alle PHP Prozesse beenden (z. B. mit dem Holzhammer killall). Mit php -m | grep gnupg
könnt Ihr testen, ob das Modul geladen wurde.
Erster Funktionstest
Um zu testen, ob alles funktioniert legen wir zunächst eine Testdatei über die Konsole an und versuchen sie dann mit PHP zu entschlüsseln. Die KeyID müsst Ihr natürlich anpassen.
$ echo "Testnachricht" | gpg -a --encrypt -r 09C9929C > encrypted.gpg
Dann erzeugen wir eine neue Datei test.php
z. B. mit
nano test.php
und füllen Sie mit diesem Inhalt. Ich habe hier mal die Zeilen kommentiert, das mache ich später nicht mehr ausführlich, um es übersichtlich zu halten.
<?php // gnupg initialisieren putenv("GNUPGHOME=/home/webserver/.gnupg"); $res = gnupg_init(); // Fingerabdruck ohne Leerzeichen und Passwort ändern! gnupg_adddecryptkey($res, "EB51FBD8E2A1081D40B94D74448CC46409C9929C", "DeinPasswort"); // Nachricht entschlüsseln und ausgeben echo trim(gnupg_decrypt($res, file_get_contents('encrypted.gpg'))); ?>
Ein Aufruf von php test.php
sollte jetzt „Testnachricht“ ausgeben. Wenn Ihr hier angekommen seid, habt Ihr es im Prinzip geschafft, denn der Rest ist nur noch etwas HTML und Javascript.
php test.php
Unser Loginformular
Um Daten vom Besucher zum Webserver zu bringen benötigen wir ein einfaches Formular. Dieses kann so aussehen, kann aber natürlich auch andere Daten enthalten oder anderen Zwecken dienen (Anmeldung, etc.). Das einzige „Besondere“ ist der Aufruf onsubmit, der die Daten später verschlüsseln wird. Die Variable „this“ steht hier für das Formular, das abgeschickt werden soll.
<form method="post" onsubmit="encrypt_form(this)"> <label for="user">Benutzer:</label> <input id="user"type="text" name="user" value="" /> <label for="pass">Passwort:</label> <input id="pass" type="password" name="pass" value="" /> <input type="submit" value="Login" /> </form>
Die Post verschicken
Beim Javascript habe ich mich für die wunderbare Lösung von Dr. Herbert Hanewinkel entschieden, weil sie einfach einzusetzen ist und alles mitbringt, was wir benötigen. Alle Scripte stehen unter GPL Lizenz. Meine Erweiterungen stehen unter WTFPL. Die einzelnen Teile zu erklären, würde sicher den Rahmen. Seine Scripte habe ich nur leicht überarbeitet, damit sie nach dem Google Closure Compiler noch funktionieren. So erhalten wir eine Datei, die wir einfach in unsere Seite einfügen können. Die Copyrighthinweise befinden sich im header, der gnupg.js.
Um die Scripte zu nutzen, kopiert Ihr zunächst die gnupg.js in den Ordner, indem auch das Formular liegt. und fügt diesen Schnipsel in Eure Seite ein. Der Publickey in Zeile 14 ist unser Publickey von oben (key.pub) mit base64_encode() bearbeitet. Ihr könnt den Schlüssel auch mit base64_encode(file_get_contents('key.pub'))
lesen und ausgeben. So hab ich das auch in der Demo gemacht.
<script type="text/javascript" src="gnupg.js"></script> <script type="text/javascript"> function encrypt_form(form) { var fields=form.getElementsByTagName("input"); var data = ''; for(var i=0;i<fields.length;i++) { if(fields[i].name!='') { data+=fields[i].name+'='+encodeURIComponent(fields[i].value)+"&"; fields[i].value=""; } } var pu=new getPublicKey(r2s("LS0tLS1CRUdJTiBQR1AgUFVCTElDIEtFWSBCTE9DSy0tLS0tClZlcnNpb246IEdudVBHIHYxLjQuMTAgKEdOVS9MaW51eCkKCm1RSU5CRTRrMUZBQkVBQ1l2STZKZFcwSWZCRVhuNzAzcURCRzVjcE81a3loM05Yd05jSThwaUwrSm9MV1J1QTYKQ3lkcUFuc2x2eGRBQ1ZFZkc2NDVlY2FCbWpaZ0szdCt0NVZFam4yM3c2WlRpU3RKWVhxVjk0VVVueUw3aDFoYwp6U1AxOGJVNWJuRjJtcjZ5QlZMQ0FFM09VSjE2UHdUczhKMU9ObTJZZW40VTE3QUpJV2NGU0owdjN3SlUyNmtMClVzdEY0Mm81UDVXc1M1R0NWTlZGSnhCQXdIeE9hTHVaOEVVVnZCOEpreGMzVytYVXhHSXViNkZMMXZnazBJVXYKWklxc1lqcElsd1hJWW1QeWthVEJQUFJEc1FPTk9iWnpTVUdGTmxwZnRQWisyZ21TUkpRa2gwdkhWdlFEaEg1UQpoWkZ6d3hod0I5OFo3K2plRkZOK1JJbWZsRmJ3UUlVU1JKNEpwSis0Rzg0YkdMQUp5R1pCTllJelBqdXlNcEp6Cng4bnFwTlVGYVBKUFJ2S1h6SlVFN0kzQ0h1dlNScFFCMDI2RitNbkRJN21GK294T0V3WGoxeGMvSW1zeDhQS1QKZ3prVERlL3hCN3pnZHF2WTNXTmN1Sm1UcXlsTjVjUEhSRDRYazBvNmRBQlM0V1dQUVBZcnV1Q2tnM3J6cWt1NwpQZGl1WHJGT0NOT1B6TlVIMzlPeDVtaTZlWm1saU1tWEF1ZFRzd3Q4T1JtTU13bWQ2WmhlOSs0UkJ2K1FnZkRWCkk1TUZZMS82T3I3Q3Z4NDQ4dFZ6NUt0YmxLYWgrT3duQnJIZVFJYjMxNTVLZUxzSEliNXd0U2M5cHBIMW1ETTEKL29QUFAxcFBJaG1qRGZXVTRDZlkzbVJsbWdqZVlITWZ5ams5cityK1ZsTTEwaWdrL05lcHhjeENrUUFSQVFBQgp0QTFOWldsdVYyVmljMlZ5ZG1WeWlRSTRCQk1CQWdBaUJRSk9KTlJRQWhzREJnc0pDQWNEQWdZVkNBSUpDZ3NFCkZnSURBUUllQVFJWGdBQUtDUkJFak1Sa0NjbVNuTk9VRUFDWW01b1JXTTZBbThFRytSM3VENGJ6TWpyZm1SOFAKemViWHM5K3p6Z1JjT3cvdEpRYjE0L1AxOWUvN1owcXBYaWxQaVYvRHR4YnFqaExEUTNwc2p1NlRxMG5kc1B3KwpqbDhRVGY5T090L2hDOXk2VlNTazhMdjJ4T2VBZ0NUQ1Z1b0E3YS9vWERyUGtTcWxoMmQrS0M4R1NaNGlobHY4CmpXcThBeEYzN3NySVRPZHh6SDk3KzNIcXpMQ1VWUmJCMkRvbzJOeHdsZWVmMXNwMHJRalIwU3J6cU1rRWRGZ0kKd1doazFWNEZJUndTb3hDSkJITHpJNkw0RTJsUXpDTDEvaFlpdWdINkVCNDJtUXloS1d0QWZoQzdvUDlpRU82MQo1ZWlBdTRPRkx2dzlKcnY1TWpuTktldUwwZ1pGMXd3QXlLNXVMWFBCaTQxN3VGWW5JbEg1RVlXdXk3L3l0MWppCnhHTDZtTiszTVYrZzg4SEptWUQrcUpzSDhnelpWdkNvVVd4UkN6cG5KazZHbGcvZWtDaVNDdjc0V0RjQjZZcFUKOUxxQm1UZEIvWFdzYmI5b1U4VC80dURmbGhhWklnY3p0MkxFWXdTalpIOEJRTnFlRm9yVEYyTXdOYStaV3AyMgpFZnAvYnR5NmdKMjgvTWRXMXphTnRLd1BFT3p6bmM4cEtIb0lwazF6alJUSFZIU3dIb1p1Z2ZrNVBLYlNBQ1pvCnJUZWI3N3ZoYVR1MnVxSkc1K2ZIWFVWeDcrMlpYMW92VG5RT3dUMmx4Q0daUkEyd1pheTFid1o1bzlXZkdpdXQKUW5hbkJqNzJUai9uNjZtMmxjTkZQVTRMWHMyeEY4TFg5bTQzeEZ6QStqbGJPd0dQaU0zYmZyeHpMZkZlUkI3aApUY2F4YnJ4UmdCbis4cmtDRFFST0pOUlFBUkFBMDVNZTBKcWxzKy8xWTAzSE02NUtuQ3JqU3cvUGVrbVZRU1ZFCnEvcUFKK3BPbDQ0RTVINzJ0YXZYWEhLVkZidjd3d2lRVHRjbmNDdDBJVTVxcUtHTUZIdXBiZzE3T3EraitmMTEKMGYrakdsSGRWVi9sd1h5S051b1lPcHNjV0Y2cGtDUTdYK1dpTjQ5RDdPOXFUL01ocUI5Q2hDUzBMblV5cmJ1Ygo3bWRFNUlQeXhlQjlVcCsvbzNWSVVMRHoreDFaWGZaVjA2OENCQTRWNUtpSXdnN1ExNFBpdGZOVjVxMkQ4bTBUClJFemFGVWxSUmdMRGRmOXNOcXpzWTUyamNhNG4reDRVSlBocTZ4WEhRV1d0ZlBQS3JPTmRCOVpjV0hka2poUWkKdEEveitwU0ZyTllOd2Z6SGNBU2l2b1lXczAyQ3Q5T0hvUk5ReFIrL25YUjBGNTRVSjZCUklyaTdzRU52bHBJVQoxeWpkUFVuZXhZQ3NNemx1bDdReFVhRTRqUTQ1MDZGWnR5eUpXMXZES3MxdStTb0swVEwzRGpTR3QwNjcxU1kzCmUvWjlJVWRZVFZNYjBWR1ByTDBpcFUvT0tTaUhYRjByOWp4b2dwWGp4TTRmWFhHa2FkTE8vQlQyUWRuMFlvVTEKc1Q2WFpKQ2dVZkJJbW44cTFaSkpiVFFuc25HZlFpMFMwdkZ4RFFVSDQ4MzIxTTFZRzZXZ2lsc0piMkVTRldNVApyTklZVDBPZUszZy9VSkcyOGhKNXAxWU5HS2JHZjBNMmlmTFBGUldLM1ZZY3B6dnJyVVFsK0FISmVxWkZWYXQxCjdSNVRzQU1LK1ViL0tnM3Q2SHZuT3Jmd0k4eSttUXBNanJhZEV0Z0xsakRWYUhsTktOcHpxdURmOXB6dSt0UmYKSDlDWVg1MEFFUUVBQVlrQ0h3UVlBUUlBQ1FVQ1RpVFVVQUliREFBS0NSQkVqTVJrQ2NtU25PalFELzlUenhzYgpSNWNLSm9mUWlUcEh1TkpvUmhjSEZnNTcvUU1yMU1BbGlQYy9lVEdqUWV6R1lXbWJzVlNUV1BLTnpYbHJJZkVBClB4eW9IQkJsaXJheUZmeDBFSnFtZ0J3NHdiWld4WUJsOWovK1dpdGJqcnZVNWdnNE9ZVHpyeVpBcU5kaklBMEgKK0VTSzE0T3ZWb2wrODhsa3VGZmxOeDMxdjZmMWJ5ckZmdk9NOFBlNUsyaGp3T1JPcDRjbFlMT3ByQWpONEpmawowWGxWcjFLZ2l3NmdMbHFPdHhiOHhsZTY5VEw0dUdmYTJwcU1zY0F6SGwzMHVXU0lqWFMxcnRza2RMN0EzRXh2CkhRUWlOMDlRSWVsTXB3OTByZG1UUTVabXdCQnN5NWZ3Yy9abHhDNnR1ZzduQU1ZUlBtRDZJemxxN3lSUC9rMk8KTkdnN2EzQWZMQXRib1JYSWxUTHU1ajdIZ0E2bmt1Y3Z3RlNUcUg4WElhWCtSK0sxYTB0WVlGM0x6UEtYS3FRVgpnZEExSHpiRDVCaTY4dXFvRVRqck5IMHZJVWhYZGxPRC9SdHpxancwdHZNVUlKUmxmUC9sY21PeTZMUmF4eEsvCk1zdFV5Q2RWYnpXMDIzay9FZDhRSEMxT0VOOGwwTC85dEFqcjljZ1BWcVB0RzFzUHhScEdOY3BGWUM4cGxLcHgKY2U2dHhRN3hlOFN5d0lFRnRUendqRXM5RENiWUtzUUtjQ3BBSXQvU1hJYTB1Q29BYkN4MnFLVGRPMTFjVHdOUgowdXVCWVVxbUttN3g2Z2pEbGZ5Nmo5Q3d4cnI5c25XMit4citWSFgyOEJ2OHNKR0ZuSTdPSkc0U3MyWWNBalpuClNKS29MQU9WNjg0bUMrcTlOd21HSHF4bmNsakg3cjdHYnovK3NnPT0KPVVNM2YKLS0tLS1FTkQgUEdQIFBVQkxJQyBLRVkgQkxPQ0stLS0tLQo=")); var gpg=document.createElement('input'); gpg.setAttribute('name', 'gpg'); gpg.setAttribute('type', 'hidden'); gpg.setAttribute('value', doEncrypt(pu.keyid,0,pu.pkey.replace(/\n/g,''),data)); form.appendChild(gpg); } </script>
Das Script sieht schlimm kompliziert aus, ist aber eigentlich ganz einfach, denn es liest nur alle input Felder aus unserem Formular und fügt die Werte zu einem String zusammen. Bearbeitete Felder werden geleert. Wenn Ihr selects oder textareas benötigt, müsst Ihr es noch anpassen. Danach wird der String mit dem öffentliche Schlüssel gesichert und ein neues Feld angelegt, indem die verschlüsselte Nachricht hinterlegt wird. Danach ist das Script fertig und das Formular wird abgeschickt.
Die Post ist da
Hier noch eine einfache Möglichkeit, die Daten zu lesen. Das Verfahren kennen wir ja schon aus unserer test.php von oben. Die Werte für Fingerabdruck und Passwort müsst Ihr natürlich wieder ändern. Die entschlüsselte Nachricht findet sich dann in der Variable $post, mit der Ihr ganz normal weiter arbeiten könnt.
Sollte der Benutzer Javascript deaktiviert oder blockiert haben, werden die Daten unverschlüsselt gesendet, für einen Fallback müssten wir dann nicht $post, sondern $_POST benutzen. Da muss jeder für sich abwägen, was ihm wichtig ist und ob er das zulassen möchte. Warnhinweise wären z. B. mit <noscript> über dem Formular möglich.
<?php if(isset($_POST) && !empty($_POST)) { echo '<h3>$_REQUEST</h3>'; print_r($_REQUEST); putenv("GNUPGHOME=/home/webserver/.gnupg"); $res = gnupg_init(); gnupg_adddecryptkey($res, "EB51FBD8E2A1081D40B94D74448CC46409C9929C", "**********"); parse_str(trim(gnupg_decrypt($res, $_POST['gpg'])), $post); echo '<h3>Entschlüsselt</h3>'; print_r($post); } ?>
Was geht nicht?
Die Signierung durch eine externe Stelle ist nicht möglich und damit fehlt eine nicht ganz unwichtige Komponente im Prozess, denn das Verfahren könnte theoretisch kompromittiert werden. Jemand könnte das Zertifikat austauschen und dann die Leitungen abhorchen. Wie wahrscheinlich das ist, muss jeder selbst beurteilen. Wenn ich Zugriff auf einen Server erlangen würde, stände das ganz unten auf meiner Todo-Liste. Mir eingegebene Passwörter irgendwo versteckt an meine russische E-Mail Adresse schicken zu lassen wäre schneller und effizienter. Eine andere Möglichkeit wäre, die DNS so umzubiegen, dass man die Daten unverschlüsselt sendet, aber auch hier braucht man einen wesentlichen Mehraufwand.
Übrigens: Die Sicherheit von TLS hängt nicht hauptsächlich von der Signierung ab, sondern vom Webmaster. Selbstsignierte Zertifikate sind nicht unsicherer, auch wenn der Browser schlimme Warnungen anzeigt. Der einzige Nachteil ist, dass man dieses Zertifikat nicht umfassend widerrufen kann. Wenn sich umgekehrt ein Serverbetreiber nicht um die Sicherheit seiner Zertifikate kümmert, dann nützt auch die grünste Leiste der Welt nichts.
Ein weiterer Schwachpunkt ist, dass das Passwort in der PHP Datei hinterlegt sein muss. Allerdings sind auch Serverzertifikate selten mit einem Passwort versehen, denn dann müsste man es ja beim Start des Webservers eingeben. Von daher relativiert sich auch dieses Risiko. Ein wirklicher Schwachpunkt ist aber, das Cookies nicht mit geschützt werden. Auch da muss man mit zusätzlichen Maßnahmen nachhelfen, entweder innerhalb von PHP oder mit der Suhosin Erweiterung.
Fazit
Daten sicher zu übertragen ist immer sinnvoll, wenn es um sensible Informationen geht. Die beschriebene Methode ist ein sehr einfaches Verfahren, um Informationen sicher zu übertragen. Es ersetzt in keinem Fall TLS! Nichtsdestotrotz bietet es einen sehr guten und sicheren Schutz gegen unerwünschte Abhörmaßnahmen.
Danke für diesen kleinen Einblick, ich glaube ich bookmarke mir das mal; für Server auf denen man kein TLS verwenden kann ist das auf jeden Fall interessant.
Rasioc
25 Jul 11 at 11:17
Sehr coole Erklärung … detaillierte Anleitung. GnuPG für PHP hatte ich bis jetzt noch nicht gehört, klingt aber cool. Müssten wir auch mal Zend_Form beibringen 😉
Denis
26 Jul 11 at 09:39
Unser Ralf hat gar nichts geschrieben – er wollte doch Lösungen haben, wenn irgendwas nicht umsetzbar ist!? Nun, hier ist eine.
Oliver
26 Jul 11 at 12:18
[…] wurde ich vom Tutorial TSL/SSL für Heimwerker von Oliver Sperke und vieles ist auch daraus […]
Formulare verschlüsselt übertragen mit GPG | Never forget ...
16 Aug 13 at 18:35
[…] das so wie hier und hier gemacht. […]
GnuPG Extension - Seite 2 - php.de
12 Dez 13 at 23:25