Gegen Cross-Site-Request-Forgery absichern

CSRF, der schlafende Gigant tritt immer mehr in Erscheinung, mehr als Analyse und weniger in Form einfach anzuwendender Lösungen für Jedermann.

Die Sicherheitsbibliothek SSEQ-LIB stellt zum Schutz gegen CSRF Tokens bereit, die in Links und Formulare eingebaut und bei der Ausführung überprüft werden. Die Tokens tragen Namen, sodass ihre Gültigkeit für bestimmte Bereiche der Webapplikation eingeschränkt werden kann und können außerdem eine einmalige Gültigkeit aufweisen, z.B. bei der Verwendung in Login-Masken.

Beispiele der Verwendung in eine Webapplikation:

<br />
<?
php
// Setzen eines Formular-Tokens
$formular '

<form>&#8218; . SEQ_FTOKEN(&#8218;kontaktformular&#8216;) . &#8218;</form>
<p>&#8218;</p>
<p>// Erzeugt z.B.:</p>
<form><input type="hidden" name="SEQ_TOKEN_4f02047c08ab94f9a731d89831d4ad72" value="7c2ec951d34f2f9830a8768485ccec60"></form>
<p>// Setzen eines Link-Tokens<br />
$formular = &#8218;<a href="index.php?' 
SEQ_LTOKEN('liste') . '">LISTE</a>&#8218;</p>
<p>// Erzeugt z.B.:<br />
<a href="index.php?SEQ_TOKEN_4f02047c08ab94f9a731d89831d4ad72=7c2ec951d34f2f9830a8768485ccec60">LISTE</a></p>
<p>?><br />

Dort, wo die entsprechend vorbereiteten Formulare oder Links ausgeführt werden, werden die Tokens geprüft:

<br />
<?
php
// Prüfen des Tokens 
SEQ_CHECK_TOKEN('kontaktformular');
?><br />

Je nach Einstellung reagiert SSEQ-LIB auf fehlende, falsche und abgelaufene Tokens mit einer Umleitung, Logout des Nutzers oder einer Antwortverzögerung. Diese können auch kombiniert werden. Zusätzlich – wenn in der Config eingeschaltet – muss der verwendete Browser der selbe sein, der bei der Erzeugung des Tokens verwendet wurde.

Konfiguration

Zu finden unter: SERVERPFAD/sseq-lib/seq_lib.php

Tokens gültig nur vom selber Webbrowser aus
$_SEQ_SESSION_HEADERSCHECK = 1;

Maximale Lebenszeit eines Tokens
$_SEQ_TOKENLIFETIME = 7200; /* zwei Stunden */

Reaktion bei ungültigen Tokens
ANMERKUNG: Gilt für alle Prüfungen der SSEQ-LIB!
$_SEQ_IDS_ONATTACK_ACTION = ‚logout redirect delay‘;

Das könnte dich auch interessieren …

14 Antworten

  1. Andreas Mauf sagt:

    Ich hab mir die Bibliothek einmal genauer angesehen. Gute Arbeit.
    Aufgefallen ist mir, dass der Wert des Tokens selbst offenbar gar nicht verglichen wird. Ist das richtig und gewollt?

  2. Erich Kachel sagt:

    Danke für den scharfen Blick!

    Durch die zusätzliche Verwaltung der einzelnen Tokennamen in der Session ist glücklicherweise der CSRF Schutz nicht komplett runter, da noch andere – wenn auch schwache – Maßnahmen greifen.

    Somit wird es schnellsten eine neue Version der SSEQ-LIB geben.

  3. John1y sagt:

    Hallo,
    leider habe ich Probleme Deine Lib bzgl. CSRF.
    Ich habe im Formular folgende Zeile stehen:

    Nachdem das Formular gesendet wurde:
    SEQ_CHECK_TOKEN(‚kontaktformular‘);

    Jedoch erhalte ich nach dem Abschicken einfach nur die Meldung „Undefined action.“.

    Kannst Du mir bitte sagen wo der Fehler liegt?

  4. Erich Kachel sagt:

    Zuallererst: nachdem das Formular gesendet wurde, bringt ein CSRF-Schutz nichts mehr. Die Prüfung muss davor statt finden.

    „Undefined action“ wird vermutlich angezeigt, weil das CSRF-Token falsch ist und ein Redirect zur Startseite erfolgen soll. Da jedoch zuvor bereits die Headers gesendet wurden (vielleicht aus deinem gesendeten Formular), kann kein Redirect erfolgen und diese Meldung wird angezeigt. Warum der Token nicht akzeptiert wurde, verrät dir ganz genau die Datei „seq_log/log.txt“. Schau dort zuerst nach.

  5. Georg Jordt sagt:

    Hallo,
    nach anfänglichen Soundproblemen („echo“ vergessen) komm ich ganz gut klar mit den Tokens.
    Exzellente Arbeit. Ein kleines Manko ist mir aber augefallen: die Funktion „SEQ_CHECK_TOKEN“ endet
    in der Regel mit „SEQ_TERMINATE_SESSION_“ unter „Vernachlässigung“ der Variable $redir_exit:

    function SEQ_CHECK_TOKEN($originname_ = “) {

    SEQ_TERMINATE_SESSION_();


    }

    Aber:

    function SEQ_TERMINATE_SESSION_($redir_exit = true) {


    if ($redir_exit) {
    // redirect to location OR ….what? ;)
    seq_terminate_(‚redirect‘);
    die;
    }

    }

    Per se wird also umgeleitet. Ist gut als Regel, aber was ist mit der Ausnahme?

    Ich hab die Funktion bei mir ein wenig ergänzt:
    function SEQ_CHECK_TOKEN($originname_ = “, $redir_exit = true) {


    SEQ_TERMINATE_SESSION_($redir_exit);
    return false;

    return true;
    }

    So kann die Funktion einen Rückgabewert liefern, anhand dessen man spezielle Entscheidungen treffen kann.

    Konkreter Fall:

    Ich benutze AMFPHP sowohl für Flash, aber besonders als JSON AJAX webservice.

    Durch setzen eines Tokens auf der Webseite und anschliessende Abrage desselben in meinem AMFPHP service (PhP class)
    kann ich per modifizierter Funktion entscheiden, was ich über JSON an einen potentiellen Hacker zurückschicke:
    nichts, irreführende Daten, „Fehler“meldungen, wüßte Beschimpfungen :).

    Endresultat: mein webservice ist (einigermassen, DOS!?!) abgesichert.

  6. Tobias sagt:

    Hallo,
    macht es Sinn sowas bei einem Kontaktformular oder Loginformular zu nutzen?
    Oder nur dort wo etwas in der Datenbank „geändert/gelöscht“ wird.

  7. Erich Kachel sagt:

    @Tobias
    Bei einem Loginformular ist es nicht _so_ wichtig, da die benötigte Eingabe der Logindaten ein automatisches Ausführen verhindert. Beim Kontaktformular hingegen macht es Sinn, da ansonsten z.B. im Namen eines (angemeldeten) Nutzers eine Nachricht abgeschickt werden kann.

  8. Tobias sagt:

    @Erich vielen Dank für die schnelle Antwort.
    Eine abschließende Frage zu den Tokens habe ich noch.
    Meine Vorgehensweise ist die folgende:

    * Link generieren SEQ_LTOKEN(‚test‘)
    * Auf der Seite wo mein Formular ist ganz oben: SEQ_CHECK_TOKEN(‚test‘);
    Danach im Formular das Hidden-Field: SEQ_FTOKEN(‚test‘)

    Was mache ich mit dem Hidden field?
    Und was mache ich wenn der User den Submit-Button klickt und die Seite neu geladen wird?
    Übergebe ich die aktuelle URL in den action Parameter vom ? – Denn ich prüfe nach dem Submit ob Fehler vorhanden sind. Sofern ja, wird das Formular mit den entsprechenden Fehlern angezeigt.

    Besten Dank.

  9. Erich Kachel sagt:

    @Tobias
    Hier eine Mögliche Struktur:

    *datei1.php*
    <a href=“datei2.php?<?php echo SEQ_LTOKEN(‚test‘, 1); >“>weiter</a>

    *datei2.php*
    <?php
    SEQ_CHECK_TOKEN(‚test‘);
    ?>
    <form method=“POST“ action=“csrf_test2.php“>
    <?php echo SEQ_FTOKEN(‚test‘); ?>
    <input type=“submit“ name=“go“ value=“go“>
    </form>

    Oder eine andere Möglichkeit für „datei2.php“ die ohne hidden-Feld auskommt. Da „ACTION“ leer ist, wird die aktuelle URL erneut aufgerufen und diese enthält gerade ein gültiges Token, nämlich das von dem Link von „datei1.php“. (BEACHTE: falls das Linktoken für nur einmaliger Nutzung bestimmt war, funktioniert nur die Variante mit dem hidden-Feld, da das Linktoken nach dem Aufruf seine Gültigkeit verloren hat!)

    *datei2.php*
    <?php
    SEQ_CHECK_TOKEN(‚test‘);
    ?>
    <form method=“POST“ action=““>
    <input type=“submit“ name=“go“ value=“go“>
    </form>

  10. Alois sagt:

    Habe zwei Tage probiert und getestet: ein gutes Stück Arbeit! Danke. Trotzdem eine Frage: gibt es eine überarbeitete Version ausschließlich für PHP 5+ ?

    Besten Dank!

  11. Alois sagt:

    Ich habe die SSEQ-LIB in unser CRM eingearbeitet. Ein tolles Stück! Danke auch.

    Aufgefallen ist mir, dass SEQ_OUTPUT() Werte mit „+“ nicht korrekt ausgibt z.B.
    Google+ -> „Google“,
    Goo+gle+ -> „Goo艗“,
    +49 -> „“

    Der Fehler liegt hier anscheinend an mb_convert_encoding() und der Option „from_encoding
    “ -> UTF-7:

    mb_convert_encoding(‚Goo+gle+‘, „UTF-8“, „7bit, UTF-7, UTF-8, UTF-16, ISO-8859-1, ASCII“) -> „Goo艗“

    mb_convert_encoding(‚Goo+gle+‘, „UTF-8“, „7bit, UTF-8, UTF-16, ISO-8859-1, ASCII“) -> „Goo+gle+“

    mb_convert_encoding(‚Goo+gle+‘, „UTF-8“, „auto“) -> „Goo+gle+“

    Entwicklungsmaschine: WinXP32, PHP 5.3.1
    Produktivumgebung: Debian6, PHP 5.3.3-7+squeeze8 noch nicht getestet

  12. Alois sagt:

    Auf einem aktuellen Debian6 (PHP 5.3.3-7+squeeze8) sieht die Sache schon etwas anders aus:

    Call-time pass-by-reference has been deprecated!

    Ich verwende ausschließlich PHP 5.3+, daher meine Frage:
    php.ini oder das Script anpassen?

    Gibt es eine bevorzugte Methode?

    Danke!

  13. admin sagt:

    Skript anpassen. Es entsteht eine aktuelle Version, die keine Warnings mehr erzeugt.

  14. admin sagt:

    Ja, eine aktuelle Version ist im Entstehen.