Facebook
Twitter
Google+
Kommentare
5

Sicherer Umgang mit der Request Variablen

Wie ihr ja wisst, war ich vor kurzem auf der PHP Konferenz und wie jedes Jahr war natürlich auch Sicherheit ein Thema. Stefan Esser ging in seinem Vortrag „Lesser known security problems“ auf die Gefahr der Superglobalen $_REQUEST ein. Klar $_GET, $POST und Konsorten sollte man möglichst wenig verwenden, aber man kommt nicht immer daran vorbei. Wenn man sie aber dann doch benutzt, muss man sicherstellen, dass sie auch richtig „entschärft“ wird. Ich habe mich mal ein paar Minuten hingesetzt und mir was zu diesem Thema überlegt. Vielleicht gibt es diesen Ansatz schon 100 mal, dann könnt ihr diesen Beitrag getrost überspringen.

Die Erfahrung, die ich gemacht habe ist, dass man sich eine Request Klasse bastelt, die einem $_GET oder $_POST Variablen zurückgibt. Was auch nicht falsch ist, denn so entkoppelt man seinen Code von HTTP. Der Request kann jetzt von irgendwo her kommen. Die meisten Ansätze geben die angefragte Variable aber einfach so zurück, wie sie im Request steht. Jetzt muss der User entscheiden, was er damit macht.

Warum erweitert man die Request Klasse nicht einfach so, dass man dort Filter registrieren kann. Diese Filter würden zum Beispiel bei jeder Variable, die abgefragt wird, die HTML Zeichen escapen. Wenn man einen bestimmten Filter nicht haben möchte, so muss man dies bei der Abfrage explizit abmelden. Ich habe mal angefangen, ein Stück Code zu entwerfen, wie ich mir die Verwendung vorstellen könnte. Ich muss dazu sagen, dass dies hier die einzigen Zeilen Code sind, die ich dazu verfasst habe. Es ist also bis jetzt wirklich nur eine Idee.

<?php

  $request = Base_Security_Request::getInstance( );

  $request->addSecurityFilter( new Base_Security_HtmlFilter( ) );
  $request->addSecurityFilter( new Base_Security_MySqlFilter( ) );

  $isReadOnly = $request->getBooleanVariable( 'isReadOnly' );
  $userId     = $request->getIntegerVariable( 'userId' );
  $username   = $request->getStringVariable( 'username',
                                              array( Base_Security_RemovedFilter::getName( ) ),
                                              array( Base_Security_AdditionalFilter::getName( ) )
                                            );

  $boolVar = $request->getVariable( 'someVar', Base_Security_Datatypes::BOOLEAN );

?>

Wir haben so natürlich den großen Vorteil, dass nur Variablen gefiltert werden müssen, die wir auch tatsächlich verwenden. Und vergessen können wir sicherheitskritische Dinge auch nicht, da es ja automatisch passiert. Braucht man doch eine ungefilterten Variable, so muss man sie explizit anfordern. Da dies nicht so häufig sein sollte, denke ich, dass dies der beste Ansatz ist. Für die Request Klasse habe ich das Singleton Pattern verwendet, da wir nur eine Instanz im ganzen Skript benötigen werden.

Wem die Idee gefällt, der kann gerne mal probieren die Klassen zu schreiben. Ich werde sie auf jeden Fall hier posten. Aber sucht lieber vorher, ob dies nicht schon jedes x-beliebige Framework macht, ich habe es noch nicht nachgeprüft.

Über den Autor

Nils Langner

Nils Langner ist der Gründer von "the web hates me" und auch der Hauptautor. Im wahren Leben leitet er das Qualitätsmanagementteam im Gruner+Jahr-Digitalbereich und ist somit für Seiten wie stern.de, eltern.de und gala.de aus Qualitätssicht verantwortlich. Nils schreibt seit den Anfängen von phphatesme, welches er ebenfalls gegründet hat, nicht nur für diverse Blogs, sondern auch für Fachmagazine, wie das PHP Magazin, die t3n, die c't oder die iX. Nebenbei ist er noch ein gern gesehener Sprecher auf Konferenzen. Herr Langner schreibt die Texte über sich gerne in der dritten Form.
Kommentare

5 Comments

  1. Stubbles macht sowas ähnliches. Deine Beispiele würden dort ungefähr so aussehen:

    $isReadOnly = $request->getValidatedValue(new stubPreselectValidator(array(true, false)), ‚isReadOnly‘);
    $userId = $request->getFilteredValue(stubStringFilterFactory::forType(‚int‘), ‚userId‘);
    $username = $request->getFilteredValue(stubStringFilterFactory::forType(’string‘)->length(1, 50)->asRequired()->defaultsTo(‚anonymous‘), ‚username‘);

    Wobei sich bei den Filtern alle beliebig kombinieren lassen. Man wird also immer daran erinnert, einen Filter oder Validator zu benutzen. Trotzdem kommt man an den rohen Wert genauso heran – man muss dann eben explizit einen entsprechenden Filter einsetzen.

    Als Singleton würde ich den Request aber nicht bauen, ich brauche den schließlich nur einmal erzeugen und kann den dann überall mitgeben wo er benötigt wird. Macht sich besser beim Testen.

    Reply
  2. Ok bei meinem Ansatz sehe ich jetzt noch ein Problem. Wie kann man es schaffen, alle Filter zu entfernen. Da ich eine Singleton habe, weiß ich ja gar nicht, welche Filter aktiv sind.

    Dafür gefällt es mir aus der Sicherheitssicht besser, dass man die Filter entfernen muss und nicht hinzufügen.

    Reply
  3. Den Ansatz finde ich problematisch. Wenn ich Werte nur in die Datenbank übernehme will ich die nur für den Eintrag in der DB absichern, aber nicht für die Ausgabe im Browser. Bei Deinem Ansatz muss ich jetzt den Filter für die Browserausgabe abstellen. Ich finde, damit drückt sich im Code nachher nicht mehr klar die Intention aus, was ich eigentlich will, nämlich einen Wert in die DB schreiben und den entsprechend vorher filtern. Bislang bin ich nämlich nicht dahinter gestiegen, was Deine Intention beim mit dem zweiten und dritten Parameter für das Filtern des Usernamen ist.

    Reply

Leave a Comment.

Link erfolgreich vorgeschlagen.

Vielen Dank, dass du einen Link vorgeschlagen hast. Wir werden ihn sobald wie möglich prüfen. Schließen