Na, Tobias ... Du bist genau der Richtige, der diese Anmerkung bringt!!! Warum? Darum:
Schlemmer / Agricolastr. 14-16, WGS 7109B | cand. math. / 09599 Freiberg
Ich schreibe im Rahmen meiner Dipl. Arbeit an der *TU Freiberg* an einer Teilautomatisierung von CA-Aufgaben mit Hilfe von PHP. Die CA soll PGP und X.509v3 Zertifikate ausstellen und selbst durch die DFN-PCA zertifiziert werden.
Hä? Versteh ich nicht. Gehts auch etwas genauer?
Klar: Ganz kurz: *einfache* Quotes anstatt *doppelter* Quotes helfen, dass kein Zeichen innerhalb der Quotes interpretiert wird.
Klar: Ganz lang: (Sorry, hab gar nicht gemerkt wie lang es wurde) Ich bekomme nach der Prüfung eines Schlüssels und der Nutzeranfrage (CA-Administrator) auf Zertifierung eben dieses Schlüssels die Passphrase für den CA-Key per 'Post'. Diesen gebe ich nun an die Funktion weiter, welche die PGP-Schlüssel zertifiziert:
--- Main --- $pass = HTTP_POST_VARS["pass"]; certify_pgp($sess_data, $pass); --- Main ---
Dort füge ich ihn zum Keyring hinzu (-ka), stelle die Integrität des Keyrings sicher (-kc) und schreite dann zum Heiligen Grahl: der Zertifizierung (-ks). Die Passphrase kann ich übrigens nicht weiter testen, außer dass sie ein string mit max. 255 Zeichen sein muss. (Oder kann ich da noch was machen???) Gestern war dann mein erster Versuch:
--- certify_pgp($data, $pass) --- exec(escapeshellcmd(PGP." +batchmode -z "$pass" -ks "$data[pgp_uid]"), $buffer, $retval); /\ /\ /\ /\
if($retval != 0) { machwas_wegen_fehler(); } --- certify_pgp() ---
Dabei hatte ich die Passphrase und die zu zertifizierende BenutzerID in *doppelte* Quotes gesetzt. Das funkte nicht. Aus $irgenwas sollten Variablen gemacht werden, aus '!!' wurde bangbang, etc.
Ich glaube, Dein Problem liegt bei escapeshellcmd() die solltest Du nicht unbedingt verwenden. Stattdessen solltest Du selbst sicherstellen, dass die Eingabe keine gefährlichen Werte enthält. Vor allem würde ich nicht die gesamte Befehlszeile entschärfen, sondern nur das, wass Du da verwenden willst. Also statt system(escapeshellcmd("gpg -z 'Mikro $oft'")); würde ich
system("gpg -z '".str_replace ("","\",str_replace("'","'",'Mikro $oft'))."' -s ..."); verwenden oder was noch Sichereres wie z.B. addslashes().
addslashes() hat mir (egal ob mit escapeshellcmd() oder ohne) die Passphrase so entstellt, das Sie nicht mehr gültig war. Berichtige mich, wenn ich falsch liege ... aber dein str_replace() gibt mir folgendes:
gpg -z '\'Mikro $oft\'' -s ...
Das führt auch nicht zum Ziel. Anbei: bei GnuPG stellt -z die Kompressions- stufe ein - bei PGP 2.6.3 gibt -z die Passphrase an. Ich muss leider noch das alte PGP verwenden, da ich ja DFN-PCA zertifiziert werden möchte. Diese unterstützt bislang nur V3 Signaturen. GnuPG macht V4. Die Scripte an gpg anzupassen, sollte zu gegebener Zeit aber kein großes Problem darstellen.
Eine ganz geniale Variante wäre natürlich, Du schickst das Passwort über eine Pipe. Man möge mich berichtigen, aber mir kommt das etwas sicherer vor. Da kann mein Mitbenutzer das Passwort nicht so ohne weiteres mittels "ps xa" rauskriegen.
Erstens: der zertifizierende Rechner ist ein Inselsystem. Er verfügt über kein externes Netz. Mitbenutzer in diesem Sinne gibt es also nicht. Zweitens: Dennoch sind gerade CA Passphrases extrem schützenswürdig. (...und sollten auch auf einem Inselsystem in keiner history stehen!!!) Der Gedanke mit der Pipe ist mir auch schon gekommen. Das macht pgp aber nicht mit. (Ich besprach das schon einmal beim vorletzten Treffen mit ... Ich hab den Namen vergessen) :(
PGP will dann richtig interaktiv werden. Eine Lese/Schreibe pipe, die das Ganze auch richtig interaktiv machen würde, gibt es aber nicht. Beispiel:
$pp = popen("pgp -ks $data[pgp_keyid]", "w"); /* write pipe open */ fputs($pp, $pass); /* Pass phrase hinterher */ pclose($pp); /* write pipe close */
gibt im error_log von Apache: cannot open tty, using stdin Unable to get terminal characteristics: ioctl: Inappropriate ioctl for device
Die Passphrase läuft damit total ins Leere. Das Ganze irgendwie umzulenken ist mir auch nicht gelungen. Ausserdem habe ich mit Lutz Donnerhacke gesprochen, der mir auch versicherte, dass es aus 'Religiösen' Gründen auch nicht funktionieren soll. (Man könnte noch Umgebungsvariablen setzen, die die Pass halten. Das will ich aber nicht.)
Ich habe dann gestern noch auf *einfache* Quotes umgestellt: exec(PGP." +batchmode +force -z '$pass' -ks '$data[pgp_uid]'", $buffer, $retval);
Das Funktioniert nun... Naja, so gut wie... PGP gibt mit als return value, trotz erfolgreichen signieren des Schlüssels, kein NULL sondern eine 4. Anfrage bei Lutz Donnerhacke läuft bereits...
Tja, das ist die Ganze Erklärung. Wenn noch Fragen sind beantworte ich die gern. Wenn Ihr irgendwo seht, wo in dieser Konstellation die Passphrase im Klartext auf Platte kommt, so sagt mir bescheid ... und am Besten, wie ich es verhindern kann...
Bis denne, Christian