benutzer19: Variable aus PHP Session in MySQL suchen

Hallo Leute, hoffe ihr könnt mir helfen oder mir eine Seite empfehlen wie ich mit diesem Problem umgehe. Ich habe eine PHP Session welche Daten aus einem Formular nutzt. Anschließend möchte ich aber noch diese Session nutzen, um mir die ID des eingegebenen Benutzernamens wiedergeben zu können. Bin euch für Hilfe sehr dankbar. Aus irgendeinem Grund wurde die Einrückung falsch übernommen, hoffe man kann alles erkennen.


if(isset($_POST["submit"])){
  
  #USERNAME ZWISCHENSPEICHER
  $_SESSION["username"] = $_POST["username"];
  header("locaction: kopfzeile_eingeloggt.php");


<!-- FORMULAR FÜR BENUTZERDATEN-->

<form action="login.php" method="post"> <!-- Gibt Daten an bereits_angemeldet.php um eingeloggten User anzuzeigen -->
      <h1>Anmelden</h1>
      <div class="abstand_oben">
        <input type="text" formaction ="bereits_angemeldet.php"   name="username" placeholder="Benutzername" required><br>
      </div>
      <div class="abstand_oben">
        <input type="password" name="pw" placeholder="Passwort" required><br>
      </div>
      <div class="abstand_oben">
        <button type="submit" name="submit" value="Absenden" >Einloggen</button>
      </div>
      <div class="abstand_oben">
        <div class="form-check">
        <input class="form-check-input" type="checkbox" value="" name="eingeloggtbleiben"> <label class="form-check-label" for="flexCheckDefault">Eingeloggt bleiben</label><br>
      </div>
      <div class="abstand_oben">
        <a href="neueraccount.php">Noch keinen Account?</a>
      </div>
    </form>

<!--USERNAME WIRD NUN IN EINEM ANDEREN DOKUMENT ANGEZEIGT -> FUNKTIONIERT AUCH -->

<!--Benutzername-->
    <div class="col-2" >
        <?php echo 'Eingeloggt als: ' . $_SESSION["username"]; #username anzeigen


<!-- BIS HIER FUNKTIONIERT ALLES -->
<!-- JETZT KOMMT DAS PROBLEM -->

#USER ID IN VARIABLE 

$userid = mysql_query($mysqli, "SELECT ACCOUNTID from accounts  WHERE USERNAME = '$_SESSION['username']) .' " ); 
echo $userid;?>
  1. Hi,

    $userid = mysql_query($mysqli, "SELECT ACCOUNTID from accounts  WHERE USERNAME = '$_SESSION['username']) .' " ); 
    

    Vermutlich gibt es wenige Usernamen, die Am Ende ein Leerzeichen und einen Punkt haben.

    cu,
    Andreas a/k/a MudGuard

    1. Hallo MudGuard,

      vergiss die Klammer nicht 😉

      Aber er kommt ja gar nicht bis dahin…

      Rolf

      --
      sumpsi - posui - obstruxi
  2. Hallo benutzer19,

    "SELECT ACCOUNTID from accounts  WHERE USERNAME = '$_SESSION['username']) .' " 
    

    Problem 1: PHP Handbuch - Parsing von Variablen. Beispiel 15.

    In der "einfachen Syntax" kann man auf Arrays zugreifen, die Strings als Key verwenden, aber der Key darf nicht in Anführungszeichen gesetzt werden. Ansonsten gibt's einen PHP Parse Error. Einschränkung der einfachen Syntax: Der Key muss ein gültiger PHP Name sein. Keys wie "user name" oder "123Name" wären ein PHP Error.

    Diesen Error hättest Du in der Ausgabe deiner Webseite sehen müssen, weil PHP damit abbricht. Wenn PHP ihn unterschlagen hat, beschäftige Dich mit der PHP Funktion error_reporting und setze den Level während der Entwicklungszeit hoch. Hier findest Du im PHP Handbuch Beispiele dafür.

    In der "komplexen Syntax" hat man diese Einschränkung nicht. Die komplexe Syntax heißt so, weil sie komplexere Zugriffe im Variablenparsing erlaubt. Dort setzt man den Variablenzugriff in geschweifte Klammern. Ein Array-Zugriff mit einem String als Key erfordert dann die Hochkommas.

    Problem 2: Die schließende Klammer und der Punkt vor dem schließenden ' dürften einen SQL Fehler auslösen Dir die Suche verhageln, er würde sie als Teil des zu suchenden Usernamens verwenden (thanks @ Andreas). Vermutlich sind die ein Relikt aus vorherigen blinden Versuchen, die Du an Stelle von systematischem Debugging gestartet hast.

    So ist es besser:

    $sql = "SELECT ACCOUNTID from accounts  WHERE USERNAME = '$_SESSION[username]'" 
    

    oder so, mit der komplexen Syntax:

    $sql = "SELECT ACCOUNTID from accounts  WHERE USERNAME = '{$_SESSION['username']}'" 
                                                          // S           P        P  S
    

    Hier hast Du 2 Arten von '. Die beiden äußeren, die ich der Kommentarzeile mit S markiert habe, sind ganz normale Hochkommas die Teil des SQL Strings sind. Die beiden inneren sind Teil des Arrayzugriffs und gehören zur PHP Syntax. Nicht ganz einfach, da den Überblick zu behalten.

    MÖGLICHES Problem 3: Du verwendest Altlasten. Es sei denn, diese mysql_query Funktion ist deine eigene.

    Eigentlich ist die Funktion mysql_query ein Teil der veralteten, missbilligten und mittlerweile aus PHP entfernten mysql-Funktionsgruppe. Die mysql_...Funktionen wurden durch die mysqli_...Funktionsgruppe ersetzt. Da Du der mysql-Funktion $mysqli als ersten Parameter übergibst, hege ich aber die Hoffnung, dass das deine eigene Funktion ist.

    Wenn es aber doch die alte PHP Funktion ist, hast Du 2 Probleme:

    • Du verwendest PHP Version 5.x - die ist ewig nicht mehr supported. Wir sind bei PHP 8.2 angekommen
    • mysql_query aus PHP 5.x gibt eine Result-Ressource zurück, nicht direkt die gefundenen Daten. Du müsstest noch einen fetch machen.

    Rolf

    --
    sumpsi - posui - obstruxi
  3. Liebe(r) benutzer19,

    ich sehe da ein paar grundlegende Probleme, die auch sicherheitsrelevant sind!

    $_SESSION["username"] = $_POST["username"];
    

    Man sieht $_SESSION['username'] später nicht mehr an, dass hier eine Benutzereingabe unbehandelt geblieben ist und daher für SQL potenziell bösartig sein kann.

    <div class="abstand_oben">
      <input type="text" formaction ="bereits_angemeldet.php"   name="username" placeholder="Benutzername" required><br>
    </div>
    

    Wie man Formularelemente korrekt verwendet, findest Du in unserem Wiki: Formulare/Beschriftungen mit label

    $userid = mysql_query($mysqli, "SELECT ACCOUNTID from accounts  WHERE USERNAME = '$_SESSION['username']) .' " ); 
    

    Hier sehen wir, wie ein ursprünglicher $_POST-Wert, also eine Benutzereingabe(!!), auf Umwegen (über ein Umkopieren nach $_SESSION) in seiner unbehandelten Form direkt in eine SQL-Query eingefügt wird. Da fehlt die unerlässliche kontext-gerechte Behandlung!

    Man kann nur von Glück reden, dass Dein Code so noch nicht lauffähig ist, sonst wäre diese Sicherheitslücke ausnutzbar.

    Liebe Grüße

    Felix Riesterer

    1. Hallo Felix,

      Man sieht $_SESSION['username'] später nicht mehr an, dass hier eine Benutzereingabe unbehandelt geblieben ist und daher für SQL potenziell bösartig sein kann.

      D'accord, aber wie macht man es richtig? Es wäre doch auch falsch, in $_SESSION['username'] einen für SQL oder HTML behandelten Wert abzulegen.

      Eigentlich kann man doch an der Stelle gar nichts tun.

      Und später im SQL - ja, verdammt, daran hätte ich auch denken müssen. Da fehlt definitiv ein mysqli_real_escape_string drumherum, oder ein prepare…

      Rolf

      --
      sumpsi - posui - obstruxi
      1. Lieber Rolf,

        aber wie macht man es richtig?

        man behandelt den $_POST-Wert entsprechend und packt ihn dann in eine Query. Mit dieser muss man dann feststellen können, ob es einen solchen User überhaupt gibt. Wenn ja, kann man den User-Namen in die Session schreiben. Aber erst nach dieser Prüfung und nicht ungeprüft aus dem $_POST-Array. Im Code des OP ist von einer solchen Prüfung nichts zu sehen.

        Es wäre doch auch falsch, in $_SESSION['username'] einen für SQL oder HTML behandelten Wert abzulegen.

        Da stimme ich Dir vorbehaltlos zu. Der Wert in $_SESSION['username'] muss ein geprüfter Wert im Klartext sein.

        Was macht man, wenn ein User aus welchen Gründen auch immer ein oder mehrere Leerzeichen um seinen User-Namen hat? Das muss ja auch getrimmt werden. Und wenn man den $_POST-Wert schon trimmt, dann ist es noch unsinniger, $_POST direkt nach $_SESSION zu kopieren.

        Liebe Grüße

        Felix Riesterer

        1. Hallo Felix,

          Wenn ja, kann man den User-Namen (aus der DB) in die Session schreiben

          Da hätt ich auch drauf kommen können...

          Die Account-ID tuts aber auch. Oder?

          Rolf

          --
          sumpsi - posui - obstruxi
          1. Wenn ja, kann man den User-Namen (aus der DB) in die Session schreiben

            Da hätt ich auch drauf kommen können...

            Die Account-ID tuts aber auch. Oder?

            Naja. Das kommt wie immer darauf an. Braucht man dauernd nur den User-Name, dann wäre es dumm, den jedes Mal aus der Datenbank zu klauben und ergo klug, den in der Session zu haben.

            Hängen da aber Rechte oder noch mehr Eigenschaften(¹) dran, dann will man die Datenbank womöglich ohnehin öfter nach diesen fragen und es wäre ein „Klacks“, dann auch den Username zu holen.


            ¹) Nehmen wir eine Spalte mit dem schönen Name 'locked' (also „gesperrt“) - das will man „manchmal“ nicht erst beim nächsten Login wissen...

        2. Was macht man, wenn ein User aus welchen Gründen auch immer ein oder mehrere Leerzeichen um seinen User-Namen hat?

          Nö. Man stellt von Anfang an sinnvolle Regeln auf, was in einem Username erlaubt ist.

          Generell (a.k.a: „oft“, „im Hinblick auf Interoperabilität“) ist es eine gute Idee, folgendes vorzuschreiben:

          • Mindestlänge: 3 Zeichen
          • Erlaubte Zeichen: ASCII-Buchstaben, Ziffern, "_"
          • Beginnt mit: Buchstabe
          • nur kleine Buchstaben (oft werden große und kleine nicht unterschieden - manchmal können das z.B. Datenbanken nicht)

          Zudem würde ich diese Eingabe stets trimmen (Copy & Paste-Fehler), Groß/Klein-Schreibung stillschweigend durch Umwandlung sanieren, eventuell nicht erlaubte Zeichen durch Löschen „sanieren“ - macht man die beiden letzteren, dann muss man das aber künftig stets tun!

          In PHP kann das so oder so ähnlich aussehen:

          <?php
          
          function saniticeUsername(
          	$s,
          	$returnSanitized = false,
          	$minLength = 3,
          	$toLower = true,
          	$RegexAllowedChars = 'A-Za-z0-9_',
          	$RegexAllowedFirstchar = 'A-Za-z'
          ) {
          
          	#first: trim the input (blanks @start or end of a name are in most cases copy & paste-errors - or typos
          	$s = trim( $s );
          
          	# check the minlenght:
          
          	if ( $minLength > strlen( $s ) ) {
          		$out['err'] ='to short';
          		$out['Username'] = false;
          		$out['is_sanitized'] = false;
          		return $out;
          	}
          	
          	#sanitice to lower:
          	if ( $toLower ) {
          		$s = strtolower( $s );
          	}
          	
          	# mask meta chars in regex:
          	$SpecialChars = explode( ' ', '/ \ [ ^ ]' );
          	foreach ( $SpecialChars as $char ) {
          		$SpecialCharsReplacements[] = '\\' . $char;
          	}
          	$pattern = '/[^' . str_replace( $SpecialChars, $SpecialCharsReplacements, $RegexAllowedChars ) . ']/'; 
          	
          	# remove all not allowed chars
          	$sanitized = preg_replace( $pattern, '', $s );
          	
          	if ( $minLength > strlen( $sanitized ) ) {
          		$out['err'] = 'to short';
          		$out['Username'] = false;
          		$out['is_sanitized'] = false;
          		return $out;
          	}
          	
          	# check the first char and make returns:
          	$firstChar = $s[0];
          	$pattern = '/[' . str_replace( $SpecialChars, $SpecialCharsReplacements, $RegexAllowedFirstchar ) . ']/'; 
          	
          	if ( 1 !== preg_match( $pattern, $firstChar ) ) {
          		$out['err'] ='first char not allowed';
          		$out['Username'] = false;
          		$out['is_sanitized'] = false;
          		return $out;
          	} 
          	
          	if ( $returnSanitized ) {
          		if ( $s != $sanitized ) {
          			$out['is_sanitized'] = true;
          		} else {
          			$out['is_sanitized'] = false;
          		}
          		$out['err'] = false;
          		$out['Username'] = $sanitized;
          		return $out;
          	} else {
          		if( $s !== $sanitized) {
          			$out['err'] = 'not allowed chars in username';
          			$out['Username'] = false;
          			$out['is_sanitized'] = false;
          			return $out;		
          		} else {
          			$out['err'] = false;
          			$out['Username'] = $sanitized;
          			$out['is_sanitized'] = false;		
          			return $out;
          		}
          	}
          }
          
          ################################## Tests: #############################
          header('Content-Type: text/plain; charset=utf-8');
          
          $userNames=[
          '0joerg',
          'Jörg',
          'Joerg',
          '<Joerg>',
          ' Jo',
          ' Jörg',
          ' Joerg',
          ' <Joerg>',
          ' Jo',
          'Jörg ',
          'Joerg ',
          '<Joerg> ',
          'Jo ',
          ' Jörg ',
          ' Joerg ',
          ' <Joerg> ',
          ' Jo ',
          '0joerg',
          ' 0joerg',
          'joerg$',
          'joe@rg'
          ];
          
          echo "Test: sanitized names allowed =============================\n";
          
          foreach ( $userNames as $username ) {
          	echo $username . ':'. PHP_EOL;
          	var_dump( saniticeUsername( $username, true ) );
          	echo PHP_EOL;  
          }
          
          echo "Test: sanitized names forbidden =============================\n";
          
          foreach ( $userNames as $username ) {
          	echo $username . ':'. PHP_EOL;
          	var_dump( saniticeUsername( $username ) );
          	echo PHP_EOL;  
          }
          
          echo "Test: $ + @ allowed =============================\n";
          foreach ( $userNames as $username ) {
          	echo $username . ':'. PHP_EOL;
          	var_dump( saniticeUsername( $username, false, 3, true, 'A-Za-z0-9@$_' ) );
          	echo PHP_EOL;  
          }
          
          
          1. Lieber Raketenwilli,

            Nö. Man stellt von Anfang an sinnvolle Regeln auf, was in einem Username erlaubt ist.

            es ging mir darum, dass beim Login-Test der vom User eingegebene Login-Name mit führenden oder nachfolgenden Leerzeichen eingegeben worden sein könnte. In diesem Fall ist es eine Frage des Komforts, ob das Script solchen Blödsinn brav trimmt, oder dem User einen fehlgeschlagenen Login entgegen schleudert. Und wenn man schon trimmt (und vielleicht alles noch auf lower-case normalisiert), dann hantiert man eben nicht mehr direkt mit $_POST, sondern mit einer Variable, deren Inhalt eben „gereinigt“ ist.

            function saniticeUsername(
            

            Da kriegt der Englischlehrer in mir aber ein Zucken im Korrigierfinger... sanitize

            Liebe Grüße

            Felix Riesterer

            1. es ging mir darum, dass beim Login-Test der vom User eingegebene Login-Name mit führenden oder nachfolgenden Leerzeichen eingegeben worden sein könnte.

              Zu welchem Zeitpunkt? Beim Login oder beim Anlegen des Accounts?

              Auf was ich geantwortet habe war:

              Was macht man, wenn ein User aus welchen Gründen auch immer ein oder mehrere Leerzeichen um seinen User-Namen hat?

              Das lässt nämlich offen, ob der gespeicherte Username gemeint ist oder das, was beim Anmelden eingetippt oder reinkopiert wurde.

              In diesem Fall ist es eine Frage des Komforts, ob das Script solchen Blödsinn brav trimmt, oder dem User einen fehlgeschlagenen Login entgegen schleudert.

              Genau! Nur wenn man Leerzeichen am Anfang, am Ende (am einfachsten; im gesamten) gespeicherten Username ausschließt kann man trimmen. Deshalb schrieb ich ...

              Nö. Man stellt von Anfang an sinnvolle Regeln auf, was in einem Username erlaubt ist.

              1. Hallo,

                Nö. Man stellt von Anfang an sinnvolle Regeln auf, was in einem Username erlaubt ist.

                warum wollt ihr den Benutzernamen überhaupt einschränken? Ich gehe mal davon aus, dass ein Nutzer beim Registrieren seines Accounts schon im eigenen Interesse Zeichen vermeidet, die er mit den üblicherweise verwendeten Endgeräten nicht oder nur sehr umständlich eingeben kann. Also wird ein Mitteleuropäer vermutlich keine koreansichen oder kyrillischen Zeichen verwenden, und vermutlich auch keine "Sonderzeichen"[1], die man mit einem üblichen Tastaturlayout (schließt Bildschirmtastatur ein) nicht direkt eingeben kann.

                Aber unabhängig von der Eingabe sollte das Backend meines Erachtens mit beliebigen Benutzernamen umgehen können. Auch Little Bobby Tables. Man muss eben nur wieder kontextspezifisch maskieren oder codieren. Ein DBMS, das nicht zwischen Groß- und Kleinbuchstaben unterscheiden kann? Ich bitte dich! Das war doch wohl nur ein konstruiertes, an den Haaren herbeigezogenes Beispiel.

                Ich würde daher nur zwei Regeln einfordern:

                • eine Längenbeschränkung, die aber relativ hoch ansetzen
                • Whitespace am Anfang oder am Schluss werden ignoriert (trim)

                Dann kann sich meinetwegen auch jemand mit dem Benutzernamen O'Malley oder α et ω registrieren und wieder einloggen.

                Einen schönen Tag noch
                 Martin

                --
                Dass Dr. Oetker in Amerika eine Puddingmine entdeckt und damit seine ersten Millionen gemacht hat, ist nur ein Gerücht.

                1. Im landläufigen Verständnis dieses Begriffs, also nicht Buchstaben oder Ziffern ↩︎

                1. Hallo Der,

                  Ein DBMS, das nicht zwischen Groß- und Kleinbuchstaben unterscheiden kann?

                  Kann schon, aber ggf. nicht will. In MYSQL ist meines Wissens per Default eine _ci Collation eingestellt.

                  Und im SelfWiki habe ich durchaus mit "Rolf b" vs "Rolf B" zu kämpfen.

                  Das Problem mit Spaces im Namen siehst Du in der ersten Zeile dieses Postings. Wenn ich Dir antworte, muss ich immer manuell korrigieren. Oder die Anrede ist falsch.

                  Rolf

                  --
                  sumpsi - posui - obstruxi
                2. Hallo Der Martin,

                  um das "Hallo Der" nochmal aufzugreifen - das war die Foren-Defaulteinstellung mit "Hallo {$vname}" als Default. Auf "Hallo {$name}" geändert. Aber ob ich damit glücklicher werde?

                  Rolf

                  --
                  sumpsi - posui - obstruxi
                  1. Hallo Rolf,

                    um das "Hallo Der" nochmal aufzugreifen - das war die Foren-Defaulteinstellung mit "Hallo {$vname}" als Default.

                    als Default?? Default ist meines Wissens "gar nichts", also keine vordefinierte Anredefloskel.

                    Aber mir war schon klar, wie das zustandekam. Das bin ich gewöhnt. 😉

                    Auf "Hallo {$name}" geändert. Aber ob ich damit glücklicher werde?

                    Dann redest du die Leute in Zukunft mit Vor- und Nachnamen an. Das mag hier und da hölzern klingen, aber zumindest benutzt du dann den Namen, den sie selbst auch gewählt haben. Also Ästhetik hin oder her, es ist zumindest eine respektvolle Einstellung.

                    Einen schönen Tag noch
                     Martin

                    --
                    Dass Dr. Oetker in Amerika eine Puddingmine entdeckt und damit seine ersten Millionen gemacht hat, ist nur ein Gerücht.
            2. Hallo

              Da kriegt der Englischlehrer in mir aber ein Zucken im Korrigierfinger... sanitize

              Wohl eher der Amerikanischlehrer. sanitise *scnr*

              Mir wurde in der Schule britisches Englisch vermittelt, dass natürlich mittlerweile auch mit reichlich amerikanischem Englisch durchsetzt ist.

              Tschö, Auge

              --
              200 ist das neue 35.