Anfänger: Sichere Formulare erstellen - erste Schritte

Hallo,

ich habe mich erstmals mit dem Erstellen von Formularen auseinander gesetzt. Nachdem das jetzt soweit läuft, möchte ich mich mit dem Sicherheitsaspekten auseinandersetzen.

Dazu habe ich im Forum gesucht und u.a. diesen Link hier:

http://www.oreilly.de/artikel/php_sicherheit.html

gefunden. Allerdings habe ich immernoch die ein oder andere Frage (und das wird nicht die letzte sein denke ich), sodass ich mich freuen würde, wenn Ihr mir mit Euren Expertenwissen helfen könntet. Ich würde mich über konstruktive Kritik freuen, letzten Endes bin ich eben noch etwas frisch auf dem Gebiet, habe aber die besten Vorsätze.

Die erste Frage:

Bei dem og Artikel ist immer von GET und POST die Rede. Ich selbst schreibe die Daten aus dem Formular in die DB mit

  
 if (isset($array)) {  
 $sender2=$array["sender2"];  
 $uhrzeit=$array["uhrzeit"];  
 $titel=$array["titel"];  
}  

und

  
mysql_query("INSERT tv (sender2, uhrzeit, titel) VALUES ('$sender2', '$uhrzeit', '$titel')");  

Ist das auch eine Form von GET und POST oder kann ich Sicherheitsmaßnahmen wie (aus dem og Text übernommen)

  
$get_safe = $_GET;  
$post_safe = $_POST;  
$cookie_safe = $_COOKIE;  
if (!get_magic_quotes_gpc()) {  
    // Rückwärtsstriche in Browservariablen einfügen  
    foreach ($_GET as $key => $value) {  
        $get_safe[$key] = addslashes($value);  
    }  
    foreach ($_POST as $key => $value) {  
        $post_safe[$key] = addslashes($value);  
    }  
    foreach ($_COOKIE as $key => $value) {  
        $cookie_safe[$key] = addslashes($value);  
    }  
}  

überspringen?

Vielen Dank!

  1. Lieber Anfänger,

    if (isset($array)) {

    $sender2=$array["sender2"];
    $uhrzeit=$array["uhrzeit"];
    $titel=$array["titel"];
    }

      
    das Umkopieren von Werten ist eine Verschleierung, die nicht sinnvoll ist. Wenn Du user-submitted data hast (und das sind die Werte in $\_GET, $\_POST, $\_COOKIE usw.), dann muss in Deinem Script immer klar sein, dass es sich eben um potentiell gefährliche Werte handelt. Das Umkopieren in andere Variablen verschleiert die Herkunft und die Gefährlichkeit der Daten. Daher: NO GO!  
      
    
    > `mysql_query("INSERT tv (sender2, uhrzeit, titel) VALUES ('$sender2', '$uhrzeit', '$titel')");`{:.language-php}  
      
    Spätestens hier ist nicht mehr ersichtlich, dass $sender2 in Wirklichkeit eine unbehandelte Usereingabe ist, die potentiell Deine Datenbank kapern kann (Stichwort "SQL injection" - Google danach und mache Dich schlau!). Daher ist Deine Verschleierung sehr sehr gefährlich.  
      
    
    > ~~~php
      
    
    > $get_safe = $_GET;  
    > $post_safe = $_POST;  
    > $cookie_safe = $_COOKIE;  
    > if (!get_magic_quotes_gpc()) {  
    >     // Rückwärtsstriche in Browservariablen einfügen  
    >     foreach ($_GET as $key => $value) {  
    >         $get_safe[$key] = addslashes($value);  
    >     }  
    >     foreach ($_POST as $key => $value) {  
    >         $post_safe[$key] = addslashes($value);  
    >     }  
    >     foreach ($_COOKIE as $key => $value) {  
    >         $cookie_safe[$key] = addslashes($value);  
    >     }  
    > }  
    > 
    
    

    In PHP gibt es (noch, in PHP6 wird es nicht mehr existieren) die "magic quotes"-Geschichte, die einfache und doppelte Anführungszeichen mit Backslashes unschädlich zu machen sucht, damit eben SQL-Injektionen nicht greifen. In Deinem Code-Beispiel wird aber genau diese "Sicherheitsmaßnahme" wieder ausgehebelt, indem die Originaleingaben des Users wieder hergestellt werden. Und schon wieder wird verschleiert: $get_safe suggeriert, dass die Werte nun "sicher" sind, dabei sind sie ja gerade _ent-_sichert worden!

    Was Du benötigst, ist der Artikel über Kontext-gerechtes Enkodieren von Daten.

    Um Deinen Thread-Titel zu konkretisieren: Dein Problem liegt nicht in den Formularen, sondern in der serverseitigen Verarbeitung der abgeschickten Daten.

    Liebe Grüße,

    Felix Riesterer.

    --
    ie:% br:> fl:| va:) ls:[ fo:) rl:° n4:? de:> ss:| ch:? js:) mo:} zu:)
    1. Danke!

      Gibt es denn soetwas wie einen Leitfaden für die Erstellung eines sicheren Formulares? Die if (isset($array))-Variante habe ich aus dem Netz, woher soll man denn praktisch wissen, ob man mit GET oder POST oder was auch immer arbeiten muss?!

      Unabhängig davon hilft der gepostete Artikel natürlich sehr, ich wusste nicht, dass man unter diesen Schlagwörtern suchen muss.

      Danke und Gruß

      1. Hi!

        Gibt es denn soetwas wie einen Leitfaden für die Erstellung eines sicheren Formulares? Die if (isset($array))-Variante habe ich aus dem Netz, woher soll man denn praktisch wissen, ob man mit GET oder POST oder was auch immer arbeiten muss?!

        Die isset()-Abfrage hat weniger sicherheitstechnische als vielmehr PHP-technische Gründe. Unter PHP müssen Variablennamen nicht explizit definiert werden. Ein Zugriff auf nicht vorhandene Variablen und auch Array-Elemente wirft üblicherweise nur im Stillen eine Notice-Meldung. Sichtbar machen kann man diese mit einem auf E_ALL gestellten error_reporting (plus display_errors auf on). Da man bei Requsts aus dem großen weiten Internet nie wissen kann, was alles an Daten mitgeliefert wird, fragt man zunächst deren Existenz ab, bevor man fortfährt. Sind die erwarteten Werte nicht vorhanden, so ist das meist ein Zeichen dafür, dass man den Request so nicht abarbeiten will.

        Was die Sicherheit anbelangt: Meiner Meinung nach wird zu oft nur ein Teilaspekt bedacht. Man weiß: "All Input is evil." Aber das ist nur die halbe Wahrheit, wenn man sich dabei auf Benutzereingaben beschränkt. Im Grunde genommen kann man das Sicherheitsdenken ziemlich weit herunterfahren, wenn man sich der eigentlichen Erfordernisse der jeweiligen Syntax annimmt. Nahezu jeder Kontext verlangt bestimmte Regeln, was die Notation von Werten anbelangt. Dazu zwei Beispiele: Die Zahl 42 wird in einem Text-Kontext als Folge der Ziffern 4 und 2 notiert. Aber auch 0x2A (hex) oder 052 (oktal) sind gültige (PHP-)Schreibweisen, die die 42 repräsentieren. - In einem String, der mit "" begrenzt wird und ein " als Datenbestandteil hat, ist das " als " zu notieren. Ein " wird also durch ein " repräsentiert. Andere Kontexte haben andere Regeln, wie &lt; für ein < unter HTML. Es ist wichtig, dass man diese kontextspezifischen Notationsregeln für die verschiedenen Werte kennt. Technisch ausgedrückt: Ein Wert muss (häufig) durch sein Literal repräsentiert werden.

        Es ist völlig unerheblich, ob ein Wert aus einer Benutzereingabe stammt, selbst notiert ist oder sonstwo entstanden ist beziehungsweise abgefragt wurde. Es müssen in jedem Fall beim Einfügen in einen anderen Kontext die Regeln dieses Kontextes zur Notation des Wertes befolgt werden. Bei einer händischen Notation von Code ist das noch einfach, weil es offensichtlich ist und oft durch einen Syntaxfehler geahndet wird. Hat man jedoch die Werte in einer Variablen stehen, so sieht man ohne eine Kontrollausgabe nicht, dass da Zeichen mit besonderer Bedeutung für bestimmte Kontexte enthalten sind, oder dass irgendwann im Verlaufe des Script-Lebens solche Sonderzeichen (auf welchen Weise auch immer) in diese Variablen gelangen können. Da das Einfügen von Variableninhalten jedoch ein häufiger Anwendungsfall ist, gibt es für die meisten Kontexte spezielle Funktionen, die die Variableninhalte (oder allgemein: des übergebenen Parameters) in die kontextgerechte Notation bringen. htmlspecialchars() für den HTML-Kontext, url_encode() für URLs, mysql(i)_real_escape_string() für Strings in MySQL-Statements.

        Lo!

        1. Ich habe an dieser Stelle noch mal eine konkrete Frage. Nach dem ersten Lesen (geht hier alles Schritt für Schritt) sieht das bei mir so aus (verkürzt) ...

            
          <?  
          if (isset($_POST['neu'])) {  
          $text = trim(htmlentities(strip_tags($_POST['text'])));  
          }  
          
          

          und dann

            
          mysql_query("INSERT tv (text) VALUES ('$text')");  
          
          

          sowie in HTML

            
          <TEXTAREA NAME="text" MAXLENGTH=3000 ROWS=5 COLS=75 wrap="virtual" style="{width: 300;}"></TEXTAREA></table>  
          
          

          Wenn ich das absende und von mir aus Test <html> ins Textfeld eingebe, landet Test <html> auch in der Datenbank. Müsste das durch diese Geschichte

            
          $text = trim(htmlentities(strip_tags($_POST['text'])));  
          
          

          nicht ausgeschlossen sein oder habe ich offensichtlich etwas falsch gemacht?

          Danke!

          1. Hi,

            $text = trim(htmlentities(strip_tags($_POST['text'])));

            Was soll denn dieser Unfug?
            Maximal das trim ist an der Stelle sinnvoll -

            mysql_query("INSERT tv (text) VALUES ('$text')");

            • und hier fehlt dafür die kontextgerechte Behandlung.

            Wenn ich das absende und von mir aus Test <html> ins Textfeld eingebe, landet Test <html> auch in der Datenbank.

            Na hoffentlich.
            Wenn es nicht dort landen würde, dann hättest du ja die Daten verfälscht - das willst du doch wohl nicht?

            MfG ChrisB

            --
            “Whoever best describes the problem is the person most likely to solve the problem.” [Dan Roam]
            1. Was soll denn dieser Unfug?
              Maximal das trim ist an der Stelle sinnvoll -

              mysql_query("INSERT tv (text) VALUES ('$text')");

              • und hier fehlt dafür die kontextgerechte Behandlung.

              Ein freundlicher Hinweis (Bsp., Link, Erklärung) würde mir besser helfen.

              1. Hi,

                Ein freundlicher Hinweis (Bsp., Link, Erklärung) würde mir besser helfen.

                Den Link zu einem guten Artikel über die Thematik hast du bereits in der ersten Antwort von Felix bekommen.

                MfG ChrisB

                --
                “Whoever best describes the problem is the person most likely to solve the problem.” [Dan Roam]
                1. Den Link zu einem guten Artikel über die Thematik hast du bereits in der ersten Antwort von Felix bekommen.

                  Was insgesamt fehlt ist mal eine Anleitung für ein Musterbeispiel. So eine Art "Daten sicher per Formular in die Datenbank übermitteln". Für einen Anfänger ist es schwer, da durchzublicken bzw. einen Anfang zu finden. Faktisch ist nicht jeder hauptberuflicher Webprogrammierer oder -codierer und hat auch nicht die Zeit, hobbymäßig einer zu werden.

                  1. Hi,

                    Was insgesamt fehlt ist mal eine Anleitung für ein Musterbeispiel. So eine Art "Daten sicher per Formular in die Datenbank übermitteln". Für einen Anfänger ist es schwer, da durchzublicken bzw. einen Anfang zu finden.

                    Du musst die Problematik an sich und ihre Ursachen verstehen, dann weisst du auch, was an welcher Stelle zu unternehmen ist.

                    Und genau das erklärt der Artikel sehr umfangreich und detailliert.

                    MfG ChrisB

                    --
                    “Whoever best describes the problem is the person most likely to solve the problem.” [Dan Roam]
                  2. Hi!

                    Den Link zu einem guten Artikel über die Thematik hast du bereits in der ersten Antwort von Felix bekommen.
                    Was insgesamt fehlt ist mal eine Anleitung für ein Musterbeispiel. So eine Art "Daten sicher per Formular in die Datenbank übermitteln". Für einen Anfänger ist es schwer, da durchzublicken bzw. einen Anfang zu finden.

                    Ja, das glaub ich gerne. Du musst nämlich stets richtig erkennen, welcher Kontext gerade vorliegt und dazu wissen, wie die Literale der dahinein einzufügenden Werte notiert werden müssen. Mehr gehört vom Prinzip her nicht dazu.

                    Faktisch ist nicht jeder hauptberuflicher Webprogrammierer oder -codierer und hat auch nicht die Zeit, hobbymäßig einer zu werden.

                    Das interessiert niemanden. Wenn ein Programm fehlerhaft ist, kann/wird die Lücke ausgenutzt werden, egal ob der Fehler von einem Profi oder einem Laien implementiert wurde.

                    Lo!

          2. Hi!

            <?

            if (isset($_POST['neu'])) {
            $text = trim(htmlentities(strip_tags($_POST['text'])));
            }

              
            Du willst isset($\_POST['text']) prüfen, aber das ist vermutlich nur ein Unaufmerksamkeitsfehler.  
              
            
            > und dann  
            >   
            > `mysql_query("INSERT tv (text) VALUES ('$text')");`{:.language-php}  
              
            Nicht alles auf einmal. Was willst du mit der HTML-spezifischen Notation im DBMS? Das ist kein HTML-Kontext. Das stört dort nur bei Vergleichen und anderen Formen der Stringverarbeitung. Der HTML-Kontext kommt irgendwann später, wenn überhaupt. Dann erst ist es angebracht sich um ihn zu kümmern.  
              
            Das wichtigste hast du aber vergessen: Du hast hier ein SQL-Statement und willst $text in einen SQL-String-Kontext einfügen. Weder trim() noch htmlentities() noch strip\_tags() haben sich um eventuell enthaltene ' gekümmert. Wenn solche in $text enthalten sind, bekommst du Syntaxfehler oder andere schlimme Krankheiten.  
              
            
            > Wenn ich das absende und von mir aus Test <html> ins Textfeld eingebe, landet Test <html> auch in der Datenbank.  
              
            Durch dein strip\_tags() landet nur Test in der Datenbank. htmlentities() macht in dem konkreten Fall nichts. Aber beispielsweise Umlaute, & und " werden dir verunstaltet (im Datenbanksinne).  
              
            
            > Müsste das durch diese Geschichte  
            > `$text = trim(htmlentities(strip_tags($_POST['text'])));`{:.language-php}  
            > nicht ausgeschlossen sein oder habe ich offensichtlich etwas falsch gemacht?  
              
            Was bringt dir strip\_tags()? Wenn du bei der Ausgabe in den HTML-Kontext htmlspecialchars() korrekt anwendest, dann kann dir egal sein, ob jemand HTML-Code oder was anderes, ähnlich aussehendes geschrieben hat. Es landet nicht als ausführbarer Code in der Ausgabe.  
              
            Selbst wenn du keinen HTML-Code in deiner Anwendung angezeigt haben möchtest, wirst du unter Umständen den Rest des Textes auch nicht haben wollen. Außerdem finden sich weitere Wege, unsinniges Zeug zu übergeben, ohne dass da irgend eine Form von Code dabei ist. strip\_tags() entbindet dich nicht von deinen "Kontrollpflichten" gegenüber unerwünschten Eingaben.  
              
              
            Lo!
            
      2. Lieber Anfänger,

        Gibt es denn soetwas wie einen Leitfaden für die Erstellung eines sicheren Formulares?

        ich habe versucht Dir zu erklären, dass es keine sicheren oder unsicheren Formulare gibt. Einzig die Auswertung der darüber abgeschickten Daten kann sicher oder unsicher sein!

        Die if (isset($array))-Variante habe ich aus dem Netz, woher soll man denn praktisch wissen, ob man mit GET oder POST oder was auch immer arbeiten muss?!

        Woher man das wissen soll? Man ist doch der Autor der entsprechenden Web-Applikation! Und als solcher macht man sich sinnvollerweise sehr gründliche Gedanken, ob man ein Formular mit der Methode POST oder GET erstellt. Immerhin haben beide Methoden ihre Vor- und Nachteile. Und je nach Deiner Entscheidung kann dann Deine Applikation nur mit entsprechend versandten Daten umgehen!

        Beispiel: Ich habe etwas unter bestimmten Stichworten bei Google gefunden. Jetzt will ich Dir meine Google-Treffer verlinken. Da Google die Suchanfrage über GET entgegennimmt, kann ich nun meine Suchanfrage verlinken.
        * Google zu "SQL injection"
        * das hießige Forum zu "SQL injection"

        Mit POST geht soetwas nicht. Daher wundere ich mich, dass z.B. die Board-Software punBB Suchanfragen nur per POST entgegennimmt. Dort kann ich keine Suchanfragen verlinken. Wie man aber sieht, ist das hiesige Forum dagegen sehr wohl dazu in der Lage.

        Manchmal ist es sinnvoll beide Methoden zu kombinieren. Beispiel:

        <form action="/mein-script.php?get-parameter1=lustig&amp;get-parameter2=schnell" method="post">  
            <p><input name="post-parameter1" value="vorbelegt" /></p>  
        </form>
        

        Das obige Formular benutzt als Methode "post". Das bedeutet, dass der Wert des <input>-Elements mittels POST an den Server übertragen wird. Dort kommt er dann als $_POST['post-parameter1'] an. Zusätzlich sind aber auch GET-Daten übermittelt worden (nämlich über die URL). Alles zusammen kommen etwa diese Daten an:

        $_GET = array(  
            'get-parameter1' => 'lustig',  
            'get-parameter2' => 'schnell'  
        );  
          
        $_POST = array(  
            'post-parameter1' => 'vorbelegt'  
            // es sei denn der User hat die Voreintragung verändert  
        );
        

        Jenachdem was Du erreichen willst, wirst Du entsprechende Formulare bauen müssen, die GET- und/oder POST-Daten verschicken werden. Die Sicherheit kannst Du aber nicht im Formular herstellen. Die muss von Deiner serverseitigen Auswertung der Daten geleistet werden!

        Unabhängig davon hilft der gepostete Artikel natürlich sehr, ich wusste nicht, dass man unter diesen Schlagwörtern suchen muss.

        Deshalb hatte ich ihn Dir verlinkt. Freut mich, wenn er Dir weiterhilft.

        Liebe Grüße,

        Felix Riesterer.

        --
        ie:% br:> fl:| va:) ls:[ fo:) rl:° n4:? de:> ss:| ch:? js:) mo:} zu:)
    2. Hi Felix.

        
      
      > > $get_safe = $_GET;  
      > > $post_safe = $_POST;  
      > > $cookie_safe = $_COOKIE;  
      > > if (!get_magic_quotes_gpc()) {  
      > >     // Rückwärtsstriche in Browservariablen einfügen  
      > >     foreach ($_GET as $key => $value) {  
      > >         $get_safe[$key] = addslashes($value);  
      > >     }  
      > >     foreach ($_POST as $key => $value) {  
      > >         $post_safe[$key] = addslashes($value);  
      > >     }  
      > >     foreach ($_COOKIE as $key => $value) {  
      > >         $cookie_safe[$key] = addslashes($value);  
      > >     }  
      > > }  
      
      

      In PHP gibt es (noch, in PHP6 wird es nicht mehr existieren) die "magic quotes"-Geschichte, die einfache und doppelte Anführungszeichen mit Backslashes unschädlich zu machen sucht, damit eben SQL-Injektionen nicht greifen. In Deinem Code-Beispiel wird aber genau diese "Sicherheitsmaßnahme" wieder ausgehebelt, indem die Originaleingaben des Users wieder hergestellt werden. Und schon wieder wird verschleiert: $get_safe suggeriert, dass die Werte nun "sicher" sind, dabei sind sie ja gerade _ent-_sichert worden!

      ???

      Hier werden Slashes hinzugefügt(!), falls die magic quotes (für GPC) deaktiviert sind.

      Viele Grüße,
      der Bademeister

      1. Lieber Bademeister,

        Hier werden Slashes hinzugefügt(!), falls die magic quotes (für GPC) deaktiviert sind.

        stimmt. Das hatte ich nicht genau genug angeschaut. Aber meine Schlussfolgerung bleibt gültig: Unsinniges Vorgehen.

        Liebe Grüße,

        Felix Riesterer.

        --
        ie:% br:> fl:| va:) ls:[ fo:) rl:° n4:? de:> ss:| ch:? js:) mo:} zu:)