Lukas aus dem WWW: Login Verfahren

Hallo Forum,
ich entwickle gerade was für unseren Kunden, dabei muss ich ein Login-Bereich einrichten.

Um gleich alles richtig zumachen, wollte ich euch mein kleines und vereinfachtes Konzept vorstellen.

  1. User gibt Namen und PW auf Seite Login.html ein, via POST wird alles an die Check_Login.php übermittelt.

  2. Check_Login.php verbindet sich mit der DB (MySQL).
    2a) Name wird verglichen
        - Name wird nicht gefunden? Zurück + Fehlermeldung!
        PW wird verglichen
        - PW ist falsch? Zurück + Fehlermeldung

  3. Name UND PW korrekt?
       ~~~php

  • session_start();
       - $_SESSION['sesid'] = md5(session_id());
   - Weiterleitung zur der Zielseite  
  
============================================================================  
  
Auf jeder Seite auf der nur die User zugriff haben sollen, würde ich folgendes abfragen:  
  
~~~php
if($_SESSION['sesid'] && $_SESSION['sesid'] == md5(session_id())) ALLES_OK();  
else FEHLER();

Ist an dieser Logik was Fehlerhaft/Bedenklich?
Wo und wie müsste ich die DB-Zugangsdaten auslagern um sie sicher zu "versteken"?

    1. User gibt Namen und PW auf Seite Login.html ein, via POST wird alles an die Check_Login.php übermittelt.

    Affenformulare - eigene Dokumente sind nicht notwendig.

    1. Check_Login.php verbindet sich mit der DB (MySQL).
      2a) Name wird verglichen
    2. Name UND PW korrekt?

    Ich würde nur eine Fehlermeldung ausgeben: "Benutzername oder Passwort nicht vorhanden oder falsch" - das ist zwar weniger Benutzerfreundlich, gibt aber einem Angreifer weniger Angriffspunkt - dh. zuerst den Benutzernamen Bruteforcen und dann das Passwort fällt flach.

    1. Affenformulare - eigene Dokumente sind nicht notwendig.

      Wie eigene Formulare?
      Ich würde bei Fehlern zurückspringen zur der Seite, das ist doch ein Affenformular. Natürlich würde JS die vorab-Validierung übernehmen, um den Server nicht unnötig zu belasten...

      Ich würde nur eine Fehlermeldung ausgeben: "Benutzername oder Passwort nicht vorhanden oder falsch" - das ist zwar weniger Benutzerfreundlich, gibt aber einem Angreifer weniger Angriffspunkt - dh. zuerst den Benutzernamen Bruteforcen und dann das Passwort fällt flach.

      Sa hatte ich das auch vor ;)

      1. Affenformulare - eigene Dokumente sind nicht notwendig.
        Wie eigene Formulare?

        Was "Wie eigene Formulare" :) ich schrieb "eigene Dokumente sind _nicht_ notwendig" - du hast aktuell scheinbar eigene Dokumente obwohl eines ausreicht.

        Ich würde bei Fehlern zurückspringen zur der Seite, das ist doch ein Affenformular.

        Jein - es müsste sich selbst aufrufen, kein anderes Script so wie du es tust - "zurückspringen" impliziert ja einen unnötigen Redirect.

        Sa hatte ich das auch vor ;)

        Dann brauchst du aber nicht kompliziert herumprüfen, das kannst du in einer Bedingung auch machen - du hast das etwas kompliziert beschrieben.

        if
          empty(benutzername) ||
          empty(passwort) ||
          falsch(benutzername) ||
          falsch(passwort)

        1. Was "Wie eigene Formulare" :) ich schrieb "eigene Dokumente sind _nicht_ notwendig" - du hast aktuell scheinbar eigene Dokumente obwohl eines ausreicht.

          Also meinst du das die Login-Seite das Prüfen des PW und Namen übernimmt, keine eigene PHP-Seite oder wie? Würde auch gehen.

          1. Also meinst du das die Login-Seite das Prüfen des PW und Namen übernimmt, keine eigene PHP-Seite oder wie? Würde auch gehen.

            Richtig - damit sparst du dir einiges an "Herumredirecterei".

            1. Richtig - damit sparst du dir einiges an "Herumredirecterei".

              Danke, werde das so Umsetzen.

              Kleine Frage zu den DB-Zugangsdaten, wie lagere ich diese am besten aus?
              In einer PHP Datei? Wenn ja, wo? Wenn der Apache ausfallen sollte, sind dann diese sichtbar?

              1. Kleine Frage zu den DB-Zugangsdaten, wie lagere ich diese am besten aus?
                In einer PHP Datei? Wenn ja, wo? Wenn der Apache ausfallen sollte, sind dann diese sichtbar?

                Speichere nie Klartext-Passworte sondern nur die mit dem Salz erweiterten Hashes.
                Verwende sha1 statt md5.

                mfg Beat

                --
                ><o(((°>           ><o(((°>
                   <°)))o><                     ><o(((°>o
                Der Valigator leibt diese Fische
                1. Verwende sha1 statt md5.

                  Beide haben ähnliche Sicherheitslücken - md5 gegen sha1 zu tauschen bringt bis auf die Stärke ansich keinen nenneswerten Vorteil.

              2. Hi!

                Kleine Frage zu den DB-Zugangsdaten, wie lagere ich diese am besten aus?
                In einer PHP Datei? Wenn ja, wo? Wenn der Apache ausfallen sollte, sind dann diese sichtbar?

                Alles was nicht ausgeliefert werden soll gehört nicht in übers Web erreichbare Dateien. Bei einem ordentlichen Hoster kann man seine Domain(s) auf Unterverzeichnisse seines Kundenverzeichnisses zeigen lassen. Dann kann man weitere Verzeichnisse neben diesen Domainverzeichnissen anlegen, in denen Dateien unerreichbar abgelegt werden können.

                Zur Not geht noch ein Zugriffsschutz über HTTP-Authentication (gern .htaccess-Schutz genannt). Aber auch da hast du das Problem dass der beispielsweise wegen einer Fehlkonfiguration nicht greift.

                Lo!

              3. Hi,

                Kleine Frage zu den DB-Zugangsdaten, wie lagere ich diese am besten aus?
                In einer PHP Datei? Wenn ja, wo? Wenn der Apache ausfallen sollte, sind dann diese sichtbar?

                Wieso stellst du Fragen zu solchen Basics, wenn du bereits für "Kunden" entwickelst?

                MfG ChrisB

                --
                Light travels faster than sound - that's why most people appear bright until you hear them speak.
          2. Hi,

            Also meinst du das die Login-Seite das Prüfen des PW und Namen übernimmt, keine eigene PHP-Seite oder wie? Würde auch gehen.

            Zwecks Prüfung der Zugangsdaten auf eine spezielle Seite weiterzuleiten, halte ich auch für antiquiert.

            Wenn die komplette Site/Applikation durchgängig in allen Seiten den Status nicht eingeloggt oder eingeloggt haben kann - dann sollte man sich m.E. auf jeder Seite einloggen können, und nach Prüfung der Daten dann gleich wieder auf dieser befinden, mit ggf. entsprechend gewechseltem Status.

            MfG ChrisB

            --
            Light travels faster than sound - that's why most people appear bright until you hear them speak.
  1. Hallo Forum,
    ich entwickle gerade was für unseren Kunden, dabei muss ich ein Login-Bereich einrichten.

    Um gleich alles richtig zumachen, wollte ich euch mein kleines und vereinfachtes Konzept vorstellen.

    1. User gibt Namen und PW auf Seite Login.html ein, via POST wird alles an die Check_Login.php übermittelt.

    Warum eine Login.html?
    Bei mir ist ein Logn auf jeder "seite" implizit möglich.
    Ob eine Login-Prüfung gefordert wird ersehe ich aus einem Formular-Id, die ebenfalls gesendet wurde.

    1. Check_Login.php verbindet sich mit der DB (MySQL).
      2a) Name wird verglichen
          - Name wird nicht gefunden? Zurück + Fehlermeldung!
          PW wird verglichen
          - PW ist falsch? Zurück + Fehlermeldung

    Wie und was wird verglichen?
    Du solltest keine Klartext Passworte auf dem Server speichern, sondern nur de hashes.

    1. Name UND PW korrekt?
         ~~~php
    • session_start();

    - $_SESSION['sesid'] = md5(session_id());

      
    was liefert die Funktion session\_id() zurück?  
    Eventuell verschlechterst du mit md5 die Qualität eines erstellten Hashes.  
      
    sha1 ist md5 vorzuziehen.  
      
    
    >    - Weiterleitung zur der Zielseite  
      
    Weiterleitungen finde ich immer doof. Warum nicht den geünschten Kontext direkt ausgeben?  
      
    
    > ============================================================================  
    >   
    > Auf jeder Seite auf der nur die User zugriff haben sollen, würde ich folgendes abfragen:  
    >   
    > ~~~php
    
    if($_SESSION['sesid'] && $_SESSION['sesid'] == md5(session_id())) ALLES_OK();  
    
    > else FEHLER();
    
    

    Hier verstehe ich nicht, warum du dir jedesmal eine md5 konversion leistest. Die gespeicherten SessionIDs sollten ja bereits hashes sein.

    Ist an dieser Logik was Fehlerhaft/Bedenklich?

    Je nachdem was du vorhast, ist es Mangelhaft. Mir fehlt zum Beispiel eine Definition für verschiedene Usertypen. Wie loggst du dich als Admin ein?

    Wo und wie müsste ich die DB-Zugangsdaten auslagern um sie sicher zu "versteken"?

    Du musst sie nicht verstecken. Du musst zuerst die Passworte richtigen Salzen und einen Hash desselben speichern.

    mfg Beat

    --
    ><o(((°>           ><o(((°>
       <°)))o><                     ><o(((°>o
    Der Valigator leibt diese Fische
    1. Du musst sie nicht verstecken. Du musst zuerst die Passworte richtigen Salzen und einen Hash desselben speichern.

      Die Datenbank-Zugangsdaten zu hashen ist aber ggf. eine schlechte Idee :) diese sollen natürlich im Klartext vorliegen :)

      Folgendes würde ich beachten;

      • das Konfigurationsfile sollte außerhalb des Wurzelverzeichnisses des Webs liegen
      • der Datenbank-Server sollte nur Verbindungen von localhost (oder wenn es ein eigener Server ist) vom Webserver annehmen.
      • der Produktiv-Datenbank-Benutzer (also jener mit dem der Webserver bzw. das Script arbeitet) hat nur die nötigen Rechte.
        -- GRANT, ALTER oder DROP wird dieser vermutlich nicht benötigen.
      1. Die Datenbank-Zugangsdaten zu hashen ist aber ggf. eine schlechte Idee :) diese sollen natürlich im Klartext vorliegen :)

        OK, aber der Vergleich kann mit SH1 erfolgen oder spricht was dagegen?

        Folgendes würde ich beachten;

        • das Konfigurationsfile sollte außerhalb des Wurzelverzeichnisses des Webs liegen

        Ich bin nicht der Server-Admin, daher keine Ahnung.

        • der Datenbank-Server sollte nur Verbindungen von localhost (oder wenn es ein eigener Server ist) vom Webserver annehmen.

        Es ist kein Webservice zwischengeschaltet, also müsste ich die SQL-Statements direkt aus dem PHP starten.

        • der Produktiv-Datenbank-Benutzer (also jener mit dem der Webserver bzw. das Script arbeitet) hat nur die nötigen Rechte.

        Das was ich entwikle braucht nur Leserechte auf der DB.

        1. Die Datenbank-Zugangsdaten zu hashen ist aber ggf. eine schlechte Idee :) diese sollen natürlich im Klartext vorliegen :)
          OK, aber der Vergleich kann mit SH1 erfolgen oder spricht was dagegen?

          Die Zugangsdaten zur Datenbank bzw. die Anmeldung an dieser wirst du meistens im Klartext vornehmen (vom DBMS abhängig) - das hat mit dem Login des Benutzers nicht zu tun.

          Ob du die Benutzerpasswörter als md5 oder sha1 vorhältst spielt keine Rolle - beide Algorithmen sind "unsicher" (für die Praxis ist diese Unsicherheit aber nicht relevant) zudem ist das für dein Login selbst auch nicht relevant. Relevant ist vor allem: was passiert, wenn die Daten flöten gehen bzw. jemand einen Datensatz im Klartext ergattert. Ist es ihm möglich den Hash aufgrund eine Rainbow-Table zu "entschlüsseln"?

          Hier hilft nur der bereits genannte Salt - der am besten je Benutzer einen unterschiedlichen Wert hat - dieser kann ruhig bekannt sein, meintwegen ist der Salt die ID des Benutzers. Dann müsste ein potentieller angreifer beim Verlust der Daten für jeden Datensatz eine eigene Rainbow-Tabelle erstellen um an mögliche Klartextpasswörter zu kommen.

          Ich bin nicht der Server-Admin, daher keine Ahnung.
          Es ist kein Webservice zwischengeschaltet, also müsste ich die SQL-Statements direkt aus dem PHP starten.
          Das was ich entwikle braucht nur Leserechte auf der DB.

          Dann sprich diese drei Punkte beim entsprechenden Serverbetreuer an - was nutzt ein sicheres Loginsystem wenn dir jemand die Datenbankzugangsdaten klaut und die Daten aller 250.000 Benutzer ausliest und als .csv bei eBay vertickt?

    2. Warum eine Login.html?
      Bei mir ist ein Logn auf jeder "seite" implizit möglich.
      Ob eine Login-Prüfung gefordert wird ersehe ich aus einem Formular-Id, die ebenfalls gesendet wurde.

      Wo besteht der Sinn darin? Ein login auf jeder Seite? Ist nicht die Vorgabe.

      1. Check_Login.php verbindet sich mit der DB (MySQL).
        2a) Name wird verglichen
            - Name wird nicht gefunden? Zurück + Fehlermeldung!
            PW wird verglichen
            - PW ist falsch? Zurück + Fehlermeldung

      Wie und was wird verglichen?

      Eingabe mit DB-Eintrag, je für Name und PW.

      Du solltest keine Klartext Passworte auf dem Server speichern, sondern nur de hashes.

      Es befindet sich bereits eine DB mit >250k Usern, für dich ich eben ein Login in diesem Bereich (siehe weiter unten) möglich mache.

      1. Name UND PW korrekt?
           ~~~php
      • session_start();

      - $_SESSION['sesid'] = md5(session_id());

      
      >   
      > was liefert die Funktion session\_id() zurück?  
      
      Die aktuelle Session-ID???  
        
      
      > Eventuell verschlechterst du mit md5 die Qualität eines erstellten Hashes.  
      >   
      > sha1 ist md5 vorzuziehen.  
      
      Ok, danke.  
      Ich mache es damit man die Session-ID nicht zufällig generieren kann, mit MD5 bzw. SH1 kann ein Angreifer nicht wissen woraus diese ID besteht, evtl. ist da noch ein extra Timestamp ect.  
        
      
      > >    - Weiterleitung zur der Zielseite  
      >   
      > Weiterleitungen finde ich immer doof. Warum nicht den geünschten Kontext direkt ausgeben?  
      
      Habe ich bereits so übernommen.  
        
      
      > > ~~~php
      
      if($_SESSION['sesid'] && $_SESSION['sesid'] == md5(session_id())) ALLES_OK();  
      
      > > else FEHLER();
      
      

      Hier verstehe ich nicht, warum du dir jedesmal eine md5 konversion leistest. Die gespeicherten SessionIDs sollten ja bereits hashes sein.

      Hier wird halt die verschlüsselte session_id() aus dem Login mit der aktuellen session_id() verglichen. Kann ja möglich sein das sie nicht übereinstimmen --> Betrugsfall --> neuer Loginversuch!

      Ist an dieser Logik was Fehlerhaft/Bedenklich?

      Je nachdem was du vorhast, ist es Mangelhaft. Mir fehlt zum Beispiel eine Definition für verschiedene Usertypen. Wie loggst du dich als Admin ein?

      Also, es geht um einen sehr grossen eShop.
      Dieser eShop bekommt einen "Member-Bereich" wo sich User einloggen können um zusehen was sie in den letzten 12 Monaten gekauft haben usw.

      Alle User haben die selbe Priorität und den selben Status, also keine Gruppen und auch keine Admins.

      1. Wo besteht der Sinn darin? Ein login auf jeder Seite? Ist nicht die Vorgabe.

        Natürlich hängt das von der Vorgabe ab - aber ohne Vorgabe würde ich das Login prinzipiell auch auf jeder Seite anbringen (oder zumindest da, wo es erforderlich wird prompten).

        Es befindet sich bereits eine DB mit >250k Usern, für dich ich eben ein Login in diesem Bereich (siehe weiter unten) möglich mache.

        Einmal alle passwörter in der Datenbank durchhashen ist für ein DMBS kein Problem und flott erledigt - 250.000 Benutzer sind eine Menge, 250.000 Datensätze in einer Tabelle eher durchschnittlich.

        Ich mache es damit man die Session-ID nicht zufällig generieren kann, mit MD5 bzw. SH1 kann ein Angreifer nicht wissen woraus diese ID besteht, evtl. ist da noch ein extra Timestamp ect.

        Eine Session-ID zu erraten ist "realtiv" einfach, wenngleich extrem unwahrscheinlich - aber für die Sicherheit des Loginverfahrens sollte sie nicht von Bedeutung sein, da das Problem an anderer Stelle liegt, wenn jemand mit einer geklauten Session ID ein bereits vollzogenes Login stehlen kann. Hier gehört einfach eine Ordentliche Session-Tabelle her - ggf. gebunden an die IP und an einen Zeitraum.

        Den timestamp hinzuzufügen ist unsinnig, da die Session ID sowieso aufgrund gewisser vom System vorgegebener Parameter generiert wird.

        Wenn du einen timesamp hinzufügst ist das erraten einer Session theoretisch sogar einfacher da sich mit einem bekannten Wert leicher Kollisionen finden lassen.