Für diejenigen, die von Google kommen: Sie sollten die Nonces wahrscheinlich nicht von der REST-API erhalten , es sei denn, Sie wissen wirklich , was Sie tun. Die Cookie-basierte Authentifizierung mit der REST-API ist nur für Plugins und Themes gedacht . Für eine Anwendung mit nur einer Seite sollten Sie wahrscheinlich OAuth verwenden .
Diese Frage besteht, weil in der Dokumentation nicht klar ist, wie Sie sich beim Erstellen von Apps für einzelne Seiten tatsächlich authentifizieren sollten, JWTs nicht wirklich für Web-Apps geeignet sind und OAuth schwieriger zu implementieren ist als die Cookie-basierte Authentifizierung.
Das Handbuch enthält ein Beispiel dafür, wie der Backbone-JavaScript-Client mit Nonces umgeht. Wenn ich dem Beispiel folge, erhalte ich eine Nonce, die von den integrierten Endpunkten wie / wp / v2 / posts akzeptiert wird.
\wp_localize_script("client-js", "theme", [
'nonce' => wp_create_nonce('wp_rest'),
'user' => get_current_user_id(),
]);
Die Verwendung von Backbone kommt jedoch nicht in Frage, ebenso wie Themen. Deshalb habe ich das folgende Plugin geschrieben:
<?php
/*
Plugin Name: Nonce Endpoint
*/
add_action('rest_api_init', function () {
$user = get_current_user_id();
register_rest_route('nonce/v1', 'get', [
'methods' => 'GET',
'callback' => function () use ($user) {
return [
'nonce' => wp_create_nonce('wp_rest'),
'user' => $user,
];
},
]);
register_rest_route('nonce/v1', 'verify', [
'methods' => 'GET',
'callback' => function () use ($user) {
$nonce = !empty($_GET['nonce']) ? $_GET['nonce'] : false;
return [
'valid' => (bool) wp_verify_nonce($nonce, 'wp_rest'),
'user' => $user,
];
},
]);
});
Ich habe ein bisschen an der JavaScript-Konsole herumgebastelt und folgendes geschrieben:
var main = async () => { // var because it can be redefined
const nonceReq = await fetch('/wp-json/nonce/v1/get', { credentials: 'include' })
const nonceResp = await nonceReq.json()
const nonceValidReq = await fetch(`/wp-json/nonce/v1/verify?nonce=${nonceResp.nonce}`, { credentials: 'include' })
const nonceValidResp = await nonceValidReq.json()
const addPost = (nonce) => fetch('/wp-json/wp/v2/posts', {
method: 'POST',
credentials: 'include',
body: JSON.stringify({
title: `Test ${Date.now()}`,
content: 'Test',
}),
headers: {
'X-WP-Nonce': nonce,
'content-type': 'application/json'
},
}).then(r => r.json()).then(console.log)
console.log(nonceResp.nonce, nonceResp.user, nonceValidResp)
console.log(theme.nonce, theme.user)
addPost(nonceResp.nonce)
addPost(theme.nonce)
}
main()
Das erwartete Ergebnis sind zwei neue Beiträge, aber ich bekomme Cookie nonce is invalid
vom ersten und der zweite erstellt den Beitrag erfolgreich. Das liegt wahrscheinlich daran, dass die Nonces unterschiedlich sind, aber warum? Ich bin in beiden Anfragen als derselbe Benutzer angemeldet.
Wenn mein Ansatz falsch ist, wie soll ich die Nonce bekommen?
Bearbeiten :
Ich habe versucht, ohne viel Glück mit Globals zu spielen . Ich habe ein bisschen mehr Glück mit der Aktion wp_loaded:
<?php
/*
Plugin Name: Nonce Endpoint
*/
$nonce = 'invalid';
add_action('wp_loaded', function () {
global $nonce;
$nonce = wp_create_nonce('wp_rest');
});
add_action('rest_api_init', function () {
$user = get_current_user_id();
register_rest_route('nonce/v1', 'get', [
'methods' => 'GET',
'callback' => function () use ($user) {
return [
'nonce' => $GLOBALS['nonce'],
'user' => $user,
];
},
]);
register_rest_route('nonce/v1', 'verify', [
'methods' => 'GET',
'callback' => function () use ($user) {
$nonce = !empty($_GET['nonce']) ? $_GET['nonce'] : false;
error_log("verify $nonce $user");
return [
'valid' => (bool) wp_verify_nonce($nonce, 'wp_rest'),
'user' => $user,
];
},
]);
});
Wenn ich jetzt das obige JavaScript ausführe, werden zwei Beiträge erstellt, aber der Endpunkt zur Überprüfung schlägt fehl!
Ich ging zum Debuggen von wp_verify_nonce:
function wp_verify_nonce( $nonce, $action = -1 ) {
$nonce = (string) $nonce;
$user = wp_get_current_user();
$uid = (int) $user->ID; // This is 0, even though the verify endpoint says I'm logged in as user 2!
Ich habe etwas Protokollierung hinzugefügt
// Nonce generated 0-12 hours ago
$expected = substr( wp_hash( $i . '|' . $action . '|' . $uid . '|' . $token, 'nonce'), -12, 10 );
error_log("expected 1 $expected received $nonce uid $uid action $action");
if ( hash_equals( $expected, $nonce ) ) {
return 1;
}
// Nonce generated 12-24 hours ago
$expected = substr( wp_hash( ( $i - 1 ) . '|' . $action . '|' . $uid . '|' . $token, 'nonce' ), -12, 10 );
error_log("expected 2 $expected received $nonce uid $uid action $action");
if ( hash_equals( $expected, $nonce ) ) {
return 2;
}
und der JavaScript-Code führt nun zu den folgenden Einträgen. Wie Sie sehen können, ist uid beim Aufruf des Verifizierungsendpunkts 0.
[01-Mar-2018 11:41:57 UTC] verify 716087f772 2
[01-Mar-2018 11:41:57 UTC] expected 1 b35fa18521 received 716087f772 uid 0 action wp_rest
[01-Mar-2018 11:41:57 UTC] expected 2 dd35d95cbd received 716087f772 uid 0 action wp_rest
[01-Mar-2018 11:41:58 UTC] expected 1 716087f772 received 716087f772 uid 2 action wp_rest
[01-Mar-2018 11:41:58 UTC] expected 1 716087f772 received 716087f772 uid 2 action wp_rest
quelle
$_GET['nonce']
, sondern den Nonce-Header oder$_GET['_wpnonce']
-Parameter. Richtig?Diese Lösung funktioniert zwar, wird jedoch nicht empfohlen . OAuth ist die bevorzugte Wahl.
Ich denke ich habe es.
Ich denke, dass wp_verify_nonce kaputt ist, da wp_get_current_user nicht das richtige Benutzerobjekt erhält.Es ist nicht so, wie Otto es illustriert.
Zum Glück hat es einen Filter:
$uid = apply_filters( 'nonce_user_logged_out', $uid, $action );
Mit diesem Filter konnte ich Folgendes schreiben, und der JavaScript-Code wird so ausgeführt, wie er sollte:
Wenn Sie ein Sicherheitsproblem mit dem Fix feststellen, rufen Sie mich bitte an. Im Moment kann ich nichts anderes als globale Probleme feststellen.
quelle
Wenn Sie sich diesen ganzen Code ansehen, scheint Ihr Problem die Verwendung von Verschlüssen zu sein. In der
init
Phase sollten Sie nur Hooks setzen und keine Daten auswerten, da nicht der gesamte Kern vollständig geladen und initialisiert wurde.Im
Das
$user
wird voraussichtlich frühzeitig für die Schließung verwendet, aber niemand verspricht Ihnen, dass das Cookie bereits verarbeitet wurde und ein Benutzer anhand dieser Informationen authentifiziert wurde. Ein besserer Code wird seinVerwenden Sie wie immer bei allen Hooks in WordPress den neuesten Hook und versuchen Sie niemals, alles vorab zu berechnen, was Sie nicht müssen.
quelle