Teil von SELFHTML aktuell Teil von Artikel Teil von PHP

Kontextwechsel erkennen und behandeln

nach unten Vorwort / Kleingedrucktes
nach unten Einleitung
nach unten Programmcode und Daten
nach unten Daten ausgeben
nach unten Fehler und deren Auswirkungen
nach unten Verhindernde Maßnahmen
nach unten Kontextwechsel erkennen und behandeln

Vorwort / Kleingedrucktes

Dieser Artikel befasst sich mit der Problematik des Kontextwechsels. „Problematik“ deshalb, weil die Nichtbeachtung des Kontextwechsels eine der häufigsten Quellen für diverse Probleme ist. Ziel ist es, die häufigsten Kontextwechsel im Web-Umfeld aufzuzeigen und wie diese behandelt werden. Das soll vorwiegend mit Beispielen für PHP und MySQL und textbasierenden Daten erklärt werden. Prinzipiell betrifft das Thema jedoch auch andere Sprachen und andere Datentypen.

Der Artikel bezieht sich auf das Zeichen als kleinste Einheit. Auf welche Weisen Zeichen im Computer durch Bits und Bytes dargestellt werden, soll hier nicht weiter thematisiert werden. Dazu sei auf das SELFHTML-Kapitel Seite Computer und geschriebene Sprache verwiesen. Unterschiedliche Zeichenkodierungen beim Datenaustausch zwischen mehreren Systemen können zwar ebenfalls als Kontextwechsel angesehen werden, doch diese Thematik und deren Probleme ist zu umfangreich, um sie auch noch in diesem Artikel unterzubringen.

Die in diesem Artikel enthaltenen Links zum englischsprachige Seite PHP-Handbuch verweisen generell auf die englische Dokumentation, da dies das Original ist und die Übersetzungen mitunter nicht aktuell genug oder nicht vollständig sind. Im Kopfteil jeder Handbuchseite kann allerdings bei Bedarf und „auf eigenes Risiko“ eine andere Sprache gewählt werden.

Fragen zu diesem Beitrag oder dem Thema können im bereichsübergreifende Seite SELFHTML Forum gestellt werden.

nach obennach unten

Einleitung

Im Anfang war das Wort … und kurz darauf bekam ein Informatiker die Aufgabe dieses Wort mit Hilfe eines Rechners zu verarbeiten. Er musste dem Computer Anweisungen zum Verarbeiten des Wortes geben, aber auch das Wort selbst musste irgendwie in den Arbeitsspeicher gelangen. Er stand vor dem Problem, Programmcode und das Wort in ein Dokument zu bringen, so dass der Computer dieses Dokument lesen und beides eindeutig voneinander unterscheiden kann. In der Literatur tritt ein vergleichbarer Fall auf, wenn wörtliche Rede in eine Erzählung eingefügt werden soll. Man schließt dabei das Gesagte in Anführungszeichen ein. Auch in den Programmiersprachen hat man meist diese Vorgehensweise übernommen.

nach obennach unten

Programmcode und Daten

Der Computer sagte: "Meine Batterie ist alle." Dann schaltete er sich selbst aus.
echo "Meine Batterie ist alle."; shutdown();

Das erste Beispiel beginnt zunächst im Erzählmodus. Das erste Anführungszeichen wechselt zur wörtlichen Rede, das zweite beendet diese und die Erzählung wird fortgesetzt.

Auch das zweite Beispiel ist auf diese Weise aufgebaut. Der Code-Parser liest die Zeichen und versucht sie gemäß seiner Syntaxregeln zu interpretieren. Code und Daten verwenden jedoch zu großen Teilen die gleichen Zeichen (Buchstaben und Satzzeichen). Wann aber gehört ein Zeichen zur Syntax und wann zu den Daten? Das erste Anführungszeichen leitet einen Kontextwechsel ein. Alle folgenden Zeichen werden nun als Daten gelesen und nicht als Code interpretiert. Das zweite Anführungszeichen kennzeichnet das Ende des Datenkontextes. Der Rest wird wieder als Code interpretiert.

Dieses Beispiel ist eindeutig interpretierbar. Anders sieht die Sachlage aus, wenn ein " (Anführungszeichen) in den Daten enthalten ist.

$text = "Der Computer sagte: "Meine Batterie ist alle." Dann schaltete er sich selbst aus.";

Das Anführungszeichen hat bereits die Bedeutung des Kontextwechsels zwischen Code und Daten. Das zweite Anführungszeichen vor dem Meine würde nun den Datenkontext beenden. Die nachfolgenden Zeichen sind früher oder später nicht mehr als gültige PHP-Syntax interpretierbar. Es kommt zu einem Syntax-Fehler.

Abhilfe ist die kontextgerechte Behandlung des Anführungszeichen. Die PHP-Syntax-Regel1) besagt: Wenn ein Anführungszeichen in einem mit Anführungszeichen eingefassten String vorkommt, muss ihm ein \ (Backslash) vorangestellt werden.

$text = "Der Computer sagte: \"Meine Batterie ist alle.\" Dann schaltete er sich selbst aus.";

Der Backslash maskiert das zweite Anführungszeichen. Es wird nun nicht mehr als Stringbegrenzer angesehen. Durch die Verwendung des Backslashs als Maskierzeichen hat dieser nun ebenfalls eine Sonderbedeutung bekommen und ist selbst zu maskieren, wenn er als Datenbestandteil vorkommt. Dies geschieht, indem man ihm einen weiteren Backslash voranstellt.

Maskierungen2) sind nur für die richtige Interpretation von Zeichen beim Lesen eines Dokuments oder Datenstroms notwendig. Die interne Verarbeitung wird meistens mit den Rohdaten stattfinden, denn Maskierzeichen sind meist auch nur normale Zeichen und dürfen bei der Stringverarbeitung (zum Beispiel Zeichen zählen) nicht berücksichtigt werden. Die Maskierzeichen werden beim Lesevorgang entfernt. Im Speicher steht der Wert also wieder als:

Der Computer sagte: "Meine Batterie ist alle." Dann schaltete er sich selbst aus.

1) Die genauen Syntax-Regeln können dem PHP-Handbuch-Kapitel zu englischsprachige Seite Strings entnommen werden.

2) Im Englischen bezeichnet Escaping den Vorgang des Maskierens.

nach obennach unten

Daten ausgeben

Ebenso wie beim Einlesen von Daten ist bei der Ausgabe ein möglicher Kontextwechsel zu beachten. Denn die Ausgabedaten des einen Systems sind nicht selten die Eingabedaten eines anderen Systems. Auf einem textbasierenden Bildschirm muss einfach nur eine in Rohform vorliegende Zeichenkette Zeichen für Zeichen dargestellt werden. Um den String jedoch beispielsweise in einem SQL-basierenden Datenbanksystem abzulegen, muss er in ein SQL-Statement eingebettet werden, weil auch SQL vorsieht, Anweisung und Daten als eine gemeinsame Zeichenfolge zu notieren. Zu beachten sind hier wiederum die Zeichen, die im Datenkontext eine Sonderbedeutung haben, insbesondere die Begrenzungszeichen.

nach obennach unten

Fehler und deren Auswirkungen

Im PHP-Code wird eine Nichtbeachtung des Kontextwechsels recht schnell durch einen Syntax-Fehler auffallen.

$sql = "SELECT feldliste FROM tabelle WHERE feld='$variable'";

In diesem Beispiel kommen zwei geschachtelte Kontextwechsel vor. Mit PHP-Code wird einen String in einer Variable abgelegt, der im weiteren Verlauf als MySQL-Statement verwendet werden soll. Der innere Kontext ist dabei der interessantere. Wenn zur Laufzeit in $variable der Wert Beispiel steht, so ergibt sich ein SQL-Statement

SELECT feldliste FROM tabelle WHERE feld='Beispiel'

Steht allerdings Beispiel mit ' drin darin, so ergibt sich

SELECT feldliste FROM tabelle WHERE feld='Beispiel mit ' drin'

Richtig hingegen wäre

SELECT feldliste FROM tabelle WHERE feld='Beispiel mit \' drin'

Auch MySQL verwendet den Backslash als Maskierungszeichen.3)

Unmaskiert ist der String „nur“ syntaktisch inkorrekt. MySQL wird sich mit einer Fehlermeldung darüber beklagen. Gelingt es hingegen jemandem, $variable so zu füllen, dass das SQL-Statement am Ende wie folgt aussieht, so kann er beispielsweise an die Daten der Benutzertabelle kommen.

SELECT feldliste FROM tabelle WHERE feld='Beispiel mit '
UNION
SELECT feldliste FROM userdaten --drin'

Somit hätte das PHP-Script eine SQL-Injection-Lücke.

Beim folgenden Beispiel gelingt das Anmelden als Administrator ohne dessen Passwort zu kennen.

$sql = "SELECT feldliste FROM users WHERE username='$username' AND password='$password'";

Als Benutzername wurde "admin' -- " eingegeben. Das fertige SQL-Statement sieht nun so aus:

SELECT feldliste FROM users WHERE username='admin' -- ' AND password=''

Die Sequenz Minus-Minus-Leerzeichen leitet einen Kommentar ein, so dass die zweite Bedingung nicht mehr zur Auswertung kommt.

Achtung: Das Risiko eines Fehlers oder SQL-Injection besteht nicht nur bei Daten die mehr oder weniger direkt aus Benutzereingaben stammen. Einem SQL-Statement ist es egal, wie und aus welchen Quellen es entstanden ist. Am Ende muss gültige Syntax entstehen und die entstandene Anweisung muss auch der Intention des Autors entsprechen. Der Kontextwechsel muss also in jedem Fall angemessen berücksichtigt werden.

3) Es ist alternativ möglich, das als Begrenzungszeichen genutzte Anführungszeichen doppelt zu notieren: 'Beispiel mit '' drin'. Die üblichere Vorgehensweise ist jedoch der vorangestellte Backslash. Auch wenn man für die Anführungszeichen die Verdopplungsmethode verwendet, muss der Backslash selbst immer noch durch Verdopplung maskiert werden.

nach obennach unten

Verhindernde Maßnahmen

Für den Kontextwechsel aus dem eben gezeigten Beispiel – String in ein MySQL-Statement einfügen – müsste man sämtliche relevante Syntax-Regeln MySQLs berücksichtigen und vor dem Einfügen des Variableninhalts selbigen nach Zeichen mit Sonderbedeutung (Anführungszeichen und einige andere) durchsuchen und diese Zeichen durch ihr maskiertes Pendant austauschen. Diesen Aufwand kann man sich sparen, denn PHP stellt eine Funktion bereit, die diese Arbeit übernimmt: englischsprachige Seite mysql_real_escape_string() oder englischsprachige Seite mysqli_real_escape_string() je nach verwendeter PHP-Extension – englischsprachige Seite mysql oder englischsprachige Seite mysqli.4)

$sql = "SELECT feldliste FROM tabelle WHERE feld='" . mysql_real_escape_string($variable) . "'";

Damit man nicht immer den String für das Einfügen der Maskierfunktion verlassen muss, kann man die Platzhalter-Funktion englischsprachige Seite sprintf() hinzunehmen.

$sql = sprintf("SELECT feldliste FROM tabelle WHERE feld1='%s' AND feld2='%s'",
               mysql_real_escape_string($variable1),
               mysql_real_escape_string($variable2));

Der einfachste Anwendungsfall von sprintf() verwendet %s als Platzhalterzeichen für einen String. Pro Platzhalter ist der Funktion ein weiterer Parameter zu übergeben, der an dessen Stelle eingefügt wird.

4) Bei einigen im asiatischen Raum verbreiten Zeichenkodierungen muss diese unbedingt mittels mysql(i)_set_charset() eingestellt werden, sonst kann mysql(i)_real_escape_string() nicht richtig arbeiten, zerstört einige Zeichen und hinterlässt unter Umständen eine Injection-Lücke. Ein SET NAMES-Statement ist ebenfalls nicht geeignet, da es mysql(i)_real_escape_string() nicht beinflusst. Für die Kodierungen der ISO-8859-Familie und UTF-8, die in Deutschland verbreitet sind, ist mysql(i)_set_charset() nicht erforderlich – jedenfalls nicht wegen sicherheitstechnischer Aspekte.

Besonderheiten: Identifier, ORDER BY und der LIKE-Operator

Identifier (Bezeichner) sind die Namen für Datenbanken, Tabellen und Felder. Diese werden meist direkt notiert, sprich: sowohl als fester Statementbestandteil als auch ohne Begrenzungszeichen. Es gibt Situationen, in denen man nicht auf die Begrenzungszeichen verzichten kann oder will oder in denen der Identifier aus Daten stammend eingebaut werden soll. Für MySQL müssen dann die Identifier mit ` (Backticks) eingefasst werden5). Für Identifier gibt es nur eine eigene Regel aber keine vorgefertigte PHP-Funktion: Ein im Identifier enthaltener Backtick ist doppelt zu notieren.

Will man die Ausgabe einer Ergebnismenge vom Anwender sortieren lassen, muss man das Sortierfeld und die Richtung in das SQL-Statement einarbeiten. Der Feld- oder Aliasname ist ein Identifier und könnte nach der Identifier-Regel behandelt werden. Gibt allerdings jemand einen Namen eines nicht vorhandenen Feldes an, so gibt es aber trotzdem eine Fehlermeldung von MySQL. Besser ist es, den übergebenen Wert gegen eine Liste der erlaubten Sortierfeldnamen zu prüfen.

Die Sortierrichtungsangaben ASC und DESC sind Bestandteil der SQL-Syntax, also Code, der ohne Kontextwechsel eingefügt werden muss. Eine SQL-Injection-Lücke verhindert man hier ebenfalls nur mit einer Prüfung.

$sql = "SELECT ... FROM ... WHERE ..."; 

$orderNames = array('feld1', 'feld2', 'alias1');
if (in_array($_GET['orderBy'], $orderNames)) {
  $orderDir = $_GET['orderDir'] == 'DESC' ? 'DESC : 'ASC';
  $sql .= sprintf(" ORDER BY `%s` %s", str_replace('`', '``', $_GET['orderBy']), $orderDir);
}

Das im Vorfeld in $sql aufgebaute SQL-Statment hat noch keine ORDER-BY-Klausel. Der GET-Parameter orderBy wird auf Vorhandensein im Array $orderNames geprüft. Ist das der Fall wird orderDir definiert auf DESC oder ASC gesetzt. Anschließend wird die ORDER-BY-Klausel an das SQL-Statement angehängt. Ein ungültiger Richtungswert ergibt ASC, ein ungültiger Feldname führt in diesem Beispiel zum kompletten Weglassen der ORDER-BY-Klausel.

Der LIKE-Operator gibt den beiden Zeichen % (Prozent) und _ (Unterstrich) eine Sonderbedeutung. Möchte man nicht, dass ein Anwender diese Zeichen als Platzhalter (wildcard character) in einem Suchmuster verwenden kann, so müssen diese mit einem vorangestellten \ (Backslash) maskiert werden. MySQL gestattet aber auch, ein beliebiges anderes Maskierzeichen (escape character) zu definieren.

5) Es gibt einen ANSI-Kompatibilitäts-Modus (Stichwort: ANSI_QUOTES), in dem werden String-Literale nur in einfachen Anführungszeichen notiert und doppelte Anführungszeichen stehen für Identifier.

Alternative: Prepared Statement

Bei einem Prepared Statement schreibt man ähnlich wie bei der bereits erwähnten Funktion sprintf() zunächst das SQL-Statement mit Platzhaltern. Dieses SQL-Statement gelangt in einem Prepare genannten Schritt zum SQL-Server. An die Platzhalter werden Variablen gebunden. Mit einem Execute werden dann die Daten aus den Variablen an den SQL-Server gesendet. In diesem Fall werden die Daten nicht in den SQL-Statement-Kontext gebracht, denn das SQL-Statement und die Daten gehen getrennte Wege zum DBMS. Demzufolge müssen und dürfen sie auch nicht dafür aufbereitet werden. Prepared Statements sind also schon vom Prinzip her unanfällig gegen fehlerhafte Maskierungen und SQL-Injection. Dem Nicht-Behandeln-Müssen der Daten steht auf der anderen Seite der etwas erhöhte Programmieraufwand gegenüber. Prepared Statements sind eigentlich dafür vorgesehen, ein Statement mit unterschiedlichen Daten mehrfach nutzen zu können. Dabei fällt das mehrfache Parsen des SQL-Statements weg. Wenn jedoch – wie bei PHP üblich – jeder Request eine eigene Datenbankverbindung aufbaut, ist dieser Vorteil nur dann gegeben, wenn während der Abarbeitung des Requests mehrere gleiche Statements aber mit unterschiedlichen Daten abgearbeitet werden sollen, wie es beim Verarbeiten von Massendaten der Fall ist. Der Vorteil der SQL-Injection-Immunität hingegen bleibt.

Beispiele zu Prepared Statements können den PHP-Handbuch-Kapiteln zu englischsprachige Seite mysqli und englischsprachige Seite PDO entnommen werden (siehe zum Beispiel bei englischsprachige Seite mysqli_prepare() beziehungsweise englischsprachige Seite PDO: Prepared statements and stored procedures).

Zahlen im (My)SQL-Statement

Achtung: Ein falsch behandelter Kontextwechsel kann ebenso fatale Folgen haben wie ein nicht behandelter. Zumindest kann es zu falschen Ergebnissen führen, wenn man für den einen Kontext maskiert, die Daten aber tatsächlich in einem anderen bringt.

SELECT feldliste FROM tabelle WHERE feld=42

Die 42 ist als Zahlenwert notiert. Dem Vergleichsoperator = folgt kein Anführungszeichen und somit wurde kein String-Kontext eingeleitet.

Wenn statt der 42 beispielsweise ein aus einer Nutzereingabe stammender Wert eingefügt werden soll (23 UNION SELECT …), so wird dieser sofort als Befehlsbestandteil angesehen. Die Funktion mysql_real_escape_string() hilft an dieser Stelle nicht, denn sie maskiert nur und quotiert nicht.

$sql = sprintf('SELECT feldliste FROM tabelle WHERE feld=%s', intval($zahl));

Die Funktion intval() liefert garantiert eine Integerzahl zurück. Wenn $zahl keine gültige Stringdarstellung einer Zahl ist, so ist das Ergebnis zumindest 0. Für Fließkommazahlen gibt es floatval(). Auch ein Typecast nach int oder float garantiert einen Zahlenwert.

Alternativ kann (zumindest unter MySQL) die einzufügende Zahl auch in String-Begrenzern notiert und mit mysql_real_escape_string() behandelt werden. Der Typecast wird dann von MySQL vorgenommen. (MySQL muss sowieso einen String in eine Zahl umwandeln, denn auch ohne Anführungszeichen ist eine Zahl in einem SQL-Statement-String in ihrer String-Darstellung notiert.)

PHP-Besonderheit Magic Quotes

Um SQL-Injection zu verhindern, wurde das PHP-Feature Magic Quotes entwickelt. Wenn es aktiviert ist, werden alle Eingabedaten (GET, POST und Cookies) vor dem Scriptstart von PHP mit der Funktion addslashes() behandelt. addslashes() behandelt zwar weniger Zeichen als mysql_real_escape_string(), doch es sind alle für (My)SQL-Injection kritischen Zeichen abgedeckt.

Oftmals hat dieses Feature jedoch mehr negative Nebenwirkungen als Nutzen. Das Problematische an ihm ist, dass es auf die Eingabedaten wirkt – also nicht kontextgerecht sondern pauschal. PHP kann vor dem Scriptstart noch nicht wissen, welchen Weg die Daten tatsächlich gehen werden. Werden die Daten in eine (My)SQL-Datenbank geschrieben, so sind sie zwar für den notwendigen Kontextwechsel vorbereitet. Müssen sie jedoch in HTML ausgegeben werden – sei es zusätzlich zum Datenbankeintrag als Kontrollausgabe oder weil sie fehlerhaft waren und dem Anwender erneut zur Korrektur vorgelegt werden müssen – so ist die SQL-Kontext-Behandlung falsch und stattdessen eine HTML-Kontext-Behandlung notwendig. Man kann zwar wegen der Vorbehandlung den SQL-Kontextwechsel ignorieren, muss aber nun zusätzlich zu allen anderen Kontextwechseln beachten, dass die Daten nicht in Rohform vorliegen und zunächst mit stripslashes() behandelt werden müssen. Empfehlenswert ist es deshalb, das Feature Magic Quotes komplett zu deaktivieren oder – wenn das zum Beispiel beim Hoster nicht geht – seine Auswirkungen rückgängig zu machen. Beides ist im PHP-Handbuch-Kapitel englischsprachige Seite Disabling Magic Quotes beschrieben.

Magic Quotes ist ein mittlerweile missbilligtes (deprecated) Feature und wird in PHP 6 nicht mehr verfügbar sein. Spätestens dann muss man sich vollständig selbst um seine Kontextwechsel kümmern.

nach obennach unten

Kontextwechsel erkennen und behandeln

Jeder unberücksichtigte Kontextwechsel kann einerseits zu einer Fehlinterpretation beim Datenempfänger führen, andererseits potenziell eine Lücke sein, die das unbeabsichtigte Einfügen von Code ermöglicht. Deshalb ist es wichtig, die Kontextwechsel zu erkennen, um sie angemessen behandeln zu können. Die Behandlung sollte stets stattfinden, auch wenn sie im Moment überflüssig erscheint, weil die Daten keine kritischen Zeichen enthalten. Damit spart man sich das böse Erwachen, wenn sich später die Daten ändern und man nicht alle Stellen nachgebessert hat.

Die Aufbereitung der Daten für einen anderen Kontext sollte erst zum Zeitpunkt der Zusammenstellung der Ausgabedaten erfolgen. Denn die aufbereiteten Daten sind in aller Regel nur für das jeweilige Ausgabemedium nützlich. Im Rest des Programms wird man mit den Rohdaten arbeiten wollen, denn sonst stören die zusätzlichen Maskierungen die normale Verarbeitung. Beispielsweise liefert eine Zeichenzählung ein anderes Ergebnis.

Im Webumfeld auftretende Kontextwechsel sind unter anderem:

SQL

MySQL wird in den Abschnitten nach oben Fehler und deren Auswirkungen sowie nach oben Verhindernde Maßnahmen beschrieben.

Andere Datenbanksysteme haben teilweise ihre eigenen Regeln. Mitunter stellt die DBMS-spezifische PHP-Extension dafür vorgesehene Funktionen bereit. Eine unvollständige Aufzählung:

SQLite stellt die Funktion englischsprachige Seite sqlite_escape_string() zur Verfügung. SQLite3 ist eine objektorientierte Implementation. Es kennt die Methode englischsprachige Seite SQLite3::escapeString(). Außerdem ist die Verwendung von Prepared Statements möglich.

Unter PostgreSQL-Extension können die Funktion englischsprachige Seite pg_escape_string() sowie Prepared Statements verwendet werden.

Oracle OCI8 hat einerseits keine Maskierfunktion und bietet andererseits die Ausführung von SQL-Statements nur als Prepared Statements an. Sollen Werte „händisch“ und unter Umgehung der Platzhalterfunktionalität in ein SQL-Statement eingefügt werden, so sind diese ebenfalls „händisch“ gemäß den Oracle-Regeln zu behandeln.

Mssql für Microsofts SQL Server bringt zwar mit mssql_query() die Möglichkeit zum direkten Ausführen von SQL-Statements mit, aber keine Funktion zum Maskieren von Daten. Stringwerte werden in einfache Anführungszeichen gesetzt, ein einfaches Anführungszeichen innerhalb den Daten ist zu verdoppeln. Allerdings können auch Prepared Statements ausgeführt werden.

Die Datenbank-Abstraktion ODBC kann unterschiedliche DBMS ansprechen und offeriert keine Maskierfunktion. Prepared Statements können verwendet werden.

Auch PDO ist eine Datenbank-Abstraktion – eine objektorientierte. Eine Besonderheit ist die Methode englischsprachige Seite PDO::quote(). Sie sorgt sowohl für die Maskierung der Daten als auch für die DBMS-gerechte Quotierung (Einfassen in Stringbegrenzungszeichen). In der PDO-Dokumentation wird jedoch ausdrücklich empfohlen, stattdessen Prepared Statements zu verwenden.

HTML

Für HTML gibt es die Funktionen englischsprachige Seite htmlspecialchars() und englischsprachige Seite htmlentities(). Die bereichsübergreifendes Kapitel HTML-eigenen Zeichen werden von htmlspecialchars() berücksichtigt. Zusätzlich dazu wandelt htmlentities() noch eine Menge anderer Zeichen in eine HTML-Darstellung, doch das ist meist nicht notwendig. (Das passende Thema dazu wäre Zeichenkodierung, das jedoch nicht im Fokus dieses Artikels liegt.)

Es ist übrigens nicht in jedem Fall erforderlich, alle HTML-eigenen Zeichen als Entity oder NCR (Numeric Character Reference: &#…;) zu notieren. Die Anführungszeichen müssen beispielsweise zwingend nur in einem mit gleichem Anführungszeichen eingefassten Attributwert umgeschrieben werden. In Fließtext ist das nicht erforderlich. Es schadet aber auch nicht, htmlspecialchars() sowohl für Attributwerte als auch für Fließtext anzuwenden.

printf('<input type="text" name="feld" value="%s">', htmlspecialchars($value));

Zu beachten ist allerdings, dass htmlspecialchars() das einfache Anführungszeichen ' normalerweise nicht umschreibt. In dem Fall muss htmlspecialchars() mit ENT_QUOTES als optionalem zweiten Parameter auf den einzufügenden Attributwert angewendet werden.

printf("<input type='text' name='feld' value='%s'>", htmlspecialchars($value, ENT_QUOTES));

Leerzeichen, Tabulatoren und Zeichen für den Zeilenumbruch sind so genannte Whitespace-Zeichen. Im HTML-Kontext werden diese als Leerzeichen dargestellt, wobei mehrere nacheinander auftretende Whitespace-Zeichen zu einem Leerzeichen zusammengefasst werden. Um einen Zeilenumbruch in der Anzeige nicht zu „verlieren“, ist er durch das HTML-Element br darzustellen. Vorgesehen ist dafür die PHP-Funktion englischsprachige Seite nl2br(), die vor jedes NewLine-Zeichen (\n) ein <br> oder <br /> stellt.

In welcher Reihenfolge ist nl2br() und htmlspecialchars() auszuführen? Die von nl2br() erzeugten Zeichen < und > müssen als solche in das HTML-Dokument geschrieben werden und dürfen nicht von htmlspecialchars() nach &lt; und &gt; umgeschrieben werden, damit das br-Element als HTML-Syntax angesehen werden kann und nicht als Datenbestandteil interpretiert wird.

printf('<textarea name="text">%s</textarea>', nl2br(htmlspecialchars($value)));

Mit der passenden Aufbereitung der Daten für den HTML-Kontext ist die Aufgabe des PHP-Scripts erledigt. Der Transport zum Empfänger findet außerhalb seines Einflussbereichs statt. Ein Browser wird das HTML-Dokument lesen und korrekt ausgezeichneten Code und Daten regelgerecht zu trennen wissen.

Daten, die ein Browser an den Webserver senden soll, werden vom Browser nicht in einen HTML-Kontext gebracht. Im Normalfall wird man diesen Weg nicht weiter betrachten müssen (solange man nicht händisch eingreift, um beispielsweise mit PHP oder Javascript Daten in eine URL für das action-Attribut eines Formular zu bringen). Ein PHP-Script bekommt die Daten in Rohform6) über die üblichen Arrays $_GET, $_POST usw. bereitgestellt. Eine Besonderheit stellt die Datenübertragung per nach unten AJAX dar.

6) Voraussetzung für die Rohform ist, dass das PHP-Feature nach unten Magic Quotes deaktiviert ist.

HTML in der Datenbank

„Vorauseilender Gehorsam“ führt oftmals zu Problemen, wenn sich die Aufgabenstellung ändert oder erweitert wird. Wenn für ein Projekt vorgesehen ist, Daten in einem DBMS zu speichern und diese auf Anforderungen von Clients in ein HTML-Dokument eingefügt auszugeben, könnte man auf die Idee kommen, die Daten gleich HTML-gerecht im DBMS abzulegen. Es klingt verlockend, die HTML-Behandlung und die DBMS-Maskierung in einem Schritt erledigen zu können. Dem stehen jedoch gravierende Nachteile gegenüber. Im DBMS kommen keine Rohdaten zu liegen, was eine Stringverarbeitung unmöglich macht. Die für die HTML-gerechte Notation verwendeten zusätzlichen Zeichen sind für ein DBMS ganz normale Zeichen, die unter anderem beim Zählen und Sortieren berücksichtigt werden und falsche Ergebnisse liefern. Hühner hat eine Länge von 6 Zeichen, H&uuml;hner hingegen besteht für das DBMS aus 11 Zeichen. Zudem wird ein & bei einer Sortierung vor den Buchstaben einsortiert, weshalb H&uuml;hner vor Hasen auftauchen werden. Für eine später hinzukommende Ausgabe in einen anderen Kontext als HTML ist die HTML-Aufbereitung ebenfalls hinderlich. Diese Nachteile umgeht man, wenn man die Aufbereitung konsequent zum Zeitpunkt des Kontextwechsels und nur für diesen einen vornimmt. Nebenbei erreicht man durch diese Konsequenz eine Konsistenz im Code: die Behandlung ist genau da zu finden, wo sie benötigt wird.

Anders zu betrachten sind die Anwendungsfälle, die gerade den HTML-Code als im DBMS abzulegende Daten ansehen, wie es zum Beispiel für ein Template-System der Fall sein kann. In Richtung DBMS sind zwar die DBMS-üblichen Maßnahmen zu ergreifen. Eine HTML-gerechte Behandlung beim Einfügen in einen HTML-Kontext darf jedoch nicht stattfinden, denn man möchte ja den HTML-Code von einem Client als Code interpretiert wissen.

Schwierig wird die Sachlage, wenn man Anwendern ermöglichen möchte, eigenen HTML-Code in einen HTML-Kontext einzufügen. Der Möglichkeiten, beispielsweise JavaScript-Code oder CSS-Formatierungen in HTML unterzubringen, gibt es einige und damit auch einige zu beachtende Missbrauchsmöglichkeiten. Ein Patentrezept kann es aufgrund der Vielfalt der möglichen Aufgabenstellungen nicht geben. Hier muss jeder Programmautor selbst gewissenhaft überlegen, was dem Anwender gestattet wird, welche Nachteile das für beide Seiten mit sich bringen kann und nicht zuletzt, wo eine kontextgerechte Behandlung ausgeführt werden muss. Systeme wie BBCode können ein Kompromiss sein. Dem Anwender wird damit eine überschaubar geringe Anzahl von Gestaltungsmöglichkeiten gegeben, bei denen nicht alle (Missbrauchs-)Möglichkeiten von HTML zur Verfügung stehen.

URL

Eine URL verwendet diverse Zeichen mit einer Sonderbedeutung – zum Beispiel der / (Schrägstrich) als Pfadtrennzeichen, = (Gleichheitszeichen) und & (Kaufmanns-Und) sowie manchmal auch ; (Semikolon) als Trennzeichen im Querystring. Wie üblich müssen diese Zeichen besonders behandelt werden, damit sie eindeutig als Datenbestandteil angesehen werden können.

Es gibt zwei Stellen in einer URL, die eigentlich7) unterschiedlich betrachtet werden müssen. Für den Querystring ist englischsprachige Seite urlencode() vorgesehen und für Daten, die im Pfadteil eingefügt werden, gibt es englischsprachige Seite rawurlencode(). Der Unterschied zwischen beiden Funktionen ist lediglich die Behandlung des Leerzeichens. rawurlencode() wandelt es zu %20, urlencode() hingegen in ein +.

$url = sprintf('http://example.com/resource?name1=%s&name2=%s', urlencode($value1), urlencode($value2));

$url = sprintf('ftp://%s:%s@ftp.example.com/resource', rawurlencode($username), rawurlencode($password));

$url = sprintf('http://example.com/resource/%s', rawurlencode($daten));

Die dritte Zeile findet beispielsweise Anwendung bei „freundlichen URLs“, bei denen die URL teilweise aus variablen Daten gebildet wird, etwa wenn die Überschrift eines Blog-Eintrags in der URL auftauchen soll.

7) Eine URL-Dekodierfunktion wird höchstwahrscheinlich alle %XX-Sequenzen inklusive %20 richtig dekodieren, auch wenn der Kontext eigentlich ein + vorschreibt. Insofern könnte man generell zu rawurlencode() greifen. Doch warum sollte man sich ohne Not und wider besseren Wissens eine Restunsicherheit ins Programm einbauen?

URL in HTML – zweifacher Kontextwechsel

Daten in eine URL zu bringen und diese URL in HTML einzufügen ist ein Fall bei dem zwei Kontextwechsel stattfinden.

$url = sprintf('http://example.com/resource?name1=%s&name2=%s', urlencode($value1), urlencode($value2));
$link = sprintf('<a href="%s">Linktext</a>', htmlspecialchars($url));

Zunächst werden die Daten in den URL-Kontext gebracht – in dem Fall mit englischsprachige Seite urlencode. Die behandelte URL wird anschließend mit englischsprachige Seite htmlspecialchars() für den HTML-Kontext aufbereitet.

Achtung: Jeder Kontextwechsel muss getrennt für sich betrachtet werden. Aufgelöst wird der Knoten „von innen nach außen“. Die für die Maskierung hinzugekommen Zeichen von inneren Kontexten gelten im äußeren Kontext als normales Nutzzeichen, das gemäß den „äußeren“ Regeln behandelt werden muss.

JavaScript

Um Daten in JavaScript-Code einzufügen, bringt PHP keine Funktion mit. Vor allem String-Begrenzungszeichen und Zeilenumbrüche müssen beachtet werden. Da in JavaScript ein String nicht über mehrere Zeilen gehen darf, ist das folgende Beispiel ungültige Syntax.

var text = 'Eine Zeile
Noch eine Zeile';

Für das Zeilenumbruchzeichen ist stattdessen ein \n zu notieren:

var text = 'Eine Zeile\nNoch eine Zeile';

Alternativ kann ein Backslash vor das (unsichtbare) Zeilenende gestellt werden:

var text = 'Eine Zeile\
Noch eine Zeile';

Diese Methode ist jedoch in der ECMAScript-Spezifikation8) als unzulässig beschrieben. Stattdessen ist \n oder \u000A zu verwenden.

Wie erwähnt gibt es keine für JavaScript-Code vorgesehene Maskierfunktion. Das folgende Beispiel zeigt, wie eine solche Funktion aussehen kann.

/**
 * Maskiert Sonderzeichen für den JavaScript-Kontext.
 *
 * @param $value string Der zu behandelnde Wert.
 * @return string
 */
function javascript_escape($value) {
  return strtr((string)$value, array(
    "'"     => '\\\'',
    '"'     => '\"',
    '\\'    => '\\\\',
    "\n"    => '\n',
    "\r"    => '\r',
    "\t"    => '\t',
    chr(12) => '\f',
    chr(11) => '\v',
    chr(8)  => '\b',
  ));
}

Alternativ dazu kann die Funktion json_encode() verwendet werden. Diese ist jedoch nicht direkt nur für Strings ausgelegt, sondern wandelt alle PHP-Typen (außer Resource aber inklusive der komplexen Typen Array und Object) in eine JSON-Darstellung. Außerdem maskiert sie nicht nur sondern quotiert (setzt Anführungszeichen um den Wert) gleichzeitig und erwartet UTF-8-kodierte Werte.

Siehe auch die Abschnitte nach unten AJAX und nach unten <script>- und <style>-Bereiche im HTML-Dokument.

8) Der Sprachkern von JavaScript ist unter dem Namen ECMAScript standardisiert worden.

JavaScript und HTML

Es gibt zu viele Möglichkeiten, Kontexte zu schachteln, deshalb soll hier nur eine als Beispiel gezeigt werden. Als Programmierer muss man sorgfältig beachten, wann welcher Kontext vorliegt. Um zu einer fehlerfreien Implementierung zu kommen, kann es hilfreich sein, sich vorzustellen und vor allem auszuprobieren, wie in den einzufügenden Daten enthaltene Begrenzungszeichen für das empfangende System aussehen. Hierzu kann man das fertig zusammengefügte Ergebnis zur Kontrolle ausgeben. Aber Achtung! Im Browser angezeigt hat dieser bereits den HTML-Kontext interpretiert. Das was der Browser wirklich bekommen hat, sieht man am besten in der Quellcode-Ansicht.

// mit XSS-Injection-Lücke
echo "<span onclick=\"tuwas('$eingabewert')\">...</span>";

Der Kontext, in dem dieser Code steht, ist PHP. Für diesen ist erforderlich, dass die Anführungszeichen des onclick-Attributes maskiert werden, womit die Behandlung für den PHP-Kontext erledigt ist. Das unbehandelte $eingabewert stellt jedoch noch eine XSS-Injection-Lücke9 dar. Derzeit bekommt der Browser das Folgende zu sehen.

<span onclick="tuwas('eingabewert')">...</span>

Der eingabewert steht in einem JavaScript-String-Kontext und der JavaScript-Code steckt in einem HTML-Kontext.

$js = sprintf("tuwas('%s')", javascript_escape($eingabewert));
printf('<span onclick="%s">...</span>', htmlspecialchars($js));

Notiert man die beiden Kontexte (HTML und JavaScript) in einzelne Anweisungen, entspannt sich zum einen die Anführungszeichensituation unter PHP und zum anderen zeigt sich deutlicher die für den jeweiligen Kontext notwendige Behandlung. Allerdings muss dabei weiterhin die Anführungszeichenschachtlung im entstehenden HTML-Code beachtet werden.

Zum Testen ob die Behandlungen erfolgreich waren, kann $eingabewert mit den kritischen Inhalten gefüllt werden, wobei besonderes Augenmerk auf die Stringbegrenzungszeichen gelegt werden sollte.

$eingabewert = "');alert('test";
// HTML-Quelltextanzeige: <span onclick="tuwas('\');alert(\'test')">...</span>
// JavaScript-Code: tuwas('\');alert(\'test')

$eingabewert = '"><b>test</b>';
// HTML-Quelltextanzeige: <span onclick="tuwas('\&quot;&gt;&lt;b&gt;test&lt;/b&gt;')">...</span>
// JavaScript-Code: tuwas('\"><b>test</b>')

Beide Kontextwechsel wurden jeweils fehlerfrei gemeistert. Der JavaScript-Code wird nicht kompromittiert. Was die Funktion tuwas() damit anstellt, steht auf einem anderen Blatt. In deren Code sind möglicherweise weitere Kontextwechsel zu beachten.

9) XSS = deutschsprachige Seite Cross-Site Scripting

AJAX

AJAX ist eine Technik, die im Browser aus dem Kontext JavaScript heraus ausgeführt wird. Wenn für die AJAX-Requests eine externe Bibliothek verwendet wird, so ist die Übergabe von Werten gemäß deren Richtlinien vorzunehmen. Verwendet man jedoch direkt das XMLHttpRequest-Objekt, so muss man sich um die kontextgerechte Behandlung der Werte selbst kümmern. Zu beachten sind zwei Stellen.

Die Methode open() erwartet unter anderem eine URL. Möchte man in dieser Daten übertragen, so ist der URL-Kontext zu beachten. Die einzufügenden Daten können mit der Funktion bereichsübergreifende Seite encodeURIComponent()10) behandelt werden. Sie ist sowohl für das Einfügen in den Pfad- als auch in den QueryString-Teil der URL verwendbar.

Im Falle eines POST-Requests nimmt die Methode send() die Daten entgegen. Sie müssen dabei im Normalfall (Content-Type: application/x-www-form-urlencoded) wie der QueryString-Teil einer URL ohne das einleitende Fragezeichen notiert werden. Auch dafür können die einzufügenden Daten mit der Funktion encodeURIComponent() behandelt werden.

10) Hinweis: encodeURIComponent() maskiert Zeichen außerhalb von ASCII als UTF-8-Bytesequenzen.

<script>- und <style>-Bereiche im HTML-Dokument

Gesetzt den Fall, man hat ein HTML-Dokument, in dem JavaScript-Code eingebettet in einem <script>-Block steht. Es ist in der HTML-Spezifikation definiert, dass in einem solchen alles als Rohtext interpretiert werden muss, auch Markup und Entitys. Lediglich die Sequenz </ hat eine Sonderstellung, denn die beendet den Elementinhalt.

Aber: Der Inhalt eines Eventhandlers (onclick-Attribute usw.) allerdings wird gemäß den üblichen HTML-Regeln interpretiert.

   ...
   <input type="submit" id="foo" />
   <div id="bar">Inhalt</div>
   ...

Falsch:
   <script type="text/javascript">
1:   document.getElementById('foo').value = '&Auml;ndern';
2:   document.getElementById('bar').innerHTML = '&lt;span style=&quot;color:red&quot;&gt;neuer Inhalt&lt;/span&gt;';
   </script>

Richtig:
   <script type="text/javascript">
3:   document.getElementById('foo').value = 'Ändern';
4:   document.getElementById('bar').innerHTML = '<span style="color:red">neuer Inhalt<' + '/span>';
   </script>

Alternative:
   <script type="text/javascript">
5:   document.getElementById('foo').value = '\u00C4ndern';
6:   document.getElementById('bar').innerHTML = '<span style="color:red">neuer Inhalt<\u002Fspan>';
   </script>

Zeile 1: Der Submit-Button foo wird nicht Ändern sondern &Auml;ndern anzeigen, da das Entity nicht vom HTML-Parser interpretiert wird.
Zeile 2: Der Browser wird HTML-Code anzeigen, weil auch das Markup ohne Interpretation zum JavaScript durchgereicht wird.

Zeilen 3 und 4: Beides wird wie gewünscht angezeigt werden. In Zeile 4 wird allerdings </ getrennt, damit nicht der script-Bereich als beendet angesehen wird.

Zeile 5 verwendet eine Unicode-Sequenz, falls das Ä nicht direkt notiert werden kann. Normalerweise sollte das nicht notwendig sein.
Zeile 6 verwendet die Unicode-Sequenz für den / (Schrägstrich), um dem HTML-Parser das </ zu verstecken. JavaScript interpretiert den Schrägstrich wieder als solchen.

Für <style>-Bereiche für CSS gilt die gleiche Regel bezüglich Markups und Entitys wie für <script>-Bereiche. Die Werte für eine CSS-Eigenschaft werden in den meisten Fällen direkt notiert (z.B. color: red; oder color: #FF0000;). Sollen diese Werte aus einer nicht vertrauenswürdigen Quelle kommen, so muss man andere Wege finden, um ungewollte Werte zu verhindern, denn ein Quotieren oder Maskieren ist hier weder vorgesehen noch möglich. Solche anderen Wege wären beispielsweise ein Abgleich gegen eine Liste von erlaubten Werten, ein Test auf Einhaltung der Syntax-Regeln oder eine anderweitig geeignete Prüfung.

E-Mail

Eine E-Mail besteht im einfachsten Fall aus Kopf-Zeilen (Header) und dem Inhalt (Body). Header-Zeilen transportieren Metadaten zur Mail, zum Beispiel Absender- und Empfänger-Angabe (From, To), den Betreff (Subject) oder Angaben zum Inhalt (Content-Type). Jede Header-Zeile endet mit einem Zeilenumbruchszeichen11). Header und Body sind durch eine Leerzeile voneinander getrennt – oder anders gesagt: die letzte Header-Zeile endet mit zwei Zeilenumbruchszeichen. Beim Einfügen von Werten in Headerzeilen ist nun zu beachten, dass darin keine Zeilenumbruchszeichen enthalten sind, sonst können beliebige Headerzeilen hinzugefügt werden. Die Funktion englischsprachige Seite mail() nimmt die drei Pflichtparameter to, subject und message entgegen sowie zwei optionale Parameter, von denen der erste namens additional_headers interessant ist, weil über ihn weitere Headerzeilen eingefügt werden können. Die Werte für to und subject werden von PHP so behandelt, dass alle enthaltenen Steuerzeichen inklusive Zeilenumbruch durch Leerzeichen ersetzt werden. Sie sind also schon seitens PHP gegen E-Mail-Header-Injection gesichert. Über additional_headers werden oftmals Angaben wie From, Cc und Bcc hinzugefügt. Dafür werden keine Steuerzeichen benötigt, vor allem keine Zeilenumbruchszeichen. Sind solche in den Eingabewerten enthalten, ist das ein recht sicheres Zeichen für einen Missbrauchsversuchs. Es lohnt sich nicht, sie gegen Leerzeichen auszutauschen, denn den Missbrauch wird man lieber generell verhindern wollen. Ein kommentarloser Script-Abbruch wäre auch angemessen. Eine eventuell ausgegebene Fehlermeldung wird ein automatischer Angreifer nicht auswerten und der normale Anwender nicht zu sehen bekommen, weil er mit harmlosen Daten den Abbruch nicht auslöst.

/**
 * Ermittelt, ob ein String nicht druckbare Zeichen enthält. 
 * 
 * @param $subject der zu testende String
 * @param $utf8 optional - true, wenn der Teststring UTF-8-kodiert ist.
 * @return boolean
 */
function is_printable($subject, $utf8 = false) {

  $pattern = '~^\P{C}+\z~' . ($utf8 ? 'u' : '');
  return (bool)preg_match($pattern, $value); 
}

if (!is_printable($from) or !is_printable($cc))
  // $from oder $cc enthält nicht nur druckbare Zeichen
  die(); // Abbruch wegen potentiellem Missbrauch

$headers = array();
$headers[] = 'Content-Type: text/plain';
$headers[] = 'From: ' . $from;
$headers[] = 'Cc: ' . $cc;
mail($to, $subject, $message, implode("\n", $headers));

In diesem Beispiel werden Angaben zum Absender und Kopie-Empfänger als optionale Header-Zeilen und eine Inhaltstypangabe eingefügt, wobei Werte für From und Cc aus einer externen Quelle kommen. Diese werden mit der Funktion is_printable() getestet, ob sie nur druckbare Zeichen enthalten. Wenn dies nicht der Fall ist, bricht das Script ab. Ansonsten werden die einzelnen Headerzeilen zunächst als je ein Element des Arrays $headers angelegt. Beim Aufruf der Funktion mail() wird aus diesem Array ein Header-Zeilen-String erstellt, dessen einzelne Angaben mit je einem Zeilenumbruchszeichen (\n) getrennt sind.

Für alle Header-Zeilen gilt, dass sie aus historischen Gründen nur 7-Bit-Zeichen enthalten dürfen. Alle Nicht-ASCII-Zeichen (darunter fallen unter anderem die Umlaute) müssen daher umkodiert werden. Ist die englischsprachige Seite Multibyte-String-Extension verfügbar, kann die Funktion englischsprachige Seite mb_encode_mimeheader() dafür verwendet werden.

mb_internal_encoding('utf8'); // andere Werte z.B. ISO-8859-1 oder Latin1

// $subject = 'Umlautärger';
$email_subject = mb_encode_mimeheader($subject, 'utf8', 'Q');
// $email_subject enthält nun =?UTF-8?Q?Umlaut=C3=A4rger?=

$email_from = sprintf('"%s" <%s>', mb_encode_mimeheader($from_name, 'utf8', 'Q'), $from_email);

Zuerst wird der MB-Extension mit der Funktion englischsprachige Seite mb_internal_encoding() mitgeteilt, in welcher Kodierung die zu verarbeitenden Strings vorliegen, was in diesem Beispiel UTF-8 sein soll. Der zweite Parameter von mb_encode_mimeheader() gibt die gewünschte Kodierung des Ergebnisses an. Die Subject-Zeile muss komplett behandelt werden. Für eine E-Mail-Adresse hingegen (im Beispiel $email_from) darf nur der optionale Name, nicht jedoch die Adresse selbst kodiert werden.

Was noch übrig bleibt ist der Nachrichtentext. Der ist bei reinem Text (plain text) unkritisch. Bei anderen Inhaltstypen (beispielsweise HTML) sind die üblichen kontextspezifischen Regeln zu beachten, wenn dort Werte eingefügt werden sollen.

11) Es gibt auch mehrzeilige Header-Zeilen, doch mit solchen kommt man nur selten direkt in Berührung.

Dateiformate, stellvertretend CSV

Für die vielfältigen Regeln der einzelnen Dateiformate soll hier nur stellvertretend das Format CSV (Comma/Character Separated Values) genannt werden. Ein allgemein verbindlicher Standard existiert nicht, es ist nur in RFC 4180 beschrieben. Für die Trennung von Datensätzen wird meist ein Zeilenumbruch verwendet, für die Felder finden Zeichen wie Komma, Semikolon, Doppelpunkt, Tabulator oder andere Verwendung. Welche Trennzeichen auch immer verwendet werden, es bleibt das übliche Problem, dass diese Zeichen auch in den Daten vorkommen können. Datenfelder müssen dann begrenzt werden. Als Folge ist auch das Begrenzungszeichen als Sonderzeichen zu beachten.

Für das Lesen und Schreiben von CSV-Dateien stellt PHP die Funktionen englischsprachige Seite fgetcsv() und englischsprachige Seite fputcsv() zur Verfügung. Außerdem gibt es die Funktion englischsprachige Seite str_getcsv() für in Strings vorliegende CSV-Daten.

Für andere Dateiformate muss im Einzelfall geprüft werden, welche Besonderheiten zu beachten sind. Wenn es nur darum geht, mehrere Daten in einer Textdatei abzulegen, so kann man die Daten in eine Struktur bringen (Array oder Objekt) und diese mit englischsprachige Seite serialize() in eine Textform bringen. Die Funktion englischsprachige Seite unserialize() erzeugt dann wieder die originale Form.

Programmausführung über Shell-Kommandos

Gerade hier ist es wichtig, Nutzdaten richtig zu behandeln, um eine ungewollte Programmausführung oder Schlimmeres zu verhindern. Da dies ein Thema für Fortgeschrittene ist, sei an dieser Stelle nur kurz auf die beiden PHP-Funktionen englischsprachige Seite escapeshellarg() und englischsprachige Seite escapeshellcmd() verwiesen. Anwendungsbeispiele können dem PHP-Handbuch entnommen werden.

Reguläre Ausdruck

Relativ selten wird man einen regulären Ausdruck aus variablen Bestandteilen zusammensetzen. Falls doch, gibt es dafür die Funktion englischsprachige Seite preg_quote().

Teil von SELFHTML aktuell Teil von Artikel Teil von PHP

© 2009 bereichsübergreifende Seite Impressum. Dieser Artikel stammt vom User dedlfix aus dem SELFHTML Forum. Besten Dank an alle Rezensenten für Vorschläge zur Erweiterung und Verbesserung des Artikels.