Kackfohgel: OOP: Behandlung von Benutzereingaben für Konstruktor

Moin!

Ich übe gerade OOP in PHP anhand kleinerer Beispiele. Dabei konnte ich mir die Frage wann und wie ich Benutzereingaben behandle nicht selbst beantworten.

Vlt. zunächst einmal ein Beispiel*:

<?php  
	// Variable für Name anlegen  
	$name = "";  
	  
	// Prüfen, ob ein Name eingetragen wurde, dann  
	if(isset($_POST["name"]) && !empty($_POST["name"]))  {  
		$name = htmlspecialchars($_POST["name"]);  
	}  
?>  
  
  
<!doctype html>  
<html>  
<head>  
	<meta charset="utf-8">  
	<title>Beispiel</title>  
</head>  
<body>  
	<form action="self_beispiel.php" method="post" name="upload" enctype="multipart/form-data">  
		<p>Name:  
			<input type="text" size="50" name="name" id="name" value="<?php echo $name; ?>">  
		</p>  
		<p><input type="submit" value="Senden"></p>  
	</form>  
  
	<?php  
		if(!empty($name)) {  
			$benutzer = new Benutzer($name);  
			echo $benutzer->gruesseBenutzer();  
		}  
	?>	  
	  
</body>  
</html>  
  
<?php  
	// Klassendefinition  
	class Benutzer {  
		  
		// Eigenschaften  
		private $name;  
				  
		// Konstruktor  
		public function __construct($name) {  
			$this->name = $name;  
		}  
				  
		// Setter/Getter  
		// ...  
						  
		// Methoden  
		public function gruesseBenutzer() {  
			return "<p>Hallo " . $this->name . "!</p>";  
		}		  
	}  
?>

Hier habe ich direkt bei der Übernahme der Postvariablen mit htmlspecialchars() eine "ausgabefähige" Variante von 'name' in $name abgespeichert, da ich so im weiteren Programm den Namen mit einem einfachen echo $name einbinden kann ohne jedes mal eine htmlspecialchars-Behandlung vorzunehmen. Dies erschien mir erst als der bessere Weg, da ich weniger redundanten Code erzeuge. Nun bin ich mir aber aus zwei Gründen nicht mehr so sicher:

  1. Die pauschale Bearbeitung ist vlt. zu kurz gedacht.Wenn ich den Namen bspw. später auch für eine SQL-Abfrage verwenden will, müsste ich diesen ja anders behandeln (mysql_real_escape_string()). Dies spricht dafür, dass ich die Variable immer erst dort behandele, wo eine Behandlung erforderlich ist.

  2. Bei größeren Programmen als das hiesige Beispiel könnte übersehen/vergessen werden, dass 'name' schon behandelt wurde und zu einer doppelten Behandlung führen.

Deshalb wollte ich das htmlspecialchars() beim speichern der Postvariablen weglassen und erst bei der Ausgabe hinzufügen.

Ich bin mir aber nicht sicher, ob ich dem Konstruktor die Variable Name so unbearbeitet übergeben darf oder dadurch eine Sicherheitslücke öffne. Meine Unsicherheit besteht natürlich auch schon bei der vorherigen Behandlung mit htmlspecialchars(), aber dadurch wurde sie mir deutlich.

Ich habe selbst mal probiert eine Zeichenkette (per Formular als Name) ") echo "Achtung!!!" zu übergeben. Hat zwar nicht geklappt, aber ich bin auch kein Hacker und weiß nicht, auf was für Ideen die so kommen, will aber trotzdem ein möglichst sicheres Script.

Also:
Darf/sollte ich eine Postvariable unbearbeitetet an den Konstruktor übergeben? Wenn "nein" wie sollte sie behandelt werden (falls keine pauschale Aussage möglich ist, auf das Beispiel bezogen)?

Freundliche Grüße
Kackfohgel

*Bitte berücksichtigt, dass ich ein einfaches Beispiel basteln wollte und deshalb ein wenig hin- und her springe zwischen Objektorientierung und Prozessorientierung.

  1. hi,

    oder so?

      
    <?php  
      
    //~ $_POST["name"]="abc";  
    	// Klassendefinition  
            class Benutzer {  
      
                    // Eigenschaften  
                    private $name;  
      
                    // Konstruktor  
                    public function __construct($name) {  
                            $this->name = $name;  
                    }  
      
                    // Setter/Getter  
    		public function getName(){  
    			return $this->name;  
    		}  
                    // ...  
      
            }  
    	// für die übersicht  
    	$loggedIn = FALSE;  
            $name = NULL;  
      
    	  
            //  - eigentlich eher eine Klasse LoginTest - Prüfen, ob ein Name eingetragen wurde, dann  
            if(isset($_POST["name"]) && !empty($_POST["name"]))  {  
                    $name = htmlspecialchars($_POST["name"]);  
    		$benutzer = new Benutzer($name);  
    		$loggedIn = true;  
            }  
    ?>  
      
    [code lang=html]  
    <!doctype html>  
    <html>  
    <head>  
            <meta charset="utf-8">  
            <title>Beispiel</title>  
    </head>  
    <body>
    

    <?php if($loggedIn === FALSE):?>
            ~~~html <form action="self_beispiel.php" method="post" name="upload" enctype="multipart/form-data">
                    <p>Name:
                            <input type="text" size="50" name="name" id="name" value="">
                    </p>
                    <p><input type="submit" value="Senden"></p>
            </form>

    `<p>Hallo :`{:.language-html} <?php echo $benutzer->getName()?>~~~html
    </p>  
    
    ~~~<?php endif?>  
    ~~~html
    </body>  
    </html>  
      
    
    ~~~[/code]  
      
    mfg  
      
    tami
    
    1. @@tami:

      nuqneH

      if(isset($_POST["name"]) && !empty($_POST["name"]))  {
                      $name = htmlspecialchars($_POST["name"]);

      htmlspecialchars() hier?

      IMHO nicht. Wenn das mal erweitert werden soll, dass Nutzer in einer DB abgelegt werden, landen dort unsinnige Daten.

      Das Escapen von HTML-Sonderzeichen muss genau dann erfolgen, wenn Daten in den HTML-Kontext gebracht werden:

      <p>Hallo : <?php [code lang=php]echo $benutzer->getName()?></p>[/code]

      Hier ist es angebracht:

      <p>Hallo : <?php [code lang=php]echo htmlspecialchars($benutzer->getName()); ?></p>[/code]

      Qapla'

      --
      „Talente finden Lösungen, Genies entdecken Probleme.“ (Hans Krailsheimer)
      1. if(isset($_POST["name"]) && !empty($_POST["name"]))  {
                        $name = htmlspecialchars($_POST["name"]);

        htmlspecialchars() hier?

        IMHO nicht. Wenn das mal erweitert werden soll, dass Nutzer in einer DB abgelegt werden, landen dort unsinnige Daten.

        Das hatte ich in meinem Ursprungspost ja auch schon beschrieben.

        Aber wie behandel ich vom Benutzer eingegebene Daten vor der Überprüfung? Also abgesehen von der kontextabhängigen Überprüfung von Sinn/Länge oder Ähnliches sind generell bestimmte Zeichen zu escapen bzw. bestimmte Zeichenkettenfunktionen anzuwenden, um Sicherheitslücken zu schließen? Analog zu htmlspecialchars() für die HTML-Ausgabe oder mysql_real_escape_string() für eine mysql-Abfrage?

        Das Escapen von HTML-Sonderzeichen muss genau dann erfolgen, wenn Daten in den HTML-Kontext gebracht werden:

        Das werde ich umstellen. Wobei: Was hälst du von einem speziellen Getter? Also sowas wie zeigeHTMLName() oder so? Dann hätte ich Funktionen und Ausgabe etwas getrennter? Ich meine ich will (zumindest noch nicht) ein Templatesystem bauen und für das Beispiel vlt. etwas zu viel (da nur eine PHP-Funktion angewendet wird), aber vlt. ist es ja irgendwann mal erforderlich für die HTML-Ausgabe eine andere Funktion zu verwenden oder neben der Behandlung mit htmlspecialchars() soll noch etwas anderes gemacht werden (bspw. erster Buchstabe groß oder bestimmte Namen zensieren oder Ähnliches)? Da wäre es doch toll, wenn ich das nur in zeigeHTMLName() ändern muss und nicht jedes mal, wo ich htmlspecialchars($name) ausgebe.

        Freundliche Grüße
        Kackfohgel

        1. Tach!

          Aber wie behandel ich vom Benutzer eingegebene Daten vor der Überprüfung?

          Wenn du Rohdaten bekommst, dann gar nicht, wenn du Daten mit "Transportsicherung" bekommst, dann zu Rohdaten dekodieren. In $_POST und $_GET stehen bereits Rohdaten, also ist dafür nichts zu tun.

          Was hälst du von einem speziellen Getter? Also sowas wie zeigeHTMLName() oder so?

          Nichts. Es kann nicht Aufgabe einer allgemein Daten verarbeitenden Klasse sein, Methoden für alle möglichen Ausgabekontexte bereitzuhalten. Eigentlich sagt man, wenn du ein "und" bei der Beschreibung dessen, was deine Klasse tut, verwenden musst, dann macht sie zu viel.

          dedlfix.

          1. Moin nochmal :-)

            ... wenn du ein "und" bei der Beschreibung dessen, was deine Klasse tut, verwenden musst, dann macht sie zu viel.

            Das werd ich mir merken.

            Freundliche Grüße
            Kackfohgel

  2. Tach!

    Ich übe gerade OOP in PHP anhand kleinerer Beispiele. Dabei konnte ich mir die Frage wann und wie ich Benutzereingaben behandle nicht selbst beantworten.

    Behandeln wofür? Damit sollte die Frage schon geklärt sein. Behandele sie wann und wofür du es brauchst an der benötigten Stelle. Ansonsten muss man sich durch das gesamte Programm hindurch merken, was bereits wofür vorbereitet ist. Und unter Umständen ist es dann für andere Anwendungsfälle unbrauchbar. Für Mini-Programme mag das auf den ersten Blick akzeptabel erscheinen. Je größer die Anwendung wird desto unbrauchbarer ist dieser Weg. Die Verarbeitung zwischen Ein- und Ausgabe läuft mit Rohdaten am besten.

      	$benutzer = new Benutzer($name);  
      	echo $benutzer->gruesseBenutzer();  
    

    Die Methode sollte nichts darüber wissen, in welchem Kontext sie aufgerufen wird und welcher Art Behandlung die Daten dafür brauchen. Ebenso sollte sie nicht nur speziell präparierte Daten sondern auch wieder nur Rohformat verarbeiten. Nur so kann ihre eigentliche Aufgabe universal gehalten werden.

    1. Die pauschale Bearbeitung ist vlt. zu kurz gedacht.Wenn ich den Namen bspw. später auch für eine SQL-Abfrage verwenden will, müsste ich diesen ja anders behandeln (mysql_real_escape_string()). Dies spricht dafür, dass ich die Variable immer erst dort behandele, wo eine Behandlung erforderlich ist.

    Richtig.

    1. Bei größeren Programmen als das hiesige Beispiel könnte übersehen/vergessen werden, dass 'name' schon behandelt wurde und zu einer doppelten Behandlung führen.

    Oder zu gar keiner, wenn du es beim Erstellen vergisst.

    Ich bin mir aber nicht sicher, ob ich dem Konstruktor die Variable Name so unbearbeitet übergeben darf oder dadurch eine Sicherheitslücke öffne. Meine Unsicherheit besteht natürlich auch schon bei der vorherigen Behandlung mit htmlspecialchars(), aber dadurch wurde sie mir deutlich.

    Bei der Verarbeitung von Rohdaten gibt es eigentlich kein Sicherheitsproblem - wenn PHP an sich korrekt arbeitet. Das entsteht bei der Ausgabe erst, wenn Code und Nutzdaten zusammen in einem Dokument/Text ausgeliefert werden sollen, so wie das bei HTML oder SQL der Fall ist.

    Ich habe selbst mal probiert eine Zeichenkette (per Formular als Name) ") echo "Achtung!!!" zu übergeben. Hat zwar nicht geklappt, aber ich bin auch kein Hacker und weiß nicht, auf was für Ideen die so kommen, will aber trotzdem ein möglichst sicheres Script.

    Schau mal den Kontextwechsel-Artikel an (zumindest den Anfang), dann solltest du wissen, wie Code-Injections funktionieren und was das Problem beim unbehandelten Einfügen ist.

    Darf/sollte ich eine Postvariable unbearbeitetet an den Konstruktor übergeben?

    Das kommt auf die Aufgabe der Klasse an. Klassen, die allgemein Daten verarbeiten, bekommen immer Rohdaten.

    dedlfix.

    1. Hallo dedlfix!

      Vielen Dank für deine sehr hilfreiche Antwort.

      Ich bin mir aber nicht sicher, ob ich dem Konstruktor die Variable Name so unbearbeitet übergeben darf oder dadurch eine Sicherheitslücke öffne. Meine Unsicherheit besteht natürlich auch schon bei der vorherigen Behandlung mit htmlspecialchars(), aber dadurch wurde sie mir deutlich.

      Bei der Verarbeitung von Rohdaten gibt es eigentlich kein Sicherheitsproblem - wenn PHP an sich korrekt arbeitet. Das entsteht bei der Ausgabe erst, wenn Code und Nutzdaten zusammen in einem Dokument/Text ausgeliefert werden sollen, so wie das bei HTML oder SQL der Fall ist.

      Naja, ich hatte genau dort eine Sicherheitslücke vermutet.

      Wenn eine Übergabe "SELECT * FROM tab WHERE 'name' = $name" eine Sicherheitslücke ist, könnte man ja vlt. auch mit new Benutzer($name); oder tuWas($name); etwas anstellen. Wie gesagt, ich habe dazu zu wenig Phantasie oder auch kriminelle Energie, aber möglicherweise kann man hier ja auch den Konstrukt beenden und anderen PHP-Code einfügen, wenn man es "unbehandelt" lässt. Speziell dazu habe ich nichts gefunden. Ob das jetzt an meinen Suchparametern gelegen hat oder es einfach kein Problem ist, vermochte ich nicht zu sagen.

      Darf/sollte ich eine Postvariable unbearbeitetet an den Konstruktor übergeben?

      Das kommt auf die Aufgabe der Klasse an. Klassen, die allgemein Daten verarbeiten, bekommen immer Rohdaten.

      Gut:) Hast du vlt. noch ein Beispiel, wo man das nicht so machen sollte?

      Freundliche Grüße
      Kackfohgel

      P.S.
      Den von dir verlinkten Artikel habe ich gelesen. Aber speziell zu der Problematik Daten an Klassen zu übergeben habe ich nichts gefunden. Aber zumindest hat er mich dazu motiviert, Daten nicht einfach unüberprüft zu übergeben und hier vorab nochmal nachzufragen ;-)

      1. P.S.
        Den von dir verlinkten Artikel habe ich gelesen.

        Also schon im Vorfeld. Den kannte ich sogar noch aus der alten Doku und ich war erfreut, den heute noch im Wiki zu finden.

      2. Tach!

        Wenn eine Übergabe "SELECT * FROM tab WHERE 'name' = $name" eine Sicherheitslücke ist, könnte man ja vlt. auch mit new Benutzer($name); oder tuWas($name); etwas anstellen.

        Nein. (Gedanklich füge ich mal um $name noch Anführungszeichen hinzu, die du verutlich vergessen hast.) Im SQL-Statement kann ein in $name enthaltenes Anführungszeichen den durch das Anführungszeichen vor $name begonnenen String beenden. Das muss verhindert werden, indem die Anführungszeichen in $name maskiert werden. Ansonsten wäre das die SQL-Injection-Lücke. Wenn PHP allerdings intern mit Strings jongliert, gibt es keine Zeichen, die darin irgendeine Sonderfunktion hätten. Bei anderen Programmiersprachen wäre das zumindest das 0-Byte, das als String-Ende-Zeichen gewertet wird. Wenn du denkst, schon die Übergabe könnte ein Problem auslösen, dann ist das beim Konstruktor schon zu spät, denn Übergaben fanden bereits vorher in Form anderer Zuweisungen statt. Auch das Eintragen in das $_POST/$_GET-Array bevor das Script startet ist eine solche.

        Darf/sollte ich eine Postvariable unbearbeitetet an den Konstruktor übergeben?
        Das kommt auf die Aufgabe der Klasse an. Klassen, die allgemein Daten verarbeiten, bekommen immer Rohdaten.
        Gut:) Hast du vlt. noch ein Beispiel, wo man das nicht so machen sollte?

        Sicherheitstechnisch spricht in keinem Fall etwas dagegen. Die Bedenken waren nur fachlicher Natur und richtigen sich nach der konkreten Aufgabe der Klasse.

        dedlfix.

        1. Hallo dedlfix!

          Wenn eine Übergabe "SELECT * FROM tab WHERE 'name' = $name" eine Sicherheitslücke ist, könnte man ja vlt. auch mit new Benutzer($name); oder tuWas($name); etwas anstellen.

          Nein. (Gedanklich füge ich mal um $name noch Anführungszeichen hinzu, die du verutlich vergessen hast.) Im SQL-Statement kann ein in $name enthaltenes Anführungszeichen den durch das Anführungszeichen vor $name begonnenen String beenden. Das muss verhindert werden, indem die Anführungszeichen in $name maskiert werden. Ansonsten wäre das die SQL-Injection-Lücke. Wenn PHP allerdings intern mit Strings jongliert, gibt es keine Zeichen, die darin irgendeine Sonderfunktion hätten. Bei anderen Programmiersprachen wäre das zumindest das 0-Byte, das als String-Ende-Zeichen gewertet wird. Wenn du denkst, schon die Übergabe könnte ein Problem auslösen, dann ist das beim Konstruktor schon zu spät, denn Übergaben fanden bereits vorher in Form anderer Zuweisungen statt. Auch das Eintragen in das $_POST/$_GET-Array bevor das Script startet ist eine solche.

          Danke für diese ausführliche Beschreibung. Dadurch wird mir das wirklich sehr gut veranschaulicht. Insbesondere mit den letzten beiden Sätzen.

          Darf/sollte ich eine Postvariable unbearbeitetet an den Konstruktor übergeben?
          Das kommt auf die Aufgabe der Klasse an. Klassen, die allgemein Daten verarbeiten, bekommen immer Rohdaten.
          Gut:) Hast du vlt. noch ein Beispiel, wo man das nicht so machen sollte?

          Sicherheitstechnisch spricht in keinem Fall etwas dagegen. Die Bedenken waren nur fachlicher Natur und richtigen sich nach der konkreten Aufgabe der Klasse.

          Gut, das hatte ich jetzt so aus deinem ersten Post hergeleitet. Wollte aber sicherheitstechnisch auf Nummer sicher gehen :-)

          Freundliche Grüße
          Kackfohgel

      3. Moin!

        Naja, ich hatte genau dort eine Sicherheitslücke vermutet.

        Wenn eine Übergabe "SELECT * FROM tab WHERE 'name' = $name" eine Sicherheitslücke ist, könnte man ja vlt. auch mit new Benutzer($name); oder tuWas($name); etwas anstellen. Wie gesagt, ich habe dazu zu wenig Phantasie oder auch kriminelle Energie, aber möglicherweise kann man hier ja auch den Konstrukt beenden und anderen PHP-Code einfügen, wenn man es "unbehandelt" lässt. Speziell dazu habe ich nichts gefunden. Ob das jetzt an meinen Suchparametern gelegen hat oder es einfach kein Problem ist, vermochte ich nicht zu sagen.

        Die Frage ist ja: WARUM ist das eine Sicherheitslücke?

        Und die Antwort ist: In deinem SQL-Beispiel erzeugst du Programmcode, der ausgeführt wird. Neben den dir bekannten Bestandteilen (die konstant im String enthalten sind) fügst du außerdem noch einen unbekannten Bestandteil ein, nämlich die Benutzereingabe. Diese kann potentiell jeglichen Zeicheninhalt (inklusive aller nichtdruckbaren Bytes) enthalten, und in Kombination wird dein bekannter Codeteil plus der unbekannte Codeteil eventuell zu einem zerstörerischen oder zumindest unerwünschten Stück Software-Sourcecode, welcher dann von der SQL-Datenbank ausgeführt wird.

        Deswegen gibt es zwei Methoden, wie man bei SQL solche Effekte verhindern kann:

        1. Prepared Statements: Als SQL-"Programm" wird lediglich ein komplett konstantes Kommando mit ein paar Platzhaltern übergeben (es untersteht deiner vollständigen Kontrolle und tut deshalb nichts unerwartetes), und die Daten werden über einen separaten Datenkanal weitergereicht. Auf diese Weise ist garantiert, dass die Daten immer als Daten, nie jedoch als Programmteil interpretiert werden.

        2. Alternativ kann man auch Escaping vornehmen, d.h. man sagt dem Datenbank-Client: Hier ist ein String, der hat evtl. komische Bytes und Zeichen - mach mal vor alles sowas eine Escape-Sequenz, damit der String in SQL nicht aus Versehen als Programmteil angesehen werden kann. Danach fügt man den abgesicherten String dann in's SQL ein und lässt es die Datenbank ausführen. Dabei sollte jetzt nichts mehr passieren.

        Darf/sollte ich eine Postvariable unbearbeitetet an den Konstruktor übergeben?

        Ja, darf man ohne Probleme, weil diese Variable nicht als Text mit weiterem Programmcode gemischt und dann interpretiert wird, sondern definitiv immer als Variable angesehen wird.

        Sofern du nicht "eval()" mit dem Variableninhalt aufrufst, kann in den POST-Daten beliebiger Text drinstehen - er wird nie als PHP-Code interpretiert werden.

        Allerdings gilt das natürlich nur solange, wie sichergestellt ist, dass tatsächlich keine Codeausführung möglich ist. Wenn dein Script den Inhalt der POST-Variablen z.B. in eine Textdatei speichert, und diese Textdatei könnte vom User her einen Namen bekommen, der auf ".php" statt ".txt" endet, und diese Datei ist auch zum Download per Webbrowser vorgesehen - dann wird der Inhalt der Datei mit großer Wahrscheinlichkeit als PHP-Code interpretiert und ausgeführt.

        Die Gefahr lauert also immer dort, wo Daten unbeabsichtigt zu Code werden könnten. Und erweitert gilt dies auch für HTML, obwohl HTML ja keine Programmiersprache an sich ist, aber zum Beispiel Javascript enthalten kann. Wenn ein Angreifer Javascript in eine Seite einschleusen kann, ist das extrem schlecht. Und selbst wenn er "nur" normales HTML einschleusen kann, aber kein Javascript, reicht das oftmals schon aus, dass ein nichtsahnender User in der Annahme, er sei auf deiner Seite in Sicherheit, was Falsches zum Anklicken untergeschoben kriegt.

        Den von dir verlinkten Artikel habe ich gelesen. Aber speziell zu der Problematik Daten an Klassen zu übergeben habe ich nichts gefunden. Aber zumindest hat er mich dazu motiviert, Daten nicht einfach unüberprüft zu übergeben und hier vorab nochmal nachzufragen ;-)

        Wie gesagt: Das Weiterreichen von Variablen innerhalb eines Skripts ist generell ungefährlich, also darf jeglicher Bytemüll auch dem Konstruktor einer Klasse übergeben werden, ohne dass man an dieser Stelle Angst haben müsste.

        Angst sollte man haben, wenn auf dem Weg von $_POST/_GET/_COOKIE/_SERVER hin zur letztendlichen Verwendung der Variablen in HTML, SQL, Javascript, CSS etc. nicht mehr nachvollzogen werden kann, ob in irgendeiner Zwischenschicht bereits eine Absicherung erfolgte, oder noch nicht. Und genau deshalb wird geraten, das zum letztmöglichen Zeitpunkt zu tun: Denn wenn man sich z.B. HTML-Code anschaut, in dem <?php echo $name ?> steht, dann will man nicht recherchieren, ob irgendwo vorher Escaping stattfand. Steht dort hingegen <?php echo htmlspecialchars($name) ?>, ist sonnenklar, dass hier Escaping stattfindet, man muss nichts suchen. (Ob es das korrekte Escaping ist, ist nochmal eine andere Frage, gerade bei HTML ist htmlspecialchars() ohne weitere Parameter nicht an jeder Stelle ausreichend.)

        - Sven Rautenberg

        1. Hallo Sven Rautenberg,

          danke für deine Ausführungen. Ich erlaube mir mal den Teil deines Postings, den ich glaube verstanden zu haben, zu streichen und würde an dieser Stelle gerne nochmal nachhaken:

          Angst sollte man haben, wenn auf dem Weg von $_POST/_GET/_COOKIE/_SERVER hin zur letztendlichen Verwendung der Variablen in HTML, SQL, Javascript, CSS etc. nicht mehr nachvollzogen werden kann, ob in irgendeiner Zwischenschicht bereits eine Absicherung erfolgte, oder noch nicht. Und genau deshalb wird geraten, das zum letztmöglichen Zeitpunkt zu tun: Denn wenn man sich z.B. HTML-Code anschaut, in dem <?php echo $name ?> steht, dann will man nicht recherchieren, ob irgendwo vorher Escaping stattfand. Steht dort hingegen <?php echo htmlspecialchars($name) ?>, ist sonnenklar, dass hier Escaping stattfindet, man muss nichts suchen. (Ob es das korrekte Escaping ist, ist nochmal eine andere Frage, gerade bei HTML ist htmlspecialchars() ohne weitere Parameter nicht an jeder Stelle ausreichend.)

          Man liest ja immer wieder, dass man Design und Code trennen sollte. Ich würde das jetzt nochmal um die HTML-Struktur ergänzen.

          Also ich habe meine Datei mit HTML-Code (Struktur). Darin binde ich eine/mehrere externe CSS-Datei ein (Design). Jetzt kommt noch eine/mehrere PHP-Dateien mit Funktionen (Code) hinzu.

          Ich würde jetzt hier sagen, dass htmlspecialchars() eine Funktion ist, die eigentlich in meiner Strukturdatei nichts zu suchen hat. Klar mir leuchtet ein, welche Vorteile es bringt, wenn man in der HTML-Datei erkennt, dass die Variable escaped wurde, ABER was ist, wenn der Benutzername max. 20 Zeichen lang sein soll, keine Sonderzeichen enthalten darf, nicht schon vergeben sein darf ... usw. ...! Da sollte ich mich doch in meiner Strukturdatei auch darauf verlassen, das meine Funktionen in der Codedatei dies ordnungsgemäß prüft und muss dies deshalb nicht in der Strukturdatei validieren.(?)

          Freundliche Grüße
          Kackfohgel

          1. Tach!

            Also ich habe meine Datei mit HTML-Code (Struktur). Darin binde ich eine/mehrere externe CSS-Datei ein (Design). Jetzt kommt noch eine/mehrere PHP-Dateien mit Funktionen (Code) hinzu.

            Ich würde jetzt hier sagen, dass htmlspecialchars() eine Funktion ist, die eigentlich in meiner Strukturdatei nichts zu suchen hat. Klar mir leuchtet ein, welche Vorteile es bringt, wenn man in der HTML-Datei erkennt, dass die Variable escaped wurde,

            Es kommt drauf an, wie du deine Ausgabe erstellst. Setzt du auf ein Template-System, dessen Syntax ohne PHP-Code auskommt, dann muss der hinter dem Template-System steckende Code für die Maskierung sorgen. Setzt du auf PHP als Template-System, dann musst du das erledigen.

            ABER was ist, wenn der Benutzername max. 20 Zeichen lang sein soll, keine Sonderzeichen enthalten darf, nicht schon vergeben sein darf ... usw. ...!

            Das ist Validierung der Eingabedaten und Teil der Verarbeitung, kommt also deutlich vor der Ausgabe und hat im Template-System nichts mehr zu suchen. Etwas anderes ist, wenn du die Ausgabe kürzen musst, weil du zum Beispiel nur den Anfang des Textes darstellen willst. Das könnte über eine Helferfunktion Teil der Ausgabe sein.

            Da sollte ich mich doch in meiner Strukturdatei auch darauf verlassen, das meine Funktionen in der Codedatei dies ordnungsgemäß prüft und muss dies deshalb nicht in der Strukturdatei validieren.(?)

            Unterscheide lieber in die drei groben Phasen Eingabe, Verarbeitung und Ausgabe. Die Eingabephase ist oft recht kurz oder nicht vorhanden, weil PHP schon fast alles (z.B. $_GET/$_POST) gebrauchsfertig bereitstellt. Dann kommt die Verarbeitung, wozu auch die Validierung gemäß deiner Geschäftslogik vor dem Speichern zählt. Und die Ausgabe bringt dann alle während der Verarbeitungsphase entstandenen Daten in die Templates (egal ob extra Template-System oder PHP).

            dedlfix.

          2. Hallo!

            Ich kann mich nur wiederholen: Schau dir einmal größere Webframeworks an, die haben über diese Feinheiten schon vor Jahren nachgedacht und zum Teil sehr saubere Lösungen gefunden. Ein wichtiger Pfeiler ist das Model-View-Controller-Pattern ergänzt durch Model-Decorators, View-Models oder ähnliches. Die View ist meistens eine Datei in einer eigens für HTML geschaffenen, äußerst beschränkten Templating-Sprache, bei der automatisch escapet wird.

            Letztlich will man nicht überall htmlentities() schreiben müssen. Ich bin da radikaler Ansicht: Wenn man eine HTML-Template-Sprache (z.B. direkt PHP) verwendet, die es erfordert, manuell Strings in den HTML-Kontext zu übertragen und zu entschärfen, dann macht man etwas falsch. Denn früher oder später vergisst man das Escapen oder escapet inkorrekt. Dann hat man mit hoher Wahrscheinlichkeit eine Sicherheitslücke. Für Webframeworks gilt »don’t make me think«. Basale Sicherheitsfeatures verhindern die schlimmsten Fehler. Man muss vom vorgegebenen Weg abweichen, um eine Sicherheitslücke durch XSS oder SQL Injection einzubauen.

            ABER was ist, wenn der Benutzername max. 20 Zeichen lang sein soll, keine Sonderzeichen enthalten darf, nicht schon vergeben sein darf ... usw. ...! Da sollte ich mich doch in meiner Strukturdatei auch darauf verlassen, das meine Funktionen in der Codedatei dies ordnungsgemäß prüft

            *Solche* Beschränkungen müssen auf jeden Fall im Code validiert werden. Oftmals ist das eine deklarative Regel im Model. Auf dem Model prüft man dann nur, ob es valide ist. Das Model gibt entsprechende Fehler zurück, die später dem Nutzer gezeigt werden können. Invalide Models kommen so gar nicht erst in die Datenbank.

            Die View sollte letztlich möglichst dumm sein und nur fertige Daten ausgeben. Sie formatiert sie vielleicht noch, wenn dafür nicht schon ein Model-Decorator existiert. Das Templating-System beachtet den Kontextwechsel bestenfalls automatisch.

            Grüße
            Mathias

  3. hi,

    *Bitte berücksichtigt, dass ich ein einfaches Beispiel basteln wollte und deshalb ein wenig hin- und her springe zwischen Objektorientierung und Prozessorientierung.

    Das Beispiel ist gut weil es die Problematik Kontextwechsel sehr deutlich macht. Das Beispiel ist schlecht, weil die Methode gruesseBenutzer() besser in eine andere Klasse reinpassen würde, weil:

    Die Klasse Benutzer hats nur mit Rohdaten. Hier steht die Frage, ob irgendwelche Eingaben woanders problematisch sein könnten, absolut nicht. Woanders heißt, im Sinne von OOP: Andere Klasse und der Kontextwechsel spielt sich zwischen verschiedenen Klassen ab.

    Horst

    --
    Missbildung ist ein Spezialfall von Casting.
    1. Hallo Horst!

      Das Beispiel ist gut weil es die Problematik Kontextwechsel sehr deutlich macht. Das Beispiel ist schlecht, weil die Methode gruesseBenutzer() besser in eine andere Klasse reinpassen würde, weil:

      Die Klasse Benutzer hats nur mit Rohdaten. Hier steht die Frage, ob irgendwelche Eingaben woanders problematisch sein könnten, absolut nicht. Woanders heißt, im Sinne von OOP: Andere Klasse und der Kontextwechsel spielt sich zwischen verschiedenen Klassen ab.

      Vielen Dank für diesen Hinweis. Was in welche Klassen gehört und wann man weitere Klassen nimmt, dass ist in der Theorie bei den Säugetier->Pferd, Fahrzeuge->Auto etc. Beispielen immer sehr einfach. In der Praxis ist das aber für mich schwierig, da ich prozess-/funktionsorientiert gelernt hat. PHP war und ist meine erste Programmiersprache womit ich mich intensiver beschäftigt habe und da habe ich bisher sozusagen einfach immer von oben nach unten von Teilaufgabe zu Teilaufgabe programmiert.

      Insbesondere um die objektorientierte Programmierung zu lernen, habe ich mich mal an Java versucht, aber da fehlte mir dann irgendwann die Motivation und ich habe mich gefragt, ob ich schon mit der nächsten "Fremdsprache" anfangen will, wo ich mir bei der anderen gerade mal ein Bier bestellen oder nach dem Weg zum Bahnhof fragen kann.

      Freundliche Grüße
      Kackfohgel

      1. hi,

        Vielen Dank für diesen Hinweis. Was in welche Klassen gehört und wann man weitere Klassen nimmt, dass ist in der Theorie bei den Säugetier->Pferd, Fahrzeuge->Auto etc. Beispielen immer sehr einfach.

        OOP ist sehr klischeebehaftet ;)

        O.g. Beispiele führen zur Neigung, komplexe Vorgänge noch viel komplexer in Programmstrukturen abzubilden.

        In der Praxis ist das aber für mich schwierig, da ich prozess-/funktionsorientiert gelernt hat.

        Marx ist die Theorie, Murx ist die Praxis. Programmiere nicht der OOP willen sondern praxisnah. Egal, ob mit oder ohne OOP, das Ziel sollte immer darin bestehen, einen Code so hinzuwerfen, dass redundanter Code vermieden wird und dass er übrschaubar bleibt, nicht nur für etwaige Mitarbeiter, auch für Dich selbst. Genausowenig sind vogelwilde Klassenhierarchien das Ziel, strebe flache Hierarchien an.

        Was z.B. könnte ein Benutzer von der Gruppe erben? Den Namen der Gruppe? Oder vielleicht doch ein paar Methoden wie Neuanlegen, Ändern, Löschen?

        Die o.g. Methoden brauchst Du in beiden Klassen, Benutzergruppe, Benutzer, um im Beispiel zu bleiben. Wenn nun der Benutzer von der Gruppe die Methoden erbt, Du wirst die in Klasse Benutzer überschreiben müssen, weil die Attribute andere sind. Und schon hast Du redundanten Code. Ja, und wenn sich dann mal der Speicherort für die Daten ändert, dann musst Du den Code an zwei Stellen ändern.

        Lösung: Mache die Methoden Neuanlegen, Ändern und Löschen unabhängig von den Attributen und unabhängig vom Speicherort! Wow, wir kommen zur Sache *G: eine Klasse 'Persistent Objects', die Klassen Benutzergruppe und Benutzer treten das Erbe an auf gleicher Ebene. Der Benutzer muss nicht von der Gruppe erben, nur weil er wissen muss, zu welcher Gruppe er gehört. Dem Benutzer wird nur ein Attribut verpasst, fertig.

        Insbesondere um die objektorientierte Programmierung zu lernen, habe ich mich mal an Java versucht, aber da fehlte mir dann irgendwann die Motivation und ich habe mich gefragt, ob ich schon mit der nächsten "Fremdsprache" anfangen will, wo ich mir bei der anderen gerade mal ein Bier bestellen oder nach dem Weg zum Bahnhof fragen kann.

        Bleib doch ersteinmal bei PHP, Klasse Benutzer, Du wirst es auf jeden Fall brauchen.

        Viele Grüße,
        Horst

      2. Hallo,

        Was in welche Klassen gehört und wann man weitere Klassen nimmt, dass ist in der Theorie bei den Säugetier->Pferd, Fahrzeuge->Auto etc. Beispielen immer sehr einfach. In der Praxis ist das aber für mich schwierig, da ich prozess-/funktionsorientiert gelernt hat.

        Mit PHP schreibt man in den meisten Fällen Webanwendungen. Die hier verfügbaren Webframeworks (Symfony, Zend, Yii, CodeIgniter, CakePHP, Laravel…) geben einem meist schon eine objektorientierte Struktur vor (z.B. ORM, MVC, Testbarkeit, Dependency Management…), sodass relativ klar ist, welcher Code in welche Klasse gehört.

        Das ersetzt natürlich nicht, sich mit der Theorie von OOP auseinanderzusetzen. Aber es macht auch keinen Sinn, sich in trockene Theorie einzuarbeiten, wenn man keinen konkreten Anwendungsfall hat. Die Theorie zeigt dir, was es für Prinzipien und Patterns gibt. In der Praxis musst du oftmals verschiedene Strukturen ausprobieren, bis die Anforderungen eingehalten werden. Dazu eignen sich konkrete Diskussionen, d.h. du fragst erfahrenere Entwickler, was sie von deiner Architektur halten.

        Insbesondere um die objektorientierte Programmierung zu lernen, habe ich mich mal an Java versucht, aber da fehlte mir dann irgendwann die Motivation …

        Die Java-Community ist sehr stark, was Software-Architektur, Softwareentwicklungs-Methodologien und Diskussionen über sauber strukturierten Code angeht. Das sind andere Communities aber auch. PHP historisch gesehen eher nicht. ;) Aber das bessert sich, zumal auf die Fähigkeiten der Sprache ausgebaut werden.

        Mathias