In unserem letzten Artikel haben wir Cross-Site Scripting (XSS) und die Funktionen behandelt, die WordPress zur Verhinderung von XSS-Angriffen bereitstellt. Heute befassen wir uns mit einem weiteren Sicherheitsproblem für Front-End-Entwickler: Cross-Site Request Forgery (CSRF).
Damit Sie nicht denken, dass diese Sicherheitsdinge unwichtig sind: Eine große Sicherheitslücke wurde kürzlich im WP SEO Plugin entdeckt, das auf über 1.000.000 WordPress-Websites installiert ist und Hackern ermöglichte, die WordPress-Datenbank mittels CSRF zu manipulieren. (Das Plugin wurde schnell behoben, aber Sie können sehen, wie beängstigend so etwas sein kann.)
Nonces und Cross-Site Request Forgery
Vereinfacht ausgedrückt ist CSRF, wenn böswillige Akteure versuchen, Benutzer (normalerweise jemanden mit Zugriff auf das WordPress-Dashboard) zu täuschen, damit sie etwas tun, was sie nicht beabsichtigen. Ein einfaches Beispiel kann helfen, dies zu veranschaulichen.
Angenommen, Sie entwickeln ein WordPress-Plugin, das angemeldeten Benutzern das Hochladen ihrer Bilder ermöglicht. Auf der Front-End der Website könnte Ihr Plugin ein Formular wie dieses generieren:
<?php if ( is_user_logged_in() ) : ?>
<form action="/submit-picture/" method="get">
<input type="text" name="picture_url">
<input type="submit">
</form>
<?php endif; ?>
Auf der Back-End verarbeiten Sie die Einreichungen des Bildformulars.
if ( is_user_logged_in() && isset( $_REQUEST['picture_url'] ) ) {
// Save the picture here
}
Angenommen, ein Hacker möchte nun ein *gefälschtes* Bild einreichen. Der Hacker hat keinen Benutzernamen oder kein Passwort – er kann also nichts tun, richtig?
Nicht ganz. Um das Konto eines Ihrer Benutzer zu kapern und ein gefälschtes Bild einzureichen, könnte der Hacker versuchen, einen angemeldeten Benutzer dazu zu bringen, auf einen Link zu klicken, der so aussieht:
http://your-site.com/submit-picture/?picture_url=fake-picture.jpg
Wenn ein angemeldeter Benutzer auf diesen Link klickt, was hindert dann die Einreichung des Bildes? Sie haben es erraten: Nichts. Der Hacker gewinnt.
Das liegt daran, dass wir nichts getan haben, um die *Absicht* des Benutzers zu bestätigen, ein Bild einzureichen. Das "F" in CSRF steht für Fälschung (Forgery): Der Hacker hat eine Anfrage im Namen des Benutzers gefälscht (nachgemacht), ähnlich wie Sie in der Grundschule Ihre Unterschrift auf Krankmeldungen *gefälscht* haben.
Wie man CSRF verhindert
Wir können CSRF-Angriffe stoppen, indem wir einige praktische Funktionen von WordPress nutzen. Um zu verhindern, dass eine Anfrage erfolgreich "gefälscht" wird, verwendet WordPress Nonces (nummern, die einmal verwendet werden), um zu überprüfen, ob die Anfrage tatsächlich vom aktuellen Benutzer stammt.
Der grundlegende Prozess sieht so aus:
- Ein Nonce wird generiert.
- Dieser Nonce wird mit dem Formular übermittelt.
- Auf der Back-End wird der Nonce auf Gültigkeit geprüft. Wenn er gültig ist, wird die Aktion fortgesetzt. Wenn er ungültig ist, stoppt alles – die Anfrage wurde wahrscheinlich gefälscht!
Lassen Sie uns einen Nonce hinzufügen
Auf der Front-End wollen wir einen Nonce zu einer Formularübermittlung hinzufügen. Dies tun wir mit der praktischen Funktion wp_nonce_field.
<form action="/submit-picture/" method="get">
<input type="text" name="picture_url">
<input type="submit">
<?php wp_nonce_field( 'submit_picture' ); ?>
</form>
Einfach, oder? Nun müssen wir bei der Verarbeitung dieses Formulars auf der Back-End nur noch einige integrierte WordPress-Funktionen verwenden, um zu überprüfen, ob der Nonce gültig war.
// By default, we can find the nonce in the "_wpnonce" request parameter.
$nonce = $_REQUEST['_wpnonce'];
if ( ! wp_verify_nonce( $nonce, 'submit_picture' ) ) {
exit; // Get out of here, the nonce is rotten!
}
// Now we can process the submission
if ( is_user_logged_in() && isset( $_REQUEST['picture_url'] ) ) {
// Save the picture here
}
Da der Nonce dem Hacker unbekannt ist, kann er keinen gefälschten Link erstellen, der Schaden anrichtet. Jeder böswillige Versuch wird bei der Überprüfung mit wp_verify_nonce abgebrochen. Wir haben den Hacker gestoppt, richtig?! In vielen Fällen ja. Wir haben es einem Hacker deutlich erschwert, eine Anfrage zu fälschen.
Nonces sind benutzerspezifisch
Aber was ist, wenn ein *böswilliger angemeldeter Benutzer* Ihrer Website ein gefälschtes Bild *für einen anderen Benutzer* einreichen möchte? Könnte der böswillige Benutzer nicht einfach die HTML-Quelle der Website ansehen, das Nonce-Feld finden und es zu seiner "bösen" URL hinzufügen, wie hier?
http://ihre-seite.com/bild-einreichen/?bild_url=gefälschtes-bild.jpg&_wpnonce=NONCEWERT
Glücklicherweise *wird es nicht funktionieren*. WordPress-Nonces sind eindeutig für die Sitzung eines Benutzers, sodass ein Hacker seinen eigenen Nonce nicht gegen den eines anderen Benutzers austauschen könnte.
Ein Hut-Tipp an Mark Allen in den Kommentaren für den Hinweis auf sitzungsbezogene Nonces.
Probieren Sie diese Nonce-Funktionen aus
WordPress ist voll von praktischen Funktionen, die das Generieren von Nonces (und die Verhinderung von CSRF) erleichtern. Hier sind einige, die Sie für verschiedene Situationen nützlich finden könnten:
Funktion: wp_verify_nonce
Was sie tut: Überprüft, ob der Nonce-Wert ordnungsgemäß von WordPress generiert wurde.
Unabhängig davon, wie Sie Ihren Nonce generieren, sollte er auf der Back-End mit wp_verify_nonce überprüft werden.
Genau wie in den obigen Beispielen verwenden Sie wp_verify_nonce, um die Absicht eines Benutzers zu überprüfen.
$submitted_value = $_REQUEST['_wpnonce'];
if ( wp_verify_nonce( $submitted_value, 'your_action_name' ) ) {
// nonce was valid...
}
Codex-Eintrag für wp_verify_nonce
Funktion: wp_nonce_field
Was sie tut: Gibt ein verstecktes <input>-Tag aus, das einen Nonce-Wert enthält.
Wird verwendet, wenn Sie ein <form> erstellen, das vor CSRF geschützt werden muss (was *so ziemlich jedes* <form> ist).
Genau wie in den obigen Beispielen ist wp_nonce_field eine praktische Funktion, die uns das Leben beim Erstellen von HTML-Formularen erleichtert.
<form>
<!-- Other form fields go here -->
<?php wp_nonce_field( 'your_action_name' ); ?>
</form>
Codex-Eintrag für wp_nonce_field
Funktion: wp_nonce_url
Was sie tut: Gibt eine URL mit einem angehängten Nonce-Wert zurück.
Wird verwendet, wenn Sie eine URL erstellen, die ein Nonce-Feld als Query-String-Parameter benötigt. Am nützlichsten bei GET-Anfragen, die eine CSRF-Validierung erfordern.
Hier ist ein Beispiel für wp_nonce_url, bei dem wir überprüfen müssen, ob ein Benutzer beabsichtigt hat, auf einen Link zu klicken.
<?php
$action_url = wp_nonce_url( '/change-color/?color=blue', 'change_color' );
// $action_url is now "/change-color/?color=blue&_wpnonce=GENERATED_VALUE"
?>
<a href="<?php echo esc_url( $action_url ); ?>">Change to blue</a>
Codex-Eintrag für wp_nonce_url
Funktion: wp_create_nonce
Was sie tut: Generiert einen Nonce-Wert.
Wird verwendet, wenn Sie einen Nonce-Wert benötigen, der nicht zu einer der oben genannten praktischen Funktionen passt.
Hier wird wp_create_nonce verwendet, um einen Nonce-Wert für die Rückgabe im JSON-Format zu generieren (nützlich für die Rückgabe in AJAX-Anfragen).
<?php
$nonce_value = wp_create_nonce( 'your_action_name' );
return json_encode( array( 'nonce' => $nonce_value ) );
?>
Codex-Eintrag für wp_create_nonce
Funktion: check_ajax_referer
Was sie tut: Prüft einen übermittelten Nonce mit wp_verify_nonce und stoppt die Ausführung, wenn die Validierung fehlschlägt.
check_ajax_referer ist eine weitere praktische Funktion, die hauptsächlich für AJAX-Anfragen verwendet wird. Sie können sie verwenden, um die Überprüfung mit wp_verify_nonce selbst zu überspringen.
<?php
// Forged requests won't get past this line:
check_ajax_referer( 'your_action_name' );
// Do your action here
?>
Codex-Eintrag für check_ajax_referer
Nonces für alles!
CSRF-Schwachstellen reichen von harmlos (z. B. Umfrage-Einreichungen) bis hin zu *extrem gefährlich* (z. B. Änderung von Passwörtern oder Berechtigungen).
Unabhängig von der Schwere sollten Sie Nonces verwenden, um CSRF zu verhindern, *jedes Mal, wenn ein Benutzer eine Aktion* in WordPress ausführt. Denn... warum auch nicht? Sie möchten nicht, dass Hacker Anfragen auf Ihrer Website fälschen, und WordPress bietet Ihnen die Werkzeuge, um sie zu stoppen.
Verwenden Sie Nonces, stoppen Sie Fälschungen und vereiteln Sie Hacker!
Ich habe eine Frage – nehmen wir an, Sie möchten einen Nonce als eindeutigen Hash-Wert generieren. Wie würden Sie diesen zufällig generierten Hash im Backend verifizieren und wie würden Sie ihn überhaupt generieren? Das scheint eine sicherere Methode zum Schutz von Formularübermittlungen zu sein.
Das ist im Grunde, was WordPress mit
wp_create_nonceund ähnlichen Funktionen tut – es generiert einen Hash-Wert und prüft ihn im Backend. Spielen Sie auf etwas Bestimmtes an, wie WP es macht?Dies ist einer der klarsten Artikel darüber, was Nonces sind und was sie in WordPress tun. Ich denke oft, dass Front-End-Entwickler die Verantwortung für alle Sicherheitsprobleme auf die Back-End-Entwickler schieben. Das könnte nicht weiter von der Wahrheit entfernt sein.
Eine Frage, die ich habe: Im Laufe der Jahre ist Google CAPTCHA weniger und weniger wirksam bei der Bekämpfung von Spam. Denken Sie, dass Nonces in WP im Laufe der Jahre dem gleichen Risiko ausgesetzt sind? Wenn ja, wie können wir Ihrer Meinung nach weniger reaktiv und proaktiver bei der Verhinderung dieser Sicherheitsrisiken werden?
Zur Info: Ich verstehe, dass Spam etwas anderes ist als Hacker :)
WP-Nonces sind nicht perfekt, aber ich glaube nicht, dass sie das gleiche Problem wie CAPTCHA haben. CAPTCHA wird weniger effektiv, wenn Bilderkennungs-Skripte besser darin werden, Zahlen aus Bildern zu lesen. Nonces können nicht auf die gleiche Weise "knackt" werden, wenn sie richtig gemacht werden.
Ich wünschte, WordPress hätte eine automatische Nonce-Generierung. Zum Beispiel müssen Sie in Ruby on Rails nie eine Nonce-Generierung oder -Prüfung schreiben – das geschieht alles automatisch. Das wäre ein wichtiger Schritt zur Verhinderung von CSRF.
In Großbritannien bedeutet das Wort 'nonce' 'Kinderschänder' – daher muss die Internationalisierung dieses Konzepts vielleicht überdacht werden!
Danke für diesen Artikel, es ist gut erklärt, wie man sich vor
CSRF-Angriffen schützt.Ich bin jedoch schockiert, dass keine Erwähnung von
POST-Anfragen anstelle vonGET-Anfragen erfolgt. Die Verwendung vonPOST-Formularen erschwertCSRF-Angriffe, da Sie Anfragen nicht einfach durch Ändern von Parametern in der URL fälschen können (beiPOSTgibt es keine Parameter in der URL).Meiner Meinung nach ist die Verwendung von
POST-Anfragen grundlegend und sollte der erste Schritt sein. Die Verwendung vonCSRF-Tokens sollte ebenfalls erfolgen, ist aber nur der zweite Schritt.Außerdem sollten Sie sich besser auf die Variablen
$_POSToder$_GETverlassen, um genau den gewünschten Wert zu erhalten, da$_REQUESTkonfigurationsabhängig ist und sein Inhalt mit Cookie-Werten geändert werden kann (siehe http://php.net/manual/en/reserved.variables.request.php), daher würde ich es in diesem Fall nicht empfehlen.Im Allgemeinen sollten Formulare
POSTanstelle vonGETverwenden (mindestens) wenn– Datenbankinhalte geändert werden (das ist hier der Fall)
– der Sitzungsstatus des Benutzers geändert wird (wie z. B. An- und Abmeldeseiten).
Viele Grüße,
Romain
POST-Anfragen erschweren es zwar (man kann jemanden nicht einfach dazu bringen, auf einen Link zu klicken), aber sie sind nicht *viel* schwieriger. Wenn ein Hacker jemanden dazu bringen kann, seine Website zu besuchen, kann er per JavaScript ein POST senden.
Ich habe GET verwendet, um die Beispiele einfach zu halten, aber Sie haben Recht – POST sollte für die meisten Formularübermittlungen verwendet werden.
Ich hatte noch nie an CSRF gedacht. Ich verwende WP nicht, aber ich kann sehen, wie man eindeutige Identifikatoren für alle Benutzeraktionen implementieren könnte. Ich muss zugeben, Sicherheit ist meine größte Schwäche (Chris, du solltest eine Umfrage dazu machen), aber es gibt offensichtlich einige einfache Gewinne, die wir alle implementieren sollten – so wie wir (versuchen), eine kurze Checkliste für Performance-Gewinne zu haben.
Ist dieser benutzerspezifische Nonce einfach das Anhängen der Benutzer-ID an den Nonce? Wenn ja, kann ein Angreifer dann nicht einfach das letzte Bit des Nonce auf die entsprechende Benutzer-ID ändern?
Gute Frage! Die Benutzer-ID wird zum *Generieren* des Nonce verwendet, sodass der Wert für diesen Benutzer eindeutig ist. Die Benutzer-ID ist im Nonce-Wert nicht sichtbar, daher kann ein Hacker die ID nicht einfach anhängen.
Sie müssen keine benutzerspezifischen Nonces erstellen. WordPress erledigt das für Sie. WordPress-Nonces sind tatsächlich sitzungsspezifisch für einen Benutzer. Selbst wenn Sie sich woanders als derselbe Benutzer anmelden, ist er nutzlos.
Siehe den Codex https://codex.wordpress.org/WordPress_Nonces#Creating_a_nonce.
Sie haben Recht! Der Beitrag wurde aktualisiert, um meinen Fehler zu korrigieren. Danke, dass Sie sich die Zeit genommen haben, mich zu korrigieren!
Es gibt auch
check_admin_referer(), das Sie ähnlich wiecheck_ajax_referer()verwenden können.Um es noch sicherer zu machen, können Sie mit
current_user_can()und der entsprechenden Berechtigung auf Berechtigungen prüfen.