mmofacts.com

[Tutorial] Sicheres Programmieren von Browsergames

gepostet vor 17 Jahre, 7 Monate von Drezil
kurze zusammenfassung:
all - and really ALL(!!) - incoming data is evil.
so.
allgemein finde ich deine verzeichnisstrucktur .. "blöd" ..
bei mir schauts atm so:
/ (index.php, login.php, ...)
/ajax/ (alles ajax-relevante)
/css/ (css eben ..)
/class/ (die klassen/module)
/inc/ (includes, die immer müssen wie user-check, globale vars etc.)
/pages/ (die buissiness-logic)
/templates/ (die darstellungslogik)
macht aber jeder anders
gepostet vor 17 Jahre, 7 Monate von n26
Eine gute Zusammenfassung auch mit Lösungsansätzen gibt es hier.
Aber trotzdem interessanter Artikel!
gepostet vor 17 Jahre, 7 Monate von Klaus
Drezil, wenn man schon Erfahrung hat ist das alter Käse. Aber vor allem für Anfänger ist es gut zu sehen, wie leicht man sogar ein Formular manipulieren kann. Daher ist es eine schöne Einführung. Ich vermisse allerdings das Thema Cross-Site-Scripting (XSS), sowie auf formaler Ebene ein Inhaltsverzeichnis.
gepostet vor 17 Jahre, 7 Monate von Nightflyer
Ich denke XSS ist bei einem Browsergame weniger die Gefahr da sich (hoffentlich) bei jedem Seitenaufruf im Game das Authentifizierungs-Script meldet und erst mal prüft ob eine valide Session vorhanden ist.
gepostet vor 17 Jahre, 7 Monate von Klaus
Klar kann ich bei einem BG XSS betreiben, wenn in Nachrichten HTML/JS nicht gefiltert wird.
Das hat auch was damit zu tun: GET Parameter in der URL
gepostet vor 17 Jahre, 7 Monate von Todi42
Schönes Ansinnen, das mal zumindest für die PHP/SQL Fraktion zusammen zu fassen. Im letzten Teil beschreibst Du, das Du in empfangenen Texten alle HTML-Sonderzeichen fluchtest. Das ist meiner Meinung nach ein Ansatz, es geht aber genaus so, das Du die Daten unübersetzt übernimmst und erst, wenn sie an das HTML Subsystem (Browser) übergeben werden die HTML-Sonderzeichen ersetzt werden. Hätte den Vorteil, das bevor man die Daten an ein nicht HTML Subsystem übergeben möchte (z.B plain text Email ) man nicht erst die escape Sequenzen wieder in ihre ursprünglichen Zeichen übersetzen müste.
Ich habe bei mir 4 Subsystem identifiziert, bei denen vor jeder Übergabe an eine Subsystem die entsprechenden Sonderzeichen des Subsystems durch die entsprechenden escape Sequenzen ersetzt werden müssen. Das hat Systematik und wenn es auch nicht an jeder Stelle eine Sicherheitslücke ist, muß es doch getan werden. Bei der Eingabe von Parametern, die in der URL Kodiert werden, müssen Sonderzeichen entsprechend ersetzt werden, dies ist keine Sicherheitslücke, muß aber auch gemacht werden, wenn man z.B. ';' in einem Forenbeitrag erlauben möchte. Als nächstes gehen die Daten in die Datenbank. Da kann ich aus einem Text keine Zahl machen, also werden alle Parameter als Texte betrachtet und Zeichen wie ' durch \' ersetzt.
Wenn jetzt Daten vom Server an den Client gehen, geht das bei mir über JSON. Dort muß man im Prinzip genau wie bei SQL dafür sorgen, das Strings auch Strings bleiben, also alle String-Terminatoren durch die entsprechenden escape Squenzen ersetzen. Aus ' wird \' aus " wird \" aus \n und \r\n wird \\n. Und als letztes folgt dann das HTML-Subsystem, bei dem dann Sonderzeichen wie < und > durch &gth etc. ersetzt werden.
gepostet vor 17 Jahre, 7 Monate von Todi42
Original von Todi42
Das ist meiner Meinung nach ein Ansatz, es geht aber genaus so, das Du die Daten unübersetzt übernimmst und erst, wenn sie an das HTML Subsystem (Browser) übergeben werden die HTML-Sonderzeichen ersetzt werden. Hätte den Vorteil, das bevor man die Daten an ein nicht HTML Subsystem übergen möchte (z.B plain text Email ) man nicht erst die escape Sequenzen wieder in ihre ursprünglichen Zeichen übersetzen müste.

Dabei muß einem natürlich auch immer klar sein, das man in der Datenbank Daten haben kann, die für das eine oder andere Subsystem ohne Fluchten an den Grenzen zu einer Sicherheitslücke führen kann.
gepostet vor 17 Jahre, 7 Monate von knalli
Original von Drezil
kurze zusammenfassung:
all - and really ALL(!!) - incoming data is evil.
so.
allgemein finde ich deine verzeichnisstrucktur .. "blöd" ..
bei mir schauts atm so:
/ (index.php, login.php, ...)
/ajax/ (alles ajax-relevante)
/css/ (css eben ..)
/class/ (die klassen/module)
/inc/ (includes, die immer müssen wie user-check, globale vars etc.)
/pages/ (die buissiness-logic)
/templates/ (die darstellungslogik)
macht aber jeder anders

Es ist aber ein guter Ansatz, wenn man das noch mal gesagt bekommt. Bei deiner Struktur fällt mir so spontan ein, wenn wir schon dabei sind:
* ist bei dir alles, was nach JS/DOM aussieht also Ajax?
* gibt es bei dir so viele Funktionen oder globale Variablen (hmm *g*), das ein eigenes Verzeichnis (langfristig) Sinn macht?
Prinzipiell lässt sich der Vorschlag des Ops ja erweitern, es geht ja auch nur um eine von mehreren Möglichkeiten, den externen Zugriff auf Scripts zu verhindern.
Die Idee finde ich eigentlich ganz gut, auch dass ein paar Bilder dabei sind.. das hilft dem einen oder anderen immer
Ein Inhaltsverzeichnis sehe ich, das zeigt nämlich mein PDF-Viewer direkt an; mein Vorschlag wäre aber auch, einfach ein automatisches Inhaltsverzeichnis einzufügen. Zudem würde ich etwas mehr Formatieren, damit meine ich Absätze, Listen (bsp. die Verzeichnisse), Zwischenüberschriften, Hervorhebungen und/oder so kleine Tipp-Icons. Oder ein rotes Ausrufezeichen an den Rand, wenn es um dieses DENYALL-wie-auch-immer geht.
gepostet vor 17 Jahre, 7 Monate von Kampfhoernchen
/doc_root/
/doc_root/game
/doc_root/game/nachrichten
/doc_root/game/gilde
...
/doc_root/game/js
/doc_root/game/img
/doc_root/portal (index.php, register.php ...)
/template
/template/portal
/template/game
/template/game/nachrichten
/tempate/game/gilde
...
/resource (Klassen)
/resource/lib (Framework)
Die Domain zeigt logischerweise auf doc_root.
gepostet vor 17 Jahre, 7 Monate von BjoernLilleike
Nicht nur die Daten vom Client sind böse, sondern auch ihm erst zu viele Daten zu übergeben. Jede Information, die der Client nicht hat, kann er nicht mißbrauchen..
Beispielsweise werden bei mir alle Anfragen mit Hilfe der player_id validiert - nur bekommt der Spieler weder seine noch irgendeine andere player_id jemals zu Gesicht.
Gerade irgendwelche IDs sollte man möglichst nur rausrücken, wenn es sich nicht vermeiden lässt und möglichst nicht für Objekte anderer Spieler.
gepostet vor 17 Jahre, 7 Monate von Drezil
Original von knalli
Original von Drezil
...

Es ist aber ein guter Ansatz, wenn man das noch mal gesagt bekommt. Bei deiner Struktur fällt mir so spontan ein, wenn wir schon dabei sind:
* ist bei dir alles, was nach JS/DOM aussieht also Ajax?
* gibt es bei dir so viele Funktionen oder globale Variablen (hmm *g*), das ein eigenes Verzeichnis (langfristig) Sinn macht?
mit ajax meine ich, dass alle anfragen von js nur hierhin gehen. also alle seiten, die dynamisch content nachliefern.
zu dem globalen: eig sind das nur 4 kleiene php-datein, aber die passen sonst nirgends rein. weil sie enthalten keine buissiness-logik, keine darstellungslogik, etc. sondern sie definieren z.b. spielweite konstanten (max anzahl von waffen in einem schiff, aktuelle anzahl an forschungsfeldern, etc.).
auch wird die db-verbindung hier instanziert und ist überall verfügbar (egal in welchem kontext). ein einfaches $pdo =& api::getPDO(); und ich hab meine db-verbindung per referenz im aktuellen kontext definiert. ist imho viel effektiver als in jeder statischen methode die verbindung neu aufzubauen
Mittlerweile finde ich meine Code-Ordnung genial gelöst. Je nachdem, WAS eitwas macht, hat es seinen festen Ort innerhalb des Projektes.
Kleines Beispiel, damit andere auch verstehen, wie ein seitenaufruf Funktioniert:
Aufgerufen wird immer die Zentrale login.php. Die macht folgendes:
1. User-Session prüfen oder falls passende Formulardaten vorliegen den User einloggen.
2. DB-Verbindung initiieren, globale sachen laden etc.
3. Den User updaten
4. Das Container-Template initialisieren
5. Die Page laden (später genauer) -> liefert Template
6. Page-Template parsen, in den container schmeissen
7. Aufräumen (db schliessen etc.)
8. container (mit inhalt) darstellen.
Das ist bei jedem seitenaufruf gleich. Die datei ist relativ kurz (100-200 zeilen) und muss auch selten geändert werden.
Kommen wir nun zum ominösen "page"-aufruf. Beispiel: der User wollte die gebäudeseite, dann steht in der geladenen page sinngemäss:
1. Modul gebäude laden
2. beabsichtigte aktion des users filtern (gebbau, gebliste, geb abbrechen, ...)
3. Daten escapen
4. Daten zur Verarbeitung escaped an das Modul weitergeben (z.b. Gebaeude::bauen($uid, $gid, ...)
5. Aktionspassendes Template laden
6. Template mit daten füttern
7. template parsefertig an die login zurückliefern
Das Modul kümmert sich hierbei um die anbindung ans Backend (DB, C++-Daemon, ...), konkret sowas wie:

abstract class Gebaeude {
[...]
static function bauen($uid, $gid, ...) {
$pdo =& api::getPDO();
$pdo->query('insert into .. (...) values (...)');
if ($pdo->errorCode() == "00000")
return true;
else
return false;
}
[...]
(nur ein Beispiel .. grad so runtergetippt .. sogar mit prüfung, ob die query erfolgreich war ..
---
So. Ich denke meinen Ansatz hab ich hinreichend dargelegt Und im Moment finde ich das System so gut. Ich habe wie gesagt die Templates (wegen Multilingual und so), die Module (in den abstrakten klassen), die eine art Datenbank-Layer bilden, meine "pages", in denen die eigentliche logik in sehr wenigen Zeilen steht (insb. verifizierung der userdaten) und so eine Erweiterung/Wartung gar kein Problem ist...
Ich mag mein System .. (das hatte ich noch nicht erwähnt, oder?? )
gepostet vor 17 Jahre, 7 Monate von Klaus
Original von BjoernLilleike
Nicht nur die Daten vom Client sind böse, sondern auch ihm erst zu viele Daten zu übergeben. Jede Information, die der Client nicht hat, kann er nicht mißbrauchen..
Beispielsweise werden bei mir alle Anfragen mit Hilfe der player_id validiert - nur bekommt der Spieler weder seine noch irgendeine andere player_id jemals zu Gesicht.
Gerade irgendwelche IDs sollte man möglichst nur rausrücken, wenn es sich nicht vermeiden lässt und möglichst nicht für Objekte anderer Spieler.

Wie führst du dann Aktionen aus? z.B. ein Upgrade von Gebäude X, Angriff auf Y...
Irgendwie musst du die Dinge doch identifizieren?
gepostet vor 17 Jahre, 7 Monate von Todi42
Original von Klaus
Wie führst du dann Aktionen aus? z.B. ein Upgrade von Gebäude X, Angriff auf Y...
Irgendwie musst du die Dinge doch identifizieren?

Über eine Session-ID sollte man an den Benutzer kommen. Und wenn man die Daten benötigt, dann benötigt man sie halt.
gepostet vor 17 Jahre, 7 Monate von Nightflyer
Tut updated

Auf diese Diskussion antworten