kleiner hobbit: PHP MySQL Datenbank durchsuhen

Hallo,

tut mir leid, wenn ich euch wiedermal störe,.... aber ich habe da noch ein weiteres problem.

undzwar stöbere ich meine Datenbank mit diesem Code durch:

$abfrage = "SELECT * FROM `table` WHERE data1 LIKE '%".$_POST[search]."%' or data2 LIKE '%".$_POST[search]."%'";  
$ergebnis = mysql_query($abfrage);  
  
while($row = mysql_fetch_array($ergebnis))  
{  
echo "  
	<tr>  
		<td>.$row["einfeldausdatenbank"].</td>  
	</tr>  
	";  
}

<input type="text" name="search" /><input type="submit" />

wenn ich nun die seite aufrufe ohne irgendeine eingabe gamacht zuhaben, wo dieser code ist, werden alle datensätze angezeigt. warum? wie kann ich es unterbinden, das die datensätze "erstmal" nicht angezeigt werden???

Vielen dank
kleiner hobbit

  1. Hello,

    [code lang=php]$abfrage = "SELECT * FROM table WHERE data1 LIKE '%".$_POST[search]."%' or data2 LIKE '%".$_POST[search]."%'";
    $ergebnis = mysql_query($abfrage);

    Au Backe! Da kann man ja in $_POST['search'] alles reinschreiben... Auch böse Zeichenketten!

    Außerdem solltest Du eben nicht $_POST[search], sondern $_POST['search'] nehmen, wenn search keine Konstante von PHP ist, die Du entsprechend festgelegt hast.

    wenn ich nun die seite aufrufe ohne irgendeine eingabe gamacht zuhaben, wo dieser code ist, werden alle datensätze angezeigt. warum? wie kann ich es unterbinden, das die datensätze "erstmal" nicht angezeigt werden???

    Bevor Du suchst, solltest Du abfragen, ob dein $_POST['swarch'] etwas enthält.

    Außerdem baut man solche Prüfungen besser so auf, dass man ein Eingabefeld und vier oder fünf Postbuttons oder Radio-Alternatives bereitstellt:

    |Eingabefeld|  [stimmt genau überein] [beginnt mit] [enthält] [endet mit] [ist leer]

    Das kann man auch noch negieren.

    Dann kann man im Script genauer bestimmen, wie das Statement aufgebaut sein soll.
    Diese Alternativen kann man natürlich auch verschlüsseln mit '+' '-' ..., so wie Google und Co das machen. Das ist aber eigentlich nicht komfortabel.

    Das '%' und das '_' muss man ggf. maskieren, wenn man nicht will, dass der user das vons sich aus benutzt bei einer Abfrage mit LIKE.

    Liebe Grüße aus Syburg bei Dortmund

    Tom vom Berg

    --
    Nur selber lernen macht schlau
    http://bergpost.annerschbarrich.de
    1. Hallo Tom,

      vielen Dank für Deine Tipps.

      Eine Sache möchte ich gerne wissen. Was soll ich den anstelle von $_POST[search] schreiben, damit es etwas sicherer ist.

      Da ich sehr neugierig bin, möchte ich gerne mal so eine böse Zeichenkette ausprobieren, könntest Du mir eine schicken, aber die soll nicht gleich meine tabelle löschen....

      Gibt es einige php flags wie register_globals die man noch ausschalten sollte?

      Gibt es einen einfachen schutz vor sql-injections???

      vielen dank.

      Gruß
      kleiner hobbit.

      1. Hello,

        Eine Sache möchte ich gerne wissen. Was soll ich den anstelle von $_POST[search] schreiben, damit es etwas sicherer ist.

        z.B.:
           mysql_real_escape_string($_POST['search'],$con)

        wenn search der name des input-Elements ist

        http://www.php.net/manual/en/function.mysql-real-escape-string.php

        Die böse Sequenz bleibe ich Dir schuldig. Da gibt es hier aber bestimmt Poster, die welche parat haben für diesen Fall.

        Liebe Grüße aus Syburg bei Dortmund

        Tom vom Berg

        --
        Nur selber lernen macht schlau
        http://bergpost.annerschbarrich.de
        1. Hello,

          Die böse Sequenz bleibe ich Dir schuldig. Da gibt es hier aber bestimmt Poster, die welche parat haben für diesen Fall.

          wenn mich nicht alles täuscht, müsste folgendes alle Sätze liefern:

          ' OR 1=1 OR 'a'='a

          durch das erste Singlequote wird die LIKE-Klausel beendet und mit einer ODER-Verknüpfung zu einem Konstant wahren Ausdruck verbunden. (das OR 'a'='a sorgt lediglich dafür, dass am Ende wieder in korrektes Statement rauskommt).

          MfG
          Rouven

          --
          -------------------
          sh:| fo:} ch:? rl:( br:& n4:{ ie:| mo:} va:) js:| de:] zu:| fl:( ss:) ls:& (SelfCode)
          Don't lick your wounds: celebrate them. The scars you bear are the signs of a competitor.  --  character Richard Webber on Grey's Anatomy: 'Where the wild things are'
          1. hallo,

            wenn ich das da unten in meine input felder eingebe

            ' OR 1=1 OR 'a'='a

            passiert garnichts. da wird nichts angezeigt.

            dann ist der schnipsel von mir ja nicht so anfällig wie ich gedacht hatte, oder?

            gruß
            kleiner hobbit

            1. Hello,

              wenn ich das da unten in meine input felder eingebe

              ' OR 1=1 OR 'a'='a

              passiert garnichts. da wird nichts angezeigt.

              dann ist der schnipsel von mir ja nicht so anfällig wie ich gedacht hatte, oder?

              vermutlich, weil magic_quotes noch eingeschaltet sind
              http://www.php.net/manual/en/function.get-magic-quotes-gpc.php

              Die sind aber für sauberes Arbeiten mit den Daten kontraproduktiv, müssen also abgeschaltet oder rückgängig gemacht werden. Nur dann muss auch mit mysql_real_escape_string() o.ä. abgesichert werden!

              Liebe Grüße aus Syburg bei Dortmund

              Tom vom Berg

              --
              Nur selber lernen macht schlau
              http://bergpost.annerschbarrich.de
              1. hallo,

                wenn ich magic_quotes nicht abschalte aber dafür mysql_real_escape_string() verwende bin ich dann auf der sicheren seite?

                gruß
                kleiner hobbit

                1. hi,

                  wenn ich magic_quotes nicht abschalte aber dafür mysql_real_escape_string() verwende bin ich dann auf der sicheren seite?

                  Sicherheit kennt kein „und“ „oder“; magic_quotes gehören abgeschaltet, Usereingaben gehören validiert und maskiert.

                  Es sei denn, du weisst ganz genau, was du tust ...

                  mfg

                  --
                  echo '<pre>'; var_dump($Malcolm_Beck`s); echo '</pre>';
                  array(2) {
                    ["SELFCODE"]=>
                    string(74) "ie:( fl:) br:> va:? ls:? fo:) rl:| n4:# ss:{ de:? js:} ch:? sh:( mo:? zu:("
                    ["Meaningful"]=>
                    string(?) "Der Sinn des Lebens ist deinem Leben einen Sinn zu geben"
                  }
                2. echo $begrüßung;

                  wenn ich magic_quotes nicht abschalte aber dafür mysql_real_escape_string() verwende bin ich dann auf der sicheren seite?

                  Dann bist du auf der unsinnigen Seite, weil du zusätzlich zur Behandlung durch die Magic Quotes noch eigene Behandlung hinzufügst und somit etwas völlig unbrauchbares entsteht und nur noch mehr Folgefehler nach sich zieht.

                  Magic Quotes steht aufgrund seiner Nebenwirkungen auf der Abschussliste. Vergiss, dass es dir irgendwelche Vorteile oder Sicherheiten bringt, denn es bringt dir auch genügend Nachteile. Deaktiviere es oder mach seine Auswirkungen einmalig am Scriptanfang rückgängig. Diese Rückgängigmachung kannst du dann bei PHP6 recht einfach entfernen und musst nicht das gesamte Script nach stripslashes() durchsuchen.

                  Nachdem nun deine Eingabedaten in Rohform vorliegen, musst du dir das Prinzip der kontextgerechten Behandlung zu Herzen nehmen. Wann immer ein Wert in einen anderen Kontext gebracht wird, behandle ihn dem Kontext entsprechend. Fügst du etwas in HTML ein, behandle die HTML-eigenen Zeichen <>& und gegebenenfalls " und '. PHP stellt dafür htmlspecialchars() zur Verfügung. Fügst du einen Wert in einen MySQL-String ein, nimm mysql_real_escape_string() zur kontextgerechten Behandlung. Fügst du etwas in eine URL ein, nimm eine URL-gerechte Behandlung. Und so weiter, und so fort. Beachte auch Schachtelungen. Ein Wert in eine URL einfügen, die in ein HTML-Dokument eingefügt werden soll benötigt zuerst eine URL-gerechte Behandlung des Wertes und die fertige URL eine HTML-gerechte Behandlung.

                  Die Behandlung ist übrigens erst genau zum Zeitpunkt des Kontextwechsels sinnvoll, nicht irgendwann weit vorher oder gar in einem früheren Bearbeitungsschritt. HTML-gerechte Behandlung hat in einer Datenbank nichts zu suchen. Auch dann nicht, wenn du heute weißt, dass diese Daten immer in einem HTML-Kontext ausgegeben werden sollen.

                  Wenn du diesen Grundsatz der kontextgerechten Behandlung konsequent (und fehlerfrei) einhältst, bist du auf der sicheren Seite was die Injection-Lücken angeht, egal was dir deine Anwender für Daten unterjubeln oder welche erwünschten Sonderzeichen sie enthalten.

                  echo "$verabschiedung $name";

            2. Hi kleiner,

              dann ist der schnipsel von mir ja nicht so anfällig wie ich gedacht hatte, oder?

              Doch, dein jetziger Schnipsel ist prinzipiell genauso gefährlich wie dein vorheriger Schnipsel. Allerdings lässt dieser SQL-Query erst mal nur SELECTs zu, so dass man eigentlich nichts löschen kann. Ich sage eigentlich, weil man theoretisch folgendes an dein Script übergeben könnte:

              '; DELETE FROM table WHERE 1=1; --

              Das würde in folgenden SQL-Befehl resultieren:

              SELECT * FROM table WHERE data1 LIKE '%'; DELETE FROM table WHERE 1=1; -- %' or data2 LIKE '%'; DELETE FROM table WHERE 1=1; -- %'

              Beachte bitte, dass die Zeichenkette -- in SQL einen Kommentar einleitet, der bis zum Zeilenende geht, effektiv steht also nur noch folgendes da:

              SELECT * FROM table WHERE data1 LIKE '%'; DELETE FROM table WHERE 1=1;

              Das könnte dir alle Einträge aus der Tabelle table löschen, allerdings lassen aktuelle PHP bzw. MySQL-Versionen dies nicht mehr zu, d.h. pro Aufruf von mysql_query() kann nur noch ein Befehl abgesetzt werden.
              Das ändert aber nichts daran, dass dies eine Sicherheitslücke ist über die du theoretisch auch Datensätze löschen kannst!!

              Jetzt gibt es aber noch eine Funktion, welche dir vermutlich ins Geschehen reinpfuscht und weshalb du keine Auswirkungen von Rouvens Beispiel siehst: Magic-Quotes. Wenn magic_quotes_gpc in deiner PHP-Konfiguration aktiviert ist und du

              ' OR 1=1 OR 'a'='a

              in das Textfeld tipst, hast du in der Variable $_POST['suche'] schlussendlich folgendes stehen:

              ' OR 1=1 OR 'a'='a

              Also dasselbe, als wie wenn du addslashes() auf $_POST['suche'] aufgerufen hättest, sodass der SQL-Befehl am Ende so aussieht:

              SELECT * FROM table WHERE data1 LIKE '%\' OR 1=1 OR \'a\'=\'a%' or data2 LIKE '%\' OR 1=1 OR \'a\'=\'a%'

              und nicht so, wie Rouven es „beabsichtigt” hat:

              SELECT * FROM table WHERE data1 LIKE '%' OR 1=1 OR 'a'='a%' or data2 LIKE '%' OR 1=1 OR 'a'='a%'

              Jetzt magst du dir denken, dass dann ja für dich alles OK wäre - aber das ist falsch. Magic Quotes sind eine veraltete Technik und werden (steht auch auf der oben verlinkten Seite im PHP Handbuch) in zukünftigen PHP-Versionen nicht mehr existieren! Das heißt mit PHP 5.3 (spätestens mit PHP 6.0) hätte dein Script dann mit Sicherheit eine richtige[TM] Sicherheitslücke!

              Was du jetzt tun sollst? Magic Quotes deaktivieren und die Eingaben des Besuchers richtig behandeln.

              Viele Grüße,
                ~ Dennis.

  2. Guten Tag,

    $abfrage = "SELECT * FROM table WHERE data1 LIKE '%".$_POST[search]."%' or data2 LIKE '%".$_POST[search]."%'"; $ergebnis = mysql_query($abfrage);

    wenn ich nun die seite aufrufe ohne irgendeine eingabe gamacht zuhaben, wo
    dieser code ist, werden alle datensätze angezeigt. warum? wie kann ich es
    unterbinden, das die datensätze "erstmal" nicht angezeigt werden???

    Wenn search nicht per POST übergeben wird, wird beide Male nach LIKE %% gesucht - %% ist jeder beliebiger String (ein % ist hier eigentlich überflüssig). Die Bedingung lautet also: Wenn ein beliebiger String in data1 oder ein beliebiger String in data2 gefunden wird, liefere den Record zurück.

    Noch drei Anmerkungen:

    1. Sollten data1 und data2 verbatim aus dem Schema übernommen sein, möchte ich dir nahelegen, deine Datenbank ordentlich zu normalisieren.
    2. Der Code-Schnippsel oben ist anfällig für SQL Injections. Du solltest jede Benutzereingabe, egal woher sie kommt, sorgfältig überprüfen.
    3. Array-Identifier müssen gequotet werden, wenn es keine Konstanten sind (und noch in ein paar Spezialfällen nicht). Da du offenbar keinen Fehler gemeldet bekamst, solltest du in der php.ini (oder im Skript) error_reporting auf E_STRICT und display_errors auf On konfigurieren.

    Gruß
    Christoph Jeschke

    --
    Zend Certified Engineer
    Certified Urchin Admin
    1. Hello,

      $abfrage = "SELECT * FROM table WHERE data1 LIKE '%".$_POST[search]."%' or data2 LIKE '%".$_POST[search]."%'"; $ergebnis = mysql_query($abfrage);

      Noch drei Anmerkungen:

      1. Sollten data1 und data2 verbatim aus dem Schema übernommen sein, möchte ich dir nahelegen, deine Datenbank ordentlich zu normalisieren.
      2. Der Code-Schnippsel oben ist anfällig für SQL Injections. Du solltest jede Benutzereingabe, egal woher sie kommt, sorgfältig überprüfen.
      3. Array-Identifier müssen gequotet werden, wenn es keine Konstanten sind (und noch in ein paar Spezialfällen nicht). Da du offenbar keinen Fehler gemeldet bekamst, solltest du in der php.ini (oder im Skript) error_reporting auf E_STRICT und display_errors auf On konfigurieren.

      Und wenn wir schon mal bei der Schelte sind:
      4) 'select *' sollte man wirklich nur verwenden, wenn man tatsächlich alle Spalten benötigt.
         anderenfalls sollte dafür "select $fieldlist" benutzt werden, wobei $fieldlist die Namen
         der gewünschten Spalten enthält. Bsp.:
         $fieldlist = " name, vorname, plz, order, index ";

      Und auch wenn Vinzenz wieder meckert: Die Spaltennanmen würde ich bei mysql immer in Backticks einpacken lassen, wenn  das Statement automatisch gemeriert wird :-D

      Liebe Grüße aus Syburg bei Dortmund

      Tom vom Berg

      --
      Nur selber lernen macht schlau
      http://bergpost.annerschbarrich.de
      1. Guten Tag,

        Und wenn wir schon mal bei der Schelte sind:

        Ach, naja, Schelte ...

        Und auch wenn Vinzenz wieder meckert: Die Spaltennanmen würde ich bei mysql
        immer in Backticks einpacken lassen, wenn  das Statement automatisch gemeriert
        wird :-D

        Ich konnte so spontan jetzt nichts finden: Was spricht denn gegen das Quoting mit Backticks an dieser Stelle? Vinzenz?

        Gruß
        Christoph Jeschke

        --
        Zend Certified Engineer
        Certified Urchin Admin
        1. Hallo,

          » Und auch wenn Vinzenz wieder meckert: Die Spaltennanmen würde ich bei mysql
          » immer in Backticks einpacken lassen, wenn  das Statement automatisch gemeriert

          bei automatischer Generierung spricht nichts gegen Einpacken in Backticks.
          Bei automatischer Generierung spricht nichts gegen qualifizierte Bezeichner.

          Ich konnte so spontan jetzt nichts finden: Was spricht denn gegen das Quoting mit Backticks an dieser Stelle? Vinzenz?

          Beim Schreiben von Hand spricht *alles* gegen *nicht erforderliches* Quoting, weil Statements unlesbar werden, man Spaltennamen benutzt, die man besser nicht benutzen sollte. Viel wichtiger: ich mag keine Backticks :-)

          Freundliche Grüße

          Vinzenz

          1. Hello,

            [...] Viel wichtiger: ich mag keine Backticks :-)

            Ach so. Die Taste ist kaputt auf Deiner Tastatur. :-)

            Liebe Grüße aus Syburg bei Dortmund

            Tom vom Berg

            --
            Nur selber lernen macht schlau
            http://bergpost.annerschbarrich.de
          2. Guten Tag,

            Beim Schreiben von Hand spricht *alles* gegen *nicht erforderliches* Quoting,
            weil Statements unlesbar werden, man Spaltennamen benutzt, die man besser nicht
            benutzen sollte.

            Wieso sollte man keine Spaltennamen verwenden? Was würdest du sonst verwenden?

            Viel wichtiger: ich mag keine Backticks :-)

            Achso.

            Gruß
            Christoph Jeschke

            --
            Zend Certified Engineer
            Certified Urchin Admin
            1. Hallo,

              » Beim Schreiben von Hand spricht *alles* gegen *nicht erforderliches* Quoting,
              » weil Statements unlesbar werden, man Spaltennamen benutzt, die man besser nicht
              » benutzen sollte.

              Wieso sollte man keine Spaltennamen verwenden? Was würdest du sonst verwenden?

              wo schreib' ich so was? Nirgends!
              Ich benutze im Normalfall Spaltennamen, die Quoting nicht erfordern, reservierte Wörter meide ich.

              Freundliche Grüße

              Vinzenz

      2. echo $begrüßung;

        1. 'select *' sollte man wirklich nur verwenden, wenn man tatsächlich alle Spalten benötigt.

        Hast du eine konkrete und zeitgemäße Begründung, warum man SELECT * vermeiden soll? (Sven R. und Cheatah konnten oder wollten mir bislang keine geben. Rouven hatte in der erstverlinkten Diskussion zumindest einen konkreten Anwendungsfall bei dem * ungünstig ist.)

        echo "$verabschiedung $name";

        1. Hello,

          Hast du eine konkrete und zeitgemäße Begründung, warum man SELECT * vermeiden soll?

          Klar.
          Es ist einigen meiner Klienten schon oft genug passiert, dass sie ihren Datenbankserver zum Stehen gebracht haben (oder den PHP-Parser ausgekickt haben).

          Wenn Du aus einer ewig breiten Tabelle eigentlich nur zwei Spalten benötigst und mit "select * " abgefragst, dann bekommst Du eine entsprechend große Ergebnismenge. Die muss zwischengespeichert werden. Wenn dann genügend viele Zeilen abgefragt werden und auch noch gleichzeitige Anfragen kommen, wird geswappt und dann bricht die Performance zusammen.

          Das kann man dann entweder durch Speichertuning oder durch Verbesserung der Statements korrigieren :-)

          In real genutzten Systemen ist das schneller der Fall, als man denken mag.

          Genauso tödlich ist es, die Resultsets nicht sofort nach beendeter Nutzung wieder freizugeben, wenn das Script noch ein eWeile länger laufen muss danach. Wenn es sofort nach der Datenoperation beendet ist, macht sich das nicht so bemerkbar, da ja die Ressourcen am Ende des Scriptes ohnehin  automatisch wieder freigegeben werden.

          Habe ich es jetzt in Deinem Sinne ausreichend plastisch geschildert, warum man Ergebnismengen klein halten soll und frühzeitig wieder freigeben soll? ;-))

          Liebe Grüße aus Syburg bei Dortmund

          Tom vom Berg

          --
          Nur selber lernen macht schlau
          http://bergpost.annerschbarrich.de
          1. echo $begrüßung;

            » Hast du eine konkrete und zeitgemäße Begründung, warum man SELECT * vermeiden soll?
            Wenn Du aus einer ewig breiten Tabelle eigentlich nur zwei Spalten benötigst und mit "select * " abgefragst, dann bekommst Du eine entsprechend große Ergebnismenge.

            Das ist einer der konkreten Fälle, in denen SELECT * falsch angewendet wurde. Besonders dann macht es sich bemerkbar, wenn gößere Datenmengen (BLOB, TEXT) in den Zeilen stehen, die man aktuell nicht benötigt (wobei dann vermutlich auch noch ein schlechtes Datendesign hinzukommt).

            Habe ich es jetzt in Deinem Sinne ausreichend plastisch geschildert, warum man Ergebnismengen klein halten soll und frühzeitig wieder freigeben soll? ;-))

            Die Ergebnismenge klein zu halten ist ein legitimes Argument, wenn es sich um potentiell große Datenmengen handelt. Ein, zwei überflüssige Spalten mit ein paar wenigen Bytes drin, sind für mich aber kein schlagkräftiges Argument gegen SELECT *. Solange eine nachvollziehbare Begründung zu einer Ablehnung gegeben wird, habe ich dagegen nichts einzuwenden. Oft liest man jedoch nur noch ein aufgrund eines dogmatischen Reflexes ausgesprochenes "Verbot".

            echo "$verabschiedung $name";

            1. Mahlzeit dedlfix,

              Die Ergebnismenge klein zu halten ist ein legitimes Argument, wenn es sich um potentiell große Datenmengen handelt. Ein, zwei überflüssige Spalten mit ein paar wenigen Bytes drin, sind für mich aber kein schlagkräftiges Argument gegen SELECT *.

              Dann hast Du noch nie an einem größeren Projekt/Produkt gearbeitet, bei dem viele unterschiedliche Leute auf viele unterschiedliche Arten und Weisen auf viele unterschiedliche Tabellen zugreifen.

              Angenommen, es gibt eine relativ zentrale Tabelle, die zu Beginn des Projekts nur wenige Spalten und wenige Datensätze enthält. Angenommen, jemand entwirft also eine Abfrage mit "SELECT *", obwohl er nur 3 der Spalten benötigt ... aber "ist ja legitim" (Stichwort "Tippfaulheit").

              Im Laufe dieses Projekts wird diese zentrale Tabelle um mehrere Spalten erweitert und füllt sich mit mehreren zehntausend Datensätzen.

              Und irgendwann wird das Statement, das zu Anfang noch legitim war, immer langsamer und la n g  s a    m      ee e e   r ... und irgendwann kackt regelmäßig der Server ab.

              Und warum? Nur weil ein Entwickler tippfaul war?

              Sorry, aber IMHO ist "SELECT *" NIEMALS legitim. Die Erfahrung habe ich nun schon in mehreren Projekten gemacht. Man weiß nie, wie sich eine Tabelle in Zukunft entwickeln wird und man sollte sich genau deshalb nie darauf verlassen, dass es bei der jetzigen Anzahl an Spalten und Datensätzen bleiben wird.

              MfG,
              EKKi

              --
              sh:( fo:| ch:? rl:( br:> n4:~ ie:% mo:} va:) de:] zu:) fl:{ ss:) ls:& js:|
              1. echo $begrüßung;

                » Die Ergebnismenge klein zu halten ist ein legitimes Argument, wenn es sich um potentiell große Datenmengen handelt. Ein, zwei überflüssige Spalten mit ein paar wenigen Bytes drin, sind für mich aber kein schlagkräftiges Argument gegen SELECT *.
                Dann hast Du noch nie an einem größeren Projekt/Produkt gearbeitet, bei dem viele unterschiedliche Leute auf viele unterschiedliche Arten und Weisen auf viele unterschiedliche Tabellen zugreifen.

                Ich meinte die Anwendungsfälle, in denen vorhersehbar ist, dass sie klein bleiben. Größere Projekte verlangen selbstverständlich andere Herangehensweisen. Warum sollte man sich nicht zu Hause ein All-in-One-Gerät zur Telekommunikation hinstellen, wenn man prinzipiellen Bedarf dafür hat? In einer Firma ab einer gewissen Anzahl an (Büro-)Mitarbeitern empfiehlt es sich dagegen über andere Wege nachzudenken, beispielsweise den Arbeitsplatz mit einem (mehr oder weniger) einfachen Telefon auszustatten, ein zentrales Anrufbeantworter-System und einen über Email bedienbaren Fax-Server zu installieren. So eine Lösung skaliert besser und ist effizienter als ein AiO-Gerät überall aufzustellen. Trotzdem werde ich sie nicht für zu Hause vorschlagen oder gar das Heimgerät kategorisch ablehnen.

                Angenommen, es gibt eine relativ zentrale Tabelle, die zu Beginn des Projekts nur wenige Spalten und wenige Datensätze enthält. Angenommen, jemand entwirft also eine Abfrage mit "SELECT *", obwohl er nur 3 der Spalten benötigt ... aber "ist ja legitim" (Stichwort "Tippfaulheit").

                Tippfaulheit ist für mich kein legitimer Grund. Wenn etwas den aktuellen Erfordernissen genügt, ohne dabei sicherheitsrelvante Probleme mit sich zu bringen, warum sollte man dann mehr Aufwand treiben als nötig? Für Eleganz im Code wird man selten bezahlt. Allerdings kann sich Eleganz beziehungswiese gut wartbarer Code später auszahlen.

                Im Laufe dieses Projekts wird diese zentrale Tabelle um mehrere Spalten erweitert und füllt sich mit mehreren zehntausend Datensätzen.
                Und irgendwann wird das Statement, das zu Anfang noch legitim war, immer langsamer und la n g  s a    m      ee e e   r ... und irgendwann kackt regelmäßig der Server ab.
                Und warum? Nur weil ein Entwickler tippfaul war?

                Oder unwissend, dass in dem Fall der * die falsche Methode war. Oder die verpasste Gelegenheit, ein vorhandenes Werkzeug zu optimieren, wenn die Ansprüche steigen. Aus dieser Erfahrung kann man lernen, dass für große Datenmengen der * mindestens ungünstig ist und in Richtlinien für große Projekte entsprechende Regeln einbauen. (Am besten begründet, damit die Anwender es nachvollziehen können und nicht nur als Gängelei empfinden.) Aber darf man diese Erfahrung auf jede Situation verallgemeinern?

                Sorry, aber IMHO ist "SELECT *" NIEMALS legitim. Die Erfahrung habe ich nun schon in mehreren Projekten gemacht.

                Da ist es aber wieder, das kategorische Ablehnen (noch dazu mit Großbuchstaben), nur weil man in bestimmten Situationen damit schlecht gefahren ist. In den meisten Fälle, in denen ich eine Ablehnung beobachte handelt es sich um Wald- und Wiesenscripte, bei denen es nicht darauf ankommt.

                Man weiß nie, wie sich eine Tabelle in Zukunft entwickeln wird und man sollte sich genau deshalb nie darauf verlassen, dass es bei der jetzigen Anzahl an Spalten und Datensätzen bleiben wird.

                Ja, prinzipeill gesehen mag das für eine Anzahl von Aufgabenstellungen zutreffen, für beispielsweise ein Gästebuch einer privaten Website wird das sicher nicht eintreten und da halte ich es für übertrieben, ein SELECT * zu verteufeln.

                echo "$verabschiedung $name";

                1. Mahlzeit dedlfix,

                  »» Sorry, aber IMHO ist "SELECT *" NIEMALS legitim. Die Erfahrung habe ich nun schon in mehreren Projekten gemacht.

                  Da ist es aber wieder, das kategorische Ablehnen (noch dazu mit Großbuchstaben),

                  Gut, dann andersherum: es gibt Gründe, warum man "SELECT *" vermeiden sollte. Diese Gründe sind aber nicht immer vorhanden, so dass man - Deiner Meinung nach - in diesen Fällen "SELECT *" durchaus sorglos verwenden kann.

                  Gibt es denn Gründe, warum man "SELECT *" verwenden sollte? Oder anders gesagt: was spricht dagegen, es generell zu vermeiden? Ist Dir irgendein Anwendungsfall bekannt, bei dem es sinnvoller, hilfreicher oder gar "richtiger" wäre, "SELECT *" zu verwenden?

                  Wenn ich folgende "Entscheidungshilftabelle" zugrundelege,

                  Verwendung von "SELECT *" | Vermeidung von "SELECT *"
                  ---------------------------+--------------------------

                  • kann in bestimmten Fäl-  | - in diesen bestimmten
                      len Probleme verursachen |   Fällen keine Probleme
                                               |
                  • ansonsten keine Probleme | - ansonsten keine Probleme

                  dann ist doch recht offensichtlich, bei welcher Entscheidung weniger Probleme zu erwarten sind bzw. die Wahrscheinlichkeit des Auftretens von Problemen geringer ist.

                  Kurz gesagt: was - außer Tippfaulheit - spricht für "SELECT *"?

                  Wenn Du mir dafür einen nachvollziehbaren Grund nennen kannst, bin ich bereit, von meinem kategorischen NEIN abzurücken ...

                  Ich bin gespannt.

                  MfG,
                  EKKi

                  --
                  sh:( fo:| ch:? rl:( br:> n4:~ ie:% mo:} va:) de:] zu:) fl:{ ss:) ls:& js:|
                  1. echo $begrüßung;

                    Gibt es denn Gründe, warum man "SELECT *" verwenden sollte?

                    Ein * liefert genau das was seine Aufgabe ist: alle Spalten. Wenn man das haben möchte, warum soll man ihn dann nicht einsetzen? Ungenommen davon bleibt, das mögliche geänderte Aufgabenstellungen in der Zukunft angemessen berücksichtigt werden müssen.

                    Oder anders gesagt: was spricht dagegen, es generell zu vermeiden?

                    Es spricht nichts dagegen, wenn jemand für sich entscheidet, dass er den * nicht verwendet. Die nicht generell vorhandenen negativen Auswirkungen sprechen aber gegen eine generelle Ablehnung und das Propagieren dieser als unumstößliches Dogma.

                    Ist Dir irgendein Anwendungsfall bekannt, bei dem es sinnvoller, hilfreicher oder gar "richtiger" wäre, "SELECT *" zu verwenden?

                    Der * hat seine bekannte Eigenschaft. Wenn ich diese haben will, kann ich da auch einen Vorteil darin sehen, dass ich nicht darauf achten muss, alle Spaltennamen hinschreiben zu müssen und keine dabei zu vergessen. Auch in der Zukunft kann es sein, dass ich nach einer Erweiterung der Tabelle immer noch alle Spalten brauche. Oder aber auch nicht, was abhängig vom konkreten Fall ist und nicht generell geklärt werden kann.

                    Wenn ich folgende "Entscheidungshilftabelle" zugrundelege,

                    Eine Entscheidungshilfe ohne konkret zu entscheidenden Fall halte ich nicht für besonders ergiebig.

                    Kurz gesagt: was - außer Tippfaulheit - spricht für "SELECT *"?

                    Es geht mir nicht um die generelle Klärung der Frage. Ich maße mir nicht an, allgemeingültige Grundsätze aufstellen zu können. Es geht mir darum, dass man die Entscheidung für oder gegen etwas bewusst in einem konkret vorliegenden Anwendungsfall trifft. Denn erst dann kann man entscheiden, ob die Eigenschaften für diesen Fall positiv oder negativ zu werten sind und seine (Einzelfall-)Entscheidung treffen.

                    Wenn Du mir dafür einen nachvollziehbaren Grund nennen kannst, bin ich bereit, von meinem kategorischen NEIN abzurücken ...

                    Ich propagiere nicht den * als allein selig machende Errungenschaft, doch sehe ich auch keinen Grund, ihn als das Werk des Gehörnten zu verdammen.

                    Wir beide werden uns sicher nicht einigen können. Ich werde nicht zu einem Verfechter der bedingungslosen Ablehnung von SELECT * und du wirst vermutlich nicht von deiner Meinung gegen den * abrücken, was dein gutes Recht ist und ich dir auch nicht auszureden gedenke. Doch gelegentlich werde ich dagegen protestieren, wenn es jemand SELECT * als generellen, unverzeihlichen Fehler verkaufen will.

                    echo "$verabschiedung $name";

            2. Hello,

              Die Ergebnismenge klein zu halten ist ein legitimes Argument, wenn es sich um potentiell große Datenmengen handelt. Ein, zwei überflüssige Spalten mit ein paar wenigen Bytes drin, sind für mich aber kein schlagkräftiges Argument gegen SELECT *. Solange eine nachvollziehbare Begründung zu einer Ablehnung gegeben wird, habe ich dagegen nichts einzuwenden. Oft liest man jedoch nur noch ein aufgrund eines dogmatischen Reflexes ausgesprochenes "Verbot".

              naja, ich habe unter meinen Klienten einige Kontaktforen. Da tummeln sich in Spitzenzeiten soviele User, dass 1200 bis 3000 Requests gleichzeitig in der Abarbeitung sind. Da zählt jedes Byte! Bitte nicht verwechseln mit "online sind 2000 User...". Das sind dann sicher um die 5000 bis 10000.

              Nachdem ich das erste Mal einen Alarmanruf bekommen hatte, habe ich nur an einigen Stellen, an denen es mir ins Auge spracng, ein Free-Result eingebaut. Da war das Problem schon mal beseitigt und die Userzahl stieg auch postwendend weiter. Das zweite Mal habe ich dann nach den "Select *" gesucht. Das hat dann erstaunlicherweise nochmal genausoviel gebracht.

              Das gilt inzwischen für alle Plattformen dieser Art, die ich zur Kontrolle bekommen habe.

              Liebe Grüße aus Syburg bei Dortmund

              Tom vom Berg

              --
              Nur selber lernen macht schlau
              http://bergpost.annerschbarrich.de
              1. echo $begrüßung;

                Das gilt inzwischen für alle Plattformen dieser Art, die ich zur Kontrolle bekommen habe.

                Aus Fehlern (auch anderer) sollte man lernen, keine Frage. Allerdings nicht, dass die Lösung für einen speziellen Fall generell für alle Probleme gleichwertig anwendbar ist. Es müssen schon auch noch mindestens vergleichbare Voraussetzungen gegeben sein. Deshalb sollte eine Lösung immer wieder auf Verwendbarkeit und das Aufwand-Nutzen-Verhältnis überprüft werden, wenn man sie einzusetzen gedenkt. Was nützt es denn, in feinster OOP und mit den tollsten Entwurfsmustern herumzuhantieren - hat sich ja schließlich in anderen Projekten als vorteilhaft erwiesen - wenn das Gästebuch mit drei Zeilen herkömmlichen Codes genauso effizient oder gar besser funktioniert. Zugegeben, das Verhältnis zwischen * und $feldliste ist nicht so gravierend wie mein Beispiel, aber gelegentlich muss man mal übertreiben um (hoffentlich) verständlicher rüberzukommen.

                echo "$verabschiedung $name";

                1. Hello,

                  [...] Was nützt es denn, in feinster OOP und mit den tollsten Entwurfsmustern herumzuhantieren - hat sich ja schließlich in anderen Projekten als vorteilhaft erwiesen - wenn das Gästebuch mit drei Zeilen herkömmlichen Codes genauso effizient oder gar besser funktioniert.

                  Auf Dein Gästebuch-Script in drei Zeilen bin ich gespannt ;-D

                  Liebe Grüße aus Syburg bei Dortmund

                  Tom vom Berg

                  --
                  Nur selber lernen macht schlau
                  http://bergpost.annerschbarrich.de
        2. Moin!

          Hast du eine konkrete und zeitgemäße Begründung, warum man SELECT * vermeiden soll?

          Faustregeln helfen auch uninformierten Entwicklern, typische Fehler zu vermeiden.

          Und "SELECT *" ist ein typischer Fehler, wenn es um Performance der Datenbank geht - das kann man der geführten Diskussion ja eindeutig entnehmen.

          - Sven Rautenberg

          1. echo $begrüßung;

            Faustregeln helfen auch uninformierten Entwicklern, typische Fehler zu vermeiden.

            Dem Stärkeren muss ich mich beugen, doch auch dann will ich eigentlich wissen, warum die Faust in meine Richtung bewegt wurde und das kommt (viel zu) oft bei einem "xy ist böse" zu kurz.

            echo "$verabschiedung $name";

            1. Moin!

              Dem Stärkeren muss ich mich beugen, doch auch dann will ich eigentlich wissen, warum die Faust in meine Richtung bewegt wurde und das kommt (viel zu) oft bei einem "xy ist böse" zu kurz.

              eval() ist böse.

              Globale Variablen sind böse.

              'SELECT *' ist böse.

              ...

              Wenn man das weiterführen will, würde man bei so vielen Dingen ankommen, die pauschal erstmal böse sind, dass es verwundert, dass sie überhaupt erfunden wurden. Und bei näherer Betrachtung kommt man zu der Erkenntnis, was "böse" denn in diesem Zusammenhang bedeutet: Es soll vor unbedarfter Nutzung der "bösen" Technik abschrecken, als Warnzeichen: "Vor der Benutzung Gehirn unbedingt einschalten und Optionen abwägen!".

              Wenn für den uninformierten Skripter hängen bleibt "Nicht benutzen", dann ist das der gewünschte Effekt.

              - Sven Rautenberg

    2. Hallo Christoph,
      ich habe eben deinen Beitrag gelesen den ich sehr interessant finde.
      Eine Frage habe ich dazu.

      »» $abfrage = "SELECT * FROM table WHERE data1 LIKE '%".$_POST[search]."%' or data2 LIKE '%".$_POST[search]."%'"; $ergebnis = mysql_query($abfrage);

      Noch drei Anmerkungen:

      1. Sollten data1 und data2 verbatim aus dem Schema übernommen sein, möchte ich dir nahelegen, deine Datenbank ordentlich zu normalisieren.

      Ich weiss das man Spalten nicht benennen soll wie Textfeld1, Textfeld2, Textfeld3. Aber gibt es nicht Situationen wo es nicht anders geht oder eine andere Lösung eher aufwendig ist?
      Ich habe z.b. eine kleine Anwendung wo auf einer Seite 6 Textfelder sind. Diese heissen auch auf dem Form textfeld1, textfeld2, textfeld3 usw.
      Was in die Textfelder eingegeben wird bzw. wozu sie stehen ist flexibel.
      Es kann also einmal sein:
      Name = textfeld1
      email = textfeld2
      Ort = textfeld3

      Dann kann es aber wieder sein.
      Login = textfeld1
      Abteilung = textfeld2
      email = textfeld3

      Die Tabelle wo die Daten gespeichert werden ist dann auch so aufgebaut wie die Textfelder heissen. Also
      TableID, UserID, Textfeld1, Textfeld2,Textfeld3,Textfeld4...

      Ich habe schon hin und her überlegt aber keine andere vernünftige Lösung gefunden.

      vielen Dank und viele Grüße
      hawk

      1. Vielleicht kann auch jemand anderes als Christoph meine Frage beantworten?

        vielen Dank
        Gruss
        hawk

        Hallo zusammen,»» Hallo Christoph,

        ich habe eben deinen Beitrag gelesen den ich sehr interessant finde.
        Eine Frage habe ich dazu.

        »»
        »» »» $abfrage = "SELECT * FROM table WHERE data1 LIKE '%".$_POST[search]."%' or data2 LIKE '%".$_POST[search]."%'"; $ergebnis = mysql_query($abfrage);
        »»
        »» Noch drei Anmerkungen:
        »» 1) Sollten data1 und data2 verbatim aus dem Schema übernommen sein, möchte ich dir nahelegen, deine Datenbank ordentlich zu normalisieren.

        Ich weiss das man Spalten nicht benennen soll wie Textfeld1, Textfeld2, Textfeld3. Aber gibt es nicht Situationen wo es nicht anders geht oder eine andere Lösung eher aufwendig ist?
        Ich habe z.b. eine kleine Anwendung wo auf einer Seite 6 Textfelder sind. Diese heissen auch auf dem Form textfeld1, textfeld2, textfeld3 usw.
        Was in die Textfelder eingegeben wird bzw. wozu sie stehen ist flexibel.
        Es kann also einmal sein:
        Name = textfeld1
        email = textfeld2
        Ort = textfeld3

        Dann kann es aber wieder sein.
        Login = textfeld1
        Abteilung = textfeld2
        email = textfeld3

        Die Tabelle wo die Daten gespeichert werden ist dann auch so aufgebaut wie die Textfelder heissen. Also
        TableID, UserID, Textfeld1, Textfeld2,Textfeld3,Textfeld4...

        Ich habe schon hin und her überlegt aber keine andere vernünftige Lösung gefunden.

        vielen Dank und viele Grüße
        hawk

        vielen Dank und viele Grüße
        hawk

      2. echo $begrüßung;

        Ich weiss das man Spalten nicht benennen soll wie Textfeld1, Textfeld2, Textfeld3. Aber gibt es nicht Situationen wo es nicht anders geht oder eine andere Lösung eher aufwendig ist?

        Bei gespeicherten Daten kann ich mir den Sinn hinter einer nichtssagenden Bezeichnung schlecht vorstellen. Wenn es sich allerdings um einen Automatismus handelt, der als eine Art Blackbox vor sich hin werkelt und zwischendurch solche Namen verwendet, sie aber anschließend wieder in was brauchbares übersetzt sehe ich darin kein Problem.

        Ich habe z.b. eine kleine Anwendung wo auf einer Seite 6 Textfelder sind. Diese heissen auch auf dem Form textfeld1, textfeld2, textfeld3 usw.
        Was in die Textfelder eingegeben wird bzw. wozu sie stehen ist flexibel.

        Flexibel heißt aber hier nicht willkürlich, oder? Wenn es nur flexibel ist, hast du ja irgendeine Zuordung zwischen dem Feld und der Bedeutung der darin einzutragenden Daten. Und diese Zuordnung kannst / solltest du am Ende aus dem durchnummerierten Bezeichner und der Zuordung wieder etwas sinnvolles zaubern können.

        Die Tabelle wo die Daten gespeichert werden ist dann auch so aufgebaut wie die Textfelder heissen. Also
        TableID, UserID, Textfeld1, Textfeld2,Textfeld3,Textfeld4...

        Warum heißen sie denn so und nicht nach ihrem Inhalt? Willst du damit sagen, dass mal ein Name, mal eine Abteilung und ein anderes Mal eine Email-Adresse drinsteht? Wie unterscheidest du das dann bei der weiteren Verwendung? Und vor allem, wie findest du beispielsweise alle Datensätze mit einer bestimmten Email-Adresse?

        echo "$verabschiedung $name";

      3. Guten Tag,

        Ich weiss das man Spalten nicht benennen soll wie Textfeld1, Textfeld2,
        Textfeld3. Aber gibt es nicht Situationen wo es nicht anders geht oder eine
        andere Lösung eher aufwendig ist?

        Wie viel aufwendiger wäre es, nachträglich herauszufinden, welche Information du in foobar1 gespeichert hast?

        Ich habe z.b. eine kleine Anwendung wo auf einer Seite 6 Textfelder sind. Diese
        heissen auch auf dem Form textfeld1, textfeld2, textfeld3 usw.
        Was in die Textfelder eingegeben wird bzw. wozu sie stehen ist flexibel.
        Es kann also einmal sein:
        Name = textfeld1
        email = textfeld2
        Ort = textfeld3

        Dann kann es aber wieder sein.
        Login = textfeld1
        Abteilung = textfeld2
        email = textfeld3

        Warum mischst du denn die Daten? Wie stellst du später fest, ob foobar1 gerade ein Vor- oder eine Nachname ist?

        Gruß
        Christoph Jeschke

        --
        Zend Certified Engineer
        Certified Urchin Admin