Https Interface für LDAP-Passworte: Difference between revisions

From
Jump to navigation Jump to search
 
(24 intermediate revisions by 2 users not shown)
Line 48: Line 48:
Die Rechtevergabe findet in der "slapd.conf" statt. Unsere Konfiguration lautet:
Die Rechtevergabe findet in der "slapd.conf" statt. Unsere Konfiguration lautet:


<pre>
[[Image:ldap_conf1.png]]
<nowiki>
access to attrs=sambaNTPassword,sambaLMPassword,userPassword,shadowLastChange
by dn="uid=seminarAdmin,dc=jupiter,dc=de" write
by anonymous auth
by * none
</nowiki>
</pre>


====== LDAPS ======
====== LDAPS ======
Line 56: Line 63:


==== Formular ====
==== Formular ====

TODO: Webanwendung (HTTPS), Ablaufdiagramm.
[[Image:php_screenshot2.png|thumb|200px|Screenshot HTML-Formular]]
TODO: Sicherheitsdiskussion

TODO: Codeausschnitt: Konnektieren zur LDAP Datenbank über SSL, Secure Socket aufbauen und kommunizieren)
Die zentrale Stelle, an der die Passworte geändert werden können, besteht aus einem HTML-Formular, welches die Variablen $user (Benutzername), $pass (altes Passwort) und $newpass1 (neues Passwort) via POST an den PHP-Interpreter durchreicht.

[[Image:php_ablauf.png|thumb|200px|Ablaufdiagramm des PHP-Skripts]]

Es werden nacheinander folgende Prüfungen durchgeführt:

* Wurde ein Benutzername eingegeben?
* Sind die neuen Passworte identisch (neues Passwort muss zur Bestätigung erneut eingegeben werden)?
* Genügt das neue Passwort den Sicherheitsanforderungen (Mindestlänge)?
* Wurde der grafische CAPTCHA-Text korrekt gelöst (Ein CAPTCHA versucht auszuschließen, dass das Formular automatisiert ausgefüllt wird und dient hier als Mittel gegen "Brute Force"-Angriffe)?
* Ist der zu ändernde Account vorhanden und stimmt das alte Passwort?

Durch diese Reihenfolge wird sichergestellt, dass das Verwaltungssystem nur bei sinnvollen Eingaben belastet wird. Eine Verbindung zum LDAP-Server wird erst zum Schluß aufgebaut, um via "bind" festzustellen, ob die eingegebenen Account-Daten gültig sind. Dies geschieht SSL-gesichert via LDAPS:

<pre><nowiki>
$ldapconn = ldap_connect ($ldaphost, 636);

if (!(@ldap_bind($ldapconn, 'uid=' . $_POST['user'] . ',ou=people,dc=jupiter,dc=de', $_POST['pass']))) {
if (ldap_error($ldapconn) == 'Invalid credentials')
return ('Account nicht vorhanden oder falsches Passwort.');
return ('LDAP: ' . ldap_error($ldapconn) . ' (' . ldap_errno($ldapconn) . ')');
}
</nowiki></pre>

Ist das "bind" erfolgreich, baut das PHP-Skript einen SSL-Socket (Port 8000) zum Java-Programm auf und überträgt Benutzername, altes und neues Kennwort.

<pre><nowiki>
$request = ('uid=' . $_POST['user'] . ',ou=people,dc=jupiter,dc=de' . "\n" .
$_POST['pass'] . "\n" .
$_POST['newpass1'] . "\n");

$socket_opts = array('ssl' => array(
'verify_peer' => false,
'allow_self_signed' => true)
);

$context = stream_context_create ($socket_opts);
$fp = @stream_socket_client ("ssl://$javahost:8000", $errno, $errstr, 5, STREAM_CLIENT_CONNECT, $context);

if (!$fp)
return ('JAVA: ' . $errstr . ' (' . $errno . ')');

fputs ($fp, $request);
while (!feof($fp)) {
$response .= fgets ($fp, 128);
}
fclose($fp);
</nowiki></pre>

Der vom Java-Programm zurückgegebene Statuscode ($response) erzeugt die dazugehörige Statusbeschreibung im Klartext, welche anschließend auf der Formular-Seite ausgegeben wird.


==== Passwortänderungssoftware ====
==== Passwortänderungssoftware ====
Line 76: Line 133:
Die verwendeten Datenstrukturen: "connections" ist ein Container von Connectionklassen und connection_timestamps ist ein Feld von Zahlen, die einem Timestamp beim Verbindungsaufbau einer Verbindung entsprechen. Es existiert ein pointer der durch diese Liste iteriert, so dass der Eintrag, der dem pointer folgt, der Timestamp der "maximumConnectionsPerSec"-ten Verbindung entspricht. Ist dieser Wert älter als eine Sekunde so kann die Verbindung angenommen werden. Sonst wird die Verbindung abgelehnt.
Die verwendeten Datenstrukturen: "connections" ist ein Container von Connectionklassen und connection_timestamps ist ein Feld von Zahlen, die einem Timestamp beim Verbindungsaufbau einer Verbindung entsprechen. Es existiert ein pointer der durch diese Liste iteriert, so dass der Eintrag, der dem pointer folgt, der Timestamp der "maximumConnectionsPerSec"-ten Verbindung entspricht. Ist dieser Wert älter als eine Sekunde so kann die Verbindung angenommen werden. Sonst wird die Verbindung abgelehnt.


<pre><nowiki>
[[Image:Java_incoming2.png]]
while(true)
{
Socket client = listen.accept();

if (new java.util.Date().getTime() - oneSec > connection_timestamps[pointer]) {
connection_timestamps[pointer] = new java.util.Date().getTime();
pointer = (pointer + 1) % maximumConnectionsPerSec;
Connection c = null;
try {
c = new Connection(this, client);
} catch (SocketException e) {
System.out.println("Problem beim Verbindungsaufbau, Verbindung wird ignoriert.");
continue;
}
connections.addElement(c);

} else {
System.out.println("Verbindung abgelehnt");

/* Im Falle eines agressiven Angriffs, kann auch der Socket geschlossen werden,
so dass alle eingehenden Verbindungsversuche vom Betriebssystem ignoriert werden
koennen:
exit(-1);
*/
}
}
</nowiki></pre>


====== Berechnung der Hashwerte ======
====== Berechnung der Hashwerte ======
Line 82: Line 168:
[[image:java_hash.png]]
[[image:java_hash.png]]


== Installation und Download ==
== Download und Installation ==

==== Webformular ====

Download: [http://informatik.hu-berlin.de/~crudolf/its/formular.zip Webformular (25 KB)]

Bedingung für die Installation des Webformulars ist ein PHP-fähiger Webserver. Der PHP-Interpreter muss zusätzlich mit dem Parameter "--with-ldap" installiert worden sein, damit die Skripte Zugriff auf eine LDAP API besitzt. Anschließend reicht es, das entpackte Archiv in den WWW-Ordner des Webservers zu kopieren und anschließend die IP Adressen in der "formular.php" des Archivs auf die IP Adressen des LDAP Servers zu setzen, auf dem auch die Passwortänderungssoftware installiert ist.

==== Passwortänderungssoftware ====

Download: [http://informatik.hu-berlin.de/~crudolf/its/pw_changing.zip Passwortänderungssoftware (2 MB)]

Die Quellen der Passwortänderungssoftware sind als Download verfügbar. Sie können mit dem Apache Ant [http://ant.apache.org/] übersetzt werden. Dafür existiert das Ziel "build" zum Bauen der Quellen. Zum Testen des Servers existiert ein Testclient, der mit "runClient" gestartet werden kann. Mit diesem kann zumindestens der SSL-Socket und die Zertifikate getestet werden.
Der eigentliche Server lässt sich mit dem Ziel "run" starten. Ausgaben, Debugausgaben und Fehlernachrichten werden direkt auf der Konsole ausgegeben.
Das Archiv enthält alle notwendigen Bibliotheken, so dass sich die Größe auf 2 MB beläuft.

Ein einfaches, lauffähiges selbstsigniertes Zertifikat liegt ebenfalls bei. Dieses muss auf dem Webserver installiert sein (siehe oben). Das Zertifikat wurde mit OpenSSL erstellt und mit dem SDK-Tool "keytool" zu einem Java kompatiblen Keystore umgewandelt.

Latest revision as of 18:42, 25 September 2007

Problemstellung

Die benutzerseitige Authentifizierung gegenüber anderen Diensten erfolgt oft über Passworte. Systeme speichern und übertragen heute keine Passworte im Klartext sondern als Hash. Unsere Aufgabe bestand darin, ein sicheres Web-Interface zu erstellen, welches bei Passwortänderung alle Hashwerte, die für verschiedene Systeme (Windows/Unix) benötigt werden, in eine LDAP-Datenbank einträgt. Das System sollte modular und erweiterbar sein.


Analyse

Im Folgenden werden wir die Problemstellung analysieren, um die Anforderung an unser System genau spezifizieren zu können:

  • Szenario - Es existieren Benutzer, welche für verschiedene Systeme angemeldet sind, welche eine Authentifikation benötigen. Die Authentifizierungsdaten bestehen aus einem Benutzernamen und einem Passwort, welche zentral in einem Verwaltungssystem gespeichert werden.
  • Verwaltungssystem - Ein Verwaltungssystem speichert die Authentifizierungsdaten. Dazu gehören Benutzername und Passworthashes verschiedener Hashfunktionen. Dies soll eine große Anzahl von unterschiedlichen Systemen ermöglichen, welche unterschiedliche Hashfunktionen benutzen. Hier ist das Verwaltungssystem eine LDAP Datenbank. Es muss beachtet werden, dass die Benutzer nicht das Recht haben, die eigenen Passwörter zu ändern.
  • System - Ein System ist ein Dienst oder eine Anwendung, die Authentifizierungsdaten nicht lokal speichert, sondern diese von einem Verwaltungssystem bezieht. Die Systeme haben keine Rechte die Passwörter der Benutzer zu manipulieren, um Konsistenz in den Passworthashes zu gewährleisten. Ein Linuxlogin oder eine SAMBA-Authentifikation sind Beispiele für solche Systeme.
  • Webserver - Der Webserver stellt das Formular zur Passwortänderung bereit. Da der Webserver potentiell unsicher ist, dürfen auf dem Webserver keine kritischen Informationen gespeichert werden.
  • Benutzer - Die Benutzer des Szenarios melden sich an jedes System mit der gleichen Benutzer/Kennwort-Kombination an.

Annahmen:

  • Alle Akteure sind potentielle Angreifer, alle Systeme, damit auch der Webserver, sind gefährdet.
  • Das Verwaltungssystem und somit die LDAP Datenbank ist sicher, was durch logische Positionierung der Geräte in einer Demilitarisierten Zone erreicht werden kann. Diese Annahme muss getroffen werden, da durch ein kompromittiertes Verwaltungssystem keine Datensicherheit für die Benutzer mehr gewährleistet werden kann.

Anforderungen:

  • Die Verfügbarkeit des Verwaltungssystems ist für dieses Szenario besonders wichtig, da die Benutzer bei Ausfall dieses Verwaltungssystems keine Arbeit mehr verrichten können. Unsere Anwendung darf die Verfügbarkeit dieses Systems nicht gefährden.
  • Die Anwendung (das Formular, sowie die Logik der Passworänderung) muss in dem Sinne sicher sein, dass ein Angreifer durch Abhören beliebiger Datenleitungen nicht in der Lage ist, Informationen zu beziehen, welcher Benutzer sein Passwort ändert oder welche Passwörter eingegeben wurden.
  • Die Webanwendung muss sicher gegen "Man in the middle"-Attacken sein, unter der Annahme, dass der Benutzer vor jeder Passwortänderung ein durch den Browser angezeigtes Zertifikat gründlich prüft.
  • Die Webanwendung und jede Logik der Passwortänderung sollte robust gegen "Denial of Service"-Attacken sein. Im Extremfall kann sich das System für eine festgelegte Zeitspanne abschalten, um dem wichtigeren Verwaltungssystem möglichst viele Ressourcen exklusiv zur Verfügung zu stellen.
  • Die Benutzer und Systeme haben nicht die Möglichkeit, das Passwort in dem Verwaltungssystem zu ändern, sondern müssen dies über die Webanwendung tun.

Lösungsansätze

1. TODO: Logik auf dem Webserver -> Nicht mit Anforderung vereinbar, dass Webserver unsicher ist 2. TODO: Unsere Architektur

Architektur

TODO: Bild, -beschreibung TODO: Begriffe: Webserver, LDAP-Server, Passwortänderungssoftware TODO: SeminarAdminBenutzer

Implementierung

LDAP Konfiguration

Für die Lösung dieser Aufgabe haben wir die freie Implementierung OpenLDAP [1] benutzt. Im Folgenden beschreiben wir die Konfiguration der LDAP Datenbank. Dabei zeigen wir, wie die Benutzerkonfiguration vollzogen und wie ein Zertifikat erstellt und installiert werden kann, welches die Webanwendung überprüft, um "Man in the middle"-Attacken vorzubeugen.

Benutzerkonfiguration

Nach Anlegen des "seminarAdmin" (Beispielsweise durch eine graphischen Oberfläche wie phpldapadmin [2]. Diese lässt sich sehr leicht durch "apt-get install phpldapadmin" installieren und muss anschließend nur noch in ein Apache Webordner einhängen. Anschließend müssen wir dem "seminarAdmin" die Rechte geben, alle Passwortfelder zu manipulieren. Die Rechtevergabe findet in der "slapd.conf" statt. Unsere Konfiguration lautet:


access to attrs=sambaNTPassword,sambaLMPassword,userPassword,shadowLastChange
   by dn="uid=seminarAdmin,dc=jupiter,dc=de" write
   by anonymous auth
   by * none

LDAPS

Für die Aktivierung des LDAPS Ports muss zunächst ein Zertifikat erstellt werden. Dabei kann mit Tools wie OpenSSL gearbeitet werden [3]. Um das Zertifikat zu installieren sind verschiedene Änderungen notwendig. Zunächst muss LDAP in der "sldap.conf" erklärt werden wo das Zertifikat zu finden ist. Dafür müssen die Elemente TLSCertificateFile, TLSCertificateKeyFile und TLSCACertificateFile mit dem Pfad zu dem Zertifikat gesetzt werden. In der "ldap.conf" muss dann die Client Authority (mit der das Zertifikat signiert wurde) eingetragen werden (TLS_CACERT) und TLS_REQCERT auf "never" gesetzt werden. Ein Client kann nun durch Einspielen des Serverzertifikats (zum Beispiel in den "/etc/ssl/certs" Ordner) das Zertifikat akzeptieren.

Formular

Screenshot HTML-Formular

Die zentrale Stelle, an der die Passworte geändert werden können, besteht aus einem HTML-Formular, welches die Variablen $user (Benutzername), $pass (altes Passwort) und $newpass1 (neues Passwort) via POST an den PHP-Interpreter durchreicht.

Ablaufdiagramm des PHP-Skripts

Es werden nacheinander folgende Prüfungen durchgeführt:

  • Wurde ein Benutzername eingegeben?
  • Sind die neuen Passworte identisch (neues Passwort muss zur Bestätigung erneut eingegeben werden)?
  • Genügt das neue Passwort den Sicherheitsanforderungen (Mindestlänge)?
  • Wurde der grafische CAPTCHA-Text korrekt gelöst (Ein CAPTCHA versucht auszuschließen, dass das Formular automatisiert ausgefüllt wird und dient hier als Mittel gegen "Brute Force"-Angriffe)?
  • Ist der zu ändernde Account vorhanden und stimmt das alte Passwort?

Durch diese Reihenfolge wird sichergestellt, dass das Verwaltungssystem nur bei sinnvollen Eingaben belastet wird. Eine Verbindung zum LDAP-Server wird erst zum Schluß aufgebaut, um via "bind" festzustellen, ob die eingegebenen Account-Daten gültig sind. Dies geschieht SSL-gesichert via LDAPS:

    $ldapconn = ldap_connect ($ldaphost, 636);

    if (!(@ldap_bind($ldapconn, 'uid=' . $_POST['user'] . ',ou=people,dc=jupiter,dc=de', $_POST['pass']))) {
        if (ldap_error($ldapconn) == 'Invalid credentials')
            return ('Account nicht vorhanden oder falsches Passwort.');
        return ('LDAP: ' . ldap_error($ldapconn) . ' (' . ldap_errno($ldapconn) . ')');
    }

Ist das "bind" erfolgreich, baut das PHP-Skript einen SSL-Socket (Port 8000) zum Java-Programm auf und überträgt Benutzername, altes und neues Kennwort.

    $request = ('uid=' . $_POST['user'] . ',ou=people,dc=jupiter,dc=de' . "\n" .
            $_POST['pass'] . "\n" .
            $_POST['newpass1'] . "\n");

    $socket_opts = array('ssl' => array(
                        'verify_peer'       => false,
                        'allow_self_signed' => true)
                   );

    $context = stream_context_create ($socket_opts);
    $fp = @stream_socket_client ("ssl://$javahost:8000", $errno, $errstr, 5, STREAM_CLIENT_CONNECT, $context);

    if (!$fp)
        return ('JAVA: ' . $errstr . ' (' . $errno . ')');

    fputs ($fp, $request);
    while (!feof($fp)) {
        $response .= fgets ($fp, 128);
    }
    fclose($fp);

Der vom Java-Programm zurückgegebene Statuscode ($response) erzeugt die dazugehörige Statusbeschreibung im Klartext, welche anschließend auf der Formular-Seite ausgegeben wird.

Passwortänderungssoftware

Ablaufdiagramm der Änderungssoftware

Die sich auf dem LDAP-Server befindliche Java-Software horcht auf einen Port und erwartet eingehende SSL-Sockets. Für jeden dieser Sockets liest sie zunächst die drei ankommendenden Datenzeilen, welche als "Benutzername", "altes Passwort", "neues Passwort" interpretiert werden. Anschließend wird überprüft ob die Authentifizierungsdaten korrekt sind. Dafür wird versucht sich an der LDAP Datenbank zu binden. Ist dies erfolgreich gewesen, so wird anschließend der "seminarAdmin" gebunden, der die Änderung an dem Benutzerkonto vornimmt, siehe Abbildung.

Im Folgenden wollen wir die möglichen Angriffe auf diese Software diskutieren:

  • DoS - Eine DoS Attacke auf diese Software könnte wie folgt ablaufen:
    • Es wird eine beliebig große Anzahl von Sockets zu dem Javaprogramm aufgebaut. In diesem Moment wird die Verfügbarkeit des Verwaltungssystem nicht gefährdet, da die Software Anfragen ablehnt, sobald zuviele Verbindungsanfragen eingekommen sind. Dabei wird stets geprüft ob eine bestimmte Anzahl von Verbindungsanfragen pro Sekunde überschritten wird.
    • Alternativ kann ein Angriff über sehr große Pakete erfolgen, in der Hoffnung, einen Bufferoverflow zu erzeugen. Diesem wird entgegengewirkt, indem die Software nicht mehr als 100 Zeichen pro Datensatz akzeptiert und bei Überschreitung die Verbindung schließt.
    • Viele Verbindungen könnten sehr lang geöffnet bleiben. Dies wird verhindert, indem eine Systemeinstellung vorgenommen wird, so dass jeder Socket auf diesem Port maximal eine Sekunde geöffnet bleibt. Wird diese Zeit überschritten, schließt die Software automatisch die Verbindung. Dieses Verhalten kann vom Formular erkannt und eine Fehlermeldung ausgegeben werden.
  • Brute Force - Eine Brute Force Attacke wird hier nicht von einem DoS Angriff unterschieden. Wenn die Software erkennt, dass die eingehenden Benutzerdaten falsch sind, zählt diese einen Zähler hoch und bei Überschreitung eines bestimmten Wertes sperrt sich der Socket auf Grund dessen, dass ein Angriff vermutet wird, da das Formular eigentlich die Daten überprüft.

Nun wollen wir interessante Codestellen aufzeigen. Dafür zeigen wir zunächst, wie die eingehenden Verbindungen verwaltet und die Hashwerte des Passwortes berechnet werden.

Verwaltung eingehender Verbindungen

Die verwendeten Datenstrukturen: "connections" ist ein Container von Connectionklassen und connection_timestamps ist ein Feld von Zahlen, die einem Timestamp beim Verbindungsaufbau einer Verbindung entsprechen. Es existiert ein pointer der durch diese Liste iteriert, so dass der Eintrag, der dem pointer folgt, der Timestamp der "maximumConnectionsPerSec"-ten Verbindung entspricht. Ist dieser Wert älter als eine Sekunde so kann die Verbindung angenommen werden. Sonst wird die Verbindung abgelehnt.

	while(true)
	{
		Socket client = listen.accept();

		if (new java.util.Date().getTime() - oneSec > connection_timestamps[pointer]) {
			
			connection_timestamps[pointer] = new java.util.Date().getTime();
			pointer = (pointer + 1) % maximumConnectionsPerSec;
			
			Connection c = null;
			try {
				c = new Connection(this, client);
			} catch (SocketException e) {
				System.out.println("Problem beim Verbindungsaufbau, Verbindung wird ignoriert.");
				continue;
			}
			connections.addElement(c);

		} else {
			System.out.println("Verbindung abgelehnt");

			/* Im Falle eines agressiven Angriffs, kann auch der Socket geschlossen werden,
			 so dass alle eingehenden Verbindungsversuche vom Betriebssystem ignoriert werden 
			 koennen: 
			 exit(-1);
			*/
		}
	}
Berechnung der Hashwerte

Die Berechnung der Hashwerte wird mit der MessageDigest-Factory von Java vorgenommen. Da wir möglichst alle Hashwerte des Passwortes berechnen wollen, da diese nicht im Nachhinhein ohne Kenntniss des Klartextpassworts berechenbar sind, haben wir den Securityprovider [4] Bouncycastle [5] installiert und nutzen verschiedene Hashfunktionen, die dieser Provider bereitstellt. Die Windowshashes LM- (Lan Manger) und NT-Passwortfelder wurden mit dem freien Package von Luigi Dragone [6] berechnet und gesetzt. File:Java hash.png

Download und Installation

Webformular

Download: Webformular (25 KB)

Bedingung für die Installation des Webformulars ist ein PHP-fähiger Webserver. Der PHP-Interpreter muss zusätzlich mit dem Parameter "--with-ldap" installiert worden sein, damit die Skripte Zugriff auf eine LDAP API besitzt. Anschließend reicht es, das entpackte Archiv in den WWW-Ordner des Webservers zu kopieren und anschließend die IP Adressen in der "formular.php" des Archivs auf die IP Adressen des LDAP Servers zu setzen, auf dem auch die Passwortänderungssoftware installiert ist.

Passwortänderungssoftware

Download: Passwortänderungssoftware (2 MB)

Die Quellen der Passwortänderungssoftware sind als Download verfügbar. Sie können mit dem Apache Ant [7] übersetzt werden. Dafür existiert das Ziel "build" zum Bauen der Quellen. Zum Testen des Servers existiert ein Testclient, der mit "runClient" gestartet werden kann. Mit diesem kann zumindestens der SSL-Socket und die Zertifikate getestet werden. Der eigentliche Server lässt sich mit dem Ziel "run" starten. Ausgaben, Debugausgaben und Fehlernachrichten werden direkt auf der Konsole ausgegeben. Das Archiv enthält alle notwendigen Bibliotheken, so dass sich die Größe auf 2 MB beläuft.

Ein einfaches, lauffähiges selbstsigniertes Zertifikat liegt ebenfalls bei. Dieses muss auf dem Webserver installiert sein (siehe oben). Das Zertifikat wurde mit OpenSSL erstellt und mit dem SDK-Tool "keytool" zu einem Java kompatiblen Keystore umgewandelt.