Inside-Informationen über Pokefans: was bei uns so unter der Haube läuft. Kurioses, Interessantes oder auch einfach nur Einblicke in das, was wir so den lieben langen Tag machen.

Zum Inhalt | Zum Menü | Zur Suche

How to hack Pokefans!

Packt sqlmap wieder ein - ihr habt doch nicht wirklich gedacht, dass ich euch hier beibringe, wie man Pokefans zerlegt. Nein, wir befassen uns heute mit der guten Definiton von "hacken" - programmieren. Aber wenn ich eine Chance auf Clickbait sehe, dann muss ich sie schon nutzen, irgendwie... :D

Pokefans ist ja ein recht umfangreiches Projekt, und man braucht ein bisschen Startwissen, um hier mitmachen zu können. Bei knapp 1000 Dateien im Repository verliert man vielleicht auch ein bisschen den Überblick. Dafür ist dieser Blogpost da.

Die Struktur des Repositories habe ich ja schon vor einem Jahr (runterscrollen!) beschrieben. Daran hat sich nichts geändert. Vielleicht solltest du das jetzt nochmal lesen. Seither hat sich ein bisschen was getan, die Struktur stimmt aber noch. Um das hier jetzt etwas einfacher zu halten, führe ich den Begriff der Magie ein. Immer, wenn das Verständnis der Mechanik nicht wirklich notwendig ist, um zu verstehen, dass die so ist, wie sie ist, passieren Dinge automagisch oder sind einfach hochgradig uninteressant. Diese Worte werde ich hier noch ein paar mal Verwenden :) Selbstverständlich hat alles einen guten Grund, warum es so funktioniert, wie es funktioniert - vermutlich verwirrt dich das am Anfang aber nur, weil es schwer ist, sich das vorzustellen, ohne das gesamte Framework sehr tief zu kennen. Begnügen wir uns also mal ab und an damit, dass Dinge mit Magie passieren - schließlich ist ja jede hinreichend fortgeschrittene Technik von Magie nicht zu unterscheiden :)

Was setze ich hier voraus? Du solltest grundlegendes Verständnis von folgenden Begriffen haben. Wenn nicht, geh, und schlag' sie nach.

  • Klasse
  • Konstruktor
  • Methode
  • while
  • for
  • foreach
  • if
  • else
  • try
  • catch

Nachdem wir diese grundsätzlichen Sachen jetzt geklärt hätten, können wir ja anfangen!

Schauen wir uns mal einen typischen Konstruktor an. Den hier zum Beispiel.


public HomeController(IBreadcrumbs crumbs, Entities entities, Cache c)
        {
            this.breadcrumbs = crumbs;
            _entities = entities;
            cache = c;
        }

Im Konstruktor bekommt dein Controller ein paar interessante Sachen. Hier bekommst du eine Klasse, die Breadcrumbs verwaltet, den Datenbanklayer (Entities) und den Cache. Das ganze passiert automagisch, wir speichern uns das weg, damit wir das nachher in den Klassenmethoden verwenden können. Falls du dir das nicht merken kannst: Einfach Kopierpaste verwenden. Damit machst du sicher nichts falsch.

Das Wichtigste aus dem Konstruktor sind dabei die Entities. Die sind deine Schnittstelle zur Datenbank. Jede Klasse von hier, die nicht im Migrations-Ordner ist und nicht Entities heißt entspricht einer Tabelle in der Datenbank. Dass sich das zusammenfindet stellt hier Magie sicher. Wir können uns damit begnügen, dass es in der Regel so ist (da gibt's zwar ein, zwei Ausnahmen, aber die sind hochgradig uninteressant).

Wie benutze ich das jetzt? Nun, die Entities haben für jede Datenbanktabelle eine Eigenschaft. Da sind die Daten drin. Und die können wir uns jetzt rausholen. Wenn ich z.B. alle Benutzer haben möchte, deren Name ein 'x' beinhaltet, dann sieht das so aus:


    List<User> users = _entities.Users.Where(x => x.Username.Contains('x')).ToList();

oder kürzer:


    var users = _entities.Users.Where(x => x.Username.Contains('x'));

Was da genau der Unterschied ist, ist für uns erstmal hochgradig uninteressant. Man sollte jedoch beachten, dass die letztere Variante nicht direkt an eine View übergeben werden kann, da müssen wir dann beim Übergeben noch ToList() aufrufen.

Jetzt kann ich auch sagen, ich will nicht eine Liste von Benutzern, sondern ich will einen bestimmten Benutzer, z.B. anhand seiner UserID haben. Das geht so:


    User user = _entities.Users.FirstOrDefault(x => x.UserId == 42);

Fertig.

Innerhalb der Klammer steht ein sogenannter Lambda-Ausdruck. Das ist nichts anderes als die kompakte Schreibweise einer Funktion, die auf jeden Eintrag der Datenbank angewandt wird. Kann man sich so vorstellen - aus dem Lambda aus dem vorherigen Beispiel könne man z.B. das hier machen:


    bool lambda(User x) {
        return x.UserId == 42;
    }

Wenn der Vergleich true wird, dann landet das Element in der Liste, wenn nicht, dann halt nicht. Eigentlich recht einfach, oder?

Wenden wir uns weiterer Magie zu. Jede URL mappt auf eine bestimmte Methode. Wie das passiert, besprechen wir nachher, im Moment begnügen wir uns damit, dass wir gerade eine URL aufgerufen haben, die hier rauskommt. Auch das passiert für uns automagisch und die Details sind hochgradig uninteressant - vorerst.

Wichtig sind hier erstmal zwei Dinge:

  1. Der Rückgabewert der Methode ist vom Typ ActionResult. Das ist eigentlich immer so. Der Name ist irrelevant, sollte aber halbwegs beschreibend sein.
  2. In Zeile 15 haben wir ein return mit einem ewig langen Pfad drin. Dieser Pfad gibt an, wo die View (also die Anzeigevorschrift) für diese Methode ist. Dort ist dann das HTML-Template, das die Daten aus dem Controller in HTML verwandelt.

Wenn der Computer also das abarbeitet und beim return ankommt, landen wir dann hier. Das ist die dazugehörige View. Findest du den Pfad wieder? Ja? Gut. Der Inhalt hier ist auch nicht sonderlich spannend, zeigt aber, wie die Abfolge grunsätzlich läuft: Url -> Controller-Methode -> View -> fertig.

Jetzt ist so eine statische Seite relativ langweilig. Ich will Daten übergeben! Dazu schauen wir uns ein etwas komplizierteres Beispiel an - etwa das hier. Das hier ist ein Auszug aus der neuen Fanart-Verwaltung. Halten wir mal folgendes fest:

  1. Zeile 47 holt sich Daten aus der Datenbank. Wie genau - hochgradig uninteressant für den Moment. Wir holen uns alle Fanarts mit zugehörigen Tags, die vom gerade angemeldeten Benutzer hochgeladen wurden, sortiert nachdem Zeitpunkt des Hochladens, neuere zuerst.
  2. Zeile 48 holt die Namen der Fanart-Kategorien aus dem Cache. Das geht schneller als der Datenbankzugriff. Details dazu sind wieder hochgradig uninteressant.
  3. Zeile 50 übergibt die Daten an die View. Preisfrage: Worin besteht denn der unterschied zur vorherigen Zeile?

Da kommen wir dann hier raus. Gerendert sieht das dann z.B. so aus. Quellcode.

Dabei geben wir in der View in Zeile 2 den Typ der Variable an, die wir vorhin übergeben haben - hier eine Liste. Diese Variable heißt innerhalb der View Model. Der geneigte Leser möge jetzt über folgende Fragestellungen nachdenken:

  • Wo fängt die View im Quellcode an, wo hört sie auf?
  • Wo sind die Schleifen hinverschwunden? Was haben sie generiert?
  • Was macht Url.Action(...)?
  • Was könnte @variable machen?

Hier die Auflösung:

  • Die View geht von Zeile 70 bis Zeile 104.
  • Die äußere Schleife hat den Code von Zeile 82 bis 102 generiert. Wäre mehr als eine Fanart hochgeladen, würde sich dieser Block entsprechend wiederholen.
  • Die innere Schleifen haben Z85 - 89 respektive Z93-98 generiert. Die zweite Schleife macht dabei nichts anderes, als die Fließkommazahl der Bewertung als Sterne darzustellen.
  • Url.Action(...) generiert einen Link. Wir geben dabei Klasse und Methode des Ziels an, die URL wird anhand der Mappingtabelle gefunden
  • @variable gibt Variablen aus - und zwar XSS-Sicher, alles HTML wird escaped, damit es keinen Schaden anrichten kann.

Wie mappe ich jetzt eine URL zu einer Methode in einer Klasse? In unserem Beispiel sind wir im ManageController und unsere Methode heißt Index. Das zugehörgige Mapping gibts hier:


            context.Routes.Add("fanartManage", new DomainRoute(
                            "fanart." + ConfigurationManager.AppSettings["Domain"],
                            "verwaltung",
                            new { action = "Index", controller = "Manage" },
                            dataTokens
                ));

Auch hier passiert wieder viel automagisch, was uns nicht weiter kümmern braucht. Wichtig ist nur folgendes:

  1. Der Name - hier fanartManage - muss eindeutig sein. Keine Sorge, es fliegt dir um die Ohren, wenn es nicht eindeutig ist.
  2. Die korrekte Subdomain muss angegeben werden.
  3. Die Url nach dem / muss angegeben werden. Da wir hier bei http://fanart.pokefans.rocks/verwaltung/ rauskommen wollen, ist das nur "verwaltung" (beachte, dass ich keinen abschließenden / gesetzt habe!).
  4. Action ist der Name der Methode, die aufgerufen werden soll, wenn jemand diese URL ansurft. Controller ist die Klasse, in der sich die Methode befindet. Beachte, dass die Klasse in Wahrheit ManageController heißt und sich im Unterordner "Controllers" der entsprechenden Area befindet. Das muss so sein, da fährt die Eisenbahn drüber.
  5. die DataTokens musst du einfach reinkopieren. Was die machen, ist hochgradig uninteressant.

Tja, dann bleibt mir auch nicht mehr viel zu sagen, außer: happy hacking. Und falls es noch fragen gibt... einfach melden. Es gibt keine dummen Fragen.

Kommentar eintragen

Kommentare können mit einfacher Wiki-Syntax formatiert werden.

Folgende haben zum gleichen Thema veröffentlicht

Trackback-URL : http://inside.pokefans.net/index.php?trackback/11

Die Kommentare dieses Eintrags als Atom-Feed abonnieren