Schwuffi: Bewertung: Ist dieses Skript einen Pfifferling wert?

Hallo,

ich hab gerade quasi mein erstes Skript vollendet und wollte mal von den Fachleuten hier hören, ob das denn auch etwas taugt oder man es lieber so wie es ist zum Mond schießen sollte :)

Es ist keinesfalls perfekt und man kanns daher bestimmt eleganter machen, aber wie gesagt - das ist mein ersters Mal.

Das ganze soll ein Lexikon darstellen. Über die Variable $buchstb wird erstmal eine Übersicht aller Begriffe mit dem gleichen Anfangsbuchstaben erstellt, über die Variable $wort wird dann eben eine Erklärung zu einem Wort ausgegeben.

Die DB enthält folgende Felder:

id
buchstabe (zur Selektion nach Anfangsbuchstabe)
begriff (Ausgabe des jew. Begriffs in der Tabelle usw.)
url
definition
wort (entspricht im Grunde dem Feld "Begriff" allerdings hab ich hier Umlaute o.ä. durch ae, ue usw. ersetzt und überlange Worte abgekürzt, damit es keine Probleme in der URL bzw. beim Verlinken gibt; weiß nicht ob das sein muss, erschien mir aber sinnvoll).

Das Skript sieht dann so aus (nicht lachen ;)):

<?php  
  
  #######  
  #Schutz vor SQL-Injections  
  #######  
  
  if(get_magic_quotes_gpc() == false)  
  {  
     $buchstb = addslashes($_GET['buchstb']);  
     $wort = addslashes($_GET['wort']);  
  }  
  else  
  {  
      $buchstb = $_GET['buchstb'];  
      $wort = $_POST['wort'];  
  }  
  
  
  $title = "Lexikon: $buchstb";  
  include("../mysql/connect.php");  
  
  #######  
  #Anzahl an Spalten der Tabelle festlegen  
  #######  
  
  $spalten = 4;  
  $i=0;  
  
  echo "<h1>Lexikon</h1>  
 <b>$buchstb</b>";  
  
  
  #######  
  #Tabelle mit Begriffen des gewaehlten Anfangsbuchstaben anlegen  
  #######  
  
  $query = "SELECT begriff, url FROM lexikon WHERE buchstabe = '$buchstb'" or die(mysql_error());  
  $result = mysql_query($query);  
  while($row = mysql_fetch_object($result))  
    {  
    if(($i%$spalten) == 0)  
 {  
 echo "</tr>\n\t<tr>\n\t";  
    }  
    echo "\t<td><a href=\"$row->url\">$row->begriff</a></td>\n\t";  
    $i++;  
    }  
    if(($i%$spalten) == 0)  
    {  
    echo "</tr>\n</table>";  
 }  
 else  
 {  
    echo "\t<td colspan=\"".($spalten-($i % $spalten))."\">&nbsp;</td>\n\t</tr>\n</table>";  
    }  
  
    #######  
    #Seite abschließen falls kein Wort oder zum Wort kein Buchstabe angegeben wurde  
    #######  
  
    if(($wort == "") OR (($buchstb == "") AND ($wort != "")))  
    {  
    echo "<div id=\"fuss\" ><a href=\"#top\" title=\"Nach oben\">Nach oben</a></div></div>";  
 include("../foot.php");  
 }  
  
 #######  
 #Ausgabe der Defintion des jeweiligen Wortes  
 #######  
  
 else  
 {  
 $query_wort = "SELECT buchstabe, definition FROM lexikon WHERE wort = '$wort'" or die (mysql_error());  
     $result_wort = mysql_query($query_wort);  
     while($row = mysql_fetch_object($result_wort))  
      {  
  
      #######  
      #Ausgabe nur wenn das Wort auch mit dem angegebenen Buchstaben beginnt, sonst Seite abschließen  
      #######  
  
      if($row->buchstabe == $buchstb)  
  {  
  echo "<h1>$begriff</h1>";  
  echo $row->definition;  
  echo "</div>";  
  include("../foot.php");  
  }  
  else  
  {  
  echo "<div id=\"fuss\"><a href=\"#top\" title=\"Nach oben\">Nach oben</a></div></div>";  
  include("../foot.php");  
  }  
  }  
 }  
?>

Was mir selbst besonders missfällt (ohne überhaupt zu wissen, wie gut/schlecht das Skript ist), ist der letzte Teil ab der Ausgabe der Begriffserklärung. Ist es überhaupt sinnvoll, dass alles in eine Skript zu stecken?

Die Zeile if(($wort == "") OR (($buchstb == "") AND ($wort != ""))) hab ich erstellt, da es sonst möglich ist, z.B. händisch die URL zu ändern. D.h. man hätte z.B. die Seite index.php?buchstb=A und fügt daran &wort=Linse an; damit hätte man oben die Liste mit allen Worten mit Buchstabe "A" und darunter die Erklärung eines Wortes mit einem anderen Buchstaben. Ist zwar nicht weiter schlimm, aber soll halt nicht sein.

Was sagt ein Profi oder zumindest Nicht-absoluter-Laie dazu? :)

  1. Letzter Absatz:

    Die Zeile if(($wort == "") OR (($buchstb == "") AND ($wort != ""))) hab ich erstellt, ...

    Sollte natürlich dieser Code sein: if($row->buchstabe == $buchstb)

  2. Hallo Schwuffi,

    Vorweg: Ich habe mir dein Script nicht näher angesehen.

    Die DB enthält folgende Felder:

    id
    buchstabe (zur Selektion nach Anfangsbuchstabe)

    Das heißt also, ich kann den Anfangsbuchstaben ändern, ohne das Wort an sich zu ändern. Diese beiden Daten sind aber abhängig voneinander. Deshalb würde ich hier lieber mehrere Tabellen nutzen, und diese mit Relationships verknüpfen. Allerdings habe auch ich nicht viel Erfahrung mit Datenbanken.

    Beste Grüße
    Richard

    1. Hi,

      Das heißt also, ich kann den Anfangsbuchstaben ändern, ohne das Wort an sich zu ändern. Diese beiden Daten sind aber abhängig voneinander.

      Ja, das ist leider ein Problem, welches ich halt mit umständlichen Abfragen umgangen habe :)

      Wenn möglich soll das Feld "buchstabe" auch aus der DB verschwinden.

  3. Hi,

    #Schutz vor SQL-Injections
      if(get_magic_quotes_gpc() == false)
      {
         $buchstb = addslashes($_GET['buchstb']);
         $wort = addslashes($_GET['wort']);

    Zum Escapen für mysql wäre mysql_real_escape_string geeigneter.

    include("../mysql/connect.php");

    was aber erst nach dieser Zeile benutzt werden kann ...

    $query = "SELECT begriff, url FROM lexikon WHERE buchstabe = '$buchstb'" or die(mysql_error());

    Die Zuweisung dieses Strings liefert immer einen Wert, der zu true gewandelt wird, da er nicht leer ist.
    Wozu an dieser Stelle (Stringzuweisung in PHP) überhaupt die eventuelle Abfrage des mysql_error dienen sollte, ist nicht klar.

    $result = mysql_query($query);
      while($row = mysql_fetch_object($result))

    Hier, wo tatsächlich auf die Datenbank zugegriffen wurde, fehlt die Fehlerbehandlung und es wird gnadenlos das $result verwendet, egal, ob die Datenbank einen Fehler geliefert hat oder nicht.

    $query_wort = "SELECT buchstabe, definition FROM lexikon WHERE wort = '$wort'" or die (mysql_error());

    s.o.

    $result_wort = mysql_query($query_wort);
         while($row = mysql_fetch_object($result_wort))

    s.o.

    cu,
    Andreas

    --
    Warum nennt sich Andreas hier MudGuard?
    Schreinerei Waechter
    O o ostern ...
    Fachfragen unaufgefordert per E-Mail halte ich für unverschämt und werde entsprechende E-Mails nicht beantworten. Für Fachfragen ist das Forum da.
  4. echo $begrüßung;

    Die DB enthält folgende Felder:

    id
    buchstabe (zur Selektion nach Anfangsbuchstabe)

    Dieses Feld ist im Grunde genommen überflüssig, da sich dessen Inhalt beim Abfragen leicht ermitteln lässt. Doppelte Datenhaltung sollte man vermeiden, wenn es nicht triftige Gründe dafür gibt. Du wirst ja wohl kaum einen Ansturm erwarten, bei dem selbst diese kleine Rechenoperation unvertretbar viel Zeit verbraucht. Mein Vorschlag: Nutze von den MySQL-Stringfunktionen SUBSTRING()/SUBSTR().

    begriff (Ausgabe des jew. Begriffs in der Tabelle usw.)
    url
    definition
    wort (entspricht im Grunde dem Feld "Begriff" allerdings hab ich hier Umlaute o.ä. durch ae, ue usw. ersetzt und überlange Worte abgekürzt, damit es keine Probleme in der URL bzw. beim Verlinken gibt; weiß nicht ob das sein muss, erschien mir aber sinnvoll).

    Gibt es nicht im Prinzip nicht, wenn man beachtet, dass Daten immer kontextspezifisch kodiert werden, wenn man sie in einen bestimmten Kontext bringen möchte. In dem Fall bietet sich an, eine URL-Kodierung zu verwenden.

    <?php

    #######
      #Schutz vor SQL-Injections
      #######

    if(get_magic_quotes_gpc() == false)
      {
         $buchstb = addslashes($_GET['buchstb']);
         $wort = addslashes($_GET['wort']);
      }
      else
      {
          $buchstb = $_GET['buchstb'];
          $wort = $_POST['wort'];
      }

    Magic Quotes und addslashes() soll vor SQL-Injection schützen, wie ja auch dein Kommentar aussagt. Doch ist es weder sinnvoll, generell addslashes() anzuwenden, noch für den konkreten Fall MySQL ausreichend, weil einfach zu wenig Zeichen berücksichtigt werden. Wie bereits erwähnt, sollen Daten, wenn man sie in einen bestimmten Kontext bringen möchte, kontextspezifisch kodiert werden. Dies soll aber erst kurz bevor die Daten in diesen Kontext übergeben werden geschehen, und intern sollte möglichst immer mit Rohdaten gearbeitet werden. Angenommen du möchtest die eingegebenen Daten am Bildschirm anzeigen und Magic Quotes hat zugeschlagen, zeigt es statt O'Conner nun O'Conner an. Was macht der unerfahrene Programmierer? Er wendet an der Stelle stripslashes() an (und vergisst auch gern noch htmlspecialchars()) und macht die ganze Sache unübersichtlicher. Das bewährte EVA-Prinzip (1. Eingabe, 2. Verarbeitung, 3. Ausgabe) gilt auch für Daten.

    • Entgegennehmen und Transportsicherung entfernen
    • Verarbeitung
    • Ausgeben, dabei Transportsicherung hinzufügen
      Mein Vorschlag: Magic Quotes deaktivieren (wird sowieso mit PHP6 gestorben sein) oder, falls nicht möglich, deren Auswirkungen am Scriptanfang rückgängig machen. Die MySQL-Maskierung übernimmt mysql_real_escape_string() und zwar erst dann, wenn der SQL-Befehl zusammengebaut wird oder unmittelbar davor. Dabei sollte aber nicht der Inhalt der Variablen geändert werden, mit denen man später noch weiter rechnen möchte.

    Noch ein Punkt ist, dass du einfach auf Inhalte von $_GET/$_POST zugreifst, ohne zu prüfen, ob diese überhaupt vorhanden sind.
    Mein Vorschlag: Prüfe mit isset() auf Vorhanden sein und initialisiere die Variable ansonsten mit einem Default-Wert. Verwende außerdem ein auf E_ALL gestelltes error_reporting beim Entwickeln von PHP-Scripten, da dir damit Hinweismeldungen auf Zugriffe auf nicht initialisierte Variablen nicht vorenthalten werden. Prüfe deine Scripte auf Schwachstellen, indem du auch mal gezielt völlig unerwartete Werte übergibst (z.B. HTML-Code in Benutzereingaben, ' und "-Zeichen). Auch vom Script selbst erzeugte Ausgaben, wie Links mit Parameteranhang sind nicht gegen Missbrauch sicher.

    echo "<h1>Lexikon</h1>
    <b>$buchstb</b>";

    Da haben wir es schon, das vergessene htmlspecialchars() zum Entschärfen bzw. Transportsichern von Daten, die in den HTML-Kontext gebracht werden sollen. Das ist eine Einfallstelle für Code-Injection. Wenn jemand statt eines Buchstaben HTML- und/oder clientseitigen Scriptcode übergibt, gibts du das einfach so aus. Außerdem hast du doch nur einen einzigen Buchstaben erwartet. Du solltest prüfen, dass du auch nur einen Buchstaben übergeben bekommst. Das geht auch indem du einfach nur den ersten Buchstaben z.B. mit substr() ausschneidest. Diese Eingabedatenprüfung sollte gemäß EVA gleich nach Entfernen der Transportsicherung erfolgen und nicht quer übers Script verstreut.
    Mein Vorschlag: Gleich nach der Magic-Quotes-Rück-Behandlung prüfen, dass Buchstabe nur ein Zeichen enthält oder das erste ausschneiden.
    Es ist beim Ausschneiden besser, substr() statt eines direkten Zeichenzugriffs zu verwenden, weil $string{0} bzw. $string[0] bei einem Leerstring ins Leere greift und eine Hinweismeldung ergibt.

    $query = "SELECT begriff, url FROM lexikon WHERE buchstabe = '$buchstb'" or die(mysql_error());

    Das die() wird nie aufgerufen werden, da die Zuweisung in diesem Fall stets true ergibt. Und es gehört dort auch gar nicht hin, sondern, wenn überhaupt, hinter die mysql_*-Funktionen.

    $result = mysql_query($query);

    Also hier dran. Allerdings ist das Sterbenlassen eines Scripts mit Ausgabe einer MySQL-Fehlermeldung zwar für das Entwickeln hilfreich, jedoch für den Endanwender sehr benutzerunfreundlich. Es ist ja nicht sein Verschulden, dass der Fehler auftritt, deswegen sollte man ihm auch nicht die Fehlermeldung als Reaktion auf sein Handeln präsentieren.
    Mein Vorschlag: Fehlermeldungen in einem Logmechanismus eintragen (Email an den Admin senden ist der einfachste Fall), dem Benutzer eine Tröstmeldung anzeigen und das Script ordnungsgemäß beenden. Natürlich müssen dann die Code-Teile, die auf die fehlgeschlagenen Operation aufbauen, umgangen werden (Stichwort: if-then-else).
    Übrigens geben (fast) alle mysql_*-Funktionen im Fehlerfall false zurück statt eines Ressourcen-Handles, wie es von den meisten nachfolgenden mysql_*-Funktionen benötigt wird. Fängt man das nicht ab, beschwert sich die PHP mit einer Warnung.

    Den Datenbank-Connect hast du in einer Include-Datei versteckt. Zum Thema Fehlerbehandlung gilt da auch das eben gesagte.

    Prüf doch mal, wie dein Script reagiert, wenn du falsche Zugangsdaten verwendest oder auch eine falsche/nicht vorhandene Datenbank auswählst und überlege aus Anwendersicht, ob dein Script angemessen reagiert.

    Positiv hervorzuheben ist, dass du dein Script kommentierst. Achte dabei darauf, den Sinn der Codestelle zu kommentieren, nicht nur was sie macht. Letzteres sollte sich aus dem Code ergeben.

    Beispiel:
    $i++; // hier wird $i um eins erhöht
    So ein Kommentar ist sinnlos, weil das offensichtlich ist. Kommentiert werden sollte, warum hier $i erhöht wird.

    Ebenfalls lobend erwähnenswert ist, dass du dich bemühst, deinen Code ordentlich zu formatieren, auch wenn dir das an einigen Stellen nicht gelungen ist. Aber vielleicht war das auch nur ein Copy'n'Paste-Fehler.

    Was mir selbst besonders missfällt (ohne überhaupt zu wissen, wie gut/schlecht das Skript ist), ist der letzte Teil ab der Ausgabe der Begriffserklärung. Ist es überhaupt sinnvoll, dass alles in eine Skript zu stecken?

    Das Script ist doch noch recht übersichtlich. Es scheint mir nicht sinnvoll, hier noch weitere Code-Dateien zu erzeugen, das erhöht nur den Pflegeaufwand.

    Wenn du Code-Teile auslagerst, achte darauf, dass sich diese Dateien auch zu Fuß aufrufen lassen, wenn sie nicht gerade außerhalb des Documentroots des Webservers abgelegt sind. Zugriffsbeschränkende Maßnahmen in der Webserverkonfiguration können hilfreich sein. Ebenfalls nützlich ist es, wenn die ausgelagerten Teile (Include-Dateien) bei Direktaufruf keinerlei Ausgabe vornehmen. Ausgaben und andere Rückgabewerte sollten von Funktionen erzeugt werden, die man im Hauptprogramm aufruft. Weniger bekannt ist, dass man die Include-Datei auch quasi als eine Funktion behandeln kann. Man kann mit return etwas zurückgeben, was man im Hauptprogramm entgegennehmen kann.

    include.php:
    <?php
    $foo = <<<HTMLTEXT
    <div id="footer">...</div>
    HTMLTEXT;
    return $foo;
    // das abschließende ?> kann auch weggelassen werden. Das verhindert ungewollte Whitespace-Zeichen, die sich gern nach ihm einschmuggeln.

    in der main.php:
    echo include 'include.php';
    oder:
    $bla = include 'include.php';

    Teile, die keinen PHP-Code enthalten, bindet man auch besser mit readfile() statt include ein. Der Dateiinhalt wird von readfile() nicht geparst. Das ist vor allem dann von Vorteil, wenn es jemandem gelungen sein sollte, einen ungewünschten Dateinamen/URL unterzuschieben, kann doch dann der darin enthaltene PHP-Code auf dem Server keinen Schaden anrichten.

    echo "$verabschiedung $name";

    1. Wow, das nenn ich doch mal eine Analyse. Vielen Dank (auf was hab ich mich da eingelassen :-))

      buchstabe (zur Selektion nach Anfangsbuchstabe)

      Dieses Feld ist im Grunde genommen überflüssig, da sich dessen Inhalt beim Abfragen leicht ermitteln lässt. ... Mein Vorschlag: Nutze von den MySQL-Stringfunktionen SUBSTRING()/SUBSTR().

      Ich hatte es mal mit substr() probiert, aber dabei wurde mir von der DB kein Wert ausgegeben. Das ganze sollte doch so aussehen: $query = "SELECT begriff, url FROM lexikon WHERE substr('begriff',0,1) = '$buchstb'"; (oder nur mal zum Testen " ... substr('begriff',0,1) = 'A'").

      Was ist da schon wieder falsch?

      wort (entspricht im Grunde dem Feld "Begriff" ..

      ... In dem Fall bietet sich an, eine URL-Kodierung zu verwenden.

      Alles klar, werd ich mich mal informieren.

      Mein Vorschlag: ... Die MySQL-Maskierung übernimmt mysql_real_escape_string() und zwar erst dann, wenn der SQL-Befehl zusammengebaut wird oder unmittelbar davor.

      Leider fand ich zig verschiedene Seiten, die Beispiele zur Sicherheit anbieten; war ja schon froh, dass ich dann eine - so dachte ich - gute Lösung gefunden hätte. Auf der einen Seite heißt es htmlspecialchars(); hilft auf einer anderen strip_tags(); ...

      Deiner Meinung nach, sollte das hier ja dann helfen, z.B. Bsp. #3 ?

      Das die() wird nie aufgerufen werden, da die Zuweisung in diesem Fall stets true ergibt. Und es gehört dort auch gar nicht hin, sondern, wenn überhaupt, hinter die mysql_*-Funktionen.

      Das war in der Tat falsch, und sollte da auch gar nicht mehr (und auch sonst nirgends) stehen

      Positiv hervorzuheben ist, dass du dein Script kommentierst. Achte dabei darauf, den Sinn der Codestelle zu kommentieren, nicht nur was sie macht. Letzteres sollte sich aus dem Code ergeben.

      Danke für den Tipp, wobei die Kommentare hier nur dazu dienen sollten den Lesern im Forum kurz zu sagen worum es hier geht; für mich selbst werd ich die dann noch wie von dir vorgeschlagen ausweiten.

      Ebenfalls lobend erwähnenswert ist, dass du dich bemühst, deinen Code ordentlich zu formatieren, auch wenn dir das an einigen Stellen nicht gelungen ist. Aber vielleicht war das auch nur ein Copy'n'Paste-Fehler.

      Tabs werden wohl verschluckt :-)

      Die ganzen restlichen Vorschläge muss ich mir nochmal gesondert anschauen wenn das Script mal bis hierhin läuft. Sonst wird mir das alles zu viel ;-)

      1. Hi,

        Ich hatte es mal mit substr() probiert, aber dabei wurde mir von der DB kein Wert ausgegeben. Das ganze sollte doch so aussehen: $query = "SELECT begriff, url FROM lexikon WHERE substr('begriff',0,1) = '$buchstb'"; (oder nur mal zum Testen " ... substr('begriff',0,1) = 'A'").
        Was ist da schon wieder falsch?

        substr('begriff',0,1) ist immer das kleine b - denn Du hast dort den String 'begriff', nicht den Spaltennamen begriff.

        cu,
        Andreas

        --
        Warum nennt sich Andreas hier MudGuard?
        Schreinerei Waechter
        O o ostern ...
        Fachfragen unaufgefordert per E-Mail halte ich für unverschämt und werde entsprechende E-Mails nicht beantworten. Für Fachfragen ist das Forum da.
        1. substr('begriff',0,1) ist immer das kleine b - denn Du hast dort den String 'begriff', nicht den Spaltennamen begriff.

          Okay, das klappt nun (auch wenn ich mit '' nach wie vor gar nichts bekommen habe). Soweit, so gut :)

          Ds mit der SQL-Injection muss ich mir noch mal anschauen; da hab ich noch etwas dran zu knabbern ;-)

          Wie kann ich denn nun außerdem sicherstellen, dass nur Wörter aufgerufen werden können, wenn auch der Passende Buchstabe dazu ausgewählt wurde?
          Ich hab da ja im Moment diese if-Abfrage (if($row->buchstabe == $buchstb)), aber das scheint mir nicht sehr elegant.

          Zu dem hab ich ja auch die Abfrage, ob ein Wort oder zu dem Wort auch ein Buchstabe angegeben wurde (if(($wort == "") OR (($wort != "") AND ($buchstb == "")))). Das müsste ich doch auch anders lösen können?

          1. echo $begrüßung;

            substr('begriff',0,1) ist immer das kleine b - denn Du hast dort den String 'begriff', nicht den Spaltennamen begriff.
            Okay, das klappt nun (auch wenn ich mit '' nach wie vor gar nichts bekommen habe). Soweit, so gut :)

            Ah, da war doch noch was ... MySQL zählt hier anders. Da beginnt ein String mit dem Zeichen 1 nicht wie in PHP mit 0.

            Wie kann ich denn nun außerdem sicherstellen, dass nur Wörter aufgerufen werden können, wenn auch der Passende Buchstabe dazu ausgewählt wurde?

            Das würde ich gern dir überlassen, da das ist keine sicherheitskritsche Angelegenheit ist, sondern nur Nachdenken und das richtige Kombinieren der von dir gestellten Bedingungen ist. :-)

            echo "$verabschiedung $name";

            1. Es ist besser, (Schritt 1) wenn die Daten gleich zum Scriptbeginn von diesen Magic Quotes (oder anderen Transportsicherungen) bereinigt werden.

              D.h. ganz zu Beginn prüfen ob magic_quotes_gpc aktiv ist und gegebenenfalls mit addslashes bereinigen:

                if(get_magic_quotes_gpc() == false)  
                {  
                   $buchstb = addslashes($_GET['buchstb']);  
                   $wort = addslashes($_GET['wort']);  
                }  
                else  
                {  
                $buchstb = $_GET['buchstb'];  
                $wort = $_POST['wort'];  
                }
              

              (aber so ist's vermutlich falsch, denn das war ja meine allererste Variante :))

              Dann (Schritt 2) durchlaufen sie das Affenformular, wobei bei der Ausgabe der Daten in die Formularfeld-Vorbelegung (<input ... value=[hier]>) HTML-spezifisch kodiert wird (htmlspecialchars()).

              D.h. bei echos spuck ich meine Werte mit htmlspecialchars() wieder aus!? (wobei das bei mir doch gar nicht der Fall ist, da ich ja hier keine Formulare habe)

              Sind keine Fehler mehr im Formular geht es weiter zum Datenbank-Teil (Schritt 3) und dort werden die Daten, so wie sie am Ende von Schritt 1 waren - also "entmagicquoted"/transportentsichert aber noch nicht "htmlspecialchared", in den SQL-String eingefügt und dabei mit mysql_real_escape_string() behandelt.

              Also sowas wie:

              $query = sprintf("SELECT begriff, url FROM lexikon WHERE substr(begriff,1,1) = '%s'", mysql_real_escape_string($buchstb));

              Das würde ich gern dir überlassen, da das ist keine sicherheitskritsche Angelegenheit ist, sondern nur Nachdenken und das richtige Kombinieren der von dir gestellten Bedingungen ist. :-)

              Tjo, dann ist es ja so wie es im Moment ist korrekt (oder gar perfekt ;-)).

              Ich könnt vielleicht vor der zweiten MySQL-Abfrage ein elseif buchstb = substr(begriff) einbauen, aber im Grunde verschieb ich meine jetzige Lösung damit ja nur nach oben!?

              1. echo $begrüßung;

                Es ist besser, (Schritt 1) wenn die Daten gleich zum Scriptbeginn von diesen Magic Quotes (oder anderen Transportsicherungen) bereinigt werden.

                D.h. ganz zu Beginn prüfen ob magic_quotes_gpc aktiv ist und gegebenenfalls mit addslashes bereinigen:

                Bereinigen bedeutet nicht, die Daten durch Maskieren gegen irgendetwas abzusichern, sondern das Gegenteil, sie in ihre Roh-Form zu bringen, sie zu demaskieren, damit man vernünftig damit weiterarbeiten kann.

                addslashes()/Magic Quotes bereinigt nicht, es fügt eine Transportsicherung hinzu. Diese zusätzlichen Zeichen stören bei Stringoperationen und allerlei Ausgaben, die nicht im Zusammenhang mit Datenbankbefehlen stehen. Die Stringlänge von O'Conner ist 8 Zeichen. Durch M.Q. wird es aber zu O'Conner und strlen() ergibt 9 Zeichen. Diese Form ist also für die Weiterverarbeitung ungeeignet.
                Wie ich schon ausführte, ist M.Q. nicht wirklich geeignet, in SQL-Befehlen für Datenintegrität zu sorgen. Solange es dieses Feature noch in PHP gibt, ist es deshalb besser, seine Auswirkungen komplett zu entfernen, bevor man mit den Daten weiterarbeitet. Erst zur Ausgabe erfolgt die Absicherung gemäß den Regeln des Ausgabemediums.

                (wobei das bei mir doch gar nicht der Fall ist, da ich ja hier keine Formulare habe)

                Das war ja nur mal ein Beispiel. Manchmal geht es mir darum, nicht nur speziell das eine Problem des Fragenden zu lösen, sondern auch die grundsätzliche Problematik eines bestimmten Themas zu erläutern. Das soll verdeutlichen, warum eine Vorgehensweise prinzipiell besser ist als eine bestimmte anderen, obwohl man mit der auch zum Ziel kommt.

                echo "$verabschiedung $name";

                1. deshalb besser, seine Auswirkungen komplett zu entfernen, bevor man mit den Daten weiterarbeitet.

                  Also dann einfach ersetzen durch "nichts" (str_replace("\"", "", $buchstb);) oder wie?

                  Und $query = sprintf("SELECT begriff, url FROM lexikon WHERE substr(begriff,1,1) = '%s'", mysql_real_escape_string($buchstb)); stimmt so ungefähr (weil du's nicht mehr erwähnt hast)?

                  1. echo $begrüßung;

                    Also dann einfach ersetzen durch "nichts" (str_replace("\"", "", $buchstb);) oder wie?

                    stripslashes() ist die Gegenfunktion zu addslahes()/Magic Quotes. Wenn du es für mehr als ein/zwei Werte anwenden möchtest, empfiehlt sich eine Lösung wie im Example 31-2 unter Disabling Magic Quotes angeführt.

                    Und $query = sprintf("SELECT begriff, url FROM lexikon WHERE substr(begriff,1,1) = '%s'", mysql_real_escape_string($buchstb)); stimmt so ungefähr (weil du's nicht mehr erwähnt hast)?

                    Ja, passt.

                    echo "$verabschiedung $name";

                    1. stripslashes() ist die Gegenfunktion zu addslahes()/Magic Quotes. Wenn du es für mehr als ein/zwei Werte anwenden möchtest, empfiehlt sich eine Lösung wie im Example 31-2 unter Disabling Magic Quotes angeführt.

                      Hm, aber stripslashes entfernt doch durch addslashes (oder von mir aus auch generell) erzeugte Slashes. Geht's bei Magic Quotes nicht um Anführungszeichen? Oder soll dadurch aus " nur " werden?

                      Irgendwie versteh ich nun gar nichts mehr :)

                      1. echo $begrüßung;

                        Hm, aber stripslashes entfernt doch durch addslashes (oder von mir aus auch generell) erzeugte Slashes. Geht's bei Magic Quotes nicht um Anführungszeichen? Oder soll dadurch aus " nur " werden?

                        Irgendwie versteh ich nun gar nichts mehr :)

                        Magic Quotes macht nichts anderes als addslashes() auf alle EGPCS-Variablen ($_GET, $_POST usw.) anzuwenden, es fügt \ vor die Zeichen ' " \ und Null-Bytes. stripslahes() nimmt nur diese vier Kombinationen und entfernt daraus die . Es entfernt nicht alle Backslashes sondern eben nur die aus diesen Kombinationen.
                        mysql_real_escape_string() macht im Prinzip auch nichts anderes als addslashes(), nur dass es mehr Zeichen berücksichtigt, weil es direkt von der MySQL-API bereitgestellt wird und damit viel besser weiß, welche Zeichen zu behandeln sind.

                        echo "$verabschiedung $name";

                        1. Wenn also magic_quotes_gpc auf Off steht, kann man sich das alles sparen und ist quasi schon "auf der sicheren Seite" (zumindest was die "Eingangsbehnadlung" der Variablen - oder was auch immer - angeht)!?

                          1. echo $begrüßung;

                            Wenn also magic_quotes_gpc auf Off steht, kann man sich das alles sparen und ist quasi schon "auf der sicheren Seite" (zumindest was die "Eingangsbehnadlung" der Variablen - oder was auch immer - angeht)!?

                            Ja.
                            Probier es doch einfach alles mal aus. Wenn du noch nicht hast, installier dir einen Webserver mit PHP und teste, was mit den Daten passiert, wenn Magic Quotes eingeschaltet ist, und wenn es aus ist, wie sich addslashes() und stripslashes() auswirkt und wie mysql_real_escape_string() arbeitet. Verwende dabei auch mal Zeichen, die von mysql_real_escape_string() berücksichtigt werden und von addslashes() nicht (z.B. Zeilenumbrüche).

                            echo "$verabschiedung $name";

                            1. Ah ja, I see. Auf meinem Webspace sind die Magic Quotes aber auch aus, von daher ist das in diesem Fall gar nicht das Problem.

                              Und langsam sieht das Ganze doch nach etwas aus :-)

                              Vielen Dank schon mal, für die schnelle und ausführliche Hilfe dedlfix. Besonders bzgl. MySQL-Schutz hab ich mich ja zum Schluss totgesucht.

                              Soweit, so gut. Mittlerweile hab ich also noch das Feld "buchstabe" aus der DB entfernt. Zudem kann auch das Feld "url" dran glauben, denn die jeweilige Adresse zu jedem Begriff kann ich ja über den Begriff selbst und dessen Anfangsbuchstaben zusammensetzen: index.php?buchstb=".substr($row->begriff,0,1)."&wort=$row->begriff.

                              Ich hoff da schreitet nicht gleich wieder jemand ein und sagt, das wäre nicht so sinnvoll ;-)

                              Nun hänge ich als nächstes bei der korrekten Eingabe der Daten in die DB, bzw. dem Auslesen des Begriffs (wegen Umlauten, Leerzeichen ...).

                              Meine Seiten nutzen bisher alle den Zeichensatz ISO-8859-1, drum hab ich auch in der DB die Begriffe mit HTML-Entities (&auml; etc.) angegeben; zudem gibts hier und da Leerzeichen.

                              Das scheint auch so zu klappen, Opera und FF wandeln Sonderzeichen auch in Escapezeichen um (ä wird zu %E4 etc.). Allerdings zeigt der IE Umlaute so wie sie sind an.

                              Muss ich da jetzt noch was ändern, oder passt das so?

                              1. echo $begrüßung;

                                Nun hänge ich als nächstes bei der korrekten Eingabe der Daten in die DB, bzw. dem Auslesen des Begriffs (wegen Umlauten, Leerzeichen ...).
                                Meine Seiten nutzen bisher alle den Zeichensatz ISO-8859-1, drum hab ich auch in der DB die Begriffe mit HTML-Entities (&auml; etc.) angegeben; zudem gibts hier und da Leerzeichen.

                                Wenn du eine Zeichenkodierung verwendest, die die von dir verwendeten Zeichen enthält, brauchst du dafür keine Ersatzschreibweise zu verwenden. Wichtig ist nur, dass die Zeichensatz-Angabe auch mit der tatsächlichen Kodierung der Daten übereinstimmt, sonst gibt es Fehlinterprätationen. ISO-8859-1 enthält die deutschen Umlaute. Wenn du also einen Editor verwendest, der dir ISO-8859-1 oder etwas kompatibles dazu ausgibt (das macht hierzulande quasi jeder (Windows-)Editor im Grundzustand), ist die $xuml;-Schreibweise überflüssig.

                                Das scheint auch so zu klappen, Opera und FF wandeln Sonderzeichen auch in Escapezeichen um (ä wird zu %E4 etc.). Allerdings zeigt der IE Umlaute so wie sie sind an.
                                Muss ich da jetzt noch was ändern, oder passt das so?

                                Das muss dich nur dann interessieren, wenn es dabei Probleme gibt. Die Anzeige in der URL-Zeile des Browsers ist weniger relevant. Es kommt darauf an, was er wirklich versendet. Wenn also bei PHP alles richtig ankommt, sehe ich keinen Handlungsbedarf. (Was natürlich nicht ausschließt, dass du auch so mit dem Thema beschäftigen kannst.)
                                Probleme kann es geben, wenn die Datenbank eine andere Kodierung zur Datenübertragung verwendet, also das PHP. Das ist bei MySQL ab Version 4.1 relevant und auch nur dann, wenn etwas anderes als ISO-8859-1 als System-Default eingestellt ist.

                                Ein Werkzeug zum Anschauen des HTTP-Verkehrs ist die LiveHTTPHeaders-Extension für den Firefox. Für den IE gibt es auch solche Erweiterungen.
                                Das Zeichensatz-Thema unter MySQL ist recht umfangreich, da recht fein geliederte Einstellmöglichkeiten an allen möglichen Ecken vorhanden sind: Character Set Support.

                                echo "$verabschiedung $name";

                                1. Okay, alles klar :)

                                  Dann dank ich dir nochmal recht herzlich für die Hilfe. Find ich toll, dass man hier so qualifizierte Leute antrifft.

                                  Jetzt muss ich das ganze in den nächsten Tagen nur noch ordentlich verpacken, und dann sollte das doch ordentlich laufen (zumindest für meine Verhältnisse :)).

      2. echo $begrüßung;

        $query = "SELECT begriff, url FROM lexikon WHERE substr('begriff',0,1) = '$buchstb'"; (oder nur mal zum Testen " ... substr('begriff',0,1) = 'A'").
        Was ist da schon wieder falsch?

        Wie sucht man Fehler? Eine der wichtigsten Methoden ist, sich Variableninhalte und Ergebnisse von Operationen und auch Zwischenergebnisse von Operationsteilen ausgeben zu lassen.
        In deinem Fall wäre das, mal die Datenbank mit
          SELECT substr('begriff',0,1) FROM lexikon
        zu befragen. Wenn du dann nicht stutzig wirst, dass du immer b als Antwort bekommst, ...
        Wie MudGuard schon sagte hast du hier einen String verwendet. Spaltennamen werden im Allgemeinen ohne Anführungszeichen notiert. Wenn du doch welche brauchst, weil du einen reservierten Bezeichner als Spaltennamen verwendet hast, dann sind bei MySQL Backticks zu verwenden.

        Mein Vorschlag: ... Die MySQL-Maskierung übernimmt mysql_real_escape_string() und zwar erst dann, wenn der SQL-Befehl zusammengebaut wird oder unmittelbar davor.

        Leider fand ich zig verschiedene Seiten, die Beispiele zur Sicherheit anbieten; war ja schon froh, dass ich dann eine - so dachte ich - gute Lösung gefunden hätte. Auf der einen Seite heißt es htmlspecialchars(); hilft auf einer anderen strip_tags(); ...

        Deiner Meinung nach, sollte das hier ja dann helfen, z.B. Bsp. #3 ?

        Das ist eigentlich etwas, das ich so nicht vorschlagen würde. Dieses Beispiel kombiniert Eingabe- und Ausgabedatenbehandlung. Damit erreicht es zwar sein Ziel, ist aber programmstrukturtechnisch ungünstig und nur bei kleineren Scripten handhabbar.

        Angenommen, du hast ein Affenformular und anschließend soll eine Datenbank mit den Eingaben beschrieben werden. Durch Magic Quotes bekommst du immer ' wenn jemand ' eingibt. Mit jeder "Affen-Runde" vermehren sich die -Zeichen, wenn der Anwender sie nicht jedesmal korrigiert. Ein stripslashes() kurz vor dem SQL-Befehl-Zusammenbau entfernt nur die Zeichen der letzten Runde.
        Es ist besser, (Schritt 1) wenn die Daten gleich zum Scriptbeginn von diesen Magic Quotes (oder anderen Transportsicherungen) bereinigt werden. Dann (Schritt 2) durchlaufen sie das Affenformular, wobei bei der Ausgabe der Daten in die Formularfeld-Vorbelegung (<input ... value=[hier]>) HTML-spezifisch kodiert wird (htmlspecialchars()). Sind keine Fehler mehr im Formular geht es weiter zum Datenbank-Teil (Schritt 3) und dort werden die Daten, so wie sie am Ende von Schritt 1 waren - also "entmagicquoted"/transportentsichert aber noch nicht "htmlspecialchared", in den SQL-String eingefügt und dabei mit mysql_real_escape_string() behandelt.

        echo "$verabschiedung $name";