katzefutter123: PHP-Code optimierung

Hallo liebes SelfHTML-Forum, ich beschäftige mich seid einiger Zeit mit PHP.

Inwieweit kann ich den dieses einloggscript überarbeiten? Ist es schön programmiert?

Ich bitte um Rückmeldungen und verbesserungsvorschäge :)

<?php
	session_start();
	$_SESSION['online'] =0;
?>
<html>
<head>
  <title>login</title>
  <meta http-equiv="Content-Type" content="text/html;
  charset=iso8859-1">
</head>

<body>
<form method="post" action="">
<table>
<tr><td>	Vorname  : </td><td> <input type="text" name="name"></td></tr>
<tr><td>	Passwort : </td><td><input type="password" name="pass" ></td></tr>
<tr><td>	<input type="submit" name="reg" value="Registrierung"></td></tr>
</table>
</form>
</body>
</html>
 
<?php
if (isset($_POST['name']))
{
	
	@$name=$_POST['name'];
	@$pass=$_POST['pass'];
		
	$server="localhost";
	$benutzer="root";
	$passwort="";
	$datenbank="test";
	
	$verbindung = mysqli_connect($server,$benutzer,$passwort) or die("Keine Serververbindung");
	$db=mysqli_select_db($verbindung,$datenbank);
	if($db==TRUE){
			if($_POST['name']!="" && $_POST['pass']!="" )
			{
				$sql = "SELECT * FROM login WHERE Name='{$name}' AND Passwort ='{$pass}'; ";
				$abfrage = mysqli_query($verbindung, $sql);
				if(mysqli_num_rows($abfrage) >= 1)
				{
					$_SESSION['online'] =1;
					echo "LOGIN erfolgreich</br>";
					echo "<form method='post' action='weiter.php'> <input type='submit' name='weiter' value='WEITER'></td></tr>";
					echo "</form>";
				}
				
			}
		
	
	}
	mysqli_close($verbindung);
}	
?>
  1. Tach!

    Inwieweit kann ich den dieses einloggscript überarbeiten?

    Es ist kaputt. Man kann sich anmelden, ohne ein Passwort zu kennen. Beachte den Kontextwechsel, um SQL-Injection zu vermeiden.

    dedlfix.

    1. Tach!

      Inwieweit kann ich den dieses einloggscript überarbeiten?

      Es ist kaputt. Man kann sich anmelden, ohne ein Passwort zu kennen. Beachte den Kontextwechsel, um SQL-Injection zu vermeiden.

      dedlfix.

      Wie meinst du das? hier funktioniert alles:O?

      1. Tach!

        Es ist kaputt. Man kann sich anmelden, ohne ein Passwort zu kennen. Beachte den Kontextwechsel, um SQL-Injection zu vermeiden.

        Wie meinst du das? hier funktioniert alles:O?

        Lies den verlinkten Artikel, da steht drin, was das Problem ist und wie Lösungen aussehen.

        Gib mal im Namensfeld ein admin' -- ein und ein falsches Passwort (wenn admin einer der Nutzernamen ist).

        dedlfix.

        1. Gib mal im Namensfeld ein admin' -- ein und ein falsches Passwort (wenn admin einer der

          in dem Fall wird mir eine Warnung ausgegeben und ich werde nicht eingeloggt... ich lese mir aber erstmal den artikel durch

          1. Gib mal im Namensfeld ein admin' -- ein und ein falsches Passwort (wenn admin einer der

            in dem Fall wird mir eine Warnung ausgegeben und ich werde nicht eingeloggt... ich lese mir aber erstmal den artikel durch

            Hm.

            Eingabe admin'--:

            SQL: 
            "SELECT * FROM login WHERE Name='{$name}' AND Passwort ='{$pass}'; ";
            
            -> "SELECT * FROM login WHERE Name='{admin'--}' AND Passwort ='{$pass}'; ";
            

            Ok. Das geht nicht. Aber:

            Eingabe in user: egal' or ''='';#; als Passwort: egal

            mysql> select * from users where username='{egal' or ''='';#} and password='{egal}';
            +----+----------+--------------------------------------------------------------+
            | id | username | password                                                     |
            +----+----------+--------------------------------------------------------------+
            |  1 | foo      | $2a$10$Ktv2Ar1JEm2W7gie/7DNwut1vfLNDj6G7uzux6P..EK557PL0RLM6 |
            +----+----------+--------------------------------------------------------------+
            1 row in set (0.01 sec)
            

            geht ab wie Schmitts Katze. Bitte lese über mysqli_real_escape_string nach und hashe das Passwort.

            1. Tach!

              SQL: 
              "SELECT * FROM login WHERE Name='{$name}' AND Passwort ='{$pass}'; ";
              
              -> "SELECT * FROM login WHERE Name='{admin'--}' AND Passwort ='{$pass}'; ";
              

              Die geschweiften Klammern werden von PHP weginterpretiert. Man kann sie meist weglassen (wie auch in diesem Fall), man kann sie aber auch schreiben, um Variablennamen innerhalb von Strings eindeutig zu kennzeichnen, zum Beispiel wenn es danach gleich mit anderen Buchstaben weitergeht.

              dedlfix.

              1. Die geschweiften Klammern werden von PHP weginterpretiert. Man kann sie meist weglassen (wie auch in diesem Fall), man kann sie aber auch schreiben, um Variablennamen innerhalb von Strings eindeutig zu kennzeichnen, zum Beispiel wenn es danach gleich mit anderen Buchstaben weitergeht.

                Aha. Ich verbinde dann immer Strings, damit ich mir nicht so viel über die Eigenarten einzelner Programmiersprachen merken muss. Du hättest mitteilen sollen, dass er dann dennoch mit der Eingabe von egal' or ''='';# als Benutzername Boris Becker(*) ist.


                *) Wurde nach Ansicht einiger berühmter mit "Bin ich mit schon drin?" als mit Tennis oder merkwürdigen Behauptungen über merkwürdige Vorgänge in Besenkammern.

                1. Danke an alle, ich werde jetzt mal versuchen alles umzuändern...Inwieweit kann man an die Daten kommen wenn ich sie nicht Hashe?

                  1. Inwieweit kann man an die Daten kommen wenn ich sie nicht Hashe?

                    Da fällt so viel ein, dass ich sehr froh bin, weder Architekt noch Bauingenieur zu sein.

                    • Durch einen Einbruch in Deinen Webserver. Ist ja nicht undenkbar, dass Du ähnlich unsicheres Zeug programmierst.
                    • Jemand knackt deine PhpMyAdmin-Installation
                    • Du meldest Dich in einem Hotel-WLAN an. Die Verbindung ist unverschlüsselt und der Nachtportier ist Jurastudent, kann trotzdem mit wireshark umgehen und bekommt mehr Geld für die Daten als für den Job. (das gilt allerdings immer, ist vom hashen unabhängig)
                    • Einem Mitarbeiter Deines Providers wird gekündigt und er nimmt Daten mit nach Hause.
                    • Die Staatsanwaltschaft beschlagt den Server bzw. die Festplatten wegen eines anderen kriminellen Kunden, sein krimineller Anwalt erhält Akteneinsicht. Die Daten werden genutzt oder verkauft. (Denke nicht, das gibt es nicht...)
                    • Ein Geheimdienst verschafft sich Zugriff, ein Mitarbeiter hält sich für die kriminelle Version von Snowden. Oder der Geheimdienst hat "gute" (Für Dich: höchst schädliche) Ideen, was er damit anfangen kann. Im Falle einer Kontrolle hat der Dienst dann eine Weltraumtheorie (BND) oder die Akten verschwinden, was mit einer besonderen Art der partiellen Gedächntnislosigkeit gepaart wird (Verfassungsschutz). (Denke nicht, das gibt es nicht...)
                    • Dein Datenbank-Passwort wird erraten oder gerät durch die obigen Umstände in die Wildbahn.
                    • Dein Datenbank-Passwort kann als Text ausgelesen werden. Und das musst Du im Klartext speichern. (Genauer genommen kannst Du es durchaus verschlüsseln, musst dann aber den Schlüssel dazu im Klartext speichern...)
                  2. Inwieweit kann man an die Daten kommen wenn ich sie nicht Hashe?

                    Das ist die falsche Frage. Wenn du Daten nicht im Klartext benötigst (wie eben ein Passwort), dann speichere sie nicht im Klartext. Im Auto schnallst du dich schließlich auch an, obwohl du dir ziemlich sicher bist, niemals im Leben gegen einen Baum zu fahren.

                    Dass eine Datenbank gegen Fremdzugriffe sicher sei, haben schon ganz andere gedacht; Meldungen über den Verlust von Kundendaten gehen regelmäßig durch die einschlägige Presse. Zuletzt traf es einen größeren Versandhändler namens Pollin, dem wohl eine sechsstellige Zahl an Datensätzen entfleuchte. Und wie es um deine Kenntnisse in Sachen Sicherheit steht, solltest du deinem fehlgeschlagenen Versuch, ein Login-Formular zu basteln, sehen.

                    Füge Benutzernamen und Passwort zu einem Wert zusammen, bilde eine Prüfsumme (Neudeutsch Hash) drüber, das Ganze gerne auch drei Dutzend mal hintereinander, und speichere das Ergebnis bzw. vergleiche es mit der gespeicherten Prüfsumme.

                    Warum die Prüfsumme aus Name und Passwort? Weil vorberechnete Prüfsummentabellen existieren, in denen für alle möglichen und viele unmögliche Wörter die Prüfsummen schon eingetragen sind – braucht man nur reingucken, schon hat man das Passwort zur Prüfsumme. Der Aufwand für solche Tabellen ist riesig, war aber machbar. Der Aufwand, für Kombinationen aus Wörten und allen erdenklichen Namen (oder anderen Werten, etwa der E-Mail-Adresse) Prüfsummen vorauszuberechnen, ist hingegen zu groß. Statt Namen oder Adresse wird auch irgendein Zufallswert genutzt, der muss dann allerdings nochmal extra gespeichert werden, Nutzername oder Adresse liegen hingegen schon vor. Dieser Vorgang wird "salzen" genannt (für ganz echte Experten: "salt").

                    Aus dem gleichen Grund –den Aufwand für vorberechnete Tabellen so groß wie möglich zu machen– wird die Summenbildung gerne etliche Male mit sich selbst wiederholt: Zuerst Passwort und Kombinationswert verbinden, Prüfsumme aus der Kombination bilden. Dann die entstandene Prüfsumme mit dem Kombinationswert verbinden und wieder die Prüfsumme bilden, dies x-mal wiederholen. Beim Anlegen oder Anmelden bedeuten solche Wiederholungen nur den Bruchteil einer Sekunde Verzögerung. Soll eine Tabelle für Abermillionen Passwörter vorausberechnet werden, summieren sich diese Bruchteile hingegen zu einer gigantischen Wartezeit.

                    1. Dass eine Datenbank gegen Fremdzugriffe sicher sei, haben schon ganz andere gedacht; Meldungen über den Verlust von Kundendaten gehen regelmäßig durch die einschlägige Presse. Zuletzt traf es einen größeren Versandhändler namens Pollin, dem wohl eine sechsstellige Zahl an Datensätzen entfleuchte.

                      Da gehe ich zu fast 100% mit. Einziger Zweifel: War Pollin der wirklich die zu letzt getroffene Bude? Das wird mit jedem beliebig kurzem Zeitabschnitt unwahrscheinlicher.

                      Aus dem gleichen Grund –den Aufwand für vorberechnete Tabellen so groß wie möglich zu machen– wird die Summenbildung gerne etliche Male mit sich selbst wiederholt:

                      Ja. Aber nicht, jedenfalls nicht ohne Not, selbst erfinden.

                      Zuerst Passwort und Kombinationswert verbinden, Prüfsumme aus der Kombination bilden. Dann die entstandene Prüfsumme mit dem Kombinationswert verbinden und wieder die Prüfsumme bilden, dies x-mal wiederholen.

                      Nein! Nichts Eigenes erfinden - jedenfalls nicht ohne Not. Und die herrscht gerade nicht mehr, denn:

                      PHP: Password hashing

                      existieren nunmehr schon recht lang und kümmern sich auch um das "salzen" - was aber der noch älteren Literatur nicht bekannt ist. Findet man Funktionen wie md5() oder sha1() (oder eben die Speicherung im Klartext oder eben solche eigenen Konstrukte, dann gilt es das Buch zuzuschlagen oder sich eine andere Webseite zu suchen.

                      Der im Prinzip sehr gut mögliche Nachbau der obigen Funktionen mit cyrypt() und ein wenig Zauberei mit Zufallsfunktionen und Zeichensalat um einen ausreichend zufälligen Salt zu bauen ist inzwischen zeitlich überholt, man sollte dann lieber ein Update der PHP-Installation in Angriff nehmen.

                      Dann wäre da noch die Sache mit dem Speicherort der Zugangsdaten zur Datenbank. Die gilt aber auch für Dateien mit gehaschten Passwörtern.

                      1. Dann wäre da noch die Sache mit dem Speicherort der Zugangsdaten zur Datenbank. Die gilt aber auch für Dateien mit gehaschten Passwörtern.

                        Das PDF ist zwar ein guter Anfang, aber allein beim schnellen Drüberlesen sind mir mehrere Fehler bzw. Probleme aufgefallen.

                        • Der Dateiname mit .ht_irgendwas führt dazu, dass die Datei wegen des Unterstrichs als versteckt behandelt wird. Problematisch, wenn nicht gleich klar ist, warum der Ordner "leer" ist, falls ein unerfahrener Entwickler am Projekt startet. Schutzwirkung: eher gering (Webserver- und configabhängig)
                        • Keine Short Tags: Die sollte man unabhängig von dieser Problemstellung ohnehin nicht mehr verwenden
                        • Ablage außerhalb DocumentRoot: sinnvolle Maßnahme - bester Ratschlag. Wenn der Hoster keinen Ordner außerhalb DocumentRoot anbietet, diesen vielleicht wechseln ;)
                        • .htaccess: sinnvolle Maßnahme
                        • index.html Fehlerhafter Statuscode, Formulierungen zweifelhaft - besser gar nix schreiben (leere index.html) als Angreifer neugierig machen
                        • Dateirechte: hängen vom Hoster und User, unter dem der Webserver läuft, ab - Pauschal Lesezugriff für alle User am Server(!) freischalten bedenklich
                        • Pfade fix auf $_SERVER['DOCUMENT_ROOT'] beziehen - bedenklich, schränkt die Portabilität der Anwendung (Installation ins Subverzeichnis, ...) unnötig ein

                        just my 50 cent ;)

                        1. Du hast natürlich mit vielen der Aussagen recht (die einzige echte Ausnahme steht unten). Das Werk stammt aus dem Dezember 2009 und braucht- wegen mysqli und der in PHP7 entfernten mysql-Funktionen ohnehin dringend ein Update. Was mich frappiert ist folgendes: Alles was Du aufzählst ist mir auch beim Durchlesen aufgefallen. Inklusive des Rates zum Wechseln des Hosters..., exklusive des unten stehenden und exklusive der Geschichte um $_SERVER['DOCUMENT_ROOT']. Dafür aber inklusive des fehlenden Hinweises darauf, dass man die Einstellungen auch in einer PHP.ini setzen kann.

                          • Der Dateiname mit .ht_irgendwas führt dazu, dass die Datei wegen des Unterstrichs als versteckt behandelt wird.

                          Die Regel im Apache (2.4) heisst:

                          <FilesMatch "^\.ht">
                          	Require all denied
                          </FilesMatch>
                          
                          1. Hallo Querdenker,

                            • Der Dateiname mit .ht_irgendwas führt dazu, dass die Datei wegen des Unterstrichs als versteckt behandelt wird.

                            Die Regel im Apache (2.4) heisst:

                            <FilesMatch "^\.ht">
                            	Require all denied
                            </FilesMatch>
                            

                            Apache ist bei weitem nicht der einzige Webserver.

                            LG,
                            CK

                              • Der Dateiname mit .ht_irgendwas führt dazu, dass die Datei wegen des Unterstrichs als versteckt behandelt wird.

                              Die Regel im Apache (2.4) heisst:

                              <FilesMatch "^\.ht">
                              	Require all denied
                              </FilesMatch>
                              

                              Apache ist bei weitem nicht der einzige Webserver.

                              Oh. Ernsthaft? Ich dachte, ich mache hier die Späße.

                      2. Hallo Querdenker,

                        Zuerst Passwort und Kombinationswert verbinden, Prüfsumme aus der Kombination bilden. Dann die entstandene Prüfsumme mit dem Kombinationswert verbinden und wieder die Prüfsumme bilden, dies x-mal wiederholen.

                        Nein! Nichts Eigenes erfinden - jedenfalls nicht ohne Not. Und die herrscht gerade nicht mehr, denn:

                        Doch, die herrscht. Die PHP-API unterstützt nur bcrypt, bei dem die grundlegenden Probleme des Passwort-Hashings nicht behoben werden: bcrypt ist durch FPGAs und ASICS implementierbar, womit die Verarbeitungsgeschwindigkeit massiv steigt. Es wird in der Szene zur Zeit PBKDF2 empfohlen, eventuell noch scrypt mit hohen Parametern. Ich hoffe, dass Argon2 sich als zuverlässig erweisen wird.

                        Nichtsdestotrotz hast du natürlich recht, dass man die Hashing-Funktionen nicht selber implementieren sollte.

                        LG,
                        CK

                        P.S. willst du die Scharade nicht langsam aufgeben? Es ist offensichtlich.

                        1. Doch, die herrscht. Die PHP-API unterstützt nur bcrypt, bei dem die grundlegenden Probleme des Passwort-Hashings nicht behoben werden: bcrypt ist durch FPGAs und ASICS implementierbar, womit die Verarbeitungsgeschwindigkeit massiv steigt.

                          Mag sein, dass es Fortschritte gibt. Aber dank password_needs_rehash() kann man automatisch und ohne manuelles Zutun feststellen, ob der Hash der derzeit beste (implementierte) ist und das (soeben richtig eingegebene) Passwort neu hashen. Zumindest die Passwörter der aktiven Benutzer werden dann also mit dem derzeit besten (implementierten) Algorithmus gehasht gespeichert.

                          Im Wiki gab es mal eine Quelle, die zeigte das sehr schön:

                          Ein stilles Update können Sie auch durchführen: Nach einem erfolgreichen Login führen Sie folgendes aus:

                          if ( password_needs_rehash($hash, PASSWORD_DEFAULT) ) {
                              $newHash = password_hash( $password, PASSWORD_DEFAULT );
                              /* Jetzt den neuen Hash speichern! */ 
                          };
                          

                          Bei Benutzern, die X Jahre lang nicht da waren kann man ja über eine Löschung nachdenken. Einfach das letzte erfolgreiche Login speichern... und per cronjob monatlich einmal nachsehen. Ob man dann ein Mail senden und noch eine gewisse Frist stillhalten will, den Zugang komplett löscht oder nur das Passwort (eventuell nur die mit dem schlechten Hash) ist eine Frage der Geschäftslogik, welche die Geschäftsleitung unter Beachtung aller eigenen Interessen (also auch der Compliance) und der Gesetze beantworten muss.

                          1. Hallo Spaßvogel,

                            Doch, die herrscht. Die PHP-API unterstützt nur bcrypt, bei dem die grundlegenden Probleme des Passwort-Hashings nicht behoben werden: bcrypt ist durch FPGAs und ASICS implementierbar, womit die Verarbeitungsgeschwindigkeit massiv steigt.

                            Mag sein, dass es Fortschritte gibt. Aber dank password_needs_rehash() kann man automatisch und ohne manuelles Zutun feststellen, ob der Hash der derzeit beste (implementierte) ist und das (soeben richtig eingegebene) Passwort neu hashen.

                            Ja. Ich habe da nicht widersprochen und mehrfach erwähnt, dass man diese API verwenden sollte. Was ich kritisiert habe war die Aussage, es herrsche keine Not.

                            LG,
                            CK

                            1. Was ich kritisiert habe war die Aussage, es herrsche keine Not.

                              Ich denke, bis zur "Not" ist es noch ein Stück hin und bis dahin hoffentlich ein neuer Algo in PHP implementiert.

          2. Tach!

            Gib mal im Namensfeld ein admin' -- ein und ein falsches Passwort (wenn admin einer der

            in dem Fall wird mir eine Warnung ausgegeben und ich werde nicht eingeloggt... ich lese mir aber erstmal den artikel durch

            Das -- hätte eigentlich ein Kommentar werden sollen, wodurch der Rest der Zeile ignoriert werden würde. Aber da man Kommentare in SQL nur sehr selten verwendet, hatte ich nicht die richtige Syntax im Kopf. Nach den beiden Minus muss ein Leerzeichen folgen. Oder aber man nimmt gleich ein # ohne weitere Kopfstände als Kommentarzeichen.

            Sinn der Übung war jedenfalls, wenn du das noch nicht erkannt hast, die Prüfung des Passworts zu deaktivieren, so dass man sich als jeder beliebige Nutzer anmelden kann. Das darf natürlich aus deiner Sicht nicht sein und deswegen musst du dafür sorgen, dass solche SQL-Injection nicht stattfinden kann. Injectionen solcher Art gibt es nicht nur bei SQL sondern überall, wo Daten und Code gemischt werden. Es ist Grundlagenwissen für sichere Programmerstellung, damit umgehen zu können. Und das versucht dir der Kontextwechsel-Artikel beizubringen.

            dedlfix.

  2. @@katzefutter123

    ich beschäftige mich seid einiger Zeit mit PHP.

    Es wäre gut, sich vorher mit HTML zu beschäftigen.

    <html>
    

    Du schickst Browser in den Quirksmodus; das sollte man durch Angabe von <!DOCTYPE html> vermeiden.

    Du schickst mobile Browser in den 960px-Modus; das sollte man durch Angabe von <meta name="viewport" content="width=device-width, initial-scale=1.0"> vermeiden.

    Sonst ist’s blöd.

      <meta http-equiv="Content-Type" content="text/html;
      charset=iso8859-1">
    

    Ist iso8859-1 denn ein gültiger Alias für iso-8859-1?

    Außerdem ist ISO 8859-1 von vorgestern. Als Zeichencodierung sollte UTF-8 verwendet werden, immer und überall.

    <tr><td>	Vorname  : </td><td> <input type="text" name="name"></td></tr>
    <tr><td>	Passwort : </td><td><input type="password" name="pass" ></td></tr>
    

    Die Eingabefelder haben keine Beschriftung. Müssen sie aber haben. Also:

    <tr>
      <th><label for="name">Vorname:</label></th>
      <td> <input type="text" id="name" name="name"></td>
    </tr>
    <tr>
      <th><label for="pass">Passwort:</label></th>
      <td><input type="password" id="pass" name="pass"></td>
    </tr>
    

    Bei der Gelegenheit habe ich auch gleich die Kopfzellen als th ausgezeichent und die Plenks vor den Doppelpunkten entfernt.

    <tr><td>	<input type="submit" name="reg" value="Registrierung"></td></tr>
    

    Für Buttons gibt es das button-Element:

    <button type="submit" name="reg">Registrierung</button>
    

    Wobei die Angabe von name hier wohl überflüssig und type="submit" optional ist (weil Default).

    Der Button hat in der Tabelle auch nichts zu suchen. (Wobei die Tabelle an sich schon fragwürdig ist; aber mit Augen zu noch vertretbar.)

    </html>
    
    <?php
    

    Nach dem schließenden </html>-Tag sollte nichts mehr im generierten HTML stehen. Das Tag gehört hinter den PHP-Block.

    LLAP 🖖

    --
    “The best way to help people learn: answer their coding question an hour later, they’ll have likely figured it out by then.” —Todd Motto
    Selfcode: sh:) fo:} ch:? rl:) br:> n4:& va:| de:> zu:} fl:{ ss:| ls:# js:|
  3. Hallo Katzefutter,

    neben allen anderen Hinweisen, die schon gegeben wurden, möchte ich Dir auch noch gebundene Parameter statt SQL-Bastelei ans Herz legen. Das erspart einiges an Injektions-Kopfschmerzen.

    Rolf