Sichere WordPress-REST-API: OAuth 2.0 und benutzerdefinierte, autorisierte Endpunkte

Inhalt
Eine sichere WordPress-REST-Schnittstelle braucht drei getrennte Entscheidungen: Wie wird die Gegenstelle erkannt, welche Aktion ist erlaubt und welche Daten werden angenommen? OAuth 2.0 kann dabei helfen. Ein eigenes OAuth-System ist jedoch keine Voraussetzung für jede Integration.
Anwendungspasswort oder OAuth?
Anwendungspasswörter sind seit WordPress 5.6 Bestandteil von Core. Über HTTPS authentifizieren diese Zugangsdaten ein WordPress-Konto. Die Rechte stammen vom zugehörigen Konto; Administratorrechte entstehen nur bei entsprechend berechtigten Konten. Ein eigenes Integrationskonto mit gezielt vergebenen Fähigkeiten kann deshalb eine geeignete Lösung sein. Standardmäßig besitzt ein einzelnes Anwendungspasswort keine eigenen engen OAuth-Scopes. Widerruf, sichere Aufbewahrung und Rechteprüfung bleiben erforderlich.
OAuth 2.0 ergänzt delegierte Autorisierung mit einem Autorisierungsserver und einer passenden WordPress-Anbindung. Core stellt keinen vollständigen OAuth-2.0-Server bereit. Scopes müssen definiert und am Endpunkt tatsächlich durchgesetzt werden. Kurze Laufzeiten, Refresh-Tokens und Rotation sind Eigenschaften der gewählten Implementierung, keine automatische Folge eines Bearer-Headers. Bei einer Benutzerfreigabe ist ein Authorization-Code-Verfahren mit PKCE nach aktueller Sicherheitspraxis passend; reine Dienstzugriffe benötigen eine dafür unterstützte Lösung. OAuth ist weder gleichbedeutend mit JWT noch mit Verschlüsselung.

Authentifizierung ersetzt keine Berechtigungsprüfung
Ein gültiges Token beantwortet noch nicht, ob eine bestimmte Ressource bearbeitet werden darf. Abhängig vom System gehören Aussteller, Zielgruppe, Gültigkeit, Widerrufsmodell, Scope und zugeordnetes Konto zur Prüfung. Bei JWT kommen unter anderem erlaubte Algorithmen und verlässliche Schlüssel hinzu; bloßes Dekodieren prüft keine Signatur. Diese Aufgaben gehören in eine gepflegte Authentifizierungsanbindung statt in eine undefinierte Hilfsfunktion.
Der permission_callback einer Route prüft die erlaubte Aktion, beispielsweise über current_user_can(). Bei Zugriffen auf konkrete Beiträge oder Kundenobjekte ist zusätzlich die Berechtigung für das jeweilige Objekt nötig. Ein ausdrücklich zurückgegebenes true erlaubt den Zugriff; WP_Error verweigert mit einer verständlichen Antwort. Core behandelt auch false und null als Ablehnung. Andere Werte wie 0 oder eine leere Zeichenfolge dürfen nicht als Fehlerantwort verwendet werden: Core wertet diese Rückgaben nicht wie false.
Ein vollständiges, bewusst begrenztes Prüfbeispiel
Das folgende Beispiel registriert ausschließlich eine JSON-Prüfung. Es speichert keine Telemetrie und implementiert weder OAuth noch eine Ratenbegrenzung. Vorausgesetzt sind WordPress, HTTPS und eine vorhandene unterstützte Authentifizierung. Die eigene Fähigkeit lw_validate_telemetry muss bei der Einrichtung gezielt einem passenden Konto oder einer Rolle zugewiesen werden; das Beispiel vergibt selbst keine Rechte. Für OAuth müssen die Anbindung und die Scope-Prüfung zusätzlich zum gezeigten Kontorecht bestehen.
<?php
// Example validation endpoint: no telemetry is stored.
add_action('rest_api_init', function () {
register_rest_route('lw-example/v1', '/validate-telemetry', [
'methods' => 'POST',
'permission_callback' => function () {
if (!current_user_can('lw_validate_telemetry')) {
return new WP_Error('rest_forbidden', 'Not permitted', [
'status' => rest_authorization_required_code(),
]);
}
return true;
},
'callback' => function (WP_REST_Request $request) {
if (!$request->is_json_content_type()) {
return new WP_Error('invalid_media_type', 'JSON required', ['status' => 415]);
}
if (strlen($request->get_body()) > 8192) {
return new WP_Error('body_too_large', 'Body too large', ['status' => 413]);
}
$body = $request->get_json_params();
$schema = [
'type' => 'object',
'required' => ['payload_hash'],
'additionalProperties' => false,
'properties' => [
'payload_hash' => [
'type' => 'string',
'minLength' => 64,
'maxLength' => 64,
'pattern' => '^[a-fA-F0-9]{64}$',
],
],
];
$valid = rest_validate_value_from_schema($body, $schema, 'body');
if (is_wp_error($valid)) {
return new WP_Error('invalid_body', 'Invalid JSON body', ['status' => 400]);
}
return new WP_REST_Response(
['validated' => true, 'stored' => false],
200,
['Cache-Control' => 'no-store']
);
},
]);
});
Ein zulässiger JSON-Rumpf enthält genau payload_hash mit 64 Hexadezimalzeichen. Zusätzliche Felder, falsche Typen und fehlende Werte werden verworfen. Ein solcher Hash beweist weder die Herkunft noch die Echtheit einer Nutzlast. Das Beispiel validiert lediglich das Format und antwortet ausdrücklich mit stored: false. Produktive Speicherung benötigt außerdem ein fachliches Schema, Objektberechtigungen und bei wiederholbaren Schreibaufrufen eine belastbare Idempotenzregel.
Der Code liest ausschließlich den JSON-Rumpf statt gemischter URL-, Formular- und JSON-Parameter. Das 8-KiB-Limit greift hier erst nach dem Start von PHP. Eine frühere Begrenzung im Webserver oder vorgeschalteten Dienst bleibt nötig, wenn große Anfragen bereits vor der Anwendung abgewiesen werden sollen. Die 20 isolierten Tests verwendeten kopierte WordPress-Schema- und Request-Klassen mit Ersatzfunktionen für Anmeldung und Hooks; eine echte HTTP- oder OAuth-Integration wurde nicht ausgeführt.
Prüfreihenfolge und Belastungsgrenzen
WordPress und PHP laufen bereits, bevor das REST-Argumentschema geprüft wird. Core prüft registrierte Argumente im normalen Dispatch vor dem permission_callback und dem eigentlichen Callback. Zusätzliche fachliche Prüfungen können im Callback vor der Verarbeitung erfolgen, wie im Beispiel. Das Schema verhindert daher keinen PHP-Start. Ein eigenes validate_callback muss auch Typfehler korrekt behandeln; eine Regex allein ist keine vollständige Nutzlastprüfung.
Eine Begrenzung vor PHP kann frühe Verarbeitungskosten reduzieren. Eine anwendungsnahe Begrenzung kann nach authentifiziertem Client und teurer Operation unterscheiden. Proxys sind nicht grundsätzlich auf IP-Adressen beschränkt; gemeinsam genutzte Adressen und ungeprüfte Weiterleitungsheader brauchen besondere Behandlung. Atomare Zähler, klare Zeitfenster, ein definiertes Ausfallverhalten und passende 429-Antworten gehören zur Umsetzung. Keine dieser Maßnahmen garantiert allein Schutz vor jeder Überlastung.
Fragen und Antworten
Genügt eine signierte Anfrage mit Zeitstempel gegen Wiederholung?
Ein Zeitfenster begrenzt alte Anfragen, verhindert aber keine Wiederholung innerhalb des erlaubten Fensters. Je nach Protokoll werden einmalige Kennungen mit atomarer Speicherung und Ablauf, gebundene Anfragebestandteile sowie eine fachliche Idempotenzregel benötigt. Geheimnisrotation und TLS bleiben wichtig. Eine Eigenkonstruktion ist nicht automatisch einfacher als ein gut unterstütztes Standardverfahren.
Muss /wp/v2/users generell abgeschaltet werden?
Öffentliche Autoreninformationen und geschützte Kontofelder sind zu unterscheiden. Pauschales Entfernen kann Editor, Plugins oder Integrationen stören. Ein gezieltes Zugriffskonzept muss auch Autorenarchive und weitere öffentliche Quellen berücksichtigen. Unterschiedliche Login- und Anzeigenamen ersetzen weder starke Anmeldung noch Berechtigungsprüfungen.
Welche Fehlerfälle gehören zur Abnahme?
Fehlende Zugangsdaten, fehlendes Recht, abgelaufene oder falsche Tokens, ungültige Typen, übergroße Rümpfe, zusätzliche Felder, wiederholte Schreibaufrufe und ein ausgefallener Token- oder Limiterspeicher. Einwilligungs- oder Zugriffstokens gehören nicht in öffentliche Logs. Nach erfolgreicher Anmeldung muss auch ein Zugriff auf ein fremdes Objekt weiterhin scheitern.
Quellen
- WordPress: REST authentication
- WordPress: Application Passwords
- WordPress: Custom endpoints
- WordPress: REST dispatch
- WordPress: Permission callback handling
- WordPress: Schema validation
- IETF RFC 9700: OAuth 2.0 security
- IETF RFC 9421: HTTP message signatures
- IETF RFC 8725: JWT security
- OWASP: REST security
- WordPress: REST API handbook
- OWASP: Top 10