Hans: Authentifizierung

Hallo,

ich komme so langsam weiter mit meinen ersten PHP Gehversuchen und habe eine Anwendung gebaut, in der ich Teilbereiche mit einer Authentifizierung schützen muß. Dabei habe ich eine DB (Postgresql) die eine User Tabelle (auth_user), eine Gruppen Tabelle (auth_group) und eine Verknüpfungstabelle (auth_lookup_user) zwischen den beiden hat. So kann jeder User in beliebigen Gruppen Mitglied sein.

Bisher habe ich folgenden Code geschrieben: login.php

if ($_SERVER['REQUEST_METHOD'] == 'POST') {

	//Userdb Recordset
	$sqlString_mytab = "SELECT * FROM auth_user";
	$result_mytab = pg_query($dbconn , $sqlString_mytab) or die('Connection failed (pg_query): ' . pg_last_error());
	
	//per default nicht vertrauenswürdig
	$usertrust = false;

	//Username
        $username = $_POST['username'];
	//Pw als md5-hash
	$hashpw = hash('md5', $_POST['passwort']);
		
	//DB Schleife
	while ($row = pg_fetch_assoc($result_mytab)) {	
		if($username === $row['user_name'] && $hashpw === $row['user_pwd']){
			$usertrust = true;
			$userid = $row['userid'];
			$username = $row['user_name'];
		}
	}


	//  Wenn Bedingung erfüllt Sessions setzen
	if ($usertrust) {
        
           //Ermitteln, welche Gruppenzugehörigkeit der USER hat
	   $sqlString_group_user = "SELECT auth_group.group_name, 
           auth_group.group_desc,auth_lookup_user.id FROM auth_group
	   INNER JOIN auth_lookup_user ON auth_group.groupid = auth_lookup_user.groupid
	   WHERE auth_lookup_user.userid = ".$userid;
	   $result_group_user = pg_query($dbconn , $sqlString_group_user) or die('Connection failed (pg_query): ' . pg_last_error());
		
	   $group_user_array = array();
	   while ($row_group_user = pg_fetch_assoc($result_group_user)) { 
		array_push($group_user_array,$row_group_user['group_name']);
	   }               

           session_start();
	   //Sessionvariablen setzen
	   $_SESSION['clientsession_uid'] = $userid;
           $_SESSION['clientsession_un'] = $username;
	   $_SESSION['clientsession_ug'] = $group_user_array;

	}
}

Auf einer geschützten Seiten wird nun immer über eine Funktion geprüft ob die Sessionvariablen "clientsession_uid" und "clientsession_ug" vorhanden sind und eventuell Code ausgegeben der nur für eine bestimmte Gruppe gedacht ist: seitexy.php

if(isset($_SESSION['clientsession_uid']) && isset($_SESSION['clientsession_ug']){
   //Seiteninhalt
   in_array("gruppe1",$_SESSION['clientsession_ug'],true){
       //schreibe geschützten Code für Gruppe1
   }
}

Mich würde nun interessieren, ob das so sicher ist, oder obe ich da ein paar böse Schnitzer drin habe. Danke für Euer Feedback Hans

  1. PS natürlich so:

    if(isset($_SESSION['clientsession_uid']) && isset($_SESSION['clientsession_ug'])){
       //Seiteninhalt
       if(in_array("gruppe1",$_SESSION['clientsession_ug'],true){
           //schreibe geschützten Code für Gruppe1
       }
    }
    

    Hans

  2. Moin!

    Mich würde nun interessieren, ob das so sicher ist, oder obe ich da ein paar böse Schnitzer drin habe.

    Ja, Böser Schnitzer. Du verwendest md5(). Das sollte man nicht mehr tun, weil es mit rainbow-tables angreifbar ist.

    Jörg Reinholz

    1. Moin!

      Ich korrigiere mal den Link zur richtigen Stelle:

      Ja, Böser Schnitzer. Du verwendest md5(). Das sollte man nicht mehr tun, weil es mit rainbow-tables angreifbar ist.

      Jörg Reinholz

    2. Hallo Jörg,

      Ja, Böser Schnitzer. Du verwendest md5(). Das sollte man nicht mehr tun, weil es mit rainbow-tables angreifbar ist.

      Once again: die rainbow tables sind nicht das Problem, dafür gibt es salting. MD5 gilt als gebrochen weil es eine Menge bekannter Kollisionen gibt.

      LG,
      CK

      1. Moin!

        Hallo Jörg,

        Ja, Böser Schnitzer. Du verwendest md5(). Das sollte man nicht mehr tun, weil es mit rainbow-tables angreifbar ist.

        Once again: die rainbow tables sind nicht das Problem, dafür gibt es salting. MD5 gilt als gebrochen weil es eine Menge bekannter Kollisionen gibt.

        Noch einer:

        Von Deiner Quelle:

        "Die Fälschung benötigt also einen zusammenhängenden Datenblock von 1024 Bit = 128 Byte, was den Einsatz stark einschränkt."

        Das dürfte für Passwörter zutreffen, denn kaum jemand verwendet einen Salt mit einer Länge, dass Salt + Passwort 128 Byte lang sind. Das viel verwendete Apache-MD5 verwendet 32 Bit also 4 Byte ...

        ... und weiter:

        "Eine andere Angriffsmethode stellen Regenbogentabellen dar. In diesen Tabellen sind Zeichenketten mit den zugehörigen MD5-Hashwerten gespeichert. Der Angreifer durchsucht diese Tabellen nach dem vorgegebenen Hashwert und kann dann passende Zeichenketten auslesen. Dieser Angriff kann vor allem eingesetzt werden, um Passwörter zu ermitteln, die als MD5-Hashes gespeichert sind. Die dazu notwendigen Regenbogentabellen sind jedoch sehr groß und es bedarf eines hohen Rechenaufwands, um sie zu erstellen. Deshalb ist dieser Angriff im Allgemeinen nur bei kurzen Passwörtern möglich. Für diesen Fall existieren vorberechnete Regenbogentabellen, bei denen zumindest der Rechenaufwand zum Erstellen der Liste entfällt. Die Verwendung eines Salt, also eines zufälligen nicht vorhersehbaren Wertes, welcher an den Klartext angefügt wird, kann die Effektivität von vorberechneten Regenbogentabellen jedoch zunichtemachen."

        Allerdings muss man hier die Aussagen,

        1. "Die dazu notwendigen Regenbogentabellen sind jedoch sehr groß und es bedarf eines hohen Rechenaufwands, um sie zu erstellen. Deshalb ist dieser Angriff im Allgemeinen nur bei kurzen Passwörtern möglich."
        2. "Die Verwendung eines Salt, also eines zufälligen nicht vorhersehbaren Wertes, welcher an den Klartext angefügt wird, kann die Effektivität von vorberechneten Regenbogentabellen jedoch zunichtemachen."

        unter den starken Vorbehalt stellen, dass die verfügbare Rechenleistung (für brut force) und Speicherplatz inzwischen stark gewachsen sind, so dass auch

        • größere Rainbow-Tables gespeichert werden können
        • längere Passwörter angegriffen werden können

        Ich hab jetzt sogar mal irgendwo md5-rainwbows als Terrabyte-große Partitions-Images gesehen... als Torrent-download.

        Jörg Reinholz

        1. Hallo Jörg,

          Von Deiner Quelle:

          "Die Fälschung benötigt also einen zusammenhängenden Datenblock von 1024 Bit = 128 Byte, was den Einsatz stark einschränkt."

          Weiter lesen!

          LG,
          CK

  3. Moin!

    $sqlString_mytab = "SELECT * FROM auth_user";
    

    Hier mag es gerade noch vertretbar sein. Aber gewöhne Dir grundsätzlich ab, den Asterix zu verwenden, setze dort die Spalten ein. Du wirst irgendwann merken, wie genial es ist, vom Formular über die Datenbank bis hinein in assoziative Arrays stets die gleichen Bezeichner zu verwenden. Sind es nämlich mal mehr als 3 Spalten, dann weisst Du sehr genau, was Du gerade programmierst.

    $username = $_POST['username'];
    

    Von sowas lass die Finger! Bei $_POST['username'] weisst Du immer und auch in 30 Jahren auf den ersten Blick, dass das "vergiftet" sein kann. Mit der Übergabe verlierst Du diese Information. Dto. für $_GET, $_COOKIE, $_SERVER, $_ENV ...

    Jörg Reinholz

    1. Hallo Jörg, vielen Dank für die Infos, den Asterix und die Variablenzuweisung werde ich in Zukunft unterlassen. Was mich vor allem noch interessiert ist ob das Vorgehen mit den Sessions korrekt ist, hab sowas halt noch nie gemacht... Danke + Gruß Sven

      1. Hallo

        vielen Dank für die Infos, den Asterix und die Variablenzuweisung werde ich in Zukunft unterlassen. Was mich vor allem noch interessiert ist ob das Vorgehen mit den Sessions korrekt ist, hab sowas halt noch nie gemacht...

        Eine Session ist explizit dazu da, einen Benutzer über verschiedene Anfragen hinweg wiederzuerkennen und bei Bedarf für ihn über die Zeit der Sitzung Daten (zwischen) zu speichern. Passt also grundsätzlich.

        Tschö, Auge

        --
        Es schimmerte ein Licht am Ende des Tunnels und es stammte von einem Flammenwerfer.
        Terry Pratchett, „Gevatter Tod“
    2. $username = $_POST['username'];
      

      Von sowas lass die Finger! Bei $_POST['username'] weisst Du immer und auch in 30 Jahren auf den ersten Blick, dass das "vergiftet" sein kann.

      Da widerspreche ich. Ich kann deine Argumentation gut nachvollziehen, wenn man das POST-Array immer ausdrücklich referenziert, dann sieht man sofort, dass es sich um Eingabedaten handelt, die von Nutzern stammen können und deshalb auch manipuliert sein können. Wenn man diese Variablen dann in einen anderen Kontext bringt, zum Beispiel in eine SQL-Abfrage oder in eine HTML-Ausgabe, dann wittert man sofort, dass hier eine Gefahr bestünde, wenn man die Variablen nicht entsprechend maskiert/behandelt. Allerdings sollte man die kontextgerechte Behandlung immer durchführen, wenn ein Kontextwechsel stattfindet, nicht nur dann, wenn es sich um Eingabedaten handelt. Auch vom Entwickler gesetzte Variablen können in unvorhersehbaren Kontexten nämliche Fehler oder Sicherheitslücken verursachen. Oft ist es auch schwer nachzuvollziehen, ob eine Variable aus einer Benutzereingabe stammt oder nicht, weil sie zum Beispiel als Parameter diverse Male durch das Programm gereicht wurde. Man sollte sich also antrainieren, Kontextwechsel immer zu behandeln, und zwar unmittelbar bevor der Wechsel stattfindet, nicht früher (und nicht wenn es zu spät ist). Dann stellt auch das Umkopieren von $_POST-Variablen in eigene Variablenbezeichner kein Problem dar. Ich finde es dient sogar enorm der Leserlichkeit von Programmcode, wenn man häufige Array- oder Objekt-Zugriffe durch prägnante Variablennamen abkürzt.

      Mit der Übergabe verlierst Du diese Information. Dto. für $_GET, $_COOKIE, $_SERVER, $_ENV ...

      Was nicht schlimm ist.

  4. Moin!

    Ich habe da noch eine "Geschmackssache" gefunden bei der wohl viele [include(iSelf)] mal irgendwie "schlampen", das hat also nichts mit Funktion oder Sicherheit zu tun, kann Dir aber durchaus mal als schlechter Stil vorgehalten werden:

    $group_user_array = array();
    

    wird besser als

    $arGroupUsers = array();
    

    oder

    $ar_group_users = array();
    

    notiert, also die Array-Eigenschaft vorangestellt und im Name klar gemacht, dass es sich um eine Mehrheit (Plural) handelt. Man muss es zwar mit diesen Feinheiten nicht übertreiben - aber es hilft, sich ein paar Regeln anzugewöhnen, die auch andere verstehen.

    Ich weiss das ziemlich genau, denn ich habe mit einem BASIC angefangen, welches nur 2 Zeichen als Bezeichner erlaubte. Da hatte man bei "größeren" Sachen (bei 32 kB war eh Schluss) schnell mal Zettel und Stift zu nehmen um den Inhalt (nicht gemeint: Wert) der Variablen zu notieren...

    Das war echt unbequem und es hat mich sehr gefreut, dass es inzwischen besser geht.

    Jörg Reinholz