Die Authorisierung von Anfragen einloggter Nutzer erfolgt in Webanwendungen sehr oft mit Hilfe von Sitzungscookies.
Kann ein Angreifer ein solches Ausspionieren oder erraten, kann er oft die gesamte Sitzung des eingeloggten Nutzers übernehmen und mit seinen Rechten die Webanwendung / Webseite nutzen.
Derartige Angriffe sind in der Vergangenheiten manigfaltig dokumentiert worden. Ein erster Schritt zum Schutz der Sitzungscookies ist die Nutzung von HTTPS.
Die Nutzung von HTTPS reicht jedoch nicht in allen Fällen aus, wie die BEAST, CRIME und BREACH Angriffe zeigen.
Der Übernahme einer Sitzung kann jedoch zusätzlich auch dadurch begegnet werden, dass weitere Voraussetzungen erfüllt sein müssen, um eine Sitzung zu übernehmen.
Dazu gehören:
- Bindung an die IP-Adresse
- Bindung an den User-Agent
Die Bindung an den User-Agent ist jedoch durch einen Angreifer prinzipiell einfach zu fälschen.
Die Bindung an eine IP-Adresse hat folgende Nachteile
- alle Anwendungen auf einem PC oder hinter einem NAT teilen sich diese IP, in diesem Kreis kann also der Angriff nicht verhindert werden und
- bei der Verwendung von temporären Adressen (i.B. IPv6 privacy extension, aber auch Einwahladressen) wird die Sitzung für den regulären Nutzer ungültig, sobald die Adresse gewechselt wird.
Im Besonderen die IPv6 privacy extension Adressen werden mit der zunehmenden Einführung von IPv6 öfter auftreten. Dies kann dadurch mitgiert werden, dass die Sitzung nur an das 64-Bit Prefix der IPv6-Adresse gebunden wird, aber in der Folge auch ein Angreifer im lokalen Subnetz wieder nicht erkannt werden kann.
In diesem Artikel soll daher die Möglichkeit vorgestellt werden, die Sitzung mit HTTPS Client Zertifikaten abzusichern.
Der Ablauf einer Sitzung ist nun wie folgt:
Sitzungsaufbau:
- HTTPS Client Zertifikat erzeugen und im Browser installieren
- Sitzungs-ID an Client-Zertifikat binden.
Sitzungsverwendung: Die Sitzungs-ID kann nur bei der Verwendung des passenden Client-Zertifikates genutzt werden.
Sitzungsende: Die Sitzungs-ID wird ungültig oder deauthentifiziert, das Zertifikat kann weiter verwendet werden.
Zur Erzeugung des Schlüsselpaarres im Browser kann entweder das HTML5 keygen Element (FF/Chrome/Safarie/uvm.) oder Internet Explorer spezifischer Code verwendet. Der Webserver erzeugt sodann nur ein signiertes Zertifikat, welches sodann vom Browser importiert wird. Dadurch braucht noch nichteinmal der PrivateKey zwischen Browser und Webserver übertragen zu werden. Als Bibliothek kann phpseclib verwendet werden.
Für das KeyGen Element kann der öffentliche Schlüssel dabei unter Verwendung der Methode "loadSPKAC" geladen werden. Dazu werden aus dem übertragenen öffentlichen Schlüssel alle Leerzeichen, Sonderzeichen und Zeilenumbrüche entfernt und diesem der String "SPKAC=" voran gestellt, damit der Import klappt. Für den Internet Explorer kann die Methode "loadCSR" genutzt werden.
include(dirname(dirname(__FILE__))."/config.php");
require_once 'Crypt/Random.php';
require_once 'Crypt/RSA.php';
require_once 'File/X509.php';
/**
get and parse request
/
if (isset($_REQUEST["csr2"]) && !empty($_REQUEST["csr2"])) {
// IE
$ie = true;
$csr = $_REQUEST["csr2"];
$csr = "-----BEGIN CERTIFICATE REQUEST-----\n".trim($csr)."\n-----END CERTIFICATE REQUEST-----";
} elseif (!empty($_REQUEST["csr"])) {
// FF+CC
$ie = false;
$spkac = $_REQUEST["csr"];
$spkac = explode("\n", $spkac);
$spkac = array_map('trim',$spkac);
$spkac = "SPKAC=".implode("", $spkac);
} else
die("empty request");
$x509spkac = new File_X509();
if ($ie) {
if (!$x509spkac->loadCSR($csr)) die("unparseable");
} else {
if (!$x509spkac->loadSPKAC($spkac)) die("unparseable");
}
if (!$x509spkac->validateSignature()) die("invalid signature");
$pubkey = $x509spkac->getPublicKey();
// get private/public key for CA cert
if (!is_readable($privkey[0])) die($privkey[0]." is unreadable!");
$CAPrivKey = new Crypt_RSA();
$CAPrivKey->setPassword($privkey[1]);
$CAPrivKey->loadKey(file_get_contents($privkey[0])) or die ("failed to load private key");
if (!is_readable($cacert)) die("$cacert is unreadable!");
$issuer = new File_X509();
$issuer->loadX509(file_get_contents($cacert));
$issuer->setPrivateKey($CAPrivKey);
if (!$issuer->validateSignature(false)) die("invalid issuer signature");
// generate to-be-signed cert
$subject = new File_X509();
$subject->setDNProp('id-at-organizationName', 'MyWebSite');
$subject->setDNProp('id-at-commonName', 'MySiteClient-'.bin2hex(crypt_random_string(8)));
$subject->setPublicKey($pubkey);
// get serial
if (file_exists($serialfile) && !is_readable($serialfile)) die($serialfile." is unreadable!");
if (file_exists($serialfile) && !is_writeable($serialfile)) die($serialfile." is unwriteable!");
$serial = 1;
if (file_exists($serialfile))
$serial = (int) file_get_contents($serialfile);
$serial++;
file_put_contents($serialfile, $serial);
// Read certificate request and validate it.
$x509 = new File_X509();
$x509->setSerialNumber($serial);
$x509->setEndDatedate('D, d M y H:i:s O', strtotime('+1 year')));
$result = $x509->sign($issuer, $subject) or die("failed to sign");
if (!$ie) {
// Chrome needs DER; does not accept PEM
$certout = $x509->saveX509($result, FILE_X509_FORMAT_DER);
} else {
$certout = $x509->saveX509($result, FILE_X509_FORMAT_PEM);
}
$cert = new File_X509();
$cert->loadX509($x509->saveX509($result, FILE_X509_FORMAT_PEM));
$cert->loadCA(file_get_contents($cacert));
if (!$cert->validateSignature()) die("invalid cert signature");
/ now $certout contains certificate in PEM or DER format /
Im Webserver wird eingerichtet, dass für die Startseite die Verwendung des HTTPS Client Zertikates optional ist. Nach der Einrichtung des Zertifikates im Browser muss dieser nun dazu gebracht werden, das Zertifikat auch zu verwenden. Bei Firefox geht dies, indem eine Seite mit erforderlicher HTTP Client Zertifikat Authentifizierung aufgerufen wird, im Internet Explorer muss der SSL Cache geleert werden (wobei die Verwendung von JavaScript sich dabei als unzuverlässig heraus gestellt hat). Beim Google Chrome gab es aktuell keine Probleme.
Beachte beim Debuggen: Auch wenn das Zertifikat im Browser gelöscht wird, bleibt eine offene SSL Sitzung weiter bestehen - und der Webserver teilt weiterhin die Verwendung des HTTPS Client Zertifikates mit.
Trackbacks