Bisher haben Sie die Grundlagen der Entwicklung von Plugins und Themes für WordPress kennengelernt. Jetzt wäre ein guter Zeitpunkt, auch die Sicherheitsauswirkungen des Schreibens von Code zu berücksichtigen.
In dieser Lektion lernen Sie, wie Sie mit Blick auf die Sicherheit entwickeln.
Sie lernen die Vorteile der Gewährleistung der Codesicherheit kennen, erfahren, welche Schritte zur Absicherung Ihres Codes erforderlich sind, und wo Sie weitere Informationen zum sicherheitsbewussten Entwickeln finden.
Haftungsausschluss
Diese Lektion wurde als Einführung in das sicherheitsbewusste Entwickeln von Plugins und Themes erstellt. Der in dieser Lektion verwendete Code ist stark vereinfacht. Sie sollten keinen der in diesem Tutorial verwendeten Codes in Ihren Plugins oder Themes einsetzen.
Bitte lesen Sie die vollständige Dokumentation zur Sicherheit im WordPress Developer-Handbuch unter https://developer.wordpress.org/apis/security/, um sicherzustellen, dass Sie die richtigen Methoden und Verfahren befolgen.
Was bedeutet sicherheitsbewusstes Entwickeln?
Sicherheitsbewusstes Entwickeln ist der Prozess, sicherzustellen, dass Ihr Code nicht nur funktioniert, sondern auch keine Sicherheitslücken einführt.
Wenn der Code Ihres Plugins oder Themes Sicherheitslücken enthält, könnte dies jede WordPress-Website, auf der Ihr Produkt installiert ist, potenziellen Angriffen aussetzen und dazu führen, dass diese Website kompromittiert wird.
Beim Schreiben von Code ist es wichtig, eine sicherheitsbewusste Haltung zu entwickeln und darüber nachzudenken, wie Ihr Code böswillig verwendet werden könnte.
- Vertrauen Sie keinen Daten, unabhängig davon, ob es sich um Benutzereingaben, Daten einer Drittanbieter-API oder sogar Daten in Ihrer Datenbank handelt. Prüfen Sie immer, ob sie gültig und sicher zu verwenden sind.
- WordPress verfügt über eine Reihe von APIs, die Sie bei allgemeinen Aufgaben unterstützen können, z. B. beim Bereinigen von Benutzereingaben, beim Validieren von Daten und beim Maskieren von Ausgaben. Verwenden Sie diese APIs zur Validierung und Bereinigung Ihrer Daten, anstatt eigene Funktionen zu schreiben.
- Bleiben Sie über häufige Sicherheitslücken auf dem Laufenden und halten Sie Ihren Code aktuell, um diese zu verhindern.
An welcher Stelle des Entwicklungsprozesses spielt Sicherheit eine Rolle?
Sicherheit sollte in jeder Phase des Entwicklungsprozesses berücksichtigt werden.
Im Allgemeinen tritt die größte Anzahl der gefundenen Sicherheitslücken auf der PHP-Ebene auf, die auf dem Server ausgeführt wird.
Daher konzentriert sich diese Lektion auf die wichtigsten und häufigsten vorbeugenden Maßnahmen in Ihrem PHP-Code.
Eingaben bereinigen
Einer der ersten Schritte bei der Entwicklung besteht darin, sicherzustellen, dass alle Benutzereingaben bereinigt werden. Das bedeutet, dass alle vom Benutzer übergebenen Daten, etwa eine Formularübermittlung oder ein URL-Parameter, darauf geprüft werden, ob sie sicher verwendet werden können.
In diesem Beispielcode werden die Felder für Name und E-Mail aus einem von einem Plugin generierten Formular übermittelt und anschließend in einer benutzerdefinierten Datenbanktabelle namens form_submissions gespeichert;
$name = $_POST['name'];
$email = $_POST['email'];
global $wpdb;
$table_name = $wpdb->prefix . 'form_submissions';
$sql = "INSERT INTO $table_name (name, email) VALUES ('$name', '$email')";
$result = $wpdb->query($sql);
Wie Sie sehen, werden die Daten ohne Bereinigung direkt in der Datenbank gespeichert.
Das bedeutet: Wenn ein Benutzer den Namen John'; DROP TABLE form_submissions;-- übermitteln würde, würde die SQL-INSERT-Abfrage ausgeführt, gefolgt von der DROP-Abfrage, und die Tabelle würde aus der Datenbank gelöscht!
WordPress verfügt über eine API zur Datenbereinigung, mit der eingehende Daten bereinigt werden können. Sie können die Funktionen sanitize_text_field und sanitize_email verwenden, um die Felder für Name und E-Mail zu bereinigen, bevor sie in der Abfrage verwendet werden.
$name = sanitize_text_field( $_POST['name'] );
$email = sanitize_email( $_POST['email'] );
global $wpdb;
$table_name = $wpdb->prefix . 'form_submissions';
$sql = "INSERT INTO $table_name (name, email) VALUES ('$name', '$email')";
$result = $wpdb->query($sql);
Beachten Sie, dass der Code einem wichtigen Grundsatz der Datenbereinigung folgt: Sie führen die Bereinigung so früh wie möglich durch.
Weitere Informationen zu den verfügbaren Funktionen zur Datenbereinigung für WordPress-Entwickler finden Sie auf der Seite Daten bereinigen in der WordPress-Entwicklerdokumentation.
Daten validieren (4:06)
Daten zu validieren bedeutet, sie anhand eines vordefinierten Musters (oder mehrerer Muster) zu prüfen, wobei das eindeutige Ergebnis entweder gültig oder ungültig lautet.
Nicht vertrauenswürdige Daten können aus vielen Quellen stammen: von Benutzern, Drittanbieter-APIs oder sogar aus Ihrer Datenbank. Besonders dann, wenn etwas anderes sie verändert hat, müssen auch Daten aus der Datenbank als nicht vertrauenswürdig betrachtet werden. Selbst Website-Administratoren können Fehler machen und falsche oder unsichere Daten eingeben. Daher ist es wichtig, Ihre Daten immer zu prüfen.
In diesem Beispiel erfordert eine Löschfunktion, dass eine numerische ID an einen admin-ajax-Callback übermittelt wird:
add_action( 'wp_ajax_delete_form_submission', 'wp_learn_delete_form_submission' );
function wp_learn_delete_form_submission() {
if ( ! isset( $_POST['id'] ) ) {
wp_send_json_error( 'Ungültige ID' );
}
$id = $_POST['id'];
global $wpdb;
$table_name = $wpdb->prefix . 'form_submissions';
$sql = "DELETE FROM $table_name WHERE id = $id";
$result = $wpdb->get_results( $sql );
return wp_send_json( array( 'result' => $result ) );
}
Hier wird die ID ohne Validierung direkt in der SQL-Abfrage verwendet. Das bedeutet erneut, dass bei Übermittlung der ID 1; DROP TABLE form_submissions; dieselbe SQL-DROP-Abfrage nach dem Löschvorgang ausgeführt und die Tabelle gelöscht würde.
Um dies zu verhindern, können Sie die Typumwandlungsfunktion von PHP verwenden, um sicherzustellen, dass der Wert von $id immer eine Ganzzahl ist. Dazu wird (int) vor dem Variablennamen eingefügt, etwa so:
$id = (int) $_POST['id'];
Beachten Sie, dass dies nur funktioniert, wenn das erste Zeichen der über das $_POST-Array übergebenen Zeichenfolge in eine Ganzzahl umgewandelt werden kann. Andernfalls ist der Wert von $id 0. In diesem Fall empfiehlt es sich, den Code so zu aktualisieren, dass er diesen Fall behandelt.
Auch dieser Code folgt einem wichtigen Grundsatz der Datenvalidierung: Sie führen die Validierung so früh wie möglich durch.
if ($id === 0){
// bei einem Fehler frühzeitig zurückkehren
return wp_send_json( array( 'result' => 'Ungültige ID übergeben' ) );
}
Weitere Informationen zu den verschiedenen Möglichkeiten der Datenvalidierung finden Sie im Abschnitt Daten validieren in der WordPress-Entwicklerdokumentation.
Ausgaben maskieren
Ein weiterer Sicherheitsaspekt besteht darin, sicherzustellen, dass alle Informationen, die Sie an den Browser ausgeben, sicher sind, einschließlich Text, HTML- oder JavaScript-Code sowie Daten aus der Datenbank.
Auch wenn Ihr Code nicht für die Quelle der angezeigten Daten verantwortlich ist, ist er dafür verantwortlich, diese sicher anzuzeigen.
In diesem Beispiel ruft der Code die Formulareinreichungen aus der Datenbank ab, durchläuft sie anschließend und gibt die Daten der Einreichungen in einem Administrationsbildschirm im WordPress-Dashboard aus:
$submissions = wp_learn_get_form_submissions();
?>
<div class="wrap" id="wp_learn_admin">
<h1>Administration</h1>
<table>
<thead>
<tr>
<th>Name</th>
<th>E-Mail</th>
</tr>
</thead>
<?php foreach ($submissions as $submission){ ?>
<tr>
<td><?php echo $submission->name; ?></td>
<td><?php echo $submission->email; ?></td>
<td><a class="delete-submission" data-id="<?php echo $submission->id?>" style="cursor:pointer;">Löschen</a></td>
</tr>
<?php } ?>
</table>
</div>
<?php
Hier gibt es drei Datenbestandteile, die maskiert werden müssen: die Felder $submission->name und $submission->email sowie $submission->id.
<td><a class="delete-submission" data-id="<?php echo (int) $submission->id?>" style="cursor:pointer;">Löschen</a></td>
Für die Felder für Name und E-Mail können wir die integrierte WordPress-Funktion esc_html() zum Maskieren verwenden. Die ID kann maskiert werden, indem sie in eine Ganzzahl umgewandelt wird, wie Sie es bei der Datenvalidierung tun würden.
<td><?php echo esc_html( $submission->name ); ?></td>
<td><?php echo esc_html( $submission->email ); ?></td>
<td><a class="delete-submission" data-id="<?php echo (int) $submission->id?>" style="cursor:pointer;">Löschen</a></td>
Beachten Sie, dass dieser Code einem wichtigen Grundsatz beim Maskieren von Daten folgt: Sie maskieren die Daten so spät wie möglich.
Weitere Informationen zu den verschiedenen Möglichkeiten zum Maskieren von Ausgaben finden Sie im Abschnitt Daten maskieren in der WordPress-Entwicklerdokumentation.
Ungültige Anfragen verhindern
Bei jeder Anfrage ist es wichtig zu prüfen, ob sie gültig ist. Das bedeutet, zu überprüfen, ob die Anfrage von einer vertrauenswürdigen Quelle stammt.
Beispielsweise könnten Sie einen Shortcode haben, der ein Formular rendert, in dem Benutzer ihre Informationen übermitteln können.
Die Funktion zum Rendern des Formulars könnte etwa so aussehen.
add_shortcode( 'wp_learn_form_shortcode', 'wp_learn_form_shortcode' );
function wp_learn_form_shortcode() {
ob_start();
?>
<form method="post">
<input type="hidden" name="wp_learn_form" value="submit">
<div>
<label for="email">Name</label>
<input type="text" id="name" name="name" placeholder="Name">
</div>
<div>
<label for="email">E-Mail-Adresse</label>
<input type="text" id="email" name="email" placeholder="Email address">
</div>
<div>
<input type="submit" id="submit" name="submit" value="Submit">
</div>
</form>
<?php
$form = ob_get_clean();
return $form;
}
?>
Wenn der Benutzer das Formular übermittelt, werden die Daten an den Server gesendet. Die Daten werden anschließend verarbeitet, bereinigt und in der Datenbank gespeichert.
add_action( 'wp', 'wp_learn_maybe_process_form' );
function wp_learn_maybe_process_form() {
if (!isset($_POST['wp_learn_form'])){
return;
}
$name = sanitize_text_field( $_POST['name'] );
$email = sanitize_email( $_POST['email'] );
global $wpdb;
$table_name = $wpdb->prefix . 'form_submissions';
$sql = "INSERT INTO $table_name (name, email) VALUES ('$name', '$email')";
$result = $wpdb->query($sql);
if ( 0 < $result ) {
wp_redirect( WPLEARN_SUCCESS_PAGE_SLUG );
die();
}
wp_redirect( WPLEARN_ERROR_PAGE_SLUG );
die();
}
Da das Formular auf jeder Seite erscheinen kann, auf der der Shortcode verwendet wird, könnte ein böswilliger Benutzer versuchen, eine POST-Anfrage an das Formular zu senden, entweder um nach einer Sicherheitslücke im Plugin zu suchen oder um mehrere Anfragen an das Formular zu senden.
Um dies zu verhindern, können Sie prüfen, ob die Anfrage von einer vertrauenswürdigen Quelle stammt. Dazu können Sie einen sogenannten Nonce implementieren, also eine nur einmal verwendete Zahl.
Zunächst können Sie im Formular selbst ein Nonce-Feld hinzufügen, indem Sie die Funktion wp_nonce_field verwenden und der Funktion eine Nonce-Aktion sowie einen Nonce-Namen übergeben:
wp_nonce_field( 'wp_learn_form_nonce_action', 'wp_learn_form_nonce_field' );
Wenn das Formular im Frontend gerendert wird, wird dem Formular ein verstecktes Feld hinzugefügt. Dabei werden der Nonce-Name als ID- und Namensattribut des versteckten Felds und der generierte Nonce als Feldwert verwendet. Diese Daten werden beim Übermitteln des Formulars gesendet.
Anschließend können Sie in der Funktion, die die Formulardaten verarbeitet, mit der Funktion wp_verify_nonce überprüfen, ob der Nonce gültig ist. Dazu übergeben Sie dieser Funktion das per POST übermittelte Nonce-Feld und die Nonce-Aktion. Wenn das Ergebnis dieser Überprüfung false ist, können Sie frühzeitig abbrechen und so die weitere Ausführung des Codes verhindern.
if ( ! wp_verify_nonce( $_POST['wp_learn_form_nonce_field'], 'wp_learn_form_nonce_action' ) {
wp_redirect( WPLEARN_ERROR_PAGE_SLUG );
die();
}
Immer wenn Ihr Code eine Webanfrage stellt, sei es durch eine Weiterleitung zu einer neuen URL, das Senden von Daten an ein Formular per POST oder eine AJAX-Anfrage, sollten Sie prüfen, ob die Anfrage gültig ist.
Weitere Informationen zur Verwendung von Nonces in Ihren Plugins finden Sie im Abschnitt Nonces in der WordPress-Entwicklerdokumentation.
Nicht authentifizierte Benutzer verhindern
Abhängig von der Funktionalität Ihres Codes empfiehlt es sich, bestimmte Funktionen auf Benutzer mit einer bestimmten Berechtigungsstufe zu beschränken. Beispielsweise könnten Sie eine Funktion haben, die Daten aus der Datenbank löscht.
function wp_learn_delete_form_submission() {
if ( ! isset( $_POST['id'] ) ) {
wp_send_json_error( 'Ungültige ID' );
}
$id = (int) $_POST['id'];
global $wpdb;
$table_name = $wpdb->prefix . 'form_submissions';
$sql = "DELETE FROM $table_name WHERE id = $id";
$result = $wpdb->get_results( $sql );
return wp_send_json( array( 'result' => $result ) );
}
Auch wenn Sie Ihr Bestes tun, um zu verhindern, dass Personen ohne entsprechende Berechtigung diese Funktion ausführen, ist es dennoch sinnvoll, Prüfungen für diesen Fall einzubauen.
WordPress verfügt über ein robustes System für Benutzerrollen und -berechtigungen. Damit können Sie entweder die standardmäßigen Benutzerrollen und Berechtigungen verwenden oder eigene erstellen.
In diesem Fall könnte es genügen, nur Benutzern die Funktion zu erlauben, die über die Berechtigung zum Verwalten von Website-Optionen verfügen. Dies ist eine Standardberechtigung, die der Administratorrolle zugeordnet ist.
function wp_learn_delete_form_submission() {
if ( ! current_user_can( 'manage_options' ) ) {
return wp_send_json( array( 'result' => 'Authentifizierungsfehler' ) );
}
// restlicher Funktionscode
}
Die WordPress-Entwicklerdokumentation enthält einen ausführlichen Abschnitt zu Benutzerrollen und -berechtigungen. Dieser umfasst eine Liste der standardmäßigen Berechtigungen sowie Informationen dazu, wie Sie eigene Berechtigungen erstellen können.
Weiterführende Informationen
Um sich auf ein sicherheitsbewusstes Entwickeln vorzubereiten, lesen Sie unbedingt den Eintrag Sicherheit in der WordPress-Entwicklerdokumentation. Er enthält alle Beispiele aus dieser Lektion sowie zusätzliche Informationen zu bewährten Sicherheitsverfahren, häufigen Sicherheitslücken und weiteren Beispielcodes.