Einführung in die Programmierung mit HTML und PHP
HTTP-Parameter und CSS: Anzeige des Tic-Tac-Toe - Spielfeldes
Zentrale - Lehrgänge - Einführung HTML+PHP - TicTacToe-Anzeige

Lehrziel

Unser Lehrziel für heute: Das Tic-Tac-Toe - Spielfeld mit Steinen und Spielstand

TicTacToe - fünfter Schritt


Dazu brauchen wir:
Den Psalm mit dem "selber Experimentieren" kennen Sie bereits. Die expliziten Hinweise zum Nachschlagen in Dokumentationen gehen jetzt zurück. Es sind aber immer noch alle wichtigen Elemente verlinkt. Nur die Idee, diese Links (und Ihre Lesezeichen) auch zu benutzen, sollte sich allmählich eingelaufen haben.

Aufbau eines Spielfeldes

Bevor wir an das Anzeigen von Spielsteinen gehen können, brauchen wir etwas, wo wir die Belegung notieren können. Sowas wie ein Blatt Papier und Bleistifte - im Computer.

Die kennen Sie vom Prinzip her schon: Von der letzten Lektion. Da hatten wir Variablen als Zwischenspeicher kennengelernt. Erstmal nur für den Seiten-Titel. Aber das ändern wir jetzt. Die nächste Variablen-Anwendung ist das Spielfeld mit den gesetzten Steinen.

Wie man Zustände notiert, darf sich jeder Programmierer in jeder Situation fortlaufend aufs neue ausdenken. - Da gibt es keine Einschränkung und mindestens zig verschiedene Möglichkeiten. Ich tendiere gern zu maximaler Vereinfachung (Keep It Stupid Simple - KISS): Wir können das Spielfeld als einfache lineare Zeichenkette (statt eines zweidimensionalen "Arrays") abbilden. Da haben wir insgesamt am wenigsten Arbeit mit unseren Fingern (weil die zu programmierenden Ausdrücke einfacher werden) und wir können die Sache mit den "Arrays" - und schon gar den mehrdimensionalen - noch ein bißchen von uns wegschieben.

Zeichenketten hatten wird schon (bzw. hatten Sie in der Schule ab der ersten Klasse) kennengelernt. Auch "Text" genannt.
000000000
wäre zum Beispiel eine Zeichenkette, die unsere Zwecke erfüllen würde: Sie hat genau 9 Zeichen - für unsere 9 Felder auf dem 3 x 3 Spielbrett.

Wir wissen bereits, daß wir sie in PHP (wie auch jeder anderen Programmier- und Umgangssprache) in Anführungszeichen setzen müssen, wenn wir diesen Text als Parameter gegenüber einem Dritten benutzen wollen. Und daß wir in PHP Variablen in einer bestimmten Form zu notieren und zu benennen haben (zur Erinnerung: selfhtml-wiki)...

Wir könnten jetzt noch vereinbaren, daß Feldbelegungen in der für's Tic-Tac-Toe so volkstümlich üblichen Form von "X"en und "O"en angezeigt werden, also zum Beispiel:
$Feldbelegung = "0OXXXOOXO"
Wobei sich dann allerdings zum Draufgucken durch einen Menschen die Null als Anzeiger für ein Leerfeld schlecht macht, weil sie nicht von einem "O" (wie Otto) unterschieden werden kann und eventuell durch ein Leerzeichen oder einen Punkt als Platzhalter ersetzt werden sollte:
$Feldbelegung = ".OXXXOOXO"
Oder man setzt auf "1"en und "2"en für die Belegung:
$Feldbelegung = "021112212"

Was hier rüber kommen soll, ist: Wie SIE FÜR SICH ein Modell anlegen, ist komplett Ihr Bier! Dies hier ist so ziemlich das einfachste Modell eines Objekts (Spielfelds), über das man in der Programmierung stolpern kann. Und Sie sehen bereits hier an diesem an Trivialität kaum mehr zu überbietenden Beispiel, welche Freiheiten Sie haben. Machen Sie's also, wie immer SIE lustig sind, und halten Sie sich dann aber auch an Ihr EIGENES Modell!

Übung zum Spielfeld-Modell
Aufgabe: Ab in Ihren Code damit! (Ja: Diese einzelne Zeile!)
Und legen Sie dazu für diese Lektion eine extra Datei an, die Sie aus dem Endzustand der letzten Lektion kopieren!
Geben Sie der Datei einen sinnvollen Namen (zum Beispiel "tictactoe-spielfeld-x.php" mit x als fortlaufende Nummer )!
Das Ergebnis sollte in etwa SO aussehen...

Tipp: Es ist generell sinnvoll, bei einer Entwicklung, die sich über längere Zeit hinzieht, zwischendurch Kopien anzulegen und zu archivieren, damit man bei eventuellen schweren Fehlern, in die man sich verrannt hat, wieder stufenweise zurück springen kann. Tageweise und eventuell stundenweise würde ich empfehlen, mit einem Archivierprogramm wie Winzip/7zip/WinRar oder etwas analogem solche Schnappschüsse anzufertigen. Gerade als Anfänger steht man oft vor der Situation, daß etwas plötzlich nicht mehr funktioniert, obwohl das, was man zuletzt gemacht hatte, mit diesem Ausfall in gar keiner Verbindung steht oder zu stehen scheint. Weil der EIGENTLICHE Fehltritt schon etwas länger zurück liegt, wo man ihn aber übersehen hatte. Bei sowas ist es ungemein entspannend, einfach mal ne ältere noch funktionierende Version danebenlegen und nachgucken zu können, worin denn nun der entscheidende Unterschied liegen mag...
Statt fortaufend hochzuzählen können Sie natürlich auch zum Programmieren immer den gleichen Dateinamen beibehalten (insbesondere wenn Ihre Datei bereits von anderswo verlinkt ist), aber dafür die Archive mit Datum und Uhrzeit versehen (was Sie eh tun sollten).

Anzeige der Spielsteine

Gut, das Spielfeld haben wir "modelliert" (war ne gigantische Aktion, gelle?!). Jetzt wollen wir aber auch, daß diese Belegung auf dem Spielbrett angezeigt wird!


Wie wollen wir die Spielsteine überhaupt anzeigen lassen?
Sie dürfen natürlich auch noch ganz andere Ideen entwickeln! Man könnte zum Beispiel auch den gesamten Feldhintergrund einfach einfärben. Obwohl: Steine sehen irgendwie hübscher aus, oder? Ich jedenfalls werde bei den Beispielen von GO-Steinen ausgehen...

Generell haben Sie bei Darstellungen mit Bildchen natürlich mehr Gestaltungsspielraum, wogegen Darstellungen mit einfachen Buchstaben geringfügig leichter zu programmieren sind. (Aber nicht in wirklich relevantem Maße - weshalb ich Ihnen dies in diesem Fall nicht nahelegen werde...)

Ich setze also an, daß wir uns auf Bildchen geeinigt haben...

Wie kommt man an solche Bildchen heran?
  • Nun kann man sich diese einfach selbst herstellen - mit einem Programm wie Gimp zum Beispiel.
  • Oder man sucht sich welche mit Google...
  • Oder man nimmt sich die aus dem Lehrgang: Hut schwarz  Hut weiß

Wie werden Bildchen in HTML angezeigt?
Kleiner Abstecher zu HTML...
<img src="Hut-schwarz.png"/>
=> Hut schwarz
Wobei man natürlich die Datei, die man da als Bild verlinkt hat, auch dort ablegen muß, wo man hin verlinkt hat. In diesem Fall wird die Datei im selben Verzeichnis erwartet wie die Datei, in der das <img>-Element steht. Falls man die Bilder woanders ablegen möchte, muß man den Pfad zur Datei in den Dateinamen aufnehmen. Entweder als relativen Pfad - wenn das Bild in einem Nachbarverzeichnis liegt - oder als absolute URL, wenn man es sich direkt aus dem Internet zieht (was ich eher nicht empfehle ). Feinschliff an den Icons brauchen wir noch nicht.
Wie setzen wir die Nullen und Einsen in diese Bildchen um?
OK, da führen schon wieder ganze Straßennetze nach Rom...

Wir wollen, daß jedes der Einzelfelder des Spielfeldes "sein" Zeichen aus der Zeichenkette bekommt. Dazu müssen wir für jedes Einzelfeld...
ein Zeichen aus der Zeichenkette herausschneiden! Und das geht so...
$Feldbelegung[0] // für das Zeichen an Position 0
$Feldbelegung[1] // für das Zeichen an Position 1
$Feldbelegung[2] // für das Zeichen an Position 2
...
Die Indizes fangen bei Null an. Die eckigen Klammern [] zum Herausschneiden von Einzelteilen aus einer Sammlung werden Ihnen noch häufig begegnen. Das nehmen Sie als generelle Methode nebenbei mit!

Wir wir solche Werte-Ausdrücke in den HTML-Code kriegen, wissen Sie noch?
Für eine einzelne Zelle der Tabelle könnte das SO aussehen:
<td><?=$Feldbelegung[0]?></td>
...Wobei das allerdings mit ganz schön Getippe einhergehen würde. Obwohl: Sind ja nur 9 Zellen...
Also: Ich empfehle es jetzt nicht unbedingt, aber Sie DÜRFEN das natürlich mal eben schnell in Ihrem Code probieren...
Wir machen das aber eh gleich besser (vor allem kürzer)... Weil: Wir wollen ja gar nicht die Ziffern bzw. Zeichen auf den Spielfeldern zu sehen bekommen! Wir wollen hübsche Bildchen!

Icons statt einfacher Zahlen oder X-O's
Wir wollen jetzt also für jedes einzelne Feld folgende Zuordnung ausführen (wobei ich von meiner gewählten Kodierung mit 0-1-2 ausgehe):
Wenn im Feld Zeichen '0' steht, dann ins Feld ausgeben: '' (...ja, neee: nix!)
Wenn im Feld Zeichen '1' steht, dann ins Feld ausgeben: '<img src="Hut-schwarz.png"/>'
Wenn im Feld Zeichen '2' steht, dann ins Feld ausgeben: '<img src="Hut-weiß.png"/>'
Fallunterscheidung
Fallunterscheidungen sind das technische Mittel, wie Sie Ihr Programm flexibel machen und auf Umgebungseinflüsse reagieren lassen. Sie gehören mit Techniken zur Vermeidung von Wiederholungen im Programmcode zu den allgemeingültigen Grund-Techniken der Programmorganisation. Restlos alles, was sonst auf der Welt der Programmierung an organisatorischen Techniken erfunden wurde, dient letztlich der Umsetzung genau dieser zwei Ziele:
  • Programme flexibel machen
  • Redundanzen (alias Wiederholungen) vermeiden
Und zwar jeweils für die beiden Aspekte:
  • Daten und
  • Programmablauf
Egal, welche Programmiersprache der Welt Sie sich greifen: In deren Sprachkernen werden Sie ausnahmslos immer ziemlich genau rund 10 Formulierungsmittel finden, die diese insgesamt 2*2 Kategorien von Organisationsmitteln bedienen (und dort zum Teil zwei, drei Alternativen bereitstellen). Mal ein bißchen mehr, mal ein wenig spartanischer, aber nie vom Prinzip abweichend.

Was wir jetzt hier benötigen, ist also eine Fallunterscheidung, und zwar ENTWEDER für Daten ODER für Programmablauf. Ich zeige Ihnen alle in PHP verfügbaren Varianten... Alle machen im Resultat exakt ein und dasselbe...
SIE wählen bitte für sich EINE davon aus! Wir werden im Laufe der Zeit sowieso noch über alle stolpern.
switch-case
if - else
Array-Index

Fallunterscheidung im Programmablauf - "switch-case"-Technik:
switch($feld)
{
    case '1': echo '<img src="Hut-schwarz.png"/>'; break;
    case '2': echo '<img src="Hut-weiß.png"   />'; break;
    default : echo '';
}
Bei einem switch steht vorn in der switch-Anweisung in der runden Klammer das Datenelement, dessen Wert als Testwert zum switchen ausgewertet wird. Hier: ($feld)
Danach folgt ein Block von case-Zweigen in geschweiften Klammern {}.

In den case-Zweigen steht hinter dem case jeweils der Wert, den jener Testwert annehmen muß, damit das Programm an dieser Stelle weiterläuft.

Die ganze Sache ist darauf ausgelegt, daß mehrere case-Zweige zusammengefaßt werden können (die Erfinder hätten auch die case-Syntax aufbohren können, aber das hätte sowohl die Interpreter/Compiler als auch die Arbeit der Tutoren erschwert, also haben die das sein gelassen).
Deswegen läuft der Programmfluß von einem case zum nächsten einfach durch, solange man ihn nicht explizit beendet.
Dafür sind die "break"-Aneisungen gedacht.

Ein "default"-Zweig wird angesprungen, falls er definiert ist und keiner der case-Zweige gepaßt hat.

-> PHP-Referenzdokumentation
Fallunterscheidung im Programmablauf - "if - else"-Technik:
if     ($feld == '1') echo '<img src="Hut-schwarz.png"/>';
elseif ($feld == '2') echo '<img src="Hut-weiß.png"   />';
else                  echo '';

In einer if-else-Konstruktion wird immer genau einer der definierten Zweige ausgeführt. Es können beliebig viele elseif-Zweige hintereinander verschachtelt werden. Ein abschließender else-Zweig nimmt sich aller restlichen Fälle an. Bei jedem if steht in runden Klammern ein boolscher Ausdruck, der wahr sein muß, damit der Zweig ausgeführt wird. Da boolsche Ausdrücke in der Praxis größtenteils durch Vergleiche erzeugt werden, lohnt sich zum Verstehen für die Praxis ein Blick in die Vergleichsoperatoren! Und da solche Ausdrücke sehr häufig über logische Operatoren (allen voran "UND", "ODER", "NICHT") verknüpft werden, lohnt sich ein Blick zu denen natürlich ebenfalls!

Bei dieser Konstruktion ist es so, daß hinter jedem "if" bzw. "else" oder "elseif" immer nur GENAU EINE EINZIGE Anweisung stehen kann. Man kann das bei Bedarf aber aufbohren, weil es in allen C-artigen Sprachen (zu denen PHP gehört) die Regel gibt, daß eine einzelne Anweisung immer auch durch einen Block von Anweisungen ersetzt werden kann. Das ginge folgendermaßen:
if (irgendein boolscher Ausdruck)
{
    Anweisung 1;
    ...
    Anweisung n;
}
Ein Block ist ein Konstrukt aus geschweiften Klammern {...}. So simpel, so effektiv. Diese Regel trifft immer und überall zu. Sie können so viele Blöcke ineinander schachteln wie Sie wollen und an wirklich jeder Stelle, wo eine einzelne Anweisung hinpaßt, einen Block hinsetzen.

-> PHP-Referenzdokumentation
Fallunterscheidung in Daten - "Array-Index"-Technik:
$icons = array
(
    '0' => '',
    '1' => '<img src="Hut-schwarz.png"/>',
    '2' => '<img src="Hut-weiß.png"   />',
);

echo $icons[$feld];

Die Fallunterscheidung bei Daten erfordert das gleichzeitige Einführen des Array-Konzepts (das ist eines der Organisationsmittel zur Vermeidung von Redundanz in Daten): Arrays sind eine Form von Datensammlung. Stellen Sie sich das Zeug wie sehr dehnbare Taschen vor. Sie können in einem Array, wenn Sie lustig sind und einen entsprechend mit RAM ausgestatteten Rechner haben, Milliarden von Daten sammeln. Die Array-Variable gestattet, all diese Daten im Stück "anzufassen" und zum Beispiel zu kopieren oder irgendwohin mitzuschleppen.

Arrays gestatten, die enthaltenen einzelnen Daten nach einem Index-System hineinzulegen und herauszunehmen. Dies ist ein Abbildungsprozeß, der es gestattet, sehr effektiv Daten aus der einen in eine andere Darstellung zu transformieren ("assoziatives Array" genannt). Diese spezeille Eigenschaft machen wir uns in diesem speziellen Anwendungsfall zunutze. Syntaktisch funktioniert das mit eckigen Klammern [], wenn es um einzelne Daten geht, oder beim Definieren eines Arrays im Block mit dem Zuordnungs-Pfeil "=>" - wie hier im Beispiel gezeigt. Die Syntax sollte intuitiv selbsterklärend sein.

Arrays in PHP gestatten beliebige Daten als Indizes (Schlüssel), intern werden alle Indizes eh zu einem Einheitsbrei vermanscht, der mit "Hashing" arbeitet. Für technische Details brauchen Sie sich nicht zu interessieren, solange Sie nicht Opfer eines Hacker-Angriffs werden. Gehen Sie davon aus, daß PHP beliebige Schlüssel zu beliebigen Daten zuordnen kann und basta!

-> PHP-Referenzdokumentation



Beachten Sie bitte:
  • Die in hellrot gezeichneten Operator-Zeichen sind syntaktische Pflicht - genau so, wie sie da stehen.
  • Die dunkelroten Schlüsselworte zur Organisation ebenfalls.
  • Programmiersprachen sind FORMALE Sprachen: Die Syntax-Regeln sind ZWINGEND STRIKT einzuhalten! Jede Lapidarität führt zu unausführbarem oder zumindest stark fehlerhaftem Code! Wenn da irgendwo eine RUNDE Klammer steht, können Sie nicht einfach so - weil heute Freitag 12 Uhr ist und die Sonne scheint - eine GESCHWEIFTE Klammer an der Stelle einsetzen! Und umgekehrt! Wo ein KOMMA steht, DARF KEIN SEMIKOLON stehen! Und umgekehrt! Wo ein Operator-Zeichen stehen MUSS, MUSS eins stehen und darf nicht einfach Luft gelassen werden!

    Lediglich bei Leerzeichen, Tabulatoren und Zeilenumbrüchen (sogenanntem "Whitespace") gestatten HTML, CSS und PHP (und fast sämtliche sonstigen Programmiersprachen) Ihnen die völlig freie Verwendung (aber: Sie dürfen natürlich Worte und Operatoren, die aus mehreren Zeichen bestehen (üblicherweise denglisch "Token" genannt), NICHT zerhäckseln!)

    Da hilft nix: Da MÜSSEN Sie durch - bei JEDER Programmiersprache bislang.
    Gucken Sie GENAU hin!

    Besser noch: Benutzen Sie Copy & Paste wann immer es geht, um Ihre Flüchtigkeitsfehler in den Griff zu kriegen! Nach einigem praktischen Üben werden die Fehler etwas weniger werden. Copy & Paste würde ich aber deshalb trotzdem immer dringend empfehlen!

  • Die Ziffern-Zeichen, mit denen Sie die Belegung Ihres Spielfeldes beschreiben, können SIE festlegen wie Sie lustig sind. Sie müssen sich bloß in Ihrem gesamten Programm an eine Festlegung halten. Sie wären sicherlich in gesundem Maße verwundert, wenn Sie mit einem Partner am Spielbrett sitzen würden, mit schwarzen und weißen Steinen spielten, und Ihr Partner urplötzlich einen Springer aus einem Schachspiel auf Brett setzen würde!
  • Ich bin zur Vereinfachung davon ausgegangen, daß vor dem Ausführen der Fallunterscheidung (für jedes einzelne Feld) das Zeichen, welches wir vorher mit $Feldbelegung[Feldnummer] gegriffen hatten, in die Zwischenvariable $feld übergeben wurde.

Soooo..., jetzt müssen wir eine Entscheidung treffen, um einigermaßen eine gemeinsame Linie weiter verfolgen zu können...
Ich tendiere ja zur Array-Variante, und die empfehle ich Ihnen jetzt mal einfach.

Arbeit verkürzen, Übersicht behalten: Schleifen
Damit sind wir aber noch nicht ganz fertig: Wir hätten auch mit der Array-Variante immer noch die Finger wund zu tippen, und die HTML-Tabelle sähe auch nicht mehr so übersichtlich aus. Es bietet sich an, die Daten in einer sogenannten "Schleife" zu verarbeiten und dabei nochmal anderen Zwischenvariablen zuzuweisen, denen wir sehr kurze Namen verpassen, so daß wir die sehr bequem und fingerschonend in die HTML-Tabelle eintragen können. Die Schleife ist eines der Instrumente zur Vermeidung von Wiederholungen im Programm.

Das geht folgendermaßen:
$icons = array
(
    '0' => '',
    '1' => '<img src="Hut-schwarz.png" alt="X"/>',
    '2' => '<img src="Hut-weiß.png"    alt="O"/>',
);

for($i = 0; $i < 9; $i++)
{
	$GLOBALS['f'.$i] = $icons[$spielfeld[$i]];
}
Die Sache mit dem icons-Array kennen Sie schon von der Fallunterscheidung. Ebenso von dort den Ausdruck "$icons[$spielfeld[$i]]". Hier wird das nun noch um eine Schleife ergänzt...

Da wäre die Syntax erwähnenswert: Eine Schleifendefinition mit "for", wie Sie sie hier sehen, enthält in der "for-Klammer" (damit meine ich die runden Klammern hinter dem "for") drei Ausdrücke, die mit Semikolons getrennt sind. Die bedeuten nacheinander:
for (Start-Aktion; Test-Aktion; Wiederhol-Aktion)
Die hier "Aktionen" genannten Dinge müssen Ausdrücke sein, die Werte liefern. Normale Anweisungen, die nicht auch als Wertelieferanten auftreten, können nicht eingesetzt werden. Braucht man aber auch nie. Üblicherweise werden for-Schleifen genau so eingesetzt, wie ich Ihnen hier im Beispiel gezeigt habe: Zum Durchzählen von Datensammlungen oder Koordinaten oder ähnlichem Zeugs. Dazu braucht man gewöhnlich eine Zählvariable (auch gern Laufvariable genannt). Und für die braucht man einen Startwert, einen Vergleich mit einem Endwert und ein Hochzählen (Inkrement) nach jedem Schleifendurchlauf. Und genau für diese drei Dinge sind die drei Plätze in der for-Schlefen-Konstruktion vorgesehen.

Die Zuweisung zur Variablen "$GLOBALS" bewirkt die gewünschte Verkürzung der Variablennamen. Die sogenante "Super-Globale" Variable "$GLOBALS" ist das Sammelbecken sämtlicher jemals existierender Variablen in der globalen PHP-Umgebung. Es ist ein Array. In das automatisch (wenn nicht wie in diesem Beispiel explizit) JEDE globale Variable eingetragen wird. Und da wir noch nichts mit "lokalen" Umgebungen zu tun hatten, ist sie für uns erstmal die geballte Macht sämtlicher PHP-Daten.
Was wir hier machen, ist das Zusammensetzen eines Array-Schlüssels aus einer extrem kurzen Vorsilbe ("f") und der Nummer des Feldes. Und was man in die $GLOBALS unter irgendeinem Namen "x" als Element einträgt, ist anschließend sofort unter dem Namen "$x" (mit vorangestelltem Dollarzeichen) als Variable wieder aus der Luft greifbar. Weil - nochmal wiederholend - PHP JEDE Variable, die man global anlegt, implizit als Element dieses Arrays "$GLOBALS" behandelt. Wir haben also nach dieser Schleife die Variablen $f0, $f1, ..., $f8 zur Verfügung. Alle belegt mit entweder nichts oder dem HTML-Text für das Icon des Spielsteins, welcher auf diesem Feld liegt.

Letzter Schritt: Die 9 Variablen in die HTML-Tabelle eintragen!
Das ist jetzt nur noch eine lapidare Kleinigkeit:
<table>
    <tr><th>    </th><th> s1     </th><th> s2     </th><th> s3     </th><th>    </th></tr>
    <tr><th> z1 </th><td><?=$f0?></td><td><?=$f1?></td><td><?=$f2?></td><th> z1 </th></tr>
    <tr><th> z2 </th><td><?=$f3?></td><td><?=$f4?></td><td><?=$f5?></td><th> z2 </th></tr>
    <tr><th> z3 </th><td><?=$f6?></td><td><?=$f7?></td><td><?=$f8?></td><th> z3 </th></tr>
    <tr><th>    </th><th> s1     </th><th> s2     </th><th> s3     </th><th>    </th></tr>
</table>

Wir hätten vorhin auch die komplette Tabelle in der Schleife konstruieren lassen können, aber das hätte dann bei weitem nicht mehr so einfach ausgesehen. Wenn Sie aber mal das Spiel aufbohren wollen sollten, werden Sie um sowas nicht umhin kommen, denn wer will schon für ein komplettes Go-Brett (oder gar noch größer) hunderte Felder per Hand ausfüllen?

Übung zur Anzeige der Spielsteine
Aufgabe: Übernehmen Sie die paar wenigen Codezeilen, die wir uns soweit angeguckt haben, in Ihren eigenen Code! Sie dürfen jetzt auch ruhig ein wenig damit experimentieren!

Aufgabe: Setzen Sie zum Schluß Spielsteine in Ihr Spielfeld!
Also tauschen Sie einige Nullen gegen Einsen oder Zweien aus!

Tipp: Provozieren Sie auch ruhig mal, gezielt Fehler aus einem korrekt funktionierenden Zustand heraus zu erzeugen, um gezielt die Reaktionen bei verschiedenen Fehlerarten kennenzulernen!
Sie bekommen Fehlermeldungen um die Ohren geschmissen, die man am besten dadurch interpretieren lernt, daß man einzelne Fehler gezielt erzeugt und sich aus diesem Wissen heraus einen Reim auf die resultierenden Fehlermeldungen zu machen versucht... Das muß man nun zwar nicht exzessiv betreiben, aber wenn sich die Gelegenheit bietet (und Sie machen mit Sicherheit mindestens Flüchtigkeitsfehler), kann man's ja mal zwischendurch als Einlage zum Entspannen mißbrauchen.

Das Ergebnis sollte in etwa SO aussehen...

Feinschliff

Tipp: Sehen Sie mal genau hin! GANZ genau! Ziehen Sie mal Ihren Firebug zu Hilfe , damit der Ihnen zeigt, wie GENAU die Geometrie der Felder auf Ihrem Spielfeld aussieht! Wer ein sehr gutes Augenmaß hat, erkennt es auch von allein...

Die Felder sind nicht wirklich rechteckig!!
Himmel-Arsch-und-Zwirn!

OK, um DAS zu erklären, müßten wir einen kleinen Marathon durch die Eingeweide von CSS vornehmen. Es hängt mit den geringfügig akademisch anmutenden Regeln von CSS zusammen, die die vertikale Ausrichtung von Bildern und Text (und anderen Elementen) betreffen. Da geht es darum, daß alle Elemente (auch Bilder) standardmäßg vertikal immer an ihren "Text-Grundlinien" aneinander ausgerichtet werden. Sollen.

Nun haben Bilder aber gar keine Text-Grundlinie! In Ermangelung dessen wird bei denen einfach deren Boden-Linie als deren "Text-Grundlinie" angesetzt. Was darauf hinausläuft, daß Bilder TATSÄCHLICH mit ihrer BODEN-Linie an der TEXT-Grundlinie von umgebendem Text ausgerichtet werden. Ätsch!

Und was hat das jetzt mit UNSEREN Bildern zu tun? Fragen Sie sich vielleicht?! Weil: WIR haben doch nicht mal ein nacktes LEERZEICHEN neben das Bild gesetzt! Das steht da GANZ ALLEIN im Walde! Da ist doch gar nix zum gegenseitigen Ausrichten da!!

Ja, und da kommt die Akademiker-Natur ein zweites Mal durch: Die Regelerfinder haben das so festgelegt, daß eine Ausrichtung immer an POTENTIELL vorhanden sein könnender Umgebung - sprich Text - stattfinden muß. Selbst wenn die gar nicht existiert. Und weil die Textgrundlinie allein vom Typ des standardmäßig an der betreffenden Stelle des HTML's gelten WÜRDENDEN Fonts abhängt und NIEMALS mit der Bodenlinie der umgebenden Box identisch ist, hängt die Unterkante unseres Steinchens eben ein paar Pixel über der Unterkante des Feldes in der Luft!

Abhilfe? Na: entweder durch "vertical-align" fürs Bild auf "bottom" (oder "top" - geht in unserem Fall gleichermaßen) oder das "display" des Bildes auf "block" - wodurch es platzfressend wird und DANN (...Akademiker halt, gelle?...) auf einmal NICHT MEHR als potentiell von Text benachbart gilt.

Aufgabe: Also in den CSS-Regelsatz aufnehmen:
.tictactoe img	{vertical-align: bottom;}

Und warum mache ich so einen Aufriß um so ein paar Pixel?
Modifizieren Sie die Rahmenbedingungen geringfügig:
  • Machen Sie die Schriftgröße größer!
  • Verkleinern Sie die Feld- und Bildgröße!
In beiden Fällen brauchen Sie bloß gewisse Größenordnungen erreichen und es fällt sofort negativ auf. Aber auch so wirkt es reichlich komisch, wenn urplötzlich beim Setzen des dritten Steines in einer Reihe die Reihenhöhe - ohne jeden weiteren erkennbaren Grund - zusammenschrumpft. Dann nämlich geht die Positionierlogik urplötzlich nicht mehr davon aus, daß da potentiell Text sein könnte, an dessen Basislinie sich die Bilder ausrichten könnten. Und sowas ist einfach albern! Man läßt das Layout nicht unbegründet urplötzlich irgendwelche vollkommen unmotivierten Hüpfer machen!

Kleinvieh macht auch Mist!

Solche marginalen Fehler läppern sich, wenn sie konsequent gesammelt werden, zu Layouts, die schlicht unbeherrschbar sind: Die zum Vermurksen neigen, sobald man die Seite auch nur in einer anderen Bildschirmauflösung, einer anderen Größe des Browserfensters oder gar in einem anderen Browser betrachtet. Gewöhnen Sie sich von vornherein "sauberes" Programmieren auch in CSS an, dann haben Sie auch keine (...na gut: weniger) Probleme beim Wechsel des Browsers!

Das Ergebnis sollte jetzt GANZ korrekt in etwa SO aussehen...

...Testen Sie das mal mit anderen Browsern!
Firefox: OK
Opera: OK
Chrome:
Safari:
Internet Explorer:

Die Ursache ist, daß es bei der Berechnung der Zellenausdehnung momentan zwei Lager gibt hinsichtlich des Einbeziehens von "Border" und "Padding". Firefox (den ich hier momentan als Standard-Werkzeug bei Ihnen voraussetze) und Opera sind da leider auf der falschen Seite. Sie behandeln die Tabellenzellen inkonsistent zwischen horizontaler und vertikaler Richtung: Horizontal rechnen sie so, wie es der gesunde Menschenverstand und auch der Standard vorschreiben, vertikal nicht.

Wobei der CSS-Standard durchaus beide Varianten als Möglichkeit vorsieht. Das wird dort "box-sizing" genannt.
box-sizing: content-box border und padding
kommen außen zur width hinzu
Das ist die standardmäßig normale Behandlung, die auch von allen Browsern angewandt wird, selbst von Firefox und Opera in der KORREKT behandelten Dimension (horizontal).
Sie sollte sich eigentlich längst durchgesetzt haben.

box-sizing: border-box border und padding
sind in der width mit enthalten
Das kann man explizit anfordern. Vom Firefox und Opera wird dies aber auch von allein angewandt, und zwar inkonsistent NUR in vertikaler Richtung und NUR bei Tabellenzellen.
Früher (in den 90er Jahren des letzten Jahrtausends) soll dieses mal die standardmäßige Berechnungsart gewesen sein. Von daher kommen wohl auch diese Inkonsistenzen, weil irgendwelcher Programmcode noch aus der damaligen Zeit stammt und noch nicht überarbeitet worden ist.

Wir können das aber inzwischen zurechthämmern: Das box-sizing kann explizit erzwungen werden, wenn auch ausgerechnet beim Firefox noch immer nur mit browserspezifischem Prefix (alle anderen können das besser). Danach sieht unser CSS doch konsistenter aus:
td,th
{
    box-sizing:     content-box;
    -moz-box-sizing:content-box;
    /* Das Standard-box-sizing bei Tabellen-Zellen ist zwischen         */
    /* verschiedenen Browsern und vertikal/horizontal inkonsistent.     */
    /* Mit dieser Einstellung hier wird (beinahe) Konsistenz erzwungen. */
}

.tictactoe td
{
    ...
    width:          100px;
    height:         100px;
    ...
}

JETZT sieht die Sache endlich WIRKLICH konsistent aus...

Solche Stellen dürfen Ihnen als Anregung dienen, sich selbst persönliche globale CSS-Dateien anzulegen, in denen Sie nach eigenen Bedürfnissen jene CSS-Inkonsistenzen, die bei Ihnen in Ihren persönlichen Layouts störend wirken, zurechtbiegen! Es gibt sogar extra Seiten im Internet, die sich speziell mit diesem Ausbessern von CSS-Inkonsistenzen zwischen den Browsern beschäftigen: normalize.css (oder google "cross browser css reset")
Denken Sie dabei daran, daß das alles nur von Menschen gemacht ist und die Sachen insgesamt in Entwicklung sind. Diese Vorlagen sind natürlich auch nicht vollständig, so daß Sie in aller Regel eigene Ergänzungen anbringen müssen. Zum Beispiel behandelt die verlinkte "normalize.css" nicht unser speziellen box-sizing-Problem bei Tabellen-Zellen.

Das ist ja schon FAST das angestrebte Endziel. Es fehlt nur noch ein WINZIGER Schritt: Die Spielfeldbelegung zu einem Server übertragen, so daß mehrere Spieler auf demselben Spielfeld gegeneinander spielen können...

Machen Sie aber jetzt erstmal eine ausgiebige Pause und verdauen Sie das gelesene und experimentierte erstmal!

Datenübertragung zwischen Browser und Server

Wir werden heute noch einen ersten Schritt hin zu einem Multiplayer-Game unternehmen. Obwohl wir uns das "richtige" Multiplayern noch für später lassen. Wir werden es bis zur Lektion 7 als abgespeckte Einzelplatz-Version benutzen, bei der aber dennoch bereits abwechselnd zwei Spieler mitmachen können. Ab Lektion 7 geht's nachher erst so richtig los...

Solange wir keine Multiplayer-Fähigkeit im Auge haben, bräuchten wir die Server-Geschichte mit dem PHP gar nicht. Als autistisches Einzelplatz-Spiel kriegt man es eventuell schneller in Javascript (oder jeder beliebigen anderen Programmiersprache außerhalb eines Browsers) aufgesetzt. Zumindest wenn man ein paar Programmier-Vorkenntnisse hat.

Aber wir wollen ganz zielgerichtet letztlich zur Multiplayer-Fähigkeit. Natürlich vorrangig, um die Einsatzmöglichkeiten von PHP kennenzulernen und Lust auf mehr zu machen. Und um Einblick in die Funktion des Zusammenspiels zwischen Browsern und Servern zu bekommen.

Daten zum Server übertragen
Eine Art der Datenübertragung zu Servern kennen Sie schon lange: Indem Sie in die URL-Zeile Ihres Browsers eine URL eintragen oder einen Link auf einer Seite anklicken oder ein Lesezeichen anklicken, wird jedesmal eine Anfrage ins Nirwana des Internets geschickt, die irgendwie bei einem Server landet, der irgendwie auf den Servernamen in Ihrer URL paßt. Und dann wertet der diese URL irgendwie aus. Grundwissen jedes Internet-Surfers!

Das ist schon mal ein guter Anfang. Da bauen wir drauf auf! Nehmen wir uns erstmal eine URL im Detail vor... Sie könnten Sich mal die URL des letzten "SO aussehen..."-Links weiter oben kopieren und in Ihrem Editor in ein leeres Dokument (oder auch eine neue PHP-Experimentierdatei) einfügen...
http://harryboeck.dyndns.org/Bildungswerk/viewphp.php?phpview=tictactoe-spielfeld-3-3.php#position

Ja, sehr bunt. Das ist richtig so. Es hat Bedeutungen:
http: Protokoll Der Name für das Verfahren, nach dem Server und Client (Browser) miteinander Daten austauschen. Für uns sekundär interessant: Es sagt uns nur, wo wir gegebenenfalls nachschlagen müßten, um technische Details bei Problemen klären zu können.
// Top-Level-Verzeichnis In allen gängigen Dateisystemen bedeutet ein Doppelslash am Anfang eines Dateipfades, daß es in der absolut toppigsten Ebene losgeht, die das System zu bieten hat. Bei Windows etwa oberhalb von Laufwerken und benachbarten Geräten im Netzwerk.

Da "http:" sich aufs Internet bezieht, bedeutet "//" die oberste im Internet greifbare Ebene. Darunter hängt das GESAMTE Internet.
harryboeck.dyndns.org Server-Name Das ist die erste Ebene im Internet, in der Zugriffe auf einzelne Rechner im Internet verteilt werden. Hier steht entweder der "schöngeistige" Rechnername oder ein Nummer des Rechners (genannt "IP"-Nummer oder kurz "IP" ("Internet Protokoll")).

Die Auswertung dieses Elements umfaßt den gesamten Prozeß des Weiterleitens der Daten im Internet bis zum Eintreffen am Server-Rechner. In unseren Fällen ist das der Apache-Server vom XAMPP-Paket.
Nach dieser Auflösung bekommt der betreffende Server ein Datenpaket in seine Eingangs-Schublade gelegt, in dem diese gesamte URL nochmal mit all ihren Bestandteilen aufgeführt ist.

An der Stelle KANN (falls vom Admin so eingerichtet) der Server nochmal den Servernamen auswerten, der in der Anfrage steht, um unterschiedlich zu reagieren. Je nachdem, ob sie ihn dann mit "Eure Majestät", "Eh, du Saftsack" oder "Nummer 127.0.0.1 vortreten!" angesprochen haben, soll der halt adäquat antworten dürfen. Nennt man akademisch auch "virtuelle Server".

Danach hat der Server sich dann endgültig entschieden, unsere Anfrage in irgendeine Kategorie einzuordnen und es geht mit der Auswertung von Pfad und Ressource weiter...
/Bildungswerk/ Pfad einer Ressource auf dem Server Dieser Pfad bezieht sich auf die Dateihierarchie, die der Server in Eigenregie verwaltet. In aller Regel ist dies nur ein Ausschnitt (ein Unterverzeichnis) irgendwo im Dateisystem des Server-Rechners (obwohl man einen Server auch so konfigurieren kann, daß er die gesamten Datenträger eines Rechners nach außen bereitstellt). Bei Ihrem XAMPP-Apache-betriebenen "localhost" geht diese Dateihierarchie normalerweise im Verzeichnis "c:\xampp\htdocs\" - auch "Server Root Directory" genannt - los (siehe Übung vom ersten Tag).

Ein Server kann auch, wenn er so programmiert bzw. konfiguriert ist, eine ganze Hierarchie von Ressourcen komplett in Eigenregie und am Dateisystem des beheimatenden Operationssystems vorbei organisieren. Oder die Anfragen nach einer noch ganz anderen Methode behandeln. Da gibt es keinerlei Vorschrift, wohl aber übliche eingelaufene Pfade... Beim Apache jedenfalls werden Pfad und Ressource auf das Dateisystem, und zwar beginnend im "Server-Root"-Verzeichnis, abgebildet.
viewphp.php Ressource auf dem Server Der Name einer Datei oder zum Beispiel eines Java-Programms oder wie auch immer der konkrete Server die Anfragen zu konkreten Ressourcen zuordnet.
Nach dieser Auflösung hat der Webserver selber nichts mehr mit der Anfrage zu tun:
  • Falls es sich um eine "einfache" Datei gehandelt haben sollte, wird die in einer Antwort ausgeliefert und fertig.
  • Falls es sich um ein Programm gehandelt haben sollte (was bei uns im Fall von "PHP"-Dateien so ist), wird das betreffende Programm gestartet und übernimmt selber die weitere Verarbeitung der Anfrage-Parameter...
?phpview=tictactoe-spielfeld-3-3.php Eine "Anfrage" an das konkrete verarbeitende Programm Ein auf dem Server laufendes Prgramm kann diese "Anfrage" (alias "query" auf englisch) entgegennehmen und auswerten. Eine solche "query" beginnt immer mit einem Fragezeichen.

Sie besteht aus "Name=Wert" - Konstrukten. Im Beispiel ist ein einzelnes solches Konstrukt eingebaut. Es können auch beliebig viele weitere solche Konstrukte angehängt werden. Wobei alle folgenden Konstrukte mit dem Trennzeichen "&" angehängt werden müssen.

Und man kann übrigens den Wert weglassen. Und wenn der fehlt, darf auch das Gleichheitszeichen wegbleiben. Das Fragezeichen darf auch ganz allein stehen, kann in dem Fall aber auch ganz weggelassen werden.

Es versteht sich von selbst, daß in den Namen und Werten keinerlei Zeichen vorkommen dürfen, die als Steuerzeichen in URL's verwendet werden, also namentlich "/?&=" sind tabu! Wir werden noch Mittel und Wege kennenlernen, damit trivial umzugehen...
#position Eine Position im anzuzeigenden Dokument, die sichtbar sein soll Die Kennzeichnung der Position wird mit dem Operator "#" vom Rest der URL getrennt. Browser sind verpflichtet, die mit der Kennzeichnung identifizierten Stellen im Dokument in den Sichtbereich zu scrollen, wenn das Dokument größer ist als letzterer. Die zugeordneten Stellen können Elemente mit "name"- oder "id"-Attribut sein.

Uns wird dieser Teil erstmal nicht interessieren. Für Foren, Blogs, Chats oder die Navigation zwischen Abschnitten in größeren HTML-Dokumenten und dergleichen ist er relevant.

Was brauchen wir davon? Um alles bis einschließlich zum Ressourcennamen (das ist im hier angeführten Beispiel das rot hinterlegte "viewphp.php"; dort, wo Sie es gleich einbauen werden, handelt es sich um Ihre Experimentierdatei, die Sie vielleicht "tictactoe.php" oder ähnlich genannt hatten) kümmert sich das Internet von allein bzw. der Webserver. Für ein Programm auf dem Server bleibt daher lediglich der "query-string" auszuwerten (wobei die URL und noch eine ganze Menge weiterer Parameter AUCH greifbar bleiben, aber UNS interessiert erstmal nur der query-String).

Um das ganze nochmal mit etwas Skizzen zu sehen, kann ich Sie an Peter Kropff wärmstens weiterempfehlen: Er schreibt sehr angenehm leicht verständlich und hat z.B. die Sendung mit der Maus - Wie funktioniert das Internet verlinkt. Wie die Tabelle von eben DORT erläutert wird, kann ich unmöglich toppen.

Daten auf dem Server entgegennehmen
Soweit, so gut: Wir wissen jetzt, daß und wie wir einen "query" in eine URL einsetzen können und daß wir diesen query verarbeiten wollen.
Aber: Wie kommt man in PHP an das Ding heran?

Der PHP-Prozessor bereitet das automatisch für uns auf, sobald er vom Apachen eine Anfrage zur Bearbeitung gesteckt kriegt: Da läuft ein automatisches Aufbereiten unter anderem der URL im Hintergrund ab, wobei bestimmte globale Array-Variablen bereitgestellt werden: (Zur Komplettübersicht aller derartiger Variablen siehe: Superglobals)

In $_REQUEST sind alle Name/Wert-Paare des "query" bereits zu Array-Elementen umgesetzt worden. Ihre Benutzung in PHP ist also so trivial wie die jeder anderen simplen Variablen. Und wie Sie Elemente aus einem Array herausgelöst kriegen, hatten Sie vorhin erst kennengelernt.

Außerdem finden Sie bei Bedarf in $_SERVER alle möglichen Parameter der Client-Anfrage einschließlich der gesamten URL, der Client-IP, der Informationen, die der Browser so von sich zu geben bereit war (ohne seinen Benutzer davon zu informieren), sowie eventuelle Variablen, die der Server zwischendurch bei der Auswertung und Durchleitung der Anfrage erzeugt haben könnte.

Übung zu query und Superglobals
Sie können sich einfach mal die Inhalte dieser Variablen ansehen, indem Sie eine rekursive Textdarstellung der Arrays in Ihre Seite (mit dem Mahagoni-Brett) hinten (vor dem Ende des body-Blocks) anhängen. Und das geht zum Beispiel so:
<pre><?php
echo '
====================
$_SERVER:
====================
';
print_r($_SERVER);
echo '
====================
$_REQUEST:
====================
';
print_r($_REQUEST);
echo '
====================
';
?></pre>
Das Ergebnis könnte SO aussehen...
Sowas nennt man auch "debuggen": Zum Zwecke des Fehlersuchens oder auch des puren Experimentierens einfach mal so Variablen aus dem Inneren von PHP in die erzeugte HTML-Seite einblenden lassen...

Sie sehen, daß ich meine doch relativ privaten Serverdaten nicht hinauslasse, wenn die Anfrage nicht VON MIR SELBST an meinen Server geht (ich habe da einen Test auf "localhost" drangehängt).

Sie sollten, um die PHP-Verarbeitung auf IHREM Server zu kontrollieren, natürlich auch IHREN EIGENEN Server ansprechen! Und das geht, indem Sie den Servernamen gegen den Ihres eigenen Servers ("localhost") und den Scriptnamen gegen den Ihres eigenen PHP-Scripts austauschen. Noch leichter geht's natürlich, wenn Sie sich wieder mal im Anlegen von Lesezeichen und Links üben:
  • Die Startseite Ihres Browsers (die wir in Lektion 1 auf ihren "localhost" eingestellt hatten) sollte immer noch einen Link auf Ihre Lehrgangs-Experimente enthalten (den wir in Lektion 2 eingebaut hatten). Und das Inhaltsverzeichnis der Lehrgangsexperimente sollten ja (hatten wir ebenfalls in Lektion 2 begonnen) fortlaufend neue Links auf Ihre einzelnen Experimente-Dateien bekommen...
  • Alternativ kann man sich die Arbeit krass vereinfachen, indem man sich Lesezeichen auf die jeweils aktuell fortlaufend unmittelbar benötigten URL's in den Browser legt - auf die vorderste Ebene ihrer Lesezeichen-Hierarchie. Damit Sie Ihre WIRKLICH WICHTIGEN Dinge immer sofort auf einen Klick finden!

Aufgabe:
  • Erweitern Sie Ihr Lehrgangs-Inhaltsverzeichnis und Ihre Browser-Lesezeichen!
  • Probieren Sie verschiedene Anhängsel (querys) aus!
  • Experimentieren Sie mit "Name=Wert" - Paaren!
  • Hängen Sie MEHRERE solche Konstruktionen (mit "&" getrennt) hintereinander!
  • Lassen Sie gezielt Teile aus und schauen Sie, welche Konsequenzen das hat!
  • Probieren Sie, in den Namen oder den Wert eckige Klammern (für Array-Index-Zugriffe) einzubauen!
    Sehen Sie sich das Ergebnis GENAU an und ziehen Sie Schlußfolgerungen!
    Diskutieren Sie das mit Lehrgangs-Kollegen und dem Tutor!
  • Für die, die das letzte in seinen Konsequenzen verstanden haben: Übermitteln Sie ein ganzes Array von Texten in Ihre Debug-Anzeige!
Spielfeld übertragen
So, jetzt wird's langsam interessanter...
Wir haben jetzt die Voraussetzungen geschaffen, daß wir eine Spielfeld-Belegung an den Server übertragen können...

Schutz vor Hacking durch Fremde
Bevor wir jetzt losschießen, noch ein paar Worte zu Ihrem eigenen Schutz: Normalerweise hängt Ihr XAMPP noch nicht im Internet. Denn dazu müßten Sie Ihren Internet-Anschluß - namentlich Ihr Modem - so konfigurieren, daß es Anfragen an einen Webserver überhaupt an Ihren Rechner durchleitet. Jedes Modem für Endnutzer ist so konfiguriert, daß es nur Anfragen von innen nach außen (und das Wiedereindringen von dazu passenden Antworten) zuläßt, nicht aber das Eindringen IRGENDWELCHER Anfragen von außen nach innen.

Etwas anders sieht es aus, wenn Sie einen Server von vornherein im Internet zu hängen haben - zum Beispiel weil Sie sich eine Website bei einem der vielen kostenlosen PHP-Hoster angelegt haben. In so einem Fall können Sie ähnlich dem, was Sie eben in meinem Beispielcode gesehen haben, die Hacking-Experimente auf IHREN EIGENEN Browser beschränken, während der Rest der Welt nur eine leere Seite zu sehen bekommt.

WIE Sie das nun konkret anlegen, da führen mal wieder viele Wege nach Rom... Ich habe mir zum Beispiel das System zugelegt, daß mein Browser, wenn er sich in einer bestimmten Form an einen von mir verwalteten Webserver wendet, eine von mir festgelegte Zufallszahl in den Anfrageparametern mitsendet (die Sie in $_SERVER wiederfinden). Speziell lasse ich einen modifizierten "HTTP_USER_AGENT" ($_SERVER['HTTP_USER_AGENT']) mitschicken.

Wobei das allerdings nur dann einen Sinn ergibt, wenn man diesen modifizierten HTTP_USER_AGENT ausschließlich zum eigenen Server schickt und nicht mit JEDER Anfrage der Browsers in die ganze Welt hinausposaunt. DAFÜR gibt es für Firefox spezielle Addons, zum Beispiel User Agent Control, wo man sowas spezifisch nach Servername einstellen kann.

Firefox User Agent Control Hier sehen Sie eine Anwendungsmöglichkeit:
  • Falls mit der LAN-Adresse angesprochen, kann ich meinen Heimserver sehen, als wenn ich eine Suchmaschine wäre. Und auf diese Weise kontrollieren, daß die Suchmaschinen auch tatsächlich genau das zu Gesicht bekommen, was sie sehen sollen. Und zum Beispiel die Pfoten von allen Hacking-Experimenten, Shops, Spielen und so weiter lassen.

  • Als localhost kann ich frei experimentieren und dabei auch massiv Fehler verursachen, ohne daß der Quatsch mir die normalen Logfiles zumüllt ("nolog").

  • Und ob als localhost oder mit dem öffentlichen Namen angesprochen weiß mein Server auf jeden Fall, ob ich es selber bin und er mir zum Beispiel Debug-Informationen liefern darf. Falls ich gegentesten möchte, nehme ich einfach einen Opera.

Das sind jetzt nur Anregungen gewesen. Das muß man nicht zwingend so machen. Viele Wege führen nach Rom...
Außerdem dürfte es für Sie in diesem Moment noch nicht vordergründig relevant sein. Aber sobald Sie Blut geleckt haben, werden Sie sehr schnell eigene PHP-Projekte aufsetzen...

Übung zum Übergeben der Spielfeldbelegung
So... Wir wollten doch letztlich zur Übermittlung des Spielfeldes kommen, nicht wahr?! Und wir hatten da schon eine Variable angelegt, die irgendwie so ähnlich wie "$spielfeld" hieß und eine Zeichenkette so ähnlich wie "102010201" beinhaltete (mal mit ein paar platzierten Steinen)...
(-> letzter Zustand - wobei SIE bitte IHREN Dateinamen und IHREN "localhost" einsetzen, ja?!)

Jetzt experimentieren wir damit ein bißchen herum...
Hängen Sie einfach mal sowas hier an die URl an:
?spielfeld=000000000
...und schauen Sie sich an, was als $_REQUEST im PHP-Script ankommt!

Bei mir kommt DAS heraus (sollte bei Ihnen gleich aussehen):
====================
$_REQUEST:
====================
Array
(
    [spielfeld] => 000000000
    [general_session_id] => e360f3c490016aa90c89d4cb8a3e59d0
)

====================

Fein! Jetzt wollen wir bloß noch, daß diese Daten aus der Anfrage in die PHP-Variable auf dem Server-Script übernommen werden... Dort lautete die Zeile mit der Spielfeld-Variable:
$spielfeld = '021112212';
Und der Parameter aus der URL fidet sich wieder als Element vom $_REQUEST:
$_REQUEST['spielfeld']
Nun brauchen wir den reingekommenen Wert nur noch der bereits vorhandenen Variablen zuweisen...
$spielfeld = $_REQUEST['spielfeld'];

Allerdings könnten wir auch noch gleich ein paar Überlegungen mit einfließen lassen, die uns eh auf die Füße fallen würden...
  • Was, wenn wir gar keinen spielfeld-Parameter übermitteln?

    Dann gäbe es gar keine solche Variable in dem $_REQUEST-Array!
    Was würde der PHP-Interpreter dann machen? (Probieren Sie's aus!)
    Wenn man KEIN Spielfeld übermittelt, möchte man ja vielleicht ein neues Spiel beginnen und deshalb ein leeres Spielfeld zu Gesicht bekommen?

    Das könnten wir bedienen, indem die Variable $spielfeld ZUERST auf einen String mit lauter Nullen gesetzt wird UND DANN, FALLS ein Spielfeld mit der Anfrage übermittelt wurde, auf jenes übermittelte Spielfeld! Dafür gibt es einen speziellen Test auf Existent einer Variablen mit folgender Syntax:
    $spielfeld = '000000000';
    if (isset($_REQUEST['spielfeld']))
        $spielfeld = $_REQUEST['spielfeld'];
    

    Sie merken hoffentlich, daß hier die Reihenfolge der Aktionen kritisch bestimmend für das Gesamtergebnis ist. - Eine typische Erscheinung von Ablauf-orientierter Programmierung!

  • Was wäre, wenn jemand völlig sinnlose Daten übermittelt?

    In unserem konkreten Beispiel würde bisher nicht viel schlimmes passieren können: Wir würden eh nur die ersten 9 Zeichen auswerten, und wenn die nicht zu den von uns vereinbarten Zeichen gehören würden, würde der PHP-Interpreter uns bei der von uns gewählten "Transformation mit Array" Warnmeldungen über nicht existierende Elemente um die Ohren hauen, aber letztlich einfach nur nichts in die Felder eintragen, womit das ganze sinnvoll bleibt: Es wird bei sinnlosen Daten halt ein neu initialisiertes Spielfeld geliefert.

    Das wäre ebenso noch der Fall, wenn wir eine der anderen Transformations-Varianten gewählt hätten. Dann sogar ohne Warnmeldungen.

    Übrigens können die Warnmeldungen bei unserer Array-Variante noch weggeblendet werden, indem folgendes Zeichen vor den Ausdruck mit dem Array-Zugriff gesetzt wird:
    $GLOBALS['f'.$i] = @$icons[$spielfeld[$i]];
    Dieses "@" bewirkt in PHP eine Unterdrückung jedweder Meldung über Fehler, Warnungen oder Hinweise. Wenden Sie sowas aber extrem zurückhaltend an! Besser nie, denn wenn später aufgrund einer Dussligkeit an anderer Stelle ein Fehler ausgerechnet in dem so behandelten Ausdruck auftritt, haben Sie die Ar... den Schwarzen Peter ... gezogen!

    Aber im allgemeinen sind solche Hacker-Angriffe potentiell gefährlich, insbesondere wenn man größere Programme hat, die man eventuell gar nicht selbst geschrieben hat, sondern nur nachnutzt. Da liegt eine zentralisierte Vorverarbeitung der reinkommenden Daten nahe. Und DAS sehen wir uns gleich im letzten Abschnitt für heute nochmal im Detail an...

Übung 2 zum Übergeben der Spielfeldbelegung
Aufgaben:
  1. Übernehmen Sie den Code zur Spielfeld-Übernahme in Ihr eigenes TicTacToe!
  2. Probieren Sie die Übergabe von Spielfeldbelegungen aus!
  3. Sichern Sie Ihre Hacking-Unterstützung gegen Fremdzugriff, indem Sie Hacking nur für Localhost zulassen!
  4. Probieren Sie die Sicherung aus, indem Sie Ihren Server mal mit "localhost", mal mit "127.0.0.1" anreden!
    (mit 127.0.0.1 sollte nix gehen, mit localhost dagegen alles)
  5. Ergänzen Sie mal wieder Ihr Lehrgangs-Inhaltsverzeichnis!

    Legen Sie mal verschiedene Links auf Ihre Spielanzeige rein, denen Sie Querys mit verschiedenen Spielfeldbelegungen mitgeben!

    Voila: Sie haben die erste Form der Steuerung von Servern mittels parametrisierter URL's erfolgreich nutzen gelernt. Das kann man zwar auch mit Formularen erreichen, aber oft genug reicht ein Link (und ein Formular wäre blanker Overkill). Richtig hübsch wird die Sache, wenn man die Links als Buttons darstellt. Nur mal so als Anregung. Wir werden die Technik in der nächsten Lektion zum Setzen auf dem Spielfeld benutzen.

Das Ergebnis sollte jetzt in etwa SO aussehen...
Ihr Inhaltsverzeichnis des Lehrgangs könnte zum Beispiel SO aussehen...

Und jetzt ist erstmal wieder Pause!
Danach kommt nochmal eine kurze Hacking-Sitzung...

Absicherung gegen Unsinn

Weil wir nun begonnen haben, ernsthaft Serverprogrammierung zu betreiben, und dazu auf dem Server in unseren PHP-Programmen Anfragen aus dem Weiten Wilden Web verarbeiten, ist es an der Zeit, die Gefahren dieser Sache ins Auge zu nehmen.

Risiken und Nebenwirkungen
Und dabei geht es nicht um naive Katz-und-Maus-Spielchen. Wer auch nur zu SORGLOS (Welten von "böse" entfernt!) dabei ist, Rechenkapazitäten am Internet bereitzustellen, kann in Deutschland, ohne auch nur EIN ATOM im Gehirn für bösartige Gedanken verschwendet zu haben, bis zu lebenslangem totalen Ruin verurteilt werden. Wenn er an die "richtigen" Richter und Anwälte gerät. Mal ein paar Kostproben (willkürlich die erstbesten aus Google-Suchen): Der besondere Leckerbissen daran: Man braucht einen von ANDEREN Leuten (insbesondere aggressiven Rechtsanwälten) für eine Straftat GEHALTENEN Vorgang gar nicht selbst begangen oder direkt verursacht haben. Er reicht, wenn man MIT-STÖRER (!) ist, der den betreffenden Vorgang nur mittelbar über zum Beispiel seine PC-Technik und seinen Internetanschluß bzw. seinen Server ERMÖGLICHT hat. Nur, weil man zu dusslig war, es zu verhindern!

EIGENTLICH ist diese Art Rechtsprechung mal für Internet-Provider und ähnliche Größenordnungen gedacht gewesen - also Dienstleister, die das ganze im großen Maßstab betreiben und dadurch auch große Mengen an Kunden gefährden können. DIE sollten in die Verantwortung genommen werden! In Verkennung dieser Relation aber werden diese Bestimmungen seit Anfang dieses Jahrtausends auch für jeden normalen Bürger durchgepeitscht, bis einschließlich Jugendlichen und Kindern.

Und wenn die auf dem eigenen Server vermittelte Straftat in so nebensächlichen, alltäglichen Delikten wie Kinderpornographie oder Whistleblowing besteht, dann kann schon mal ein SEK vor der Tür stehen...

Wobei ich die Rechtsprechung gar nicht soooo weit an den Haaren herbeigezerrt sehe: Im Sinne dessen, daß selbst alte Omas mit rechtlichen Brechstangen gezwungen werden, sich trotz Alzheimer, Gefäßverkalkung und Knochenschwund auf die letzten Tage ihres Lebens noch mit moderner Technik zu beschäftigen, könnte ich FAST geneigt sein, in dem ganzen eine positive Geruchsnote zu erkennen...

Und jetzt wieder ernsthafter: Wer sich mit Servertechnik beschäftigt, sollte schon ein wenig kräftiger zur Verantwortung gezogen werden dürfen. Immerhin verpesten in Botnetze eingebaute Zombie-Server, die rund um die Uhr im Netz hängen und eventuell auch noch in Rechenzentren stehen und eine entsprechend kräftige Internet-Anbindung und Rechenleistung haben, das Internet wesentlich stärker als nur ab und zu mal eingeschaltete Privat-PC's.

Ich gehe davon aus, daß die Rechtsprechung sich allmählich von den oben verlinkten Lach- oder Weinnummern fürs "normale Volk" weg bewegen, aber bezüglich Serverbetreibern doch einigermaßen streng bleiben wird. Als Serverprogrammierer haben Sie halt im Internet mehr Möglichkeiten und Macht als andere, aber auch mehr Verantwortung!

Eine Absicherung gegen eventuell nicht vorhandene Parameter haben wir ja schon: Wir testen einfach auf die Existenz der erwarteten Daten!
Außerdem haben wir bereits einen Schutz vor Hacking durch Fremde kennengelernt, wenn wir für uns selbst Hacking-Möglichkeiten bereithalten wollen: Wir lassen Debug-Ausgaben und Hacking-Spielereien zum Testen einfach nicht in die Öffentlichkeit!

Das Problem
Was noch fehlt für eine ordentliche Webanwendung ist eine Absicherung von erforderlichen Nutzereingaben gegen absichtlichen wie versehentlichen Unsinn. Das Problem mit "Unsinn": PHP ist eine vollwertige Programmiersprache. Man wird es als Scriptsprache zwar nicht für zeitkritische Anwendungen einsetzen, aber ansonsten bietet es alles, was jede moderne Programmiersprache mit reichhaltiger Funktionsbibliothek zu bieten hat. Es gibt insbesondere eine Vielzahl von Funktionen, die in die Internas des umgebenden Betriebssystems eingreifen können. Auch unbeabsichtigt vom Webserver-Betreiber. Es gibt zwar die Möglichkeit, solche Funktionen abzuschalten (PHP-Funktionen können in der PHP-Webserver-Konfiguration einzeln verboten werden), aber diese Methode ist fehleranfällig und aufwendig (man muß als Webmaster einen vollständigen Durchblick in sämtlichen dieser Funktionen haben).

Als Webmaster steht man da gewissermaßen zwischen mehreren Fronten:
  • Auf der einen Seite die PHP-Maschine: PHP-Interpreter-Kern und PHP-Bibliotheken, die gemeinerweise ständig in Weiterentwicklung begriffen sind. Und die man gelegentlich UPDATEN MUSS, wenn mal wieder kritische Sicherheitslücken entdeckt wurden. Und mit den Updates kommen Änderungen, vor allem Erweiterungen der Funktionalität ins System. Die man eventuell doch nicht bis ins letzte Detail kennt.
  • Auf der anderen Seite die PHP-Web-Anwendungen, die eventuell zu einem erheblichen Teil nicht von einem selbst geschrieben worden sind, sondern wo man nur Nachnutzer ist. Und die voll sind von versteckten Fehlern. Und wo ebenfalls immer wieder mal Updates notwendig sind. Und wo mit den Updates ebenfalls immer wieder neue Features reinkommen mitsamt neuen versteckten Fehlern.
  • Auf der dritten Seite dann die Hacker, deren Einfallsreichtum proportional mit der verfügbaren Zeit ansteigt und die ihre Erfahrungen über Verwundbarkeiten gern untereinander austauschen.

Der Einfallsreichtum der Hacker ist dabei das größte Problem: Sobald man irgendwo Nutzereingaben ungefiltert passieren läßt, finden die irgendwann eine Möglichkeit, dieses zu mißbrauchen.

Letztlich steckt man als Server-Betreiber inmitten eines Systems, das vorn und hinten fehlerhaft ist und ein gefundenes Fressen für Hacker, die mit der Übernahme eines Server-Rechners besonders lukrative Zombie-PC's in ihre Botnetze integrieren, weil Server-Rechner zumindest die meiste Zeit unbeaufsichtigt laufen und 24/7/365 online sind. Und meist auch noch eine relativ gute Netzanbindung haben.
Die Lösung
Nach all dem Jammern: Irgendwie muß man das ja nun doch in den Griff kriegen...

Whitelisting
Die effektivste Methode besteht darin, sämtliche Nutzereingaben auf erlaubte Werte ABZUBILDEN - durch eine Operation ähnlich dem, wie bei uns im Moment die eingegebenen Ziffern in Steine auf dem Brett umgesetzt werden:
  • Sofern eine Abbildung definiert ist - die nur dann definiert sein KANN, wenn der Programmierer bzw. Webmaster sich diesen Fall durch den Kopf gehen lassen hat und damit einverstanden ist - liegt eine ERWARTETE und AKZEPTIERTE Eingabe vor. Und es kommt ein weiterverwendbarer Wert heraus.
  • Ist KEINE gültige Abbildung definiert, dann liegt eine UNERWARTETE Eingabe vor. Es kommt KEIN weiterverwendbarer Wert heraus. Die Eingabe kann somit kontrolliert abgewiesen werden.

So ein Vorgehen nennt man auch "Whitelisting":
  • Alles, was man NICHT EXPLIZIT ERLAUBT hat, ist verboten.
  • Nur Dinge, von denen man explizit und sicher weiß, daß man sie haben will und daß sie sicher funktionieren, erlaubt man.
Um absolut sicherzugehen, nicht eventuell doch einer Falle aufzusitzen, wo das Whitelisting durch einen Bug (z.B. Nullbytes in Zeichenketten) fehlerhaft anschlägt, nimmt man dabei nicht die Nutzereingabe zur weiteren Verarbeitung, sondern wie in unserem Beispiel die abgebildeten, vom Programmierer selbst definierten Werte!

Auf diese Weise kann man zuverlässig ausschließen, daß man von neuartigen Ideen von Hackern überrascht wird. Voraussetzung ist allerdings, daß man seine Webanwendungen so organisiert, daß sie damit funktionieren können.
Wertebereiche einschränken
Es bleiben einige Eingabemöglichkeiten übrig, wo man den Anwendern zwingend Freiheiten lassen muß:
  • Texteingaben
  • Zahlen, die frei definierbare Maße angeben sollen
Andere Kategorien lassen sich in aller Regel auf diese beiden abbilden (auch ein Datum fällt letztlich unter die Kategorie Zahl).

Zahlen kann man vom Wertebereich her oft einschränken und auf jeden Fall sicherstellen, daß sie auch wirklich Zahlen sind - durch sogenannte "Typecasts". Was "casten" ist, sollte jeder Mensch, der sich schon mal mit Fantasy und zaubern befaßt hat, intuitiv verstehen: Sie lassen einen Zauberspruch auf einen Ausdruck ab - und der wandelt sein Aussehen in den Typ, den Sie von ihm haben wollen - falls er ihn nicht eh schon hatte. Wer sich für Zauberei noch nie interessiert hatte, hat an der Stelle eventuell einen Schluckauf mit der Philosophie, aber da müssen Sie halt durch!

Texteingaben können eventuell ebenfalls im Wertebereich eingeschränkt werden. Das hängt aber sehr konkret von der Anwendung ab, so daß hier keine weiteren konkreten Empfehlungen gegeben werden können.
Funktionelle Elemente herausfiltern
Wenn man Daten unbedingt frei annehmen muß, analysiert man (und dokumentiert es im Programm als Idee), wo genau diese Daten überall zur Anwendung kommen werden. Und organisiert spätestens bei diesen Anwendungen, daß sie gefiltert werden, so daß sie in diesen Anwendungen keinen funktionellen Einfluß ausüben können. Falls möglich, kann man mit einer umkehrbaren Transformation in einen ungefährlichen Zeichensatz arbeiten. Dafür gibt's in PHP eine ganze Palette an Funktionen, von denen wir gleich in der Übung eine kennenlernen werden.

Also zum Beispiel (nur als Anregung):

Verwendung in...-> filtern auf...
HTML-Ausgaben spitze Klammern der HTML-Tags: alle spitzen Klammern durch ihre nicht funktionellen Äquivalente (sogenannte "Entities") ersetzen!
"<" => "&lt;"
">" => "&gt;"
Falls die Ausgabe in HTML-Attributen Verwendung finden soll: Zusätzlich Anführungszeichen ersetzen durch ihr Äquivalent "&quot;" und ASCII-Steuerzeichen in normale Leerzeichen umwandeln!
Dateinamen in einem vorgeschriebenen Verzeichnis alles, was das Ansteuern anderer Verzeichnisse gestatten oder das Dateisystem durcheinanderbringen würde, namentlich Slashes, Backslashes, Wildcards, ASCII-Steuerzeichen
Datenbank-Abfragen alles, was Steuerzeichen für die Datenbank-Abfragen sind (kommt ein wenig auf die Datenbankabfragen an)

Diese Filterung muß mitunter (zum Beispiel bei Parametern von Datenbankabfragen oder beim Einsetzen in HTML-Attributen) mit einem Einfassen des Nutzertextes in Anführungszeichen (oder was im konkreten Fall gerade als Klammerung notwendig ist, um den Inhalt von der Umgebung eindeutig zu trennen) begleitet werden.
Programmierbares NIE mit freien Nutzereingaben füttern!
Trotz aller Filterung und Typcastung sind Nutzereingaben, die keinem Whitelisting unterzogen wurden, in extremem Maße frei.

Solche Daten setzt man in KEINEM (!) Fall als Parameter für irgendwelche Anwendungsbereiche ein, die "programmierbar" auf den Betriebszustand des Systems wirken können. Und darunter fällt eine ganze Menge. Als Anregung: Fassen Sie das hier nur als Anregung auf: Sie werden Ihren eigenen Programmcode schon kennen müssen samt der programmtechnischen Verbrechen, sie Sie sich selbst herausnehmen! Wenn Ihnen da etwas einfällt, was hier nicht aufgelistet ist, wasche ich meine Hände in Unschuld. Und wenn sich die Erzeuger irgendwelcher PHP-Anwendungen, die Sie sich aus dem Internet ziehen, irgendwelche Schweinereien herausgenommen haben, ebenso!
  • Dateinamen: Sie müssen zwingend zumindest die Pfade und Dateitypen whitelisten, die die Benutzer verwenden dürfen, sonst können Sie Ihren Server gleich meistbietend an Botnetzbetreiber versteigern!
  • Parameter von befehlsausführenden Funktionen: Es gibt eine ganze Palette von Funktionen, die Text als PHP-Programme interpretieren können oder Text als Befehl an das Betriebssystem weiterreichen können. Zusammenstellungen findet man in Foren - zum Beispiel hier bei stackoverflow.com... - oder in den PHP-Konfigurationen der Free PHP Hoster. Bei letzteren ist es Geschäftssache, daß die Systeme dicht sind. Denen über die Schulter zu schauen würde ich primär empfehlen, die Foren erst sekundär hinzuziehen!
  • Variablen- und Funktionsnamen: Wir werden am Ende des Lehrgangs noch ein Beispiel kennenlernen, wo es vom Prinzip der Einfachheit her auf der Hand liegen würde, eine Nutzereingabe direkt als Name einer Variablen oder aufzurufenden Funktion zu benutzen. => NUR MIT Whitelist!
Gefährliche (programmierbare) Funktionen abschalten
Wer als Webserver-Betreiber seinen PHP-Entwicklern (bzw. den Erzeugern von PHP-Anwendungen, die er aus dem Internet gezogen hat) nicht vertrauen kann, weil die zu dusslig sind, ihre Anwendungen Hacking-sicher zu machen, kann alle kritischen Funktionen in der PHP-Bibliothek abschalten: disable_functions, disable_classes. Es sei aber gewarnt: Wenn da nicht komplett sämtliche kritischen Funktionen abgeschaltet werden (wenn auch nur ein einziges Schlupfloch bleibt), WIRD es Hacker geben, die es ausfindig machen!
Man nehme dazu eine Vorlage, die von hinreichend verantwortungsvollen Admins ausgearbeitet wurde. Free PHP Hoster sind dafür eine gute Basis: Deren Free Accounts sind in der Regel genau so zugenagelt, daß nur einfache, ungefährliche Dinge möglich sind. Ein Blick in deren Konfiguration lohnt sich! Die PHP-Webserver-Konfiguration kann man sich aus einem PHP-Script anzeigen lassen mit:
<?php phpinfo(); ?>
Aufgabe: Ausprobieren in Ihrem eigenen Code!
Hinweis: Stecken Sie die PHP-Info nicht in einen pre-Abschnitt! Sie bringt üppig formatiertes HTML von selbst mit!
Empfindlichkeiten senken
Für einige "übliche" Schwachstellen der PHP-Programmierer wie auch der PHP-Interpreter-Entwickler (wo die gern ihre Hausaufgaben vernachlässigen - was man teilweise erst dann verstehen kann, wenn man täglich die IT-Nachrichten über Einbruchsstellen und deren Ursachen verfolgt) gibt es extra Unterstützung durch die PHP-Webserver-Konfiguration:
  • Bewegung im Dateisystem: Man kann einschränken, in welchen Verzeichnissen der PHP-Interpreter überhaupt werkeln darf => open_basedir (das stellt eine zusätzliche Einschränkung gegenüber den bereits auf Betriebssystem-Ebene einzuschränkenden Rechten des Webservers dar. (siehe dazu: PHP-Handbuch und NTFS-Rechteeinstellung))
  • Nutzer-Eingaben: Man kann einschränken, wieviele Name/Wert-Paare gleichzeitig pro Anfrage eingereicht werden können und wie tief dabei Arrays geschachtelt werden dürfen (das zu erklären sprengt momentan noch den Rahmen; zwei scherwiegende Angriffstechniken der letzen Jahre zielten darauf) => max_input_nesting_level, max_input_vars
Zwangs-Filterung von außen überstülpen
Perfektionieren kann man das ganze, indem in der Webserver-Konfiguration eingestellt werden kann, daß ein vom Webserver-Betreiber definiertes PHP-Script zwangsweise vor bzw. nach jeder anderen PHP-Datei ausgeführt werden muß. Siehe dazu auto_prepend_file und auto_append_file! Auf die Weise kann man von außen um jede beliebige PHP-Webanwendung einen Filter legen, der alles, was rein und raus geht, nach persönlich definierten Sicherheitsanforderungen durchsiebt.

Man muß dazu natürlich die Schwachstellen kennen, aber der korrigierende Eingriff geht ohne Anfassen der zu korrigierenden Anwendungen einher, die also weiterhin in sich geschlossene Pakete bleiben können, und die man zum Beispiel aktualisieren oder aus Backups zurückspielen kann, ohne den Schutz aufzuheben.

Übung zur Absicherung
Das Prinzip von Whitelisting haben wir schon allein durch unsere Methode der Erzeugung der Anzeige der Brettbelegung geübt. Und wir haben bei unserem Kenntnisstand noch etwas eingeschränkte Möglichkeiten, die anderen Techniken zu praktizieren.

Der Angriff
Mit unseren bisherigen Kenntnissen können wir uns aber nochmal an der Hacking-Unterstützung auslassen, die wir vorhin eingebaut hatten. => tictactoe-spielfeld.php (falls Sie hier klicken, müssen Sie gegebenenfalls noch den Dateinamen an Ihren eigenen anpassen...)
Momentan müßte die was anzeigen, wenn Sie Ihren eigenen localhost als solchen ansprechen, jedoch nichts, wenn Sie ihn als 127.0.0.1 ansprechen.

Angezeigt werden $_SERVER, $_REQUEST und phpinfo. Wir brauchen davon erstmal nur den $_REQUEST... Kommentieren Sie die beiden anderen Sachen vorübergehend aus! ("Auskommentieren" heißt: Programmzeilen unwirksam machen, indem man Kommentarzeichen davor setzt. Ihr Editor unterstützt das gleich für ganze Blöcke)

Probieren Sie mal folgendes: Schreiben Sie in die URL der Seite folgende Parameter:
?test=<a href="test">test</a>
Das war jetzt nur ein harmloser Test, um das Potential anzudeuten...
Wir haben - als Benutzer - den Seiteninhalt funktionell verändert:
Wir haben einen HTML-Abschnitt eingefügt. DIESMAL nur einen Link...
Den Sie anklicken können, mitten in der Debug-Ausgabe zur REQUEST-Variable.

?test=<iframe frameborder="0" scrolling="no" style="position:absolute;top:0;left:0;height:100%;width:100%;" src="http://www.heise.de/security/meldung/Banken-Seiten-weiterhin-unsicher-1179476.html"/>
Das ist schon eine Nummer schärfer. Selbes Prinzip, nur anderer Inhalt. Bei sowas meldet sich bei mir eine Sicherung von NoScript... Nach temporärer Abschaltung von Noscript habe ich genau wie Sie ganzseitig die Website von Heise vor der Nase. (Auf der es übrigens um genau diesem Problem hier geht. Werfen Sie mal einen Blick hinein...!) Obwohl die URL in der Adreßleiste von meinem localhost spricht. Mit 'frameborder="0" scrolling="no"' sogar ganz ohne Artefakte.

Verallgemeinert: Wenn IRGENDEINE Nutzereingabe ungefiltert in den HTML-output gelangt, kann ein beliebiger Hacker eine beliebige Seite des Internets auf einer Seite der angreifbaren Webanwendung erscheinen lassen. Das läßt sich mit "social engineering" ausnutzen für praktisch jede Schweinerei sowohl gegen den Betreiber des verwundbaren Webservers ("schau mal: Kinderporno bei dem" oder "die verteilen seit heute Trojaner und Viren") als auch gegen einen beliebigen Besucher ("Heise Security gab heute eine dringende Warnung vor Passwortdiebstahl bei der Deutschen Bank heraus. Die Mitarbeiter von Heise Security haben auf ihrem Server einen Security Check eingerichtet. Allen Kunden der Deutschen Bank wird dringend geraten, den Security-Check bei Heise zu absolvieren! Sie müssen dazu einmalig eine Transaktion simulieren..." oder "Schit, ich hab einen Virus bei Heise eingefangen, die waren doch sonst immer vertrauenswürdig, so daß ich Javascript nie abgeschaltet hatte!")

Schlaraffenland für Hacker!
Ich nehme an, einen echten Virus müssen wir jetzt nicht versuchen, aus irgendwelchen Schmuddelseiten oder so auf ihren localhost zu verfrachten, oder? Wobei wir das auch so einrichten könnten, daß sich so ein Virus ganz im Stillen installiert, weil man das IFRAME-Fensterchen auch ganz klein machen kann, so daß es als einzelnes Pixel nicht mehr zu sehen ist. Dann können die RICHTIGEN Exploits erstmal losgehen! Mit Javascript und Acrobat Reader und Flash und Java, Fehlern in Treibern für exotische Medienformate, Nullpointer-Referenzen, Stacküberläufen... DANN wird es erst so richtig HEISS...!

Die Verteidigung
So, das war jetzt eine der WENIGER gefährlichen Sicherheitslücken. Weniger gefährlich, weil die relativ einfach zu schließen geht, und zwar gleich mehrfach. Und das machen wir jetzt:
  • Gegenwirkung Nr. 1 - auf Clientseite: NoScript
    Und Sie sind sicher. Selbst wenn Sie dem NoScript gestatten sollten, Javascript global zuzulassen (was ich nicht mache, weil dieses auch noch jede Menge gemeinere Angriffsformen bietet). Weil NoScript den XSS-Angriff erkennt und von vornherein gar nicht erst mitmacht.

    OK, diese Sache hat nichts mit PHP- bzw. Serverprogrammierung zu tun und schützt nicht auf breiter Front Ihren Server gegenüber dem Internet bzw. die armen Opfer im Internet vor Ihrem mißbrauchbaren Server, sondern punktuell Sie als Opfer vor anderen Webprogrammierern, die bei ihren Hausaufgaben schludern. Aber Sie kennen jetzt zumindest EINEN guten Grund, WARUM!

    Aufgabe: SIE können EBENFALLS das Addon NoScript installieren!
    Falls Sie das nicht sowieso schon getan hatten...

    Aufgabe: Führen Sie dieses Hacking-Experiment danach nochmal mit Opera oder Internet Explorer oder Chrome durch (was Ihnen gerade zur Verfügung steht)! Versuchen Sie, auch für diese Browser vergleichbare Addons oder Filtermöglichkeiten zu finden! Versuchen Sie, IRGENDWIE diesen Angriff loszuwerden!

  • Gegenwirkung Nr. 2 - auf Serverseite: Debug-Ausgaben gehören grundsätzlich nicht in die Öffentlichkeit!
    Deshalb die Filterung vorhin auf den localhost. Wobei - wenn Sie nochmal genau hinschauen, ...also GANZ GENAU... - dann sehen Sie, daß ich da nicht nur
    $_SERVER['SERVER_NAME']
    testen lassen habe, sondern auch
    $_SERVER['SERVER_ADDR']
    Das liegt daran, daß der Servername-Eintrag eine freiwillige Angabe des Clients darstellt, die niemand kontrolliert. Die der also auch fälschen kann. Also: Unter welchem Namen der Client meinen oder Ihren Webserver vermeintlich gefunden haben will, darf der in seinen Datenpaketen behaupten, wie der lustig ist. ...Ja gut: DÜRFEN dürfte er eigentlich nicht, aber KÖNNEN kann er allemal!

    Aufgabe: Ergänzen Sie Ihre Filterung entsprechend!
    Falls Sie das nicht sowieso schon getan hatten...

  • Gegenmaßnahme Nr. 3 - auf Serverseite: Daten in nicht funktionale Formen transformieren!
    Konkret: Alles, was auf eine HTML-Seite ausgegeben wird, gehört so gefiltert, daß keinerlei HTML-Funktionalität hergestellt oder gestört werden kann, also HTML-Tags und gegebenenfalls Anführungszeichen für Attributwerte gehören durch Ersatzzeichen dargestellt. Das muß man nicht von Hand erledigen, dafür gibt es eine Funktion. Ändern Sie die Debug-Ausgabe von:
    print_r($_REQUEST);
    in:
    echo htmlspecialchars(var_export($_REQUEST,true),ENT_QUOTES);
    "var_export()" ist ein Ersatz für print_r, der die Daten aber nicht unmittelbar in den Ausgabestrom (also in die HTML-Seite am Punkt des Geschehens) einsetzt, sondern als Ergebnis des Funktionsaufrufs zurückgibt, so daß sie weiter verarbeitet werden können. Und die Weiterverarbeitung übernimmt die Funktion "htmlspecialchars()", die genau die beschriebene Transformation ausführt. Die kann man übrigens später auch wieder rückstandsfrei umkehren: In der verlinkten PHP-Referenzdokumentation stehen üppige Erklärungen und Querverweise dazu...

    Aufgabe: Bauen Sie diese Änderung in Ihren Code ein!

  • Gegenmaßnahme Nr. 4 - auf Serverseite: Wertebereiche einschränken, falls möglich!
    Eigentlich haben wir in unserer konkreten Anwendung ja durchaus schon eine ausreichende Maßnahme getroffen, daß beliebige Nutzereingaben zu nichts anderem als genau den von uns gewollten Anzeigezuständen führen können. Aber zur Übung können wir ja trotzdem nochmal ein Experiment anhängen...

    Im folgenden Beispiel wird das reinkommende Spielfeld erstmal zwischengespeichert (um es anschließend leichter verarbeiten zu können) und dann
    1. verlangt, daß seine Länge mit der erwarteten Länge übereinstimmt
    2. verlangt, daß seine Zeicheninhalte mit den erwarteten übereinstimmen - das sind bei uns nur genau drei Varianten, wodurch sich die Sache für eine Wertebereichseinschränkung anbietet
      (wie gesagt: Wir haben die ja eigentlich schon mit der Abbildung auf die Icons, aber hier einfach zum Üben noch eine extra Variante...)
    $neues_spielfeld = $_REQUEST['spielfeld'];
    
    if (strlen($neues_spielfeld) == 9 && strtr($neues_spielfeld,'012','000') == '000000000')
    
        $spielfeld = $neues_spielfeld;
    

    zu den einzelnen Elementen:
    strlen($neues_spielfeld) ermittelt die Länge der Zeichenkette (siehe strlen())
    == 9 vergleicht diese mit 9 (siehe Vergleichsoperatoren)
    && stellt eine logische UND-Verknüpfung zwischen zwei Werten her (siehe Logische Operatoren)
    strtr($neues_spielfeld,'012','000') transformiert eine Zeichenkette - und zwar das erste Argument - so, daß jedes Zeichen davon, das auf ein Zeichen aus dem zweiten Argument paßt, in das zugehörige Zeichen aus dem dritten Argument umgewandelt wird. (siehe strtr())

    Zu deutsch: Alle ERLAUBTEN Zeichen (0, 1 oder 2) werden in 0 umgewandelt.
    Ist IRGENDEIN Zeichen im String, das NICHT 0, 1 oder 2 ist (also UNERLAUBT), wird das NICHT umgewandelt, sondern bleibt wie es ist.
    Im Ergebnis kommt eine Zeichenkette mit nur Nullen ("000000000") heraus, wenn alle Zeichen erlaubt waren.
    War auch nur ein Zeichen nicht erlaubt, kommt IRGENDWAS ANDERES heraus.
    == '000000000' vergleicht diese mit "000000000" (dem Erwartungswert für den Fall, daß alles in Butter ist)

Aufgabe: Bauen Sie das in Ihren eigenen Code ein!

So, wenn Sie jetzt nochmal versuchen, in die Rolle eines Hackers zu schlüpfen und sich selbst zu verarschen, wird Ihnen das bereits sehr viel schwieriger fallen! Und die weiter oben, vor der Übung, genannten Techniken werden Sie sich allmählich aneignen.

Aufgabe: Experimentieren Sie ruhig noch ein bißchen weiter und verlinken Sie die Ergebnisse in Ihrem Lehrgangs-Inhaltsverzeichnis!

Wer Blut geleckt hat und sich weiter in Angriffstechniken und zugehörige Abwehrtechniken einlesen und -experimentieren möchte, dem kann ich zu sogenannten "Hackits" ("hack it!") raten.


Der Endzustand an dieser Stelle sollte in etwa so aussehen wie das ganz zu Anfang des Tages gezeigte Progrämmchen...
Sie müßten unter direktem Editieren der URL bereits eine Partie TicTacToe gegen sich selbst spielen können. Und die Sache müßte gegen Fehlbedienung und Hacking sicher sein...



Impressum
Email
aktualisiert: 2013-03-04 03:57