Anzeigen - Jobelmann Schule
Transcription
Anzeigen - Jobelmann Schule
Lutz Kleinostendarp Berufliches Gymnasium Technik Informationstechnik Unterrichtsskript zum Lerngebiet IT3: Software zur Prozessdatenverarbeitung entwickeln Einführung in die Programmierung mit PHP und MySQL Stand: 04.02.2015 Einführung in die Programmierung mit PHP und MySQL.doc Seite 1 von 54 Inhaltsverzeichnis 1 1.1 1.2 1.3 HTML für PHP................................................................................................................................. 3 Eine einfache Seite ................................................................................................................ 3 Formulare ............................................................................................................................... 4 Tabellen.................................................................................................................................. 4 2 Einbettung von PHP in HTML ......................................................................................................... 6 3 Kommentare ................................................................................................................................... 7 4 4.1 4.2 4.3 Variablen, Datentypen und Operatoren .......................................................................................... 8 Regeln für Variablennamen ................................................................................................... 8 Zahlenvariablen und Rechenoperatoren ................................................................................ 8 Textvariablen .......................................................................................................................... 9 5.1 5.2 5.3 Formularauswertung ..................................................................................................................... 11 Eingabenformular ................................................................................................................. 11 Auswertung mit $_POST ...................................................................................................... 12 Umwandlung von Zeichenketten in Zahlen .......................................................................... 13 5 6 Kontrollstrukturen .......................................................................................................................... 15 6.1 Operatoren ........................................................................................................................... 15 6.1.1 Vergleichsoperatoren .................................................................................................. 15 6.1.2 logische Operatoren .................................................................................................... 15 6.1.2.1 Logisches ODER ..................................................................................................... 15 6.1.2.2 Logisches UND ....................................................................................................... 15 6.1.2.3 Logisches NICHT .................................................................................................... 16 6.1.3 Rangordnung der Operatoren ..................................................................................... 16 6.2 Verzweigungen .................................................................................................................... 16 6.2.1 Einseitige Auswahl ...................................................................................................... 16 6.2.2 Zweiseitige Auswahl .................................................................................................... 18 6.2.3 Geschachtelte Auswahl ............................................................................................... 18 6.2.4 Mehrfachauswahl ........................................................................................................ 19 6.2.5 HTML in Verzweigungsblöcken ................................................................................... 20 6.3 Schleifen............................................................................................................................... 21 6.3.1 for-Schleife .................................................................................................................. 21 6.3.2 while-Schleife .............................................................................................................. 23 6.3.3 do-while-Schleife ......................................................................................................... 25 6.3.4 foreach-Schleife........................................................................................................... 25 7 7.1 7.2 Felder ............................................................................................................................................ 26 Numerisch indizierte Felder ................................................................................................. 26 Assoziative Felder ................................................................................................................ 27 8.1 8.2 8.3 8.4 8.5 8.6 8.7 Datenbankverbindung ................................................................................................................... 29 Der erste Kontakt ................................................................................................................. 29 Datensätze auswählen ......................................................................................................... 31 Ausgabe in eine HTML-Tabelle ........................................................................................... 32 Auswahl von Daten in einem Suchformular ......................................................................... 33 Exkurs: Rekursiver Aufruf der gleichen Datei ...................................................................... 35 Datensätze erzeugen ........................................................................................................... 37 Datensätze ändern ............................................................................................................... 38 8 9 Zugriffsautorisierung ..................................................................................................................... 41 9.1 Benutzerregistrierung ........................................................................................................... 42 9.1.1 Identifizierung .............................................................................................................. 42 9.1.2 Kennwörter .................................................................................................................. 42 9.2 Benutzerauthentifizierung .................................................................................................... 43 9.2.1 HTTP-Authentifizierung ............................................................................................... 44 9.2.2 Session-Daten in der URL ........................................................................................... 44 9.2.3 Cookies ........................................................................................................................ 45 Seite 2 von 54 Einführung in die Programmierung mit PHP und MySQL.doc 9.3 Statuserhalt durch Sessions ................................................................................................ 46 9.3.1 Das Session-Management von PHP ........................................................................... 47 9.3.2 Freizügigkeit einer PHP-Session................................................................................. 47 9.3.3 Daten einer Session verwalten .................................................................................... 48 9.3.4 Beispiel: Session zur Benutzerauthentifizierung ......................................................... 48 10 Angriffsszenarien .......................................................................................................................... 51 10.1 Informationsgewinnung ........................................................................................................ 51 10.2 Parametermanipulation ........................................................................................................ 51 10.3 Cross-Site-Scripting ............................................................................................................. 51 10.4 SQL-Injection ....................................................................................................................... 51 10.4.1 Get-Parameter ............................................................................................................. 52 10.4.2 POST-Parameter ......................................................................................................... 52 10.4.3 Cookie-Parameter ....................................................................................................... 52 10.4.4 Schutz vor SQL-Injektion ............................................................................................. 52 10.5 Autorisierung und Authentifizierung ..................................................................................... 53 10.5.1 Man-in-the-Middle-Attacke .......................................................................................... 53 10.5.2 Wörterbuchangriff ........................................................................................................ 53 10.5.3 Social Engineering....................................................................................................... 54 11 Objektorientierte Programmierung in PHP 5 ................................................................................ 55 Anhang: Verzeichnisse .......................................................................................................................... 56 Einführung in die Programmierung mit PHP und MySQL.doc 1 Seite 3 von 54 HTML für PHP In diesem Abschnitt werden die Bestandteile von HTML1, die zur Bearbeitung der PHP2Programme notwendig sind, dargestellt. Dies sind: Dokumentaufbau, Formulare und Tabellen. Zum Erlernen der Programmiersprache PHP, zum Ausprobieren der Beispiele und zum Lösen der Übungen muss man nur wenige Grundlagen von HTML beherrschen. Zum Testen benötigt man einen Texteditor und einen Web-Browser Zum Erzeugen von ansprechenden Websites, also mehreren miteinander verknüpften Seiten, sind allerdings genauere Kenntnisse notwendig. 1.1 Eine einfache Seite Innerhalb einer HTML-Seite befinden sich Text und Markierungen, die in spitzen Klammern eingebunden sind. Viele dieser Markierungen sind so genannte Container, das heißt, es gibt jeweils eine Anfangsmarkierung und eine Endmarkierung. Innerhalb des Containers <html> befindet sich das gesamte Dokument. Es besteht aus den Containern <head> und <body>. Im Container <body> steht der eigentliche Inhalt des Dokuments. <html> <head> <title> Meine erste Internetseite </title> </head> <body> Das ist der erste Text der Internetseite. </body> </html> Listing 1-1: PHP1-1.htm Geben Sie das obige Beispiel mithilfe eines Editors ein. Speichern Sie es unter dem Dateinamen PHP1-1.htm in einem Verzeichnis ihrer Wahl. Laden Sie die Datei in einen Web-Browser. Abbildung 1-1: PHP1-1.htm 1 2 Hypertext Markup Language, eine textbasierte Auszeichnungssprache zur Strukturierung von Inhalten ursprünglich „Personal Home Page Tools“, jetzt ein rekursives Akronym für PHP: Hypertext Preprocessor Seite 4 von 54 1.2 Einführung in die Programmierung mit PHP und MySQL.doc Formulare Eine besondere Stärke von PHP ist die einfache Auswertung von Formularinhalten. Durch eine solche Auswertung wird die Informationsübermittlung vom Betrachter der Webseite zum Webserver ermöglicht. Der HTML-Code: <html> <body> Bitte tragen Sie Ihren Namen ein<br> und senden Sie das Formular ab.<br> <form> <input type = "text" name="vorname" size="30"> Vorname <br> <input type = "text" name="nachname" size="30"> Nachname<br> <input type="submit"> <input type="reset"> </form> </body> </html> Listing 1-2: PHP1-2.htm Geben Sie das obige Beispiel einen und speichern Sie es unter dem Dateinamen PHP1-2.htm. In diesem Formular kann der Betrachter zwei Eintragungen vornehmen und das Formular absenden. Es fehlt allerdings noch ein passendes Server-Programm. Deshalb erfolgt bisher noch keinerlei Reaktion. Abbildung 1-2: PHP1-2.htm 1.3 Tabellen Tabellen dienen zur übersichtlicheren Darstellung größerer Datenmengen und zur mehrspaltigen Ausgabe in Dokumenten. Damit eignen sie sich besonders zur Ausgabe von Datenbankinhalten. Auch Formulare lassen sich mit Tabellen formatieren. Einführung in die Programmierung mit PHP und MySQL.doc Seite 5 von 54 <html> <body> Bitte tragen Sie Ihren Namen ein<br> und senden Sie das Formular ab.<br> <form> <table border=”0”> <tr> <td>Vorname</td> <td><input name=”vorname” size="30"></td> </tr> <tr> <td>Nachname</td> <td><input name=”nachname" size="30"></td> </tr> </table> <input type="submit"> <input type="reset"> </form> </body> </html> Listing 1-3: PHP1-3.htm Speichern Sie das obige Programm unter dem Namen PHP1-3.htm. Das Programm unterscheidet sich nur in der Anordnung von Bezeichner und Formularfeld von dem vorhergehenden. Abbildung 1-3: PHP1-3.htm Seite 6 von 54 2 Einführung in die Programmierung mit PHP und MySQL.doc Einbettung von PHP in HTML Eine grundsätzliche Bemerkung zu Beginn. In den folgenden Kapiteln sollen nicht nur die Grundkenntnisse der Sprache PHP vermittelt werden, sondern auch ein übersichtlicher, strukturierter Programmierstil. Nach meiner Erfahrung vereinfacht dies sowohl die Arbeit eines einzelnen Entwicklers als auch die Zusammenarbeit eines Entwickler-Teams. Grundsätzlich gibt es mehrere Wege, PHP-Programme in HTML-Dateien einzubetten. In den meisten PHP-Programmen wird jedoch die folgende Methode verwendet: <?php [PHP-Anweisung]; [PHP-Anweisung]; [PHP-Anweisung]; ?> Die Markierung <?php leitet eine einzelne PHP-Anweisung oder einen Block von PHPAnweisungen ein. Diese werden bis zur Markierung ?> bearbeitet, die das Ende des Blocks darstellt. Jede PHP-Anweisung wird mit einem Semikolon abgeschlossen. PHP-Blöcke können im gesamten Dokument untergebracht werden. Der Code wird von oben nach unten abgearbeitet und es kann mehrmals zwischen HTML und PHP gewechselt werden. Das folgende Beispiel verdeutlicht die Einbettung von PHP-Code in HTML: <html> <head> <title> Titelzeile der Datei</title> </head> <body> <p>Die erste Zeile in HTML</p> <?php echo "<p>Die zweite Zeile in PHP</p>"; ?> <p>Die dritte Zeile in HTML</p> <?php echo "<p>Die vierte Zeile in PHP</p>"; echo "<p>Die fünfte Zeile in PHP</p>"; ?> </body> </html> Listing 2-1: PHP2-1.php Speichern Sie das obige Programm unter dem Namen PHP2-1.php ab. Dieses Programm ist nicht direkt im Browser lauffähig. PHP ist eine serverseitige Sprache. Es ist deshalb Abbildung 2-1: PHP2-1.php notwendig, dass die Datei auf einem Web-Server ausgeführt wird. In der Installation von XAMPP liegen alle Web-Dokumente im Verzeichnis htdocs unterhalb des Hauptverzeichnisses xampp. Der Apache-Server übergibt alle Dateien mit der Endung .php zuerst dem PHP-Interpreter, bevor sie an den Client ausgeliefert werden. Das Programm kann durch den folgenden Befehl in einem Browser (Eingabe in die Adressleiste!) auf dem Server ausgeführt werden: localhost/PHP2-1.php Einführung in die Programmierung mit PHP und MySQL.doc 3 Seite 7 von 54 Kommentare Durch Kommentare wird ein Programm lesbar. Sie werden nicht ausgeführt, sondern dienen lediglich der Information des Entwicklers. Sollte es sich um eine Gruppe von Entwicklern handeln oder sollte das Programm später von anderen Entwicklern weiter bearbeitet werden, so ist eine ausführliche Kommentierung der eigenen Programmzeilen unerlässlich. Erfahrungsgemäß gibt es immer wieder Entwickler, die ihre Programme nur minimal kommentieren. Dies führt häufig zu Programmen, die (auch von dem ursprünglichen Programmierer) nicht oder nur unter größtem Aufwand weiterzuentwickeln sind. Man unterscheidet zwischen einzeiligen und mehrzeiligen Kommentaren: ein einzeiliger Kommentar beginnt mit den Zeichen // und endet am Ende der Zeile. Er wird im Allgemeinen zur Kommentierung einzelner Programmschritte verwendet. Eine mehrzeiliger Kommentar beginnt mit den Zeichen /* und endet mit den Zeichen */. Er wird üblicherweise zur Erläuterung eines ganzen Programmblocks verwendet. Ein Beispiel (PHP3-1.php): <html> <body> <?php echo "Das ist der Anfang"; // Kommentar // bis zum Zeilenende /* Ein Kommentar über mehrere Zeilen hinweg */ echo " und hier das Ende des Programms"; ?> </body> </html> Listing 3-1: PHP3-1.php Abbildung 3-1: PHP3-1.php Seite 8 von 54 4 Einführung in die Programmierung mit PHP und MySQL.doc Variablen, Datentypen und Operatoren Innerhalb eines Programms können Informationen zur späteren Verwendung in Variablen gespeichert werden. Für diese Variablen unterstützt PHP die folgenden Datentypen: ganze Zahlen Zahlen mit Nachkommastellen Zeichenketten Felder (ein- und mehrdimensional) Objekte Der Datentyp für eine Variable wird nicht vom Programmierer festgelegt, sondern richtet sich nach dem Zusammenhang der Verwendung. Eine Variable kann ihren Datentypen innerhalb eines Programms wechseln. Dies bedeutet, dass in PHP keine Variablendeklaration stattfindet und eine Variable bei ihrem ersten Erscheinen sofort benutzt werden kann. 4.1 Regeln für Variablennamen Für die Namen von Variablen (und später auch Funktionen) gelten folgende Regeln: Sie müssen mit einem Dollarzeichen "$" beginnen. Sie dürfen keine Leerzeichen enthalten. Sie dürfen nur aus Buchstaben und Ziffern bestehen. Dabei muss das erste Zeichen ein Buchstabe sein. Es wird zwischen Groß- und Kleinschreibung unterschieden. Sie dürfen keine deutschen Umlaute oder sonstige Sonderzeichen enthalten. Als einziges Sonderzeichen ist der Unterstrich "_" erlaubt. Sie dürfen mit keinem reservierten Wort identisch sein. Es empfiehlt sich, selbst erklärende Namen zu vergeben. Dadurch wird nicht nur der Programmcode besser lesbar, sondern auch Fehler durch die Verwendung falscher Variablen weit gehend vermieden. 4.2 Zahlenvariablen und Rechenoperatoren Mit dem folgenden Programm soll der Preis für eine Tankfüllung Benzin berechnet werden. Einführung in die Programmierung mit PHP und MySQL.doc Seite 9 von 54 <html> <body> <?php $liter = 14; $preis = 1.35; $zahlung = $liter * $preis; echo $zahlung; ?> </body> </html> Listing 4-1: PHP4-1.php Abbildung 4-1: PHP4-1.php Der Ablauf des Programms kann hier wie folgt beschrieben werden: Die zwei Variablen $liter und $preis werden eingeführt und ihnen werden Werte zugewiesen. Wie zu erkennen ist, handelt es sich im ersten Fall um eine Ganzzahl, im zweiten Fall um eine Zahl mit Nachkommastellen. Zu beachten ist, dass als Dezimaltrennzeichen ein Punkt zu verwenden ist. Die beiden Variablen werden multipliziert und das Ergebnis der dritten Variablen zugewiesen. Der Inhalt dieser dritten Variablen wird dann mit dem Befehl echo ausgegeben. Operator + * / % += -= Tabelle 1: Bedeutung Addition Subtraktion Multiplikation Division Modulo (Rest einer Ganzzahldivision) Kombinationsanweisung: $x += 3; erhöht den Wert von $x um 3 Kombinationsanweisung: $x -= 3; verringert den Wert von $x um 3 Rechenoperatoren Für Rechnungen können die üblichen arithmetischen Operatoren verwendet werden. Bei der Berechnung gelten die in der Mathematik üblichen Prioritäten. Also z.B. Punkt- vor Strichrechnung. Außerdem findet die Abarbeitung der Ausdrücke von links nach rechts statt. Die Reihenfolge kann allerdings mit dem Setzen von Klammern beeinflusst werden. Übung 4.1: Erweitern Sie das Beispielprogramm dahingehend, dass drei verschiedene Litermengen eingelesen werden und am Ende ein Gesamtpreis ausgegeben wird. Übung 4.2: Berechnen Sie in einem PHP-Programm den Bruttopreis eines Einkaufs. Es wurden drei Artikel gekauft. Die Nettopreise der Artikel betragen: 22,50 €, 12,30 € und 5,20 €. Der Bruttopreis berechnet sich bekanntlich aus dem Nettopreis zuzüglich 19% Umsatzsteuer. 4.3 Textvariablen Zeichenketten müssen in einfache oder doppelte Hochkommata eingeschlossen werden. Das Zeichen "." (Punkt) dient der Verkettung mehrerer Zeichenketten oder Zahlen miteinander. Dies kann auch für eine Kombination von Konstanten und Variablen genutzt werden. Auch hier kann die Kombinationsanweisung ".=" (Punkt + Gleich) zur Vergrößerung einer Zeichenkette eingesetzt werden. Falls die Zeichenketten HTML-Code enthalten, so gelangt dieser HTMLCode zur Ausführung. Seite 10 von 54 Einführung in die Programmierung mit PHP und MySQL.doc <html> <body> <?php $liter = 14; $preis = 1.35; $zahlung = $liter * $preis; $einheit1 = "Liter"; $einheit2 = "Euro"; $gesamt = "Tankfüllung: " . $liter . " " . $einheit1; $gesamt.= " kosten " . $zahlung . " " . $einheit2 . "<br>"; echo $gesamt; echo "Tankfüllung: $liter $einheit1 kosten $zahlung $einheit2<br>"; echo 'Tankfüllung: $liter $einheit1 kosten $zahlung $einheit2<br>'; ?> </body> </html> Listing 4-2: PHP4-2.php Das Programm ist eine Erweiterung der Preisberechnung des letzten Kapitels. Der Variablen $gesamt wird eine Zeichenkette zugewiesenen, die sich aus Text- und Zahlenvariablen zusammensetzt. In einer zweiten Zeile wird diese Variable verlängert. Die Textausgabe erfolgt auf drei Arten: 1. Ausgabe der Variablen. 2. Direkte Ausgabe der zusammengesetzten Zeichenkette in doppelten Hochkommata. 3. Direkte Ausgabe der zusammengesetzten Zeichenkette in einfachen Hochkommata. Abbildung 4-2: PHP4-2.php Nach der Ausführung im Browser ist zu erkennen, dass die zweite Variante in der Ausgabe der ersten entspricht. In der dritten Variante werden statt der Werte die Namen der Variablen ausgegeben. Dies ist normalerweise nicht erwünscht. Einführung in die Programmierung mit PHP und MySQL.doc 5 Seite 11 von 54 Formularauswertung Bisher hatte der Benutzer eines Programms noch keine Möglichkeit, eigene Eingaben vorzunehmen. Alle Werte mussten direkt in das Programm eingetragen sein, er konnte nur das Ergebnis betrachten. PHP kommt jedoch typischerweise gerade bei der Auswertung von Benutzereingaben aus Formularen zum Einsatz. Hierdurch wird die dynamische Informationsübermittlung zwischen Benutzer und Webserver überhaupt erst ermöglicht. Der Ablauf ist dabei typischerweise wie folgt: Zunächst erhält der Nutzer ein Formular, in dem er eigene Einträge vornehmen kann beziehungsweise unter vorgefertigten Einträgen auswählt. Nach dem Ausfüllen sendet er das Formular ab und erhält nach der Auswertung die Antwort vom Webserver. In dieser einführenden Betrachtung soll die Datenübertragung mithilfe von einzeiligen Text-Eingabefeldern ermöglicht werden. Grundsätzlich können aber auch in PHP alle Formularelemente3 verwendet werden. 5.1 Eingabenformular Der HTML-Programmcode des Formulars: <html> <body> Bitte tragen Sie Ihren Vornamen und Ihren Nachnamen ein.<br> Senden Sie anschließend das Formular ab.<br> <form action = "PHP5-1.php" method = "post"> <input type = "text" name = "vorname"> Vorname <br> <input type = "text" name = "nachname"> Nachname <br> <input type = "submit"> <input type = "reset"> </form> </body> </html> Listing 5-1: PHP5-1.htm Innerhalb des HTML-Dokuments befindet sich ein form-Container. Dieser beinhaltet in seinem Start-Tag: das Attribut action, das auf eine Datei mit dem PHP-Auswertungsprogramm verweist. Hier: PHP5-1.php. Das Attribut method, welches die Übertragungsmethode zum Webserver kennzeichnet (hier: post). Innerhalb des form-Containers befinden sich die verschiedenen Formularelemente. 3 Zum Einsatz von Formularelementen in HTML siehe z.B. de/selfhtml.org Seite 12 von 54 Einführung in die Programmierung mit PHP und MySQL.doc Abbildung 5-1: PHP5-1.htm Erweitern Sie das Beispiel so, dass eine ganze Adresse eingegeben werden kann. Zusätzlich sollen also Eingabefelder für Straße, Hausnummer, Postleitzahl und Ort im Formular vorhanden sein. Übung 5.1: 5.2 Auswertung mit $_POST Das Programm PHP5-1.htm verweist auf das Programm PHP5-1.php. Dieses auswertende PHPProgramm sieht wie folgt aus: <html> <body> <?php echo "Guten Tag, " . $_POST["vorname"] . " " . $_POST["nachname"]; ?> </body> </html> Listing 5-2: PHP5-1.php Die Ergebnisausgabe könnte dann so aussehen: Es gibt in PHP einige vordefinierte Variablen, darunter das assoziative Feld4 $_POST. Die Namen der Formular-Eingabefelder werden automatisch zu Elementen dieses Feldes umgewandelt, sofern die Übertragungsmethode post verwendet wurde. Assoziative Felder sind Arrays, deren Elemente mit einem Namen angesprochen werden können. Aus dem Text-Eingabefeld vorname wird somit der Wert $_POST["vorname"] des Feldes. 4 vergleiche hierzu Kapitel 7.2 Abbildung 5-2: PHP5-1.php Einführung in die Programmierung mit PHP und MySQL.doc Seite 13 von 54 Feld-Elemente dürfen allerdings nicht unmittelbar innerhalb einer Zeichenkette ausgegeben werden, wie dies bei einzelnen Variablen erlaubt ist. Hierdurch gerät die Ausgabezeile etwas umfangreicher. Statt mit der Methode post kann ein Formular auch mit der Methode get5 verwendet werden. In diesem Fall ersetzt das Feld $_GET das Feld $_POST. Im Allgemeinen ist aber die Methode post zu bevorzugen, da sie sicherer und universeller ist. Erweitern Sie die Ergebnisdatei derart, dass sie alle Eingabefelder aus Übung 5.1 wiedergeben kann. Die Ausgabe im Browser soll wie folgt aussehen: Übung 5.2: Abbildung 5-3: Uebung5-2.php 5.3 Umwandlung von Zeichenketten in Zahlen Ein Texteingabefeld eines Formulars gibt immer eine Zeichenkette aus. Auch an das PHPProgramm wird eine Zeichenkette übermittelt. Häufig soll jedoch Zahlen, zum Beispiel zur Ausführung von Berechnungen, ermittelt werden. Dazu sind die folgenden Regeln zu beachten. Entscheidend für die Konvertierung einer Zeichenkette ist ihr Beginn. Beginnt die Zeichenkette mit gültigen numerischen Zeichen, so werden diese genutzt. Andernfalls ergibt sich der Wert 0. Gültige Elemente von numerischen Zeichen sind: ein Vorzeichen (optional) Ziffern ein Dezimalpunkt (optional) ein Exponent (optional). Der Exponent ist ein kleines "e" oder ein großes "E", gefolgt von einer oder mehreren Ziffern. Die Zeichenkette wird wie folgt interpretiert: als ganze Zahl, falls sie nur aus Ziffern bestellt als Zahl mit Nachkommastellen, falls sie neben den Ziffern die Zeichen "." (Punkt), "e" oder "E" beinhaltet. 5 Daten werden bei der Methode „get“ in der URI übertragen (www.meineseite.de?daten=keine) während die Methode „post“ diese im Body der Nachricht (also unsichtbar) überträgt. Seite 14 von 54 Einführung in die Programmierung mit PHP und MySQL.doc Beispiele Zeichenkette "234 "234xy" "xy234" "23.4" "23.4xy" "xy23.4" "-234" "23e4" "23e-4" Tabelle 2: Wert 234 234 0 23.4 23.4 0 -234 230000 0.0023 Datentyp ganze Zahl ganze Zahl Zeichenkette Zahl mit Nachkommastellen Zahl mit Nachkommastellen Zeichenkette ganze Zahl Zahl mit (möglichen) Nachkommastellen Zahl mit Nachkommastellen Umwandlung von Zeichenketten in Zahlen Sofern man sichergehen möchte, dass eine Variable als Zahlenwert interpretiert wird, kann man die Umwandlung explizit vornehmen. Hierzu dienen die beiden Funktionen doubleval() und intval(). Die erste der beiden Funktionen wandelt dabei immer in eine Zahl mit Nachkommastellen um, die zweite erzeugt eine ganze Zahl. In Anlehnung an C ist auch eine Typumwandlung als sogenannte Cast-Operation möglich. Dabei gibt man den gewünschten Datentyp in Klammern vor der umzuwandelnden Variablen an. Die Anweisung $zahl = (int) $zahl wandelt die Variable $zahl z.B. in eine ganze Zahl um. Folgende Umwandlungen sind möglich: (int), (integer) Integer, Ganzzahl (bool), (boolean) Boolean, Wahrheitswert (float), (double), (real) float, Kommazahl (string) string, Zeichenkette (array) Array (object) Objekt Dabei sind nicht alle Umwandlungsmöglichkeiten sinnvoll. In den Beispielen dieses Skripts werden Eingabefehler des Benutzers nicht immer abgefangen. In realen Programmen sollte man aber soweit wie möglich Routinen einbauen, die Eingabefehler erkennen. Grundsätzlich gilt aber immer der Grundsatz: Kein Programm ist vollständig gegen Eingabefehler geschützt. Erstellen Sie ein Formular, in dem der Benutzer aufgefordert wird, zwei Zahlen einzugeben und das Formular abzusenden. Erstellen Sie ein PHP-Programm, das die Summe dieser beiden Zahlen ermittelt und das Ergebnis ausgibt. Übung 5.3: Einführung in die Programmierung mit PHP und MySQL.doc 6 Seite 15 von 54 Kontrollstrukturen Bisher wurden alle Dateien rein sequenziell abgearbeitet. Auch ein PHP-Programm muss aber in der Lage sein, ebenso wie andere Programme, auf unterschiedliche Bedingungen zu reagieren. Bei den Kontrollstrukturen unterscheiden wir zwischen Verzweigungen und Schleifen. Da die grundsätzliche Funktion sich nicht von der in anderen Programmiersprachen unterscheidet, werden wir uns hier im Wesentlichen auf die Syntax innerhalb eines PHP-Programms beschränken. 6.1 6.1.1 Operatoren Vergleichsoperatoren Ob eine Anweisung ausgeführt wird oder nicht, wird mithilfe von Wahrheitswerten entschieden. Diese sind das Ergebnis von Vergleichsoperationen. Die Vergleichsoperatoren entsprechen denen anderer Programmiersprachen wie C++ oder Java und sind in der folgenden Tabelle dargestellt. Operator == != > < >= <= Tabelle 3: 6.1.2 Bedeutung Gleichheit Ungleichheit größer als kleiner als größer oder gleich kleiner oder gleich gilt für Zahlen und Zeichenketten für Zahlen und Zeichenketten nur für Zahlen nur für Zahlen nur für Zahlen nur für Zahlen Vergleichsoperatoren logische Operatoren Es kommt häufig vor, dass mehrere Bedingungen miteinander verknüpft werden müssen. Hierzu benötigt man die logischen Operatoren. 6.1.2.1 Logisches ODER Das logische ODER (Zeichen || oder OR) wird verwendet, wenn nur eine von mehreren Bedingungen zutreffen muss. Dabei ist unbedingt zu beachten, dass jede der beiden einzelnen Bedingungen vollständig formuliert wird. Die Aussage pw == "bingo" || pw == "cool" ergibt wahr, wenn in die Variable pw entweder der Wert "bingo" oder der Wert "cool" eingegeben wurde. 6.1.2.2 Logisches UND Das logische UND (Zeichen && oder AND) wird verwendet, wenn alle Bedingungen zutreffen müssen. Die Aussage bn == "bingo" && pw = "cool" Seite 16 von 54 Einführung in die Programmierung mit PHP und MySQL.doc ergibt wahr, wenn die Variable bn den Wert "bingo" und die Variable pw den Wert "cool" enthält. 6.1.2.3 Logisches NICHT Mithilfe des Operators NICHT (Zeichen !) kann man den Wahrheitswert von Bedingungen umkehren. Dies ist insbesondere bei komplexeren logischen Verknüpfungen hilfreich. Die folgende Aussage ergibt in jedem Fall den Rückgabewert wahr: pw == "bingo" || !(pw == "bingo") 6.1.3 Rangordnung der Operatoren Ausdrücke mit mehreren Operatoren werden von links nach rechts unter Beachtung der Rangordnung aufgelöst. Im Folgenden eine Tabelle der Rangordnung der bisher beschriebenen Operatoren. Es wird mit der höchsten Stelle der Rangordnung begonnen. Operatoren () !, *, /, % +< , <= , > , >= ==, != && AND || OR = Tabelle 4: Bedeutung Klammern logisches NICHT; negatives Vorzeichen Multiplikation; Division; Modulo Addition; Subtraktion kleiner; kleiner oder gleich; größer; größer oder gleich gleich; ungleich logisches UND logisches ODER Zuweisung Rangordnung der Operatoren Klammern stehen an der höchsten Stelle der Rangordnung. Mit ihrer Hilfe kann also die Rangfolge der Operatoren beeinflusst werden. Im Zweifelsfall ist es empfehlenswert, zusätzliche Klammern zu verwenden. 6.2 Verzweigungen In Verzweigungen wird die Ausführung einer Anweisung von einer oder mehreren Bedingungen abhängig gemacht. Je nachdem, ob die Bedingung zutrifft, werden die entsprechenden Anweisungen ausgeführt oder nicht. Wie die meisten modernen Programmiersprachen kennt PHP einseitige, zweiseitige oder mehrseitige Auswahlbedingungen. 6.2.1 Einseitige Auswahl Die einfache if-Anweisung wird hier als bekannt vorausgesetzt und deshalb nur mit einem kleinen Beispiel vorgestellt. Einführung in die Programmierung mit PHP und MySQL.doc Seite 17 von 54 <html> <body> <?php $preis = 0.98; if ($preis < 1) echo "Der Preis liegt unter 1 Euro"; ?> </body> </html> Listing 6-1: PHP6-1.php Das Beispiel zeigt zwei Besonderheiten: Die Bedingung muss in Klammern stehen. Eine einfache Anweisung kann unmittelbar auf die Bedingung folgen. Sollen allerdings aufgrund einer Bedingung mehrere Anweisungen ausgeführt werden, so müssen diese innerhalb geschweifter Klammern stehen. Dieses nennt man einen Anweisungsblock. Das folgende Beispiel ist um eine zweite Ausgabe erweitert. <html> <body> <?php $preis = 0.98; if ($preis < 1){ echo "Der Preis liegt unter 1 Euro<br>"; echo "Das ist günstig"; } ?> </body> </html> Listing 6-2: PHP6.2.php Grundsätzlich können die geschweiften Klammern des Anweisungsblocks in einer eigenen Zeile oder mit in der Anweisungszeile stehen. Das obige Beispiel (öffnende Klammer in der Bedingungszeile, schließende Klammer in einer eigenen Zeile) verbindet die Vorteile von Übersichtlichkeit und Platzeinsparung. Wichtig ist dabei zur Übersichtlichkeit die Einrückung des Anweisungsblocks. Erstellen Sie ein Eingabeformular, in das sich der Preis eingeben lässt und werten sie diese Eingabe mit dem obigen Programm (in leicht angepasster Form) aus. Achtung: Bei einem Preis über 1 € erfolgt keine Ausgabe. Übung 6.1: Seite 18 von 54 6.2.2 Einführung in die Programmierung mit PHP und MySQL.doc Zweiseitige Auswahl Die if-else-Anweisung sieht ganz entsprechend aus: <html> <body> <?php $preis = 1.02; if ($preis < 1){ echo "Der Preis liegt unter 1 Euro<br>"; echo "Das ist günstig"; }else{ echo "Der Preis liegt bei 1 Euro oder darüber<br>"; echo "Langsam wird es teuer"; } ?> </body> </html> Listing 6-3: PHP6-3.php Übung 6.2: Verändern Sie das Eingabeformular aus Übung 6.2Übung 6.1, so dass eine zweiseitige Ergebnisauswahl stattfindet. Erstellen Sie ein Formular, indem der Benutzer ein Zugangs Passwort eintragen soll (ausnahmsweise in sichtbarer Form). Das PHP-Programm vergleicht die Eingabe mit dem gespeicherten Passwort und reagiert entsprechend mit der Ausgabe "Zugang gestattet" oder "Zugang verweigert". Übung 6.3: Übung 6.4: Erweitern Sie Übung 6.3 für drei Passwörter. Übung 6.5: Erweitern Sie Übung 6.4, so dass auch ein zugehöriger Nutzername mitgeprüft wird. 6.2.3 Geschachtelte Auswahl Die if-else-Verzweigung lässt sich auch schachteln, so dass mehr als zwei Fälle unterschieden werden können. <html> <body> <?php $preis = 1.12; if ($preis < 1){ echo "Der Preis liegt unter 1 Euro<br>"; echo "Das ist günstig"; }else{ if ($preis < 1.2){ echo "Der Preis liegt zwischen 1 Euro und 1.20 Euro<br>"; echo "Langsam wird es teuer"; }else{ echo "Der Preis liegt über 1.20 Euro<br>"; echo "Das ist viel zu teuer"; } } ?> </body> </html> Listing 6-4: PHP6-4.php Einführung in die Programmierung mit PHP und MySQL.doc Seite 19 von 54 Es soll der Preis für eine Tankfüllung berechnet werden. Drei Benzinsorten sind zu berücksichtigen: Normal (Preis: 1,50 €), Super (Preis: 1,80 €) und Diesel (Preis: 1,35 €). Im Eingabenformular soll in das erste Eingabefeld die getankte Literzahl und in das zweite Eingabefeld entweder ein N, ein S oder ein D eingegeben werden. Das PHP-Programm ermittelt abhängig von der Sorte und der Menge den zu zahlenden Betrag. Eine Prüfung auf Fehleingaben findet nicht statt. Übung 6.6: 6.2.4 Mehrfachauswahl Die Mehrfachauswahl mit switch-case bietet für einen bestimmten Typ von Verzweigungen eine andere Schreibweise. Sie kann eingesetzt werden, wenn die gleiche Variable auf mehrere feste Werte geprüft werden soll. Diese Form ist bei vielen verschiedenen Prüfungen übersichtlicher als die geschachtelte Verzweigung. Sie bietet aber keine Möglichkeit für eine Bereichsprüfung. Übung 6.6 sieht mit switch-case wie folgt aus : <html> <body> <?php switch ($_POST ["sorte"]){ case "N": $zahlung = $_POST ["liter"] * 1.50; echo $_POST ["liter"] . " L Normal kosten $zahlung Euro"; break; case "S": $zahlung = $_POST ["liter"] * 1.80; echo $_POST ["liter"] . " L Super kosten $zahlung Euro"; break; case "D": $zahlung = $_POST ["liter"] * 1.35; echo $_POST ["liter"] . " L Diesel kosten $zahlung Euro"; break; default: echo "Als Sorte dürfen nur N, S oder D eingegeben werden!"; } ?> </body> </html> Listing 6-5: PHP6-5.php Es wird ein so genannter switch-Block erzeugt. Innerhalb dieses switch-Blockes wird der Wert von $_POST["sorte"] untersucht. Die vorhandenen Fälle werden der Reihe nach verglichen. Sobald einer der Fälle zutrifft, werden die weiteren Anweisungen bearbeitet, bis man auf die Anweisung break trifft. Die Anweisungen nach dem break werden nicht mehr ausgeführt. Die optionale Anweisung default ist dann nützlich, wenn keiner der genannten Fälle zutraf. Im oben angegebenen Programm wird sie als Fehlerkontrolle genutzt, falls der Benutzer keine erlaubte Sorte eingegeben hat. Übung 6.7: Erweitern Sie das Formular aus Übung 5.3 um ein Auswahlfeld zur Auswahl einer der Rechenoperationen +, -, *, /. Erstellen Sie ein PHP-Programm, das die jeweils gewählte Rechenoperation durchführt und das Ergebnis ausgibt. Seite 20 von 54 6.2.5 Einführung in die Programmierung mit PHP und MySQL.doc HTML in Verzweigungsblöcken Falls innerhalb einer einfachen oder mehrfachen Verzweigung jeweils nur reiner HTML-Code ohne PHP-Variablen ausgegeben werden soll, ist eine gemischte Schreibweise von PHP und HTML möglicherweise einfacher. Das folgende Beispiel zeigt, wie eine Verzweigung auf mehrere PHP-Blöcke verteilt werden kann. Dazwischen steht reiner HTML-Code ohne echo, Anführungszeichen, Semikolon usw. Zu beachten ist aber, dass nach der Bedingung if ($preis < 1) und der else-Anweisung ein Doppelpunkt notiert werden muss. Dieser zeigt an, dass die Verzweigung noch nicht abgeschlossen ist. Der folgende HTML-Code wird dann nur ausgeführt, wenn die Bedingung zutrifft. Ansonsten wird mit dem nächsten Teil der Verzweigung (nächster PHP-Block) fortgesetzt. Bei dieser Variante muss der Bedingungsblock am Ende explizit mit einem endif abgeschlossen werden. <html> <body> <?php $preis = 1.12; if ($preis < 1): ?> Der Preis liegt unter 1 Euro<br> Das ist günstig <?php else: ?> Der Preis liegt bei 1 Euro oder darüber<br> Langsam wird es teuer <?php endif; ?> </body> </html> Listing 6-6: PHP6-6.php Einführung in die Programmierung mit PHP und MySQL.doc 6.3 Seite 21 von 54 Schleifen Schleifen werden verwendet, um Anweisungen in Abhängigkeit der Laufzeitbedingungen wiederholen zu können. PHP kennt die for- und die while-Schleife. Schleifen bergen immer die Gefahr in sich, einen Zustand zu erreichen, in dem sie nicht mehr angebrochen werden können. Man spricht dann von Endlosschleifen. Endlosschleifen machen sich durch Programmabstürze bemerkbar. Das Programm "hängt sich auf" und muss von außen (über das Betriebssystem) mit der Gefahr von Datenverlust beendet werden. 6.3.1 for-Schleife Die for-Schleife wird benutzt, um eine feste Anzahl von Wiederholungen zu erzeugen (Zählschleife). In seltenen Fällen ist sie Zahl der Wiederholungen schon während der Programmierung bekannt und kann als Konstante eingetragen werden. In den meisten Fällen ergibt sich die Anzahl während des Programmlaufs und wird dann als Variablenwert eingebracht. <html> <body> <?php for ($i=1; $i<=5; $i++){ echo "Zeile $i <br>"; } ?> </body> </html> Listing 6-7: PHP6-7.php Die for-Schleife besteht aus Kopf und Rumpf. Der Rumpf beinhaltet die Anweisungen zwischen den geschweiften Klammern. Der Kopf besteht aus drei Teilen Startwert der Schleifenvariablen: Mit diesem Wert erfolgt der erste Durchlauf. Bedingung zur Wiederholung: Die Schleife wird solange wiederholt, wie diese Bedingung wahr ergibt. Veränderung der Schleifenvariablen: Die Veränderung kann wie im Beispiel in Kurzform oder ausführlich ($i = $i + 2) notiert werden. Fehler können entstehen, wenn: Der Startwert der Schleifenvariablen die Prüfbedingung nicht erfüllt. In diesem Fall wird die Schleife nicht durchlaufen. Sofern in ihr für den weiteren Programmablauf wichtige Einstellungen vorgenommen werden, fehlen diese; im Programmablauf können Fehler auftreten. Die Bedingung dauerhaft erfüllt ist (Endlosschleife), die Veränderung der Schleifenvariablen also zu keinem ungültigen Wert führt. Dies ist in folgendem Beispiel des Schleifenkopfes der Fall: for ($i=3; $i>2; $i++). Die Bedingung ergibt für jeden erzeugten Wert von $i wahr. Seite 22 von 54 Einführung in die Programmierung mit PHP und MySQL.doc Übung 6.8: Schreiben Sie ein Programm, dass mit Hilfe mehrerer for-Schleifen die nachfolgenden Zeilen ausgibt. Die letzte Zahlenreihe benötigt eine zusätzliche if-Prüfung. 13 17 21 25 29 2 1.5 1 0.5 0 -0.5 -1 2000 3000 4000 5000 6000 Z5 Z7 Z9 Z11 Z13 ab1 ab2 ab3 c2 c3 c12 c13 c22 c23 13 17 21 33 37 41 45 Schleifen können sich auch innerhalb von Schleifen befinden. Man spricht dann von geschachtelten Schleifen. Dies ermöglicht die Bearbeitung von mehrdimensionalen Strukturen, die z.B. im Umgang mit Datenbankinhalten auftreten. <html> <body> <?php for ($i=1; $i<=5; $i++){ for ($j=1; $j<=3; $j++){ echo "Zeile $i / Spalte $j - "; } echo "<br>"; } ?> </body> </html> Listing 6-8: PHP6-8.php Übung 6.9: Schreiben Sie mit Hilfe von zwei geschachtelten for-Schleifen ein Programm, dass das kleine Einmaleins ausgibt. Schleifen werden häufig im Zusammenhang mit Tabellen eingesetzt. Gerade im Zusammenhang mit Datenbanken wird dadurch die Ausgabe wesentlich lesbarer. Die einfache for-Schleife vom Beginn dieses Kapitels ließe sich dann wie folgt darstellen. In dem folgenden Beispiel werden Tabellenbeginn und -ende als HTML-Code im PHP-Bereich angegeben. Hierbei ist zu beachten, dass die Zellausrichtung (align = 'right') innerhalb der Zeichenkette (also zwischen doppelten Hochkommata / Anführungszeichen) in einfachen Hochkommata angegeben werden muss, da andernfalls für PHP die Zeichenkette zu früh beendet wäre. <html> <body> <table> <?php for ($i=1; $i<=5; $i++){ echo "<tr><td>Zeile</td><td align='right'>$i</td></tr>"; } ?> </table> </body> </html> Listing 6-9: PHP6-9.php Einführung in die Programmierung mit PHP und MySQL.doc Seite 23 von 54 Die geschachtelte Schleife könnte wie folgt in eine Tabelle ausgegeben werden: <html> <body> <table> <?php for ($i=1; $i<=5; $i++){ echo "<tr>"; for ($j=1; $j<=3; $j++){ echo "<td align='right'>Zeile $i / Spalte $j</td>"; } echo "</tr>"; } ?> </table> </body> </html> Listing 6-10: PHP6-10.php Übung 6.10: Geben 6.3.2 Sie das Einmaleins aus Übung 6.9 in eine Tabelle aus. while-Schleife Mit der while-Schleife lässt sich eine unbestimmte Anzahl von Wiederholungen erzeugen. Das Ende der Wiederholungen wird durch eine Bedingung während der Schleifendurchläufe erreicht und ist nicht vorhersehbar. while-Schleifen werden häufig bei Datenbankabfragen eingesetzt, da die Anzahl der einzulesenden Elemente vorher meist nicht bekannt ist. Da die Bedingung in der while-Schleife am Anfang geprüft wird, nennt man diese Art kopfgesteuert. Im folgenden Beispiel sollen die "zufälligen" Würfelergebnisse eines Zufallsgenerators solange addiert werden, bis der Würfel eine sechs zeigt. Auf die notwendigen mathematischen Funktionen soll hier nur kurz eingegangen werden. Die Funktionen srand() initialisiert den Zufallsgenerator, damit er tatsächlich zufällige Ergebnisse produziert. Die Funktionen rand() erzeugt damit jeweils ein Würfelergebnis in den angegebenen Grenzen. Die Anzahl der Würfe sind sowohl dem Entwickler also dem Benutzer unbekannt, daher kann keine for-Schleife verwendet werden. <html> <body> <?php /* Initialisierung */ srand((double)microtime()*1000000); $summe = 0; $zaehler = 0; $zufallszahl = 0; while ($zufallszahl != 6){ $zaehler += 1; $zufallszahl = rand(1,6); //Würfel $summe = $summe + $zufallszahl; echo "Gewürfelt wurde $zufallszahl<br>"; } echo "Insgesamt $zaehler Würfe, Gesamtsumme $summe<br>"; ?> </body> </html> Listing 6-11: PHP6-11.php Seite 24 von 54 Einführung in die Programmierung mit PHP und MySQL.doc Neben dem Zufallsgenerator werden in der Initialisierung noch die Variablen $summe, $zaehler und $zufallszahl auf 0 initialisiert. Die while-Schleife prüft in ihrem Kopf, ob die Zufallszahl ungleich sechs ist. Dies ist zu Beginn der Fall, so dass der Schleifenkörper durchlaufen wird. Als erstes wird der Zähler um eins hoch gezählt. In ihm findet sich also die gesamte Zahl der bisherigen Würfel wieder. Nach der Erzeugung der Zufallszahl wird diese zu der Gesamtsumme hinzu addiert. Danach wird die Zufallszahl ausgegeben. Nach dem Wurf einer sechs wird die Schleife abgebrochen, das Programm gibt dann die gesamte Zahl der Würfe sowie die gesamte Summe aus. Zwei Spieler würfeln abwechselnd gegeneinander (Zufallsgenerator). Die Würfel jedes Spielers werden addiert. Sobald einer der beiden Spieler eine Sechs wirft, ist das Spiel zu Ende. Übung 6.11: Mithilfe der Anweisung break, die bereits aus der switch-case-Verzweigung bekannt ist, kann eine Schleife vorzeitig beendet werden. Diese zusätzliche Möglichkeit einer Schleifensteuerung kann ein Programm unter Umständen besser lesbar machen. Eine breakAnweisung innerhalb einer Schleife wird immer gemeinsam mit einer Bedingung auftreten, da der vorzeitige Schleifenabbruch nur in einem Sonderfall erfolgen sollte. Ähnlich der break-Anweisung kann die Anweisung continue dazu verwendet werden, aufgrund einer Bedingung den Rest einer Schleife zu überspringen und unmittelbar mit dem nächsten Schleifendurchlauf fortzusetzen. Im Folgenden wird das obige Beispiel dahingehend erweitert, dass eine maximale Zahl von neun Würfen gestattet ist, bevor ein Abbruch erfolgt. Dieser Abbruch erfolgt mit Hilfe der breakAnweisung. <html> <body> <?php /* Initialisierung */ srand((double)microtime()*1000000); $summe = 0; $zaehler = 0; $zufallszahl = 0; while ($zufallszahl != 6){ $zaehler += 1; $zufallszahl = rand(1,6); //Würfel $summe = $summe + $zufallszahl; echo "Gewürfelt wurde $zufallszahl<br>"; if ($zaehler >= 9) break; //Sonderfall } echo "Insgesamt $zaehler Würfe, Gesamtsumme $summe<br>"; ?> </body> </html> Listing 6-12: PHP6-12.php Die Übung 6.11 soll wie folgt erweitert werden: Das Spiel ist beendet, wenn einer der beiden Spieler 30 Punkte erreicht hat. Übung 6.12: Einführung in die Programmierung mit PHP und MySQL.doc 6.3.3 Seite 25 von 54 do-while-Schleife Die do-while-Schleife verhält sich wie eine while-Schleife. Es gilt aber, dass sie mindestens einmal durchlaufen wird, da die Bedingung für die Wiederholung erst am Ende geprüft wird. Eine solche Schleife nennt man fußgesteuert. Die Syntax lautet: do { [Anweisungen] } while (Bedingung); 6.3.4 foreach-Schleife Die foreach-Schleife wird im Zusammenhang mit assoziativen Feldern6 verwendet. Sie ermöglicht die Bearbeitung aller Elemente eines solches Feldes mit einer Schleife. Die Syntax lautet: foreach ([Feldausdruck]) { [Anweisungen] } 6 Zur Verwendung der foreach-Schleife siehe Kapitel 7.2 Seite 26 von 54 7 Einführung in die Programmierung mit PHP und MySQL.doc Felder Um eine größere Anzahl zusammengehöriger Daten zu speichern, sind einzelne Variablen (jeweils mit einem eigenen Namen) ungeeignet. Zum einen müsste jede Variable über ihren eigenen Namen angesprochen werden, zum anderen müsste eine ausreichend große Zahl von Variablen vorgehalten werden, damit alle Daten abgespeichert werden könnten. Feldvariablen (Arrays) sind hier weitaus flexibler und komfortabler. PHP unterstützt zwei Arten von Feldvariablen: Numerisch indizierte Felder. Hier werden die einzelnen Variablen des Feldes über eine laufende Nummer angesprochen. Assoziative Felder (auch Hash-Tabelle genannt). Jede Variable in dem assoziativen Feld wird über eine eindeutige Bezeichnung angesprochen. Diese Form erhält ihre Bedeutung insbesondere in mehrdimensionalen Feldern beim Einlesen von Datenbankinhalten. Felder können eine oder mehrere Dimensionen haben: Ein eindimensionales Feld kann man sich einfach als eine Liste oder einen mathematischen Vektor vorstellen. Beispiele wären eine Liste von Messwerten oder eine Zufallszahlenreihe. Ein zweidimensionales Feld kann man sich wie eine (Datenbank-)Tabelle vorstellen. In der einen Dimension wären dann die Feldnamen (Attributnamen), in der anderen die Datensätze. Zweidimensionale Felder können rein numerisch indiziert, rein assoziativ oder gemischt (dies ist der häufigste Fall bei Datenbanktabellen) auftreten. Mehrdimensionale Felder können in reiner Form oder in beliebiger Mischform auftreten. Mit steigender Zahl der Dimensionen wird allerdings die Modellvorstellung sowie die Handhabung immer schwieriger. 7.1 Numerisch indizierte Felder Als Beispiel für ein numerisch indiziertes Feld sollen hier die Temperaturen einer Messstation dienen. Dabei sollen über eine Woche täglich die Temperaturen gemessen, abgespeichert und am Ende ausgegeben werden. <html> <body> <?php $tp = array(17.5, 19.2, 21.8, 21.6, 17.5); $tp[5] = 20.2; $tp[] = 16.6; for ($i=0; $i<=6; $i++){ echo „$tp[$i] <br>“; } ?> </body> </html> Listing 7-1: PHP7-1.php Einführung in die Programmierung mit PHP und MySQL.doc Seite 27 von 54 Das Programm zeigt drei Techniken zur Erzeugung bzw. Vergrößerung von Feldern: Mit Hilfe der Funktion array() wird die Variable $tp zu einem Feld mit hier fünf Elementen. Diese Elemente werden automatisch durchnummeriert. Das erste Element in numerischen Feldern ist immer das Element 0. Felder können auch durch direkte Zuweisung einzelner Elemente erzeugt oder vergrößert werden. Die Zuweisung $tp[5] = 20.2; erzeugt hier eine neues (sechstes) Element und weist diesem den Wert 20.2 zu. Eine Zuweisung an ein schon vorhandenes Element überschreibt den alten Wert. Ein leerer Schlüssel zwischen den eckigen Klammern erzeugt das nächste Element. Der Vorteil dieser Schreibweise ist, dass man die bisherige Anzahl der Elemente nicht wissen muss und nicht aus Versehen ein vorhandenes Element überschreibt. Ein einzelnes Feld wird angesprochen, indem man nach dem Namen des Feldes die laufende Nummer (Index) des Elementes in eckigen Klammern angibt. Dies geschieht im Beispiel durch eine for-Schleife. Es sollen die Vornamen und das Alter von sechs Personen gespeichert werden. Das erste Feld soll dabei die Namen, das zweite das Alter enthalten. Die Elemente der beiden Felder sollen paarweise untereinander als Tabelle ausgegeben werden. Übung 7.1: 7.2 Assoziative Felder Die Temperaturwerte sollen nun einem assoziativen Feld zugeordnet werden. Dabei sollen die Werte jeweils einem Wochentag zugeordnet sein. <html> <body> <?php $tp = array ("Montag"=>17.5, "Dienstag"=>19.2, "Mittwoch"=>21.8); $tp["Donnerstag"] = 21.6; $tp["Freitag"] = 17.5; $tp["Samstag"] = 20.2; $tp["Sonntag"] = 16.6; // Ein bestimmtes Element ausgeben echo $tp["Montag"] . "<br>"; // Alle Elemente in eine Tabelle ausgeben // Tabellenkopf echo "<table>"; echo "<tr><td><b>Wochentag</b></td>"; echo "<td><b>Temperatur</b></td></tr>"; // Namen und Werte ausgeben foreach ($tp as $name=>$wert){ echo "<tr><td>$name</td><td align='right'>$wert</td></tr>"; } echo "</table>"; Seite 28 von 54 Einführung in die Programmierung mit PHP und MySQL.doc //Nun die Durchschnittstemperatur $summe = 0; foreach ($tp as $wert){ $summe = $summe + $wert; } $durchschnitt = $summe / 7; echo "<br>Durchschnitttemperatur: $durchschnitt"; ?> </body> </html> Listing 7-2: PHP7-2.php Die Verwendung assoziativer Felder kann zunächst etwas unübersichtlich wirken. Die direkte Bezeichnung der Elemente kann aber durchaus Vorteile bieten. In diesem Beispiel sind zwei Techniken zur Felderzeugung dargestellt: Die Funktion array() erzeugt ein Feld mit drei Elementen Jedes Element hat eine eindeutige Schlüsselbezeichnung (Key) und einen zugehörigen Wert (Value). Der Key muss dabei zwischen doppelten Hochkommata geschrieben werden. Felder können auch durch Zuweisung einzelner Elemente erzeugt oder vergrößert werden. Dabei muss der Key innerhalb der eckigen Klammern ebenfalls in doppelten Hochkommata stehen. Da der Key immer in doppelten Hochkommata stehen muss, ist die Ausgabe des einzelnen Elements mit folgendem Zeilenumbruch nur durch eine Stringzusammensetzung möglich. Alle folgenden Vorgehensweisen führen zu einem Fehler oder einer falschen Ausgabe: o echo "$tp["Montag"]<br>"; beendet die Zeichenkette zu früh. o echo "$tp['Montag']<br>"; erkennt den Index nicht. o echo '$tp["Montag"]<br>'; liefert nur den Namen des Feldelements, nicht seinen Wert. In der ersten foreach-Schleife wird bei jedem Schleifendurchlauf ein Key-Value-Paar bereitgestellt. Die Bedeutung des Zeichens "=>" ist dabei dieselbe wie bei der Zuweisung. Es werden somit sowohl der Name als auch der Wert des aktuellen Elements bereitgestellt. Ohne das Zuordnungszeichen wird in der zweiten foreach-Schleife nur der Wert des Elements bereitgestellt. Dieser Wert wird hier zur Berechnung genutzt. Die Aufgabe der Übung 7.1 soll mit assoziativen Feldern gelöst werden. Dabei sollen die Vornamen die Keys, das Alter die Werte darstellen. Übung 7.2: Erweitern Sie Übung 7.2, indem Sie ein zweidimensionales Feld erzeugen. Dazu soll zunächst ein eindimensionales assoziatives Feld $personen mit den Keys "Vorname" und "Alter" erzeugt werden. Dieses Feld weisen Sie dann jeweils einem neuen Feld $freunde als numerisch indiziertes Feldelement zu, nachdem Sie mehrmals neue Werte eingegeben haben. Die Vorgehensweise dabei ist: $freunde[] = $personen; Experimentieren Sie mit der Ausgabe des Gesamtfeldes in geschachtelten foreach-Schleifen. Übung 7.3: Einführung in die Programmierung mit PHP und MySQL.doc 8 Seite 29 von 54 Datenbankverbindung PHP bietet komfortable Schnittstellen zu einer Reihe von Datenbanken. An dieser Stelle soll ausschließlich die Schnittstelle zu MySQL angesprochen werden. Dabei wird davon ausgegangen, dass die Erzeugung der Datenbank und der Strukturentwurf vorher mit einem der Hilfstools der Datenbank durchgeführt wurden. 8.1 Der erste Kontakt Das folgende Programm zeigt alle Datensätze der Tabelle personen aus der Datenbank phpuebungen an. (Hinweis: Die hier verwendete Datenbank kann von den Seiten der Jobelmann-Schule heruntergeladen werden. Aufruf: www.jobelmann-schule.de Reiter: Sonstiges - Dokumente. Dort dem folgenden Pfad folgen: Informationen zu einzelnen Schulfächern Informationstechnik/-verarbeitung / Informatik Programmierung HTML / PHP) <html> <body> <?php /* Verbindung zu MySQL aufnehmen */ mysql_connect ("", "phptest", "phptest"); /* Datenbank auswählen */ mysql_select_db("phpuebungen"); /* SQL-Abfrage ausführen */ $res = mysql_query ("select * from personen"); /* Anzahl der Datensätze ermitteln */ $num = mysql_num_rows ($res); echo "$num Datensätze gefunden<br>"; /* Datensätze auslesen */ while ($dsatz = mysql_fetch_array ($res)){ echo $dsatz["name"] . ", "; echo $dsatz["vorname"] . ", "; echo $dsatz["personalnummer"] . ", "; echo $dsatz["gehalt"] . ", "; echo $dsatz["geburtstag"] . "<br>"; } /* Datenbank schließen */ mysql_close(); ?> </body> </html> Listing 8-1: PHP8-1.php Seite 30 von 54 Einführung in die Programmierung mit PHP und MySQL.doc Erläuterung des Programms: Die Funktion mysql_connect() öffnet eine Verbindung zum Datenbankserver. Es können drei Parameter angegeben werden: Hostname, Benutzername, Kennwort. Hier ist die Standardeinstellung von XAMPP für den Host gewählt, also localhost und der Benutzer "phptest" mit dem Kennwort "phptest".. Mit der Funktion mysql_select_db() wird die Datenbank ausgewählt, mit der gearbeitet wird. Dies ist wichtig, da MySQL mehrere Datenbanken gleichzeitig enthalten kann. Die Funktion mysql_query() führt eine SELECT-Anfrage in der aktuellen Datenbank aus und liefert das Ergebnis in einem assoziativen Feld zurück. Der Parameter der Funktion enthält dabei die SQL-Abfrage in doppelten Hochkommata, allerdings ohne schließendes Semikolon. Das zweidimensionale Feld enthält die Attributwerte als Spalten (mysql_num_fields) und die Datensätze als Zeilen (mysql_num_rows). Wenn der letztgenannte Wert 0 ergibt, hat die Abfrage kein Ergebnis zurückgeliefert, die Ergebniskennung ist aber wahr. Nur wenn die SQL-Abfrage syntaktisch falsch oder die Kommunikation mit dem MySQL-Server gestört war, liefert die Funktion als Ergebnis falsch zurück. Das Ergebnis der Abfrage wird in einer Variablen ($res) als so genannte Ergebniskennung abgelegt. Mit ihrer Hilfe lassen sich die einzelnen Komponenten des Ergebnisses ermitteln. Bei mehreren Abfragen sollte für jedes Ergebnis eine eigene Variable zur Speicherung der Ergebniskennung verwendet werden. Die Funktion mysql_num_rows() liefert die Anzahl der Datensätze der als Parameter übergebenen Ergebnismenge zurück. Die Funktion mysql_fetch_array() liefert einen Datensatz der Ergebniskennung in einem assoziativen Feld zurück. Dabei stellt der Datenbank-Feldname (bzw. der angegebene Aliasname) den Schlüssel des Feldes dar. Nach dem Aufruf der Funktion wird der Datensatzzeiger automatisch auf den nächsten Datensatz der Ergebnismenge weitergesetzt. Wenn der Datensatzzeiger am Ende der Ergebnismenge steht, ergibt der nächste Aufruf von mysql_fetch_array() den Wert falsch. Dadurch wird die while-Schleife beendet. Das Ergebnis der Abfrage lässt sich durch weitere Funktionen ermitteln, die hier kurz mit ihren Unterschieden dargestellt werden sollen: o $dfeld = mysql_result ($res, $row, $col); liefert den direkten Zugriff auf jedes Element, wobei die Zeilen- und Spaltennummer als zusätzliche Parameter (beginnend mit 0!) übergeben werden müssen. Statt der Spaltennummer kann auch der Spaltenname angegeben werden. Diese Form stellt eine recht langsame Art des Zugriffs dar. o $dsatz = mysql_fetch_row ($res); liefert den Datensatz als numerisches Feld zurück. Der Zugriff erfolgt über $dsatz[$n]. o $dsatz = mysql_fetch_array ($res); liefert den Datensatz als assoziatives Feld zurück. Der Zugriff kann über die Spaltennummer oder den Spaltennamen erfolgen. Der Spaltenname muss dabei in korrekter Groß- und Kleinschreibung angegeben werden. o $dsatz = mysql_fetch_assoc ($res); liefert ebenfalls ein assoziatives Feld, allerdings ist hier ein Zugriff über die Spaltennummer nicht zulässig. o $dsatz = mysql_fetch_object ($res); liefert den Datensatz als Objekt zurück. Nach Ablauf des PHP-Programms wird die Verbindung zur Datenbank automatisch geschlossen. Dennoch ist es guter Programmierstil, dies explizit mit mysql_close() zu tun. Einführung in die Programmierung mit PHP und MySQL.doc Seite 31 von 54 Erstellen Sie in MySQL eine Datenbank und lassen Sie sich die Werte aller Tabellen durch ein PHP-Programm anzeigen. Übung 8.1: 8.2 Datensätze auswählen PHP bietet alle Möglichkeiten zur Auswahl und Manipulation von Datensätzen durch SQLAnweisungen. Dabei sind alle Varianten der Auswahl mit WHERE, Vergleichsoperatoren, logischen Operatoren, LIKE-Operator, ORDER BY, GROUP BY usw. möglich. Zu beachten ist dabei allerdings eine eventuelle Schachtelung von PHP-Stringausdrücken und eingelagerten SQLStrings. Hierbei sind die PHP-Ausdrücke in Anführungszeichen ("), die SQL-Werte in Hochkommata (') einzuschließen. Das folgende Beispiel wählt die Namen und das Gehalt aller Personen der Übungsdatenbank aus, die 1990 oder später geboren sind und sortiert diese absteigend nach Gehalt: <html> <body> <?php mysql_connect ("", "phptest", "phptest"); mysql_select_db("phpuebungen"); $abfrage = "SELECT name, vorname, gehalt FROM personen"; $abfrage .= " WHERE YEAR (geburtstag) >= '1990'"; $abfrage .= " ORDER BY gehalt DESC"; $res = mysql_query ($abfrage); while ($dsatz = mysql_fetch_array ($res)){ echo $dsatz["name"] . ", " . $dsatz["vorname"] . ", " . $dsatz["gehalt"] . "<br>"; } ?> </body> </html> Listing 8-2: PHP8-2.php Die SQL-Anweisung wurde aus Gründen der Übersichtlichkeit in mehreren Schritten zusammengesetzt und in einer PHP-Variablen ($abfrage) gespeichert. Besonders zu beachten (und fehlerträchtig) sind die Leerzeichen zwischen den Anweisungen (hier vor WHERE und ORDER BY), da fehlende Leerzeichen von SQL mit einer Fehlermeldung quittiert werden. Die Jahresangabe zum Abgleich des Geburtstages steht in einfachen Hochkommata ('1990'). SQL-Anweisungen können durch ihre Länge, eingebaute PHP-Variable oder Feldnamen schnell unübersichtlich und damit zur größten Fehlerquelle werden. Zur Prüfung einer fehlerfreien Anweisung kann man sich die Zeichenkette zunächst ausgeben lassen (echo $abfrage;). Zeigen Sie aus der obigen Tabelle alle Daten der Personen an, deren Nachnamen mit "M" beginnt. Übung 8.2: Zeigen Sie aus der obigen Tabelle den Vornamen, Nachnamen und Geburtstag aufsteigend nach dem Alter von allen Personen an, die mehr als € 3.000,00 verdienen. Übung 8.3: Seite 32 von 54 8.3 Einführung in die Programmierung mit PHP und MySQL.doc Ausgabe in eine HTML-Tabelle Bisher werden zwar alle Daten angezeigt, die Darstellung ist aber noch sehr unübersichtlich. Die Lesbarkeit der Ergebnisse ist wesentlich besser, wenn die Daten in einer Tabelle ausgegeben werden. Dazu müssen nur die HTML-Befehle zur Erzeugung einer Tabelle an geeigneter Stelle in das PHP-Programm integriert werden. Das folgende Programm gibt die Daten des letzten Kapitels in Tabellenform aus. <html> <body> <?php mysql_connect ("", "phptest", "phptest"); mysql_select_db("phpuebungen"); $abfrage = "SELECT name, vorname, gehalt FROM personen"; $abfrage .= " WHERE YEAR (geburtstag) >= '1990'"; $abfrage .= " ORDER BY gehalt DESC"; $res = mysql_query ($abfrage); // Beginn der Tabelle echo "<table border = '1'>"; // Tabellenüberschrift echo "<tr> <td>Name</td> <td>Vorname</td> <td>Gehalt></td> </tr>"; while ($dsatz = mysql_fetch_array ($res)){ // Ausgabe der Daten in die Tabellenfelder echo "<tr>"; // neue Tabellenzeile echo "<td>" . $dsatz["name"] . "</td>"; // erste Spalte echo "<td>" . $dsatz["vorname"] . "</td>"; // zweite Spalte echo "<td>" . $dsatz["gehalt"] . "</td>"; // dritte Spalte echo "</tr>"; // Ende der Zeile } // Tabellenende (nicht vergessen!) echo "</table>"; ?> </body> </html> Listing 8-3: PHP8-3.php Innerhalb der while-Schleife wird jeweils ein Datensatz in eine Tabellenzeile ausgegeben. Somit müssen auch die Tags <tr> und </tr> innerhalb der Schleife angeordnet werden. Die Tabelle selbst wird nur einmal erzeugt (vor der Schleife) ebenso, wie die Überschrift nur einmal in einer eigenen Tabellenzeile ausgegeben wird. Am Ende muss die Tabelle noch (nach dem vollständigen Durchlauf der Schleife) geschlossen werden. Übung 8.4: Geben Sie die Daten aus Übung 8.2 in einer Tabelle aus. Übung 8.5: Geben Sie die Daten aus Übung 8.3 in einer Tabelle aus. Einführung in die Programmierung mit PHP und MySQL.doc 8.4 Seite 33 von 54 Auswahl von Daten in einem Suchformular Normalerweise ist es nicht ausreichend, wenn der Programmentwickler statische Abfragen vorgibt. Sinn einer Datenbank ist es meist, dass der Benutzer eine Auswahl treffen kann, auf deren Grundlage ihm dann Daten angezeigt werden. Dies wird durch Eingabe oder Auswahl von Werten in Formularen ermöglicht. Der Ablauf einer typischen Datenbankanwendung stellt sich wie folgt dar: Der Benutzer trägt Daten in ein Eingabefeld ein oder wählt aus einem Auswahlfeld eingaben aus. Mit dem Klick auf den Sende-Button werden diese Daten an den Webserver übertragen. Auf dem Webserver werden die gesendeten Daten von einem PHP-Programm ausgewertet, in eine SQL-Anweisung eingebettet und an den Datenbankserver gesendet. Der Datenbankserver führt die SQL-Anweisung aus und liefert die Ergebnisdaten zurück an den Webserver. Das PHP-Programm verarbeitet die zurück gelieferten Daten und erstellt für den Benutzer eine entsprechende HTML-Seite. Während des gesamten Ablaufs ist es für den Benutzer nicht erkennbar, welche Programme für ihn im Hintergrund tätig sind. Auf seine Anfrage wird ihm eine nach außen "normale" HTMLSeite zurückgeliefert, ohne dass er Kenntnisse über Programmsprachen oder Datenbanken besitzen muss. Das folgende Beispiel baut auf den letzten Beispielabfragen auf. Nur soll das Geburtsjahr nicht mehr fest vorgegeben, sondern vom Benutzer eingegeben werden. Dazu erzeugen wir eine HTML-Seite zur Eingabe und ein PHP-Programm zur Ausgabe. Zunächst das Formular. Um uns eine Fehlerprüfung zu ersparen und unsinnige Eingaben zu vermeiden wollen wir hier zunächst nur eine Auswahl aus einer vorgegebenen Liste zulassen: <html> <body> Bitte wählen Sie das Start-Geburtsjahr aus <form action = "PHP8-4.php" method = "post"> <select name = "alter" size = "1"> <option>1970</option> <option>1975</option> <option>1980</option> <option>1985</option> <option>1990</option> <option>1995</option> <option>2000</option> </select> <br> <input type = "submit"> <input type = "reset"> </form> </body> </html> Listing 8-4: PHP8-4.htm Innerhalb des Formulars kann in einer Dropdown-Liste schrittweise eine Jahreszahl ausgewählt werden. Die einzelnen Werte werden innerhalb des select-Containers über das option-Tag angegeben. Die Höhe des select-Feldes (size = "1") entscheidet darüber, wie viele Werte im geschlossenen Feld angezeigt werden. Werte größer als eins führen also zu einer Auswahlliste. Seite 34 von 54 Einführung in die Programmierung mit PHP und MySQL.doc Der ausgewählte Wert steht dem aufgerufenen PHP-Programm nach dem Absenden zur Verfügung. Die Auswertung sieht dann wie folgt aus: <html> <body> <?php mysql_connect ("", "phptest", "phptest"); mysql_select_db("phpuebungen"); $abfrage = "SELECT name, vorname, gehalt FROM personen"; $abfrage .= " WHERE YEAR (geburtstag) >= " . $_POST["alter"]; $abfrage .= " ORDER BY gehalt DESC"; $res = mysql_query ($abfrage); $num = mysql_num_rows ($res); if ($num == 0) echo "Keine Datensätze gefunden"; echo "<table border = '1'>"; echo "<tr> <td>Name</td> <td>Vorname</td> <td>Gehalt></td> </tr>"; while ($dsatz = mysql_fetch_array ($res)){ // Ausgabe der Daten in die Tabellenfelder echo "<tr>"; echo "<td>" . $dsatz["name"] . "</td>"; echo "<td>" . $dsatz["vorname"] . "</td>"; echo "<td>" . $dsatz["gehalt"] . "</td>"; echo "</tr>"; } echo "</table>"; ?> </body> </html> Listing 8-5: PHP8-4.php Es ist also zu erkennen, dass der feste Jahreswert nun durch den Inhalt der Variablen $_POST["alter"] ersetzt wurde. Die Variable wurde also mit dem in select festgelegten Namen (name =) übergeben. Dieser darf hier in Anführungszeichen stehen, da er nicht innerhalb eines PHP-Strings verwendet wird! Hinzugekommen sind hier die Ermittlung der Anzahl zurück gelieferter Datensätze und die Ausgabe einer Meldung, wenn diese Null beträgt. Eine solche Vorgehensweise ist anzuraten, da der Benutzer ansonsten nur einen leeren Bildschirm zurückbekommen würde (Die whileSchleife wird schon vor dem ersten Durchlauf beendet kopfgesteuerte Schleife). Im folgenden Beispiel sollen die Daten aller Personen ausgegeben werden, deren Anfangsbuchstaben des Nachnamens mit der Eingabe übereinstimmen. <html> <body> Auswahl der Personendaten für Nachnamen mit folgendem Anfang: <form action = "PHP8-5.php" method = "post"> <input type = "text" name = "anfang"> <br> <input type = "submit"> <input type = "reset"> </form> </body> </html> Listing 8-6: PHP8-5.htm Einführung in die Programmierung mit PHP und MySQL.doc Seite 35 von 54 Sollen Daten auf der Grundlage von Textteilen ausgegeben werden, so ist besonders auf die Verwendung der Hochkommata zu achten. Der Operator LIKE und die Platzhalter können wie gewohnt verwendet werden. <html> <body> <?php mysql_connect ("", "phptest", "phptest"); mysql_select_db("phpuebungen"); $abfrage = "SELECT name, vorname, gehalt FROM personen"; $abfrage .= " WHERE name LIKE '" . $_POST["anfang"] . "%'"; $res = mysql_query ($abfrage); $num = mysql_num_rows ($res); if ($num == 0) echo "Keine Datensätze gefunden"; echo "<table border = '1'>"; echo "<tr> <td>Name</td> <td>Vorname</td> <td>Gehalt></td> </tr>"; while ($dsatz = mysql_fetch_array ($res)){ // Ausgabe der Daten in die Tabellenfelder echo "<tr>"; echo "<td>" . $dsatz["name"] . "</td>"; echo "<td>" . $dsatz["vorname"] . "</td>"; echo "<td>" . $dsatz["gehalt"] . "</td>"; echo "</tr>"; } echo "</table>"; ?> </body> </html> Listing 8-7: PHP8-5.php Wie zu erkennen ist, muss die Eingabe für den LIKE-Operator in Hochkommata eingeschlossen werden. Das beginnende Hochkomma steht in der ersten Teilkette hinter LIKE nach einem Leerzeichen unmittelbar vor den Anführungszeichen, das schließende hinter dem Platzhalter (%). Ändern Sie das Beispielprogramm PHP8-4.htm so ab, dass die Auswahl über RadioButtons erfolgt. Zur Definition von Radio-Buttons siehe http://de.selfhtml.org. Vergeben Sie den Radio-Buttons einfache Werte (1, 2, ...) und führen Sie die Prüfung auf den ausgewählten Wert im auswertenden PHP-Programm mit Hilfe von switch durch. Übung 8.6: Fügen Sie den Programmen aus Übung 8.6 noch eine Checkbox hinzu, mit der die Ausgabe nach Gehalt aufsteigend bzw. absteigend sortiert ausgegeben wird. Übung 8.7: 8.5 Exkurs: Rekursiver Aufruf der gleichen Datei Bisher werden das Formular und das PHP-Programm in getrennten Dateien gespeichert. Der Benutzer bekommt die HTML-Seite mit dem Formular vorgelegt, trifft seine Auswahl bzw. tätigt seine Eingaben, sendet es ab und es wird ihm eine durch das PHP-Programm generierte Seite zurückgesendet. Seite 36 von 54 Einführung in die Programmierung mit PHP und MySQL.doc Gerade (aber nicht nur) in Datenbankaufgaben möchte aber ein Benutzer nach der Ausgabe häufig eine veränderte Abfrage starten. Dazu müsste er bei der jetzigen Vorgehensweise erst wieder die HTML-Seite laden. Günstiger wäre es also, wenn das Formular Teil des PHP-Programms wäre und dieses sich immer wieder selbst aufrufen könnte. Das Programm würde sich somit auch die Daten selbst zusenden. Dieses Verfahren wird im nächsten Beispiel vorgestellt: <html> <body> <?php // Prüfung, ob Daten existieren if (isset ($_POST["gesendet"])){ mysql_connect ("", "phptest", "phptest"); mysql_select_db("phpuebungen"); $abfrage = "SELECT name, vorname, gehalt FROM personen"; $abfrage .= " WHERE name LIKE '" . $_POST["anfang"] . "%'"; $res = mysql_query ($abfrage); $num = mysql_num_rows ($res); if ($num == 0) echo "Keine Datensätze gefunden"; echo "<table border = '1'>"; echo "<tr> <td>Name</td> <td>Vorname</td> <td>Gehalt</td> </tr>"; while ($dsatz = mysql_fetch_array ($res)){ // Ausgabe der Daten in die Tabellenfelder echo "<tr>"; echo "<td>" . $dsatz["name"] . "</td>"; echo "<td>" . $dsatz["vorname"] . "</td>"; echo "<td>" . $dsatz["gehalt"] . "</td>"; echo "</tr>"; } echo "</table>"; } // Ende der Datenausgabe ?> // Formulareingabe Auswahl der Personendaten für Nachnamen mit folgendem Anfang: <form action = "PHP8-6.php" method = "post"> <input type = "text" name = "anfang"> <br> <input type = "submit" name = "gesendet"> <input type = "reset"> </form> </body> </html> Listing 8-8: PHP8-6.php Der Trick liegt in der Abfrage if (isset ($_POST["gesendet"])), die prüft, ob die angegebene Parametervariable existiert. Da beim ersten Aufruf noch keine POST-Daten übertragen wurden, ist dies also nicht der Fall. Somit wird auch keine Ausgabe erzeugt. Erst wenn sich die Datei selbst aufruft (nach dem Klicken auf den Senden-Button), ist die Bedingung erfüllt und eine Ausgabe erfolgt, und zwar auch, wenn nichts eingegeben wurde, da die abgefragte Parametervariable der Sendebutton selbst ist. Übung 8.8: aufrufen. Verändern Sie Übung 8.6 und Übung 8.7 so, dass sie sich in einer Datei selbst Einführung in die Programmierung mit PHP und MySQL.doc 8.6 Seite 37 von 54 Datensätze erzeugen In vielen Fällen soll es bestimmten Benutzern auch gestattet sein, neue Datensätze einzugeben. Die Berechtigung dazu muss über den Benutzernamen auf dem Datenbankserver vorher erteilt worden sein. Im Folgenden wird davon ausgegangen, dass der Benutzer über alle notwendigen Rechte auf dem Datenbankserver verfügt. Das nachfolgende Beispiel zeigt eine Eingabeseite, die sich selbst aufruft, also das rekursive Konzept des letzten Abschnittes aufgreift. <html> <head> <?php if (isset ($_POST["gesendet"])){ mysql_connect ("", "phptest", "phptest"); mysql_select_db("phpuebungen"); $abfrage = "INSERT personen "; $abfrage .= "(name, vorname, geburtstag, gehalt) values "; $abfrage .= "('" . $_POST["fname"] . "', "; $abfrage .= "'" . $_POST["fvorname"] . "', "; $abfrage .= "'" . $_POST["fgebdat"] . "', "; $abfrage .= "'" . $_POST["fgehalt"] . "')"; // Aktionsabfrage – kein Rückgabewert mysql_query ($abfrage); // Ermittlung der Anzahl betroffener Datensätze $num = mysql_affected_rows (); // Unterscheidung nur zwischen 0 und 1 if ($num > 0){ echo "<p>Es wurde 1 Datensatz hinzugefügt</p>"; }else{ echo "<p>Ein Fehler ist aufgetreten.<br>"; echo "Es wurde kein Datensatz hinzugefügt</p>"; } } ?> </head> <body> <p>Datensatzeingabe</p> <form action = "PHP8-7.php" method = "post"> <input type = "text" name = "fname"> Nachname<br> <input type = "text" name = "fvorname"> Vorname<br> <input type = "text" name = "fgebdat"> Geburtstag (JJJJ-MM-TT)<br> <input type = "text" name = "fgehalt"> Gehalt (Dezimalpunkt!)<br> <input type = "submit" name = "gesendet"> <input type = "reset"> </form> </body> </html> Listing 8-9: PHP8-7.php Zunächst wird wieder geprüft, ob der Submit-Button seinen Namen übertragen hat, es sich also um einen Folgeaufruf handelt. Ist dies der Fall, so wird versucht, die eingegebenen Daten an die Datenbank zu übertragen und eine Meldung über den Erfolg oder Misserfolg auszugeben. Die Entscheidung darüber wird auf Grund der Funktion mysql_affected_rows getroffen, die zurückmeldet, wie viele Datensätze (Reihen) von der letzten Aktionsabfrage betroffen waren. Übertragen werden hier nur die eigentlichen Daten. Die fünfte Angabe (Personalnummer) wird automatisch erzeugt, da dieses Attribut als Autowert definiert ist. Das Beispiel zeigt außerdem, dass der gesamte PHP-Code auch im head-Bereich der html-Seite angeordnet werden kann. Seite 38 von 54 Einführung in die Programmierung mit PHP und MySQL.doc Verändern Sie das Beispielprogramm PHP8-7.php so, dass nach der (Erfolgs-) Meldung die gesamten Daten übersichtlich in einer Tabelle ausgegeben werden. Die Tabelle vergrößert sich also nach jedem Neueintrag. Übung 8.9: 8.7 Datensätze ändern An dieser Stelle soll nur auf das gezielte Ändern einzelner Datensätze eingegangen werden. Globale Änderungen wie z.B. die pauschale Erhöhung der Gehälter um einen bestimmten Prozentsatz können aber analog zu der dargestellten Vorgehensweise (meist mit deutlich weniger Aufwand) durchgeführt werden. Um einen Datensatz gezielt ändern zu können, muss er zunächst eindeutig identifiziert werden. Eine für den Anwender sehr komfortable Vorgehensweise hierzu zeigen die folgenden Beispielprogramme. Zunächst werden dem Benutzer alle Datensätze angezeigt. Dabei ist jeder Datensatz mit einem Radiobutton versehen, den der Benutzer zur Auswahl eines Datensatzes anklicken kann. Um unmittelbar eine eindeutige Beziehung zum Datensatz herzustellen, wird schon bei der Erzeugung der Ausgabetabelle der Wert jedes Radiobuttons auf den Wert des Primärschlüsselfeldes (hier. Personalnummer) gesetzt. So kann im aufgerufenen Programm unmittelbar auf den betroffenen Datensatz zugegriffen werden. (PHP8-8.php) Im nächsten Schritt wird der zu ändernde Datensatz in einem Formular angezeigt, wo der Benutzer seine Änderungen vornehmen kann. (PHP8-9.php) Nach dem Absenden werden die geänderten Daten in die Datenbank eingetragen und eine entsprechende Meldung ausgegeben. Danach hat der Benutzer die Möglichkeit, wieder zum ersten Formular zurückzukehren. (PHP8-10.php) Hier nun der Reihe nach die PHP-Dateien, zunächst die Seite zur Auswahl des Datensatzes: <html> <body> <p>Wählen Sie den zu ändernden Datensatz aus:</p> <form action = "PHP8-9.php" method = "post"> <?php mysql_connect ("", "phptest", "phptest"); mysql_select_db("phpuebungen"); $res = mysql_query ("SELECT * FROM personen"); // Beginn der Tabelle echo "<table border = '1'>"; // Tabellenüberschrift echo "<tr> <td> </td>"; //geschütztes Leerzeichen (Auswahl) echo "<td>Name</td> <td>Vorname</td>"; echo "<td>Geburtstag</td> <td>Gehalt></td> </tr>"; Einführung in die Programmierung mit PHP und MySQL.doc Seite 39 von 54 while ($dsatz = mysql_fetch_array ($res)){ // Ausgabe der Daten in die Tabellenfelder echo "<tr>"; echo "<td><input type = 'radio' name = 'auswahl'"; echo " value = '" . $dsatz["personalnummer"] . "'></td>"; echo "<td>" . $dsatz["name"] . "</td>"; echo "<td>" . $dsatz["vorname"] . "</td>"; echo "<td>" . $dsatz["geburtstag"] . "</td>"; echo "<td>" . $dsatz["gehalt"] . "</td>"; echo "</tr>"; } echo "</table>"; ?> <!-- Sendebutton --> <p><input type = "submit" value = "Datensatz ändern"></p> </form> </body> </html> Listing 8-10: PHP8-8.php Die Personalnummer des ausgewählten Datensatzes befindet sich in der Variablen auswahl und wird an das folgende Skript übertragen. Dort kann nun gezielt die Änderung vorgenommen werden: <html> <body> <?php if (isset ($_POST["auswahl"])){ mysql_connect ("", "phptest", "phptest"); mysql_select_db("phpuebungen"); $abfrage = "SELECT * FROM personen WHERE "; $abfrage .= "personalnummer = " . $_POST["auswahl"]; $res = mysql_query ($abfrage); $dsatz = mysql_fetch_array ($res); // nur 1 Datensatz! echo "<p>Daten zur Änderung:</p>"; echo "<form action = 'PHP8-10.php' method = 'post'>"; echo "<p><input type = 'text' name ='nn' value = '"; echo $dsatz["name"] . "'> Nachname</p>"; echo "<p><input type = 'text' name ='vn' value = '"; echo $dsatz["vorname"] . "'> Vorname</p>"; echo "<p><input type = 'text' name ='gt' value = '"; echo $dsatz["geburtstag"] . "'> Geburtstag</p>"; echo "<p><input type = 'text' name ='ge' value = '"; echo $dsatz["gehalt"] . "'> Gehalt</p>"; echo "<input type = 'hidden' name ='pn' value = '"; echo $dsatz["personalnummer"]."'>"; echo "<input type = 'submit' value = 'Änderungen speichern'>"; echo "<input type = 'reset'>"; echo "</form>"; }else{ echo "<p>Es wurde kein Datensatz ausgewählt</p>"; echo "<p>Zurück zur <a href = 'PHP8-8.php'>Auswahl</a></p>"; } ?> </body> </html> Listing 8-11: PHP8-9.php Seite 40 von 54 Einführung in die Programmierung mit PHP und MySQL.doc Wenn ein Datensatz ausgewählt war, werden dessen Daten eingelesen und in einem Formular zur Änderung bereitgestellt. Um die Änderungen im nächsten PHP-Skript wieder richtig speichern zu können, wird der Primärschlüssel (hier: personalnummer) in einem versteckten Textfeld (type = "hidden") gespeichert und von dort an das nächste Skript übertragen. Sofern kein Datensatz ausgewählt war, wird nur eine Meldung ausgegeben und über einen Hyperlink der Rücksprung zur Auswahlseite ermöglicht. Das Eintragen der Änderungen erfolgt nunmehr in der letzten PHP-Datei: <html> <body> <?php mysql_connect ("", "phptest", "phptest"); mysql_select_db("phpuebungen"); $abfrage = "UPDATE personen SET "; $abfrage .= "name = '" . $_POST["nn"] . "', "; $abfrage .= "vorname = '" . $_POST["vn"] . "', "; $abfrage .= "geburtstag = '" . $_POST["gt"] . "', "; $abfrage .= "gehalt = '" . $_POST["ge"] . "' "; $abfrage .= "WHERE personalnummer = " . $_POST["pn"]; mysql_query ($abfrage); $num = mysql_affected_rows(); if ($num > 0) echo "Der Datensatz wurde geändert"; else echo "Die Datensatzänderung ist fehlgeschlagen"; ?> <p>Zurück zur <a href = "PHP8-8.php">Auswahl</a></p> </body> </html> Listing 8-12: PHP8-10.php Die Änderung wird mit der SQL-Anweisung UPDATE durchgeführt, wobei die Auswahl des zu ändernden Datensatzes analog zur SELECT-Anweisung mit der WHERE-Klausel durchgeführt wird. Anschließend hat der Benutzer die Möglichkeit, zur Anzeigeseite zurückzukehren und weitere Änderungen durchzuführen. Verbinden Sie die Funktion des ersten PHP-Skriptes (PHP8-8.php: Anzeigen aller Datensätze) mit der des letzten (PHP8-10.php: Speichern der Änderungen und Erfolgsmeldung). Ein Rücksprung ist dann nicht mehr notwendig, da sich der Benutzer unmittelbar auf der richtigen Seite befindet. Denken Sie daran, dass notwendigerweise geprüft werden muss, ob die Seite mit geänderten Daten angesprungen wird (Fall 1 der zweiseitigen Auswahl in PHP8-9.php) oder ob der Rücksprung erfolgt, weil kein Datensatz ausgewählt war! Übung 8.10: Einführung in die Programmierung mit PHP und MySQL.doc 9 Seite 41 von 54 Zugriffsautorisierung Wir alle wissen, dass HTTP das Webprotokoll ist, also das Protokoll, mit dem Browser und Webserver kommunizieren. Es ist sicherlich auch bekannt, dass HTTP ein statusloses Protokoll ist. Es ist ein einfaches Anfrage/Antwort-Protokoll. Der Browser macht eine Anfrage, der Webserver antwortet und der Austausch ist beendet. Wenn eine HTTP-GET-Anfrage an einen Webserver geschickt wird und gleich darauf eine zweite Anfrage, ist das HTTP-Protokoll nicht in der Lage, diese zwei Anfragen in Verbindung zu bringen. Mitunter wird angenommen, dass so genannte persistente7 Verbindungen dieses Defizit überwinden und den Status aufrechterhalten können. Das stimmt nicht. Obwohl die Verbindung erhalten bleibt, werden die Anfragen selbst völlig unabhängig voneinander behandelt. Das wirft mehrere Probleme in HTTP auf: Authentifizierung - Das Protokoll erkennt Anfragen nicht als zusammen gehörig. Wenn Sie einen Zugriff einer Person in der Anfrage A autorisieren, wie bestimmen Sie unter diesen Voraussetzungen also, ob eine nachfolgende Anfrage B von derselben oder einer anderen Person stammt? Persistenz - Die meisten Leute benutzen das Internet, um Aufgaben zu erledigen. Per Definition erfordert eine Aufgabe, dass etwas den Status ändert (ansonsten ist nichts passiert). Wie bewirken Sie Änderungen, insbesondere Änderungen, die mehrere Schritte erfordern, wenn Sie keinen Status haben? Ein typisches Beispiel für eine Webapplikation, die auf dieses Problem stößt, ist ein Online-Shop. Die Applikation muss den Benutzer authentifizieren, so dass sie weiß, wer der Benutzer ist (da sie persönliche Angaben wie die Adresse des Benutzers und Informationen zur Kreditkarte gespeichert hat). Außerdem muss sie bestimmte Daten - wie den Inhalt des Einkaufwagens - über mehrere Anfragen aufrechterhalten. Die Lösung für diese beiden Probleme liegt darin, den notwendigen Erhalt des Status selbst zu implementieren. Diese Herausforderung ist weniger kompliziert als sie zunächst erscheinen mag. Netzwerkprotokolle bestehen oft aus Schichten, die den Status erhalten, und die auf statuslosen Schichten aufbauen und umgekehrt. HTTP zum Beispiel ist ein Protokoll der ApplikationsSchicht, über das zwei Applikationen (der Browser und der Webserver) Informationen austauschen, und das auf TCP aufsetzt. TCP ist ein Protokoll der System-Schicht (d.h. die Endpunkte sind Betriebssysteme), das statuserhaltend ist. Wenn eine TCP-Sitzung zwischen zwei Maschinen gestartet wird, gleicht das einem Gespräch. Die Kommunikation geht hin und her, bis eine der Parteien das Gespräch beendet. TCP setzt auf IP auf, das ein statusloses Protokoll ist. TCP implementiert den Status durch Reihenfolge-Nummern in den Paketen. Diese Reihenfolge-Nummern (plus die Netzwerksadressen der Endpunkte) erlauben beiden Seiten festzustellen, ob sie irgendwelche Teile des Gespräches verpasst haben und stellen eine Möglichkeit der Authentifizierung zur Verfügung, so dass jede Seite weiß, dass sie noch mit derselben Person spricht. Wenn die Reihenfolge-Nummern leicht zu erraten sind, ist es möglich, eine TCP-Sitzung zu entführen, sich also mithilfe der richtigen Reihenfolge-Nummern in das Gespräch einzuschleichen. 7 Bezüglich der logischen Verbindungen bedeutet Persistenz, dass die reale Verbindung bei Bedarf oder nach einem Ausfall wiederhergestellt wird, ohne dass die zwischenzeitliche Trennung durch die Kommunikationspartner bemerkt wird. Seite 42 von 54 9.1 Einführung in die Programmierung mit PHP und MySQL.doc Benutzerregistrierung Bevor Sie sich um die Authentifizierung von Benutzern kümmern können, müssen Sie wissen, wer die Benutzer sind. Sie benötigen mindestens einen Benutzernamen und ein Kennwort, obwohl es oft nützlich ist, noch mehr Information zu sammeln. Viele Programmierer konzentrieren sich auf die Feinheiten guter Passwort-Generierung (was schwierig, aber notwendig ist), ohne jemals über die Möglichkeiten einer eindeutigen Identifizierung nachzudenken. Zu den Gefahren im Rahmen der Benutzerregistrierung siehe auch Kapitel 10.5. 9.1.1 Identifizierung Eine sinnvolle Möglichkeit ist die Verwendung von E-Mail-Adressen als eindeutige Identifizierung von Benutzern in Webapplikationen. Die große Mehrheit der Benutzer (abgesehen von Computerfreaks) benutzt lediglich eine einzige Adresse. Diese Adresse wird für gewöhnlich auch exklusiv von diesem Benutzer verwendet, so dass sie sich bestens als vollkommen einzigartige Identifizierung eines Benutzers eignet. Mit einer Bestätigungsrückmeldung für die Anmeldung (Sie senden dem Benutzer eine E-Mail-Nachricht. die ihn auffordert zu handeln. um die Anmeldung abzuschließen) können Sie sich davon überzeugen, dass die E-Mail-Adresse gültig ist und dem Benutzer gehört, der sich anmelden möchte. Mithilfe von E-Mail-Adressen können Sie auch nachhaltiger mit Ihren Benutzern kommunizieren. Wenn die User zustimmen, E-Mails von Ihnen zu erhalten, können Sie ihnen periodische Informationen über die Site schicken und sind notfalls im Stande, ein neu generiertes Passwort an einen Benutzer zu senden (wenn eine Passwort-Wiederherstellung kritisch ist). Alle diese Aufgaben sind am saubersten durchzuführen, wenn es eine eineindeutige Zuordnung von Benutzern und E-Mail-Adressen gibt. Allerdings erleichtert die Verwendung der E-Mail-Adresse als Benutzername den Identitätsdiebstahl, da diese öffentlich bekannt sind. Ein Angreifer muss also nur noch das Passwort und nicht mehr den Nutzernamen erraten. Dieser Umstand spricht für die Generierung eines künstlichen Nutzernamens, der allerdings auf Eindeutigkeit überprüft werden müsste. 9.1.2 Kennwörter Benutzer wählen von Natur aus schlechte Kennwörter. So haben zahlreiche Studien belegt, dass - wenn man es ihnen erlaubt - die meisten Benutzer ein Kennwort wählen, das in kurzer Zeit erraten werden kann. Wie sieht ein "gutes" Kennwort aus? Ein gutes Kennwort ist eines, das nicht leicht durch automatisierte Techniken erraten werden kann. Ein "guter" Kennwort-Generator könnte folgendermaßen aussehen: function random_password($length=8){ $str =''; for($i=O; $i<$length; $i++) {$str .= chr(rand(48,122)); return $str; } Listing 9-1: Erzeugung eines Zufalls-Passwortes Einführung in die Programmierung mit PHP und MySQL.doc Seite 43 von 54 Dies erzeugt Kennwörter, die aus zufälligen, druckbaren ASCII-Zeichen bestehen und schwer zu merken sind. Damit ist das Kernproblem mit zufällig erzeugten Kennwörtern angesprochen: Menschen hassen diese Kennwörter. Je schwieriger es ist, sich ein Kennwort zu merken, desto größer die Wahrscheinlichkeit, dass es auf einem Notizzettel an den Monitor geheftet, oder in eine Datei oder E-Mail-Nachricht geschrieben wird. Alternativ wird oft der Weg gewählt, das Problem mit den Kennwörtern auf den Benutzer abzuwälzen und ein paar einfache Regeln zu implementieren. Sie können dem Benutzer zum Beispiel erlauben, sein eigenes Kennwort auszuwählen, aber verlangen, dass das Kennwort bestimmte Tests besteht. Der folgende Code führt eine einfache Validierung eines Kennworts durch: function good_password($passwordl){ if(strlen($passwordl < 8) { return 0; } if(!preg_match("/\d/", $password)){ return 0; if(!preg_match("/[a-z]/i", $password)){ return 0; } return 1; } Listing 9-2: Prüfung eines Passwortes auf Mindestgüte Diese Funktion verlangt, dass ein Kennwort mindestens acht Zeichen lang ist und sowohl Buchstaben und Ziffern enthält. Eine robustere Funktion könnte überprüfen, dass kein Wort aus einem Wörterbuch übrig bleibt, wenn die Ziffern weg gelassen werden, oder dass weder der Name des Benutzers noch seine Adresse im Kennwort enthalten sind. Die Frage der Kennwörter auf diese Art zu lösen, ist eine Taktik, der man häufig begegnet: Wenn ein Problem schwer zu lösen ist, wird es auf andere abgewälzt! Fraglos ist es gar nicht so einfach, ein sicheres Kennwort zu erzeugen, mit dem ein Benutzer glücklich ist. Viel leichter ist es, ein schlechtes Kennwort zu entdecken und den Benutzer davon abzuhalten, es zu wählen. 9.2 Benutzerauthentifizierung Das hier dargestellte System ist im Wesentlichen ein auf Tickets basierendes. Denken Sie zum Beispiel an Tickets für einen Skilift. Im Tal kaufen Sie sich ein Ticket für den Lift und heften es an Ihre Jacke. Dadurch ist das Ticket sichtbar, wo auch immer Sie hingehen. Wenn Sie versuchen, den Lift ohne Ticket oder mit einem nicht mehr gültigen Ticket zu nutzen, werden Sie zum Eingang zurückgeschickt, damit Sie sich eine neue Karte kaufen. Mit verschiedenen Maßnahmen wird sichergestellt, dass die Tickets schwer zu fälschen sind (z. B. mit Unterschriften auf den Tickets). Zuerst müssen Sie im Stande sein, die Berechtigung der Benutzer zu überprüfen. In den meisten Fällen handelt es sich um einen Benutzernamen und ein Kennwort, die übergeben werden. Sie können diese Informationen dann gegen eine Datenbank abgleichen (oder gegen einen LDAP-Server etc.). Hier ist das Beispiel einer Funktion, die eine MySQL Datenbank verwendet, um die Berechtigung eines Benutzers zu überprüfen: Seite 44 von 54 Einführung in die Programmierung mit PHP und MySQL.doc function check_credentials($name. $password) { $sql = “SELECT userid FROM users”; $sql .= “WHERE username = $name AND password = $password”; mysql_query ($abfrage); $num = mysql_affected_rows(); if ($num > 0) echo "Zugang gewaehrt"; else echo "Zugang verweigert"; } Listing 9-3: Prüfung einer Nutzerberechtigung Die Überprüfung der Benutzerangaben ist nur eine Hälfte des Spiels. Sie brauchen auch ein Verfahren für die Authentifizierung. Dabei können Sie zwischen drei Methoden wählen: HTTPAuthentifizierung, URL-Parameter und Cookies. 9.2.1 HTTP-Authentifizierung Basis-Authentifizierung ist ein Authentifizierungsschema, das in HTTP integriert ist. Wenn ein Server eine nicht berechtigte Anfrage auf eine Seite erhält, beantwortet er die Anfrage mit diesem Header: WWW-Authenticate: Basic realm='RealmFoo' In diesem Header ist RealmFoo ein willkürlicher, dem Namensraum zugeteilter Name, der geschützt wird. Der Client antwortet dann mit einem Base64-kodierten Benutzernamen/Kennwort, um sich zu authentifizieren. Diese Art der Authentifizierung steckt hinter den Fenstern zur Eingabe eines Benutzernamens/Kennwortes, die in vielen Sites auftauchen. Serverseitig ist die Authentifizierung entsprechend zu konfigurieren, beim Apache HTTP Server etwa durch Notation entsprechender durch Authentifizierungsmodule bereitgestellter Direktiven in einer .htaccess-Datei. Der bevorzugte Weg ist jedoch die Notation der Direktiven in der zentralen Serverkonfigurationsdatei httpd.conf. Mittlerweile wurde die Basis-Authentifizierung jedoch weitgehend verdrängt durch die Authentifizierung mit Hilfe von Cookies. Der große Vorteil der Basis-Authentifizierung ist, dass sie in der HTTP-Schicht verankert ist, so dass alle Dateien auf einer Site geschützt werden können - nicht nur PHP-Skripte. Das ist vor allem für Sites wichtig, die Video-/ Audio- und Bild-Dateien für registrierte Mitglieder anbieten, weil darüber auch der Zugriff auf diese Mediadateien kontrolliert werden kann. In PHP stehen der Benutzername und das Kennwort der Basis-Authentifizierung im Skript als $_SERVER['PHP_AUTH_USER'] und $_SERVER['PHP_AUTH_PW'] zur Verfügung. 9.2.2 Session-Daten in der URL Bei diesem Verfahren werden die Benutzerinformationen bei jeder Anfrage an die URL angehängt. Eine Reihe von Java-Session-Wrappers funktionieren auf diese Weise und auch im PHP-Modul für Sessions kann dieses Verfahren verwendet werden. Diese Form der Übergabe von Session-Daten erzeugt furchtbar lange und »scheußliche« URLs. Sessioninformationen können ziemlich umfangreich werden, und einfach 100 Bytes an die ansonsten elegante URL zu hängen, ist einfach hässlich. Aber es ist nicht nur eine Frage der Ästhetik. Viele Suchmaschinen ignorieren dynamische URLs (d.h. URLs mit Parametern). Hinzu kommt, dass lange URLs schwer zu kopieren sind - oft werden sie einfach abgeschnitten, so dass sie kaum per E-Mail verschickt werden können. Einführung in die Programmierung mit PHP und MySQL.doc Seite 45 von 54 Zweitens impliziert das Anhängen der Session-Daten an die URL ein Sicherheitsproblem, weil die Parameter der Session eines Users sehr leicht zu einem anderen User gelangen können. Durch Kopieren und Einfügen der URL mit der Session-ID kann die Session eines Users entführt werden, manchmal auch unbeabsichtigt. Wir werden diese Technik hier nicht weiter behandeln, da es fast immer sicherere und elegantere Lösungen gibt. 9.2.3 Cookies Seit Netscape 3.0 (1996) werden Cookies von Browsern unterstützt. Der folgende Abschnitt stammt aus der Spezifikation für Cookies von Netscape: Ein Server kann, wenn er ein HTTP-Objekt zum Client schickt, auch eine Status-Information mitsenden, die der Client speichern wird. Integriert in dieses Statusobjekt ist eine Beschreibung eines URL-Bereichs, für die der Status gültig ist. Jede zukünftige HTTP-Anfrage dieses Clients, die in diesen Bereich passt, enthält den aktuellen Wert des Statusobjekts, das somit vom Client zurück zum Server geschickt wird. Dieses Statusobjekt wird - aus welchen Gründen auch immer als Cookie bezeichnet. Cookies stellen ein unschätzbares Werkzeug zur Verfügung, um den Status zwischen Anfragen aufrechtzuerhalten. Über die Übertragung von Benutzerinformationen und Authentifizierungen hinaus können Cookies effektiv zur Übergabe großer und beliebiger Statusinformationen zwischen Anfragen verwendet werden, selbst nachdem der Browser geschlossen und erneut geöffnet worden ist. Cookies sind der de facto Standard, um transparent Informationen per HTTP-Anfragen zu übergeben. Hier sind die wichtigsten Vorteile von Cookies gegenüber der BasisAuthentifizierung: Vielseitigkeit - Cookies bieten eine ausgezeichnete Möglichkeit, vielseitige Informationen zwischen Anfragen zu übergeben. Basis-Authentifizierung ist, wie der Name schon sagt, rudimentär. Persistenz - Cookies können im Browser des Users über Sitzungen hinweg gesetzt werden (die Cookies bleiben auch erhalten, wenn der Browser geschlossen und wieder gestartet wird). Viele Seiten nutzen diesen Vorteil, um transparentes oder automatisches Einloggen - basierend auf den Informationen der Cookies - zu ermöglichen. Selbstverständlich hat dieses Verfahren Auswirkungen auf die Sicherheit, aber auf vielen Sites wird ein Teil der Sicherheit zugunsten der Benutzerfreundlichkeit geopfert. Natürlich können Benutzer den Browser so einrichten, dass Cookies von einer Site abgelehnt werden. Es liegt an Ihnen, wie viel Aufwand Sie mit Menschen betreiben, die »paranoide« Cookie-Einstellungen verwenden. Ästhetik - Basis-Authentifizierung ist das Verfahren, bei dem der Browser ein kleines Fenster für den Benutzernamen/ das Kennwort öffnet. Dieses Fenster ist nicht an das Design der Site angepasst, und das ist für viele Sites inakzeptabel. Mit einem selbst gemachten Verfahren sind Sie flexibler. Sofern Anmeldedaten oder interne Werte in Cookies gespeichert werden, sollten diese unbedingt verschlüsselt sein. Cookies zu untersuchen und zu verändern ist für einen Benutzer trivial; um so wichtiger ist es, dass die Daten im Cookie in einem Format gespeichert werden, in dem der Benutzer ihre Bedeutung nicht sinnvoll ändern kann. Versuchen Sie nicht, eigene Seite 46 von 54 Einführung in die Programmierung mit PHP und MySQL.doc Verschlüsselungsalgorithmen zu entwickeln, die Erweiterung mycrypt stellt eine große Anzahl überprüfter Algorithmen zur Verfügung. Die bessere Variante ist es aber, im Cookie nur Verbindungsinformationen zu speichern, während alle anderen Daten auf dem Server verbleiben. So hat der Nutzer keinen direkten Zugriff und kann Informationen nur schwer böswillig ändern. Diese Form wird in Kapitel 9.3.2 genauer beschrieben. Die Lebensdauer eines Cookies bestimmt die Dauer der Gültigkeit einer Authentifizierung. Einen Auslogvorgang können Sie realisieren, indem Sie das Session-Cookie leeren. Die Möglichkeit zum Ausloggen ist ein wichtiger Bestandteil der Privatsphäre des Nutzers. Daneben gibt es weitere Möglichkeiten, die Lebensdauer eines Cookies zu begrenzen: Verfall bei jeder Anfrage Dieses Verfahren erschwert die Entführung einer Session, erfordert aber beträchtliche Ressourcen auf dem Server. In den meisten Fällen rechtfertigt der Sicherheitsgewinn nicht den zusätzlichen Aufwand. Verfall nach festgelegter Zeitspanne Dieses Verfahren wird häufig angewendet. Es entspricht unserem Eingangsbeispiel des Skilift-Tickets. Bei der Erstellung des Cookies wird die Gültigkeitsspanne festgelegt und die Ausgabezeit im Cookie vermerkt. Bei einem erneuten Seitenaufruf innerhalb einer Session wird dann die Gültigkeit der Session gegen diese Zeit geprüft und die Ausgabezeit neu gesetzt. Verfall beim Schließen des Browsers Hier löscht der Browser das Cookie beim Schließen des Browserfensters. Die Daten auf dem Server bleiben dabei zunächst bestehen. Für Ihre Vernichtung sorgt der "Müllmann", die Garbage Collection. Standardmäßig wird der Garbage Collector eine inaktive Session nach frühestens 24 Minuten beseitigen. Nach diesem Zeitfenster könnte also auch ein noch geöffneter Browser nicht mehr zugriffsautorisiert sein. 9.3 Statuserhalt durch Sessions Wie wir zu Beginn des Kapitels festgestellt haben, ist http ein verbindungsloses Protokoll. Das heißt, ein Webserver erstellt mit Hilfe von PHP Dokumente, die ein Client angefordert hat und liefert diese zurück an den Browser. Danach vergisst der Webserver diese Seite und auch den Client. Sessions stellen nun die Verbindung zwischen Server und Client her. Sie speichern Sitzungsdaten und Daten, die einen Benutzer eindeutig identifizieren. An dieser Stelle soll nur die Funktionsweise von Sessions behandelt werden, ein Beispiel, wie Sessions zur Benutzerauthentifizierung eingesetzt werden können, wird in Kapitel 9.3.4 behandelt. Für die Mechanismen von Sessions ist ein Session-Management verantwortlich. Dieses speichert lokale Daten und eine eindeutige ID. Ein solcher Mechanismus ist für dynamische Seiten notwendig, die während eines mehrstufigen Programmablaufs (z.B. einem Online-Einkauf) auf die Identifikation eines Benutzers angewiesen sind. Die eindeutige Identifikation, die der Webserver jedem Besucher zu Beginn der Sitzung zuteilt, heißt Sitzungs- oder Session-ID. Diese wird in der Regel automatisch zugewiesen. Wie viele andere Skriptsprachen hat PHP dafür einen eingebauten Session-Manager. Bei anderen Sprachen muss ein solcher Mechanismus selbst nachgerüstet werden. Die Sicherheit einer Session hängt stark von der Qualität des Session-Managements ab, das PHP-eigene gilt hier als vorbildlich. Einführung in die Programmierung mit PHP und MySQL.doc 9.3.1 Seite 47 von 54 Das Session-Management von PHP Eine Session wird in PHP durch den Aufruf der Funktion session_start() erzeugt. Der Befehl erzeugt eine neue Session oder nimmt eine bestehende wieder auf. Die Identifikation der Session erfolgt dabei über die zwischen Browser und Server übermittelte Session-ID. Das Session-Management von PHP legt auf dem Webserver eine Datei im dafür vorgesehenen Verzeichnis an und speichert dort alle Session-Daten. Erreichbar sind die Daten über die Systemvariable $_SESSION, die ein Array darstellt. Der Client erhält vom Server nur die SessionID, die die Sitzung auf dem Server identifiziert – und meist gleichzeitig der Name der SessionDatei ist. Es handelt sich hier also um eine Server-Session, die schon dadurch, dass Nutzdaten den Server nicht verlassen, sicherer ist als eine Client-Session. Die Session-ID kann auf drei Arten zwischen Browser und Server übermittelt werden: in einem Cookie. Das Cookie wird bei jedem Request als HTTP-Header an den Webserver mitgeschickt. Das Cookie wird vom PHP-Interpreter bzw. von der Applikation ausgestellt. Es kann persistent (dauerhaft) oder nicht-persistent sein. Letztere Cookies verfallen mit dem Schließen des Browsers, erstere werden für längere Zeit (oft dauerhaft und ohne Wissen des Nutzers) auf der Festplatte des Clients gespeichert. Das Versenden der Session-ID in einem Cookie ist der Standard in PHP und auch der sicherste Weg, dieser Mechanismus sollte nicht ohne Grund geändert werden. in der URL-Anfrage. Hier sind zwei Arten der Übertragung möglich: URL-Rewriting – Die Session-ID ist Teil der URL, z.B. http://SESSION1234.mysite.de (Achtung, Patentschutz!) oder http://www.mysite.de/SESSION1234/index.php. $_GET-Parameter – Hier wird die Session-ID an den bereits vorhandenen Query-String als weiterer Parameter angehängt, z.B. http://www.mysite.de/index.php?PHPSESSION=SESSION1234. In der Regel wird der URL-Transport nur eingesetzt, wenn der Client keine Cookies erlaubt. Über die trans_sid-Funktionalität entscheidet PHP automatisch, welche Transportmethode benutzt werden soll. Aufgrund der Sichtbarkeit der Session-ID ist es leichter, eine URLtransportierte Session zu entführen. als Formularfeld. Das Versenden in einem versteckten Formularfeld wird von PHP nicht standardmäßig eingesetzt. Das folgende Beispiel stände im $_POST-Array zur Verfügung: <input type="hidden" name="PHPSESSID" value="1234" /> 9.3.2 Freizügigkeit einer PHP-Session Bei der Sessionverwaltung unterscheidet man zwischen permissiven (freizügigen) und strikten Systemen. In permissiven Systemen erhält jeder eine Session-ID. Falls eine vom Client übermittelte Session-ID noch nicht existiert, wird diese auf dem Server angelegt. Das bedeutet, ein Benutzer kann sich eine Session-ID ausdenken und diese an den Server übermitteln. Würde es sich um ein striktes System handeln, würde diese Session-ID verworfen und durch eine von der Sprache erzeugte ID ersetzt werden. Strikte Systeme sind also für Angriffe wie Session-Riding (das Ausführen von Aktionen unter fremden Sessions) oder Phishing weniger anfällig. PHP implementiert das permissive Session-Modell. Der Einbau von Restriktionen muss vom Programmierer durchgesetzt werden. Grundsätzlich sollte bei der Vergabe einer Session-ID geprüft werden, ob der Nutzer schon authentifiziert ist und ob ihm eine Rolle zugeordnet wurde. Seite 48 von 54 Einführung in die Programmierung mit PHP und MySQL.doc Jede Seite der Applikation muss diese Authetifizierungsdaten erneut prüfen, um den Quereinstieg in tiefer liegende Ebenen zu verhindern. 9.3.3 Daten einer Session verwalten Das Session-System von PHP speichert die Session-Daten in der Standardinstallation in dem Verzeichnis, das in der Konfigurationsdatei php.ini im Wert session.save_path festgelegt wurde. Dies geschieht in einem für Menschen lesbaren Format als serialisiertes Array. Auf einem Hostingserver (Mehrbenutzer-System) hat unter Umständen jeder Benutzer Zugriff auf die Session-Daten anderer Nutzer. Jeder Nutzer muss also Sorge tragen (durch eine eigene php.ini), die Daten in einem eigenen Verzeichnis abzulegen, zu dem nur er Zugriffsrechte hat. Das Array, welches die Session-Daten enthält, kann neben der Session-ID beliebige andere Daten beinhalten. Zugriff erhält man über die Systemvariable $_SESSION. Um z.B. den Status eines Nutzers über die verschiedenen Seiten einer Session verfügbar zu haben, kann man ihn unter $_SESSION['status'] = XX abspeichern. So lassen sich beliebig viele Daten innerhalb einer Session weitergeben. 9.3.4 Beispiel: Session zur Benutzerauthentifizierung Für die Erzeugung einer Session (und die Versendung des zugehörigen Cookies) ist es wichtig, dass noch kein http-Header versendet wurde. Die folgenden Zeilen erzeugen eine Fehlermeldung: <html> <?php session_start(); … Der Zeilenumbruch hinter dem <html>-Tag ist ein darstellbares Zeichen und führt zur Versendung des http-Headers, ein Versenden des Cookies ist nicht mehr möglich. Mit jeder neuen Anfrage versendet der Browser auch das Cookie an den Server. Dieser ist somit in der Lage, die Anfrage einer bestimmten Session zuzuordnen. Wichtig ist dazu, dass jede Seite die Session neu startet. Eine einfache Anmeldung mit Rechtesystem könnte also wie folgt ablaufen: <html> <head> <title>Benutzerzugangskontrolle - Login</title> </head> <body> <h1>Anmeldung an der Website</h1> <form method="post" action="auth2.php"> <input type="text" name="benutzer"> Benutzername<br> <input type="password" name="password"> Password<br> <input type="submit"> </form> </body> </html> Listing 9-4: auth1.php - Eingabeformular Benutzeranmeldung Die Datei auth1.php stellt lediglich ein Eingabeformular zur Abfrage von Nutzername und Passwort zur Verfügung. Da hier noch keine Auswertung erfolgt, ist auch noch kein Start einer Einführung in die Programmierung mit PHP und MySQL.doc Seite 49 von 54 Session nötig. Die Auswertung der Eingaben erfolgt in der aufgerufenen Datei auth2.php. Hier wird die Richtigkeit der eingegebenen Daten überprüft (bzw. deren Übereinstimmung mit einem Datenbankeintrag, hier in der Tabelle "users"). Für die weitere Arbeit auf den Folgeseiten werden dann nötige Daten in der Sessionvariablen gespeichert. <?php // Start der PHP-Session session_start (); $eingabename = $_POST["benutzer"]; // Password MD5-verschlüsseln! $eingabepasswort = md5($_POST["password"]); // Abfrage der Datenbank mysql_connect ("", "root", ""); mysql_select_db("phpskript"); $abfrage = "SELECT Name, Vorname, Rechte FROM nutzer"; $abfrage .= " WHERE Username = '$eingabename'"; $abfrage .= " AND Password = '$eingabepasswort'"; $res = mysql_query ($abfrage); $num = mysql_num_rows ($res); ?> <!-- Kopfdaten in HTML --> <html> <head><title>Benutzerzugangskontrolle - Auswertung</title></head> <body> <?php // Ein Datensatz identifiziert? if ($num == 1){ // Sichern der Nutzermerkmale zur späteren Verwendung $dsatz = mysql_fetch_array ($res); $_SESSION["name"] = $dsatz["Name"]; $_SESSION["vorname"] = $dsatz["Vorname"]; $_SESSION["rechte"] = $dsatz["Rechte"]; // Begrüßung echo "<p>Hallo ".$_SESSION["vorname"]." ".$_SESSION["name"]."</p>"; // Auswahlmenü zur weiteren Arbeit ?> <h1>Hier geht es zu den geschütztenSeiten</h1> <p><a href="auth3.php">Zur Seite 'Zugang mit Recht 0'</a></p> <p><a href="auth4.php">Zur Seite 'Zugang mit Recht 1'</a></p> <p><a href="auth5.php">Zur Seite 'Zugang mit Recht 2'</a></> <?php }else{ ?> <h1>Fehler in den Eingabedaten</h1> <a href="auth1.php">Zurück zur Anmeldung</a> <?php } ?> </body></html> Listing 9-5: auth2.php - Auswertung der Nutzeranmeldung Die folgende Datei (auth5.php - Beispiel einer Rechteprüfung) zeigt beispielhaft die Auswertung der in der Session gespeicherten Angaben. Die Prüfung auf Zugangsberechtigung muss auf jeder aufgerufenen Seite erfolgen. In dem Beispiel wird ein hierarchisches Rechtesystem vorausgesetzt, bei dem eine größere Kennzahl der Berechtigung auch erweiterte Rechte bedeuten. Auf der Beispielseite soll eine Berechtigung von 2 oder größer gefordert sein. Seite 50 von 54 Einführung in die Programmierung mit PHP und MySQL.doc <?php // Wiederherstellen der Session session_start (); $zugang = true; // Zugangsberechtigung vorerst vorausgesetzt ?> <!-- Kopfdaten in HTML --> <html> <head><title>Benutzerzugangskontrolle - Nutzerseite 0</title></head> <body> <?php // Prüfung, ob die nötigen Sessiondaten existieren if (isset ($_SESSION["name"]) AND isset ($_SESSION["vorname"])){ echo "<p>Hallo ".$_SESSION["vorname"]." ".$_SESSION["name"]."!</p>"; }else{ $zugang = false; } // Prüfung auf die geforderte Zugangsberechtigung (hier:2) if ($_SESSION["rechte"] < 2) $zugang = false; // fehlende Berechtigung! // hier erfolgt der eigentliche Zugang zur Seite if ($zugang){ echo "<p>Zugang gewärt</p>"; }else{ echo "<p>"; echo "Für diese Seite haben Sie nicht die nötigen Rechte"; echo "</p>"; } ?> </body></html> Listing 9-6: auth5.php - Beispiel einer Rechteprüfung Einführung in die Programmierung mit PHP und MySQL.doc Seite 51 von 54 10 Angriffsszenarien 10.1 Informationsgewinnung Zur Zeit noch nicht bearbeitet 10.2 Parametermanipulation Zur Zeit noch nicht bearbeitet 10.3 Cross-Site-Scripting Zur Zeit noch nicht bearbeitet 10.4 SQL-Injection Die „SQL-Einschleusung“ ist das Ausnutzen einer Sicherheitslücke im Zusammenhang mit SQLDatenbanken. Die meisten Anwendungen speichern in Formularen eingegebene Daten zur späteren Weiterverwendung in Datenbanken. Bei diesem Angriff wird nun versucht, über die Eingabe von Metazeichen in das Eingabeformular eigene SQL-Befehle einzuschleusen. Solche Metazeichen sind z.B. der Backslash (\), das Anführungszeichen (”), der Apostroph (’) und das Semikolon (;). Werden Formularinhalte ungeprüft in Datenbanken gespeichert, kann dadurch Schadcode eingeschleust werden. Das Ausspähen von Zugangsdaten könnte z.B. wie folgt geschehen: o o o o o o o In einem Formular „Artnum“ wird die Eingabe einer Artikelnummer zur Ausgabe der Artikeldaten erwartet. Die zugehörige SQL-Anweisung lautet dabei SELECT name, beschreibung, preis FROM artikel WHERE artNr = $_POST[‚Artnum’]; Erwartet wird also eine Nummer, die den Artikeldatensatz kennzeichnet. Ein Angreifer gibt in das Formular folgendes ein: 2 UNION SELECT name, passwort, ‚x’ FROM nutzer Die vollständige SQL-Anweisung lautet dann: SELECT name, beschreibung, preis FROM artikel WHERE artNr = 2 UNION SELECT name, passwort, ‚x’ FROM nutzer; Das ‚x’ ist notwendig, da UNION in beiden Abfrageteilen die gleiche Anzahl an von Spalten erwartet. Diese Anzahl kann durch Ausprobieren herausgefunden werden. Die Webseite gibt nun die Artikeldaten des Artikels 2 (wenn vorhanden) aus und alle Nutzernamen mit den dazugehörigen Passwörtern. Seite 52 von 54 Einführung in die Programmierung mit PHP und MySQL.doc Zum Angriff können grundsätzlich alle Daten verwendet werden, die der Client an den Server übermittelt und die nicht korrekt validiert werden. Dies kann GET, POST, aber auch Cookies betreffen. 10.4.1 Get-Parameter Daten, die an die URL angehängt werden, sind sichtbar und naturgemäß leicht zu ändern. Durch die Schreibweise /index.php?NameDesFormularfeldes=Inhalt lassen sich Schwachstellen besonders leicht identifizieren. Ändert man einen (oder mehrere) der Parameter, so können Effekte auftreten, die der Entwickler nicht bedacht hat. Durch Anhängen von ' oder " an den Parameter können z.B. Fehlermeldungen hervorgerufen werden: Die Veränderung des Aufrufs index.php?Artikelnummer=1000 durch Anhängen von '" erzeugt einen Syntaxfehler der Datenbank. Die dem Aufruf mysql_query ('SELECT * FROM artikel WHERE artikelnummer='.$_GET['Artikelnummer']); zugehörige Abfrage lautet dann: SELECT * FROM artikel WHERE artikelnummer='1000'" Dies erzeugt die Fehlermeldung: Warning: mysql_fetch_array(): supplied argument is not a valid MySQL result resource in … Anhand dieser Fehlermeldung ist nun die Datenbank bekannt. Weitere Ausspähungen können nun gezielt erfolgen. 10.4.2 POST-Parameter Die Manipulation von POST-Parametern ist nicht so einfach wie bei Get-Parametern, aber nicht unmöglich. Dazu muss das Formular lokal gespeichert werden. Dann ergänzt man das actionAttribut des Formulars um die zugehörige URL. Dies ist notwendig, da der Aufruf der Zielseite nicht mehr im ursprünglichen Umfeld erfolgt. Nun können lokal im Browser alle Formularfelder auf Schwachstellen untersucht werden. Textfelder werden meist validiert, da hier eine einfache Eingabe von Sonderzeichen in das Orginalformularfeld zum Angriff genügt. Deshalb sind besonders Radiobutton, Checkboxen oder Listboxen für Angriffe interessant, da Entwickler hier häufig der Ansicht sind, dass nur die vorbelegten Werte an den Server übermittelt werden können. Dies ist aber definitiv falsch. Durch das lokale Speichern können beliebige weitere Auswahloptionen erstellt werden. 10.4.3 Cookie-Parameter In letzter Zeit wurden auch Cookie-Parameter zu Angriffen ausgenutzt. Dies ist aber nur möglich, wenn das Cookie über die Session-ID hinaus Werte enthält, die im Programm genutzt werden. Ein Angriff ist dann möglich, wenn Werte aus den superglobalen Array $_COOKIE in einer SQL-Abfrage übernommen werden. 10.4.4 Schutz vor SQL-Injektion Sonderzeichen können mit dem PHP-Befehl mysql_real_escape_string() maskiert werden. Somit haben diese keinen Einfluss mehr auf die SQL-Abfrage und werden nicht in der Datenbank gespeichert. Die Einstellung magic_quotes_gpc = on in der Konfigurationsdatei php.ini maskiert ebenfalls alle aus GET, POST oder Cookie-Variablen stammenden Sonderzeichen mit dem Backslash. Dieser wird aber in den String eingefügt und somit in der Datenbank gespeichert. Zudem ist dieser Einführung in die Programmierung mit PHP und MySQL.doc Seite 53 von 54 Mechanismus, der nur einfache und doppelte Anführungszeichen sowie den Backslash und den NULL-Wert maskiert, als Schutz vor einer SQL-Injektion unzureichend. Hat man keinen Zugriff auf die Datei php.ini ist häufig nicht klar, ob magic_quotes gesetzt sind oder nicht. Ein zusätzliches (notwendiges) maskieren mit dem o.g. PHP-Befehl würde eventuell eine doppelte Maskierung erzeugen, was unerwünscht ist. Man behilft sich dann häufig mit der Prüfung auf die gesetzten magic_quotes { if (magic_quotes_gpc ()) } und führt dann die Funktion stripslashes() auf den String aus. Dieser entfernt eventuell vorhandene Maskierungen. Danach kann mit obigem PHP-Befehl neu maskiert werden. In jedem Fall ist die Maskierungen von Sonderzeichen der Mindeststandard zum Schutz vor SQL-Injektions. Wie in der Kapiteleinleitung zu erkennen ist, existieren auch Angriffsszenarien ohne die Verwendung von Sonderzeichen. Es sollte deshalb zusätzlich auf bestimmte Schlüsselwörter gefiltert werden. Hier sind besonders die Schlüsselwörter AND, OR, SELECT, UNION und ORDER berücksichtigt werden. Zudem sollten Feldinhalte auf zulässige Werte geprüft werden; Zahlenfelder sollten z.B. nur Ziffern enthalten und keine Buchstaben. Das Prüfen auf Schlüsselwörter kann u. U. problematisch sein, wenn diese als zulässige Inhalte vorkommen können ( beispielsweise als Name der Firma "Papier Union"). Einen guten Schutz bieten auch die mit der "neuen" MySQL-Extension "mysqli" eingeführten "Prepared Statements" sowie das in MySQL 5.0 neu eingeführte Feature "Stored Procedures". Beide Varianten machen enge Vorgaben für die abzuarbeitenden SQL-Statements, was Angriffe erschwert oder sogar verhindert. Ihre Behandlung sprengt aber den Rahmen dieser Einführung. 10.5 Autorisierung und Authentifizierung Für Login- und Anmeldeseiten jeder Art sollte SSL als absolutes Muss angesehen werden. Der Einsatz von SSL erfüllt zweierlei Aufgaben. Zum einen werden alle Daten zwischen Server und Client verschlüsselt übertragen, zum anderen kann der Client anhand eines sogenannten Zertifikates Ihre Identität nachprüfen. Solche Zertifikate werden von anerkannten Certificate Authorities (CA) wie z.B. VeriSign ausgestellt. 10.5.1 Man-in-the-Middle-Attacke Bei diesem Angriff leitet der Cracker den Datenverkehr (und die Verifikation eines SSLZertifikates) so um, dass ein von ihm gefälschtes Exemplar präsentiert wird, das jedoch meist nicht von einer anerkannten CA unterschrieben ist. Leitet er zusätzlich den DNS-Eintrag Ihrer Websites auf eine andere IP um, indem er (über Viren oder Würmer) die DNS-Funktion der Kunden manipuliert (Pharming), kann er Angriffe wie etwa das Abfischen von Passwörtern und Nutzerdaten (Phishing) perfektionieren. Ein von einer im Browser anerkannten CA ausgestelltes Zertifikat verhindert solche Man-in-the-Middle-Attacken. 10.5.2 Wörterbuchangriff Ein Wörterbuchangriff ist ein automatisierter Angriff gegen ein Authentifizierungssystem. Ein Hacker verwendet im Allgemeinen eine große Datei mit potenziellen Kennwörtern (zum Beispiel alle aus zwei Wörtern zusammengesetzten Begriffe der englischen Sprache), und versucht hinter einander, sich mit diesen Begriffen in ein Konto einzuloggen. Diese Art Angriff funktioniert zwar nicht bei zufällig generierten Kennwörtern, aber sie ist unglaublich erfolgreich, wenn Benutzer ihre eigenen Kennwörter wählen können. Seite 54 von 54 Einführung in die Programmierung mit PHP und MySQL.doc Die Herausforderung liegt darin, Wörterbuchangriffe gegen das Authentifizierungssystem zu verhindern. Letztendlich werden Wörterbuchangriffe die Sicherheit des Benutzers immer gefährden. Der Umfang verständlicher Kennwörter ist begrenzt und dagegen können noch so gute Regeln zur Vermeidung schlechter Kennwörter nichts ausrichten. Eine Lösung besteht darin, einen Account zu sperren, wenn es bei der Anmeldung mehrere Fehlschläge hintereinander gegeben hat. Die Implementierung ist ganz leicht. Sie können die ursprüngliche Funktion check_credentials so modifizieren, dass nur eine begrenzte Anzahl von Fehlschlägen zugelassen wird, bevor der Account gesperrt wird: Dazu schreiben Sie das Datum des letzten Fehlversuchs sowie die Anzahl der fehlerhaften Anmeldeversuche in den Nutzerdatensatz. Überschreitet die Anzahl einen vorgegebenen Wert, ist eine Anmeldung nicht mehr möglich. Die Aufhebung kann manuell oder durch einen Cronjob erfolgen, der die Anzahl der Fehlschläge für die Datensätze wieder auf Null setzt, bei denen der letzte Fehlschlag mehr als eine Stunde zurück liegt. Der Nachteil dieser Methode liegt darin, dass ein Hacker den Zugriff auf einen Account einer Person sperren kann, indem er absichtlich mehrfach ein falsches Kennwort eingibt. Als wenigstens partielle Lösung dieses Problem können Sie versuchen, fehlgeschlagene Anmeldungen an die IP zu binden. Sicherheit bei der Anmeldung zu garantieren ist ein endloser Kampf. Es gibt kein System, dass nicht missbraucht werden kann. Wichtig ist es, die potenziellen Risiken gegen den zeitlichen Aufwand und die Ressourcen abzuwägen, durch die Gefahren des Missbrauchs begrenzt werden können. 10.5.3 Social Engineering Obwohl es kein technisches Problem ist, sollte man nicht über Anmeldesicherheit sprechen, ohne das so genannte »Social Engineering« zu erwähnen. Der Trick dieser Angriffe besteht darin, den Benutzer dazu zu bringen, vermeintlich vertrauenswürdigen Personen Informationen zu verraten. Dabei wird u.a. zu folgenden Tricks gegriffen: Man gibt sich als System-Administrator der Site aus und schickt E-Mail-Nachrichten, in denen die Benutzer aus »Sicherheitsgründen« um ihre Kennwörter gebeten werden. Man kreiert eine Imitation der Anmeldungsseite und der Benutzer wird verleitet, sich auf dieser falschen Seite einzuloggen. Es wird versucht, eine Kombination dieser beiden Tricks anzuwenden. Es ist schwer zu glauben, dass Benutzer auf diese Tricks reinfallen, aber sie tun es sehr häufig. Eine Suche bei Google nach Betrügereien im Zusammenhang mit eBay bringt eine Vielzahl solcher Versuche zu Tage. Es ist gar nicht einfach, sich gegen dieses Vorgehen zu schützen. Der Kern des Problems liegt darin, dass die Angriffe nicht technischer Natur sind, sondern dass sie die Benutzer einfach nur zu Leichtsinn verleiten. Als Gegenmaßnahme kann man lediglich versuchen, Benutzer zu erziehen. Man muss ihnen klar machen, wie und warum man sich mit ihnen in Verbindung setzen könnte und Ihnen ein gesundes Misstrauen gegen die Weitergabe persönlicher Information einimpfen. Einführung in die Programmierung mit PHP und MySQL.doc 11 Objektorientierte Programmierung in PHP 5 Dieses Kapitel folgt in einer späteren Version Seite 55 von 54 Seite 56 von 54 Einführung in die Programmierung mit PHP und MySQL.doc Anhang: Verzeichnisse Abbildungsverzeichnis Abbildung 1-1: Abbildung 1-2: Abbildung 1-3: Abbildung 3-1: Abbildung 4-2: Abbildung 5-1: Abbildung 5-3: PHP1-1.htm ................................................................................................................... 3 PHP1-2.htm ................................................................................................................... 4 PHP1-3.htm ................................................................................................................... 5 PHP3-1.php ................................................................................................................... 7 PHP4-2.php ................................................................................................................. 10 PHP5-1.htm ................................................................................................................. 12 Uebung5-2.php ............................................................................................................ 13 Tabellenverzeichnis Tabelle 1: Tabelle 2: Tabelle 3: Tabelle 4: Rechenoperatoren ......................................................................................................... 9 Umwandlung von Zeichenketten in Zahlen ................................................................. 14 Vergleichsoperatoren ................................................................................................... 15 Rangordnung der Operatoren ...................................................................................... 16 Verzeichnis der Beispiel-Listings Listing 1-1: Listing 1-2: Listing 1-3: Listing 2-1: Listing 3-1: Listing 4-1: Listing 4-2: Listing 5-1: Listing 5-2: Listing 6-1: Listing 6-2: Listing 6-3: Listing 6-4: Listing 6-5: Listing 6-6: Listing 6-7: Listing 6-8: Listing 6-9: Listing 6-10: Listing 6-11: Listing 6-12: Listing 7-1: Listing 7-2: Listing 8-1: Listing 8-2: Listing 8-3: Listing 8-4: Listing 8-5: Listing 8-6: Listing 8-7: Listing 8-8: Listing 8-9: Listing 8-10: Listing 8-11: Listing 8-12: Listing 9-1: Listing 9-2: PHP1-1.htm ................................................................................................................... 3 PHP1-2.htm ................................................................................................................... 4 PHP1-3.htm ................................................................................................................... 5 PHP2-1.php ................................................................................................................... 6 PHP3-1.php ................................................................................................................... 7 PHP4-1.php ................................................................................................................... 9 PHP4-2.php ................................................................................................................. 10 PHP5-1.htm ................................................................................................................. 11 PHP5-1.php ................................................................................................................. 12 PHP6-1.php ................................................................................................................. 17 PHP6.2.php .................................................................................................................. 17 PHP6-3.php ................................................................................................................. 18 PHP6-4.php ................................................................................................................. 18 PHP6-5.php ................................................................................................................. 19 PHP6-6.php ................................................................................................................. 20 PHP6-7.php ................................................................................................................. 21 PHP6-8.php ................................................................................................................. 22 PHP6-9.php ................................................................................................................. 22 PHP6-10.php ............................................................................................................... 23 PHP6-11.php ............................................................................................................... 23 PHP6-12.php ............................................................................................................... 24 PHP7-1.php ................................................................................................................. 26 PHP7-2.php ................................................................................................................. 28 PHP8-1.php ................................................................................................................. 29 PHP8-2.php ................................................................................................................. 31 PHP8-3.php ................................................................................................................. 32 PHP8-4.htm ................................................................................................................. 33 PHP8-4.php ................................................................................................................. 34 PHP8-5.htm ................................................................................................................. 34 PHP8-5.php ................................................................................................................. 35 PHP8-6.php ................................................................................................................. 36 PHP8-7.php ................................................................................................................. 37 PHP8-8.php ................................................................................................................. 39 PHP8-9.php ................................................................................................................. 39 PHP8-10.php ............................................................................................................... 40 Erzeugung eines Zufalls-Passwortes .......................................................................... 42 Prüfung eines Passwortes auf Mindestgüte ................................................................ 43 Einführung in die Programmierung mit PHP und MySQL.doc Listing 9-3: Listing 9-4: Listing 9-5: Listing 9-6: Seite 57 von 54 Prüfung einer Nutzerberechtigung ............................................................................... 44 auth1.php - Eingabeformular Benutzeranmeldung ...................................................... 48 auth2.php - Auswertung der Nutzeranmeldung ........................................................... 49 auth3.php - Beispiel einer Rechteprüfung ................................................................... 50 Quellenverzeichnis Kunz, Esser, Prochaska: PHP-Sicherheit. PHP/MySQL-Webanwendungen sicher programmieren. 2., aktualisierte und überarbeitete Auflage, dpunkt.verlag: Heidelberg 2007