WordPress 4.7.1 REST API espone ancora gli utenti
Ho aggiornato WordPress alla versione 4.7.1
, e dopo ho provato a enumerare gli utenti attraverso la REST API, che dovrebbe essere stata corretta, ma sono ancora in grado di recuperare gli utenti.
https://mywebsite.com/wp-json/wp/v2/users
Output:
[{"id":1,"name":"admin","url":"","description":"","link":"https:\/\/mywebsite\/author\/admin\/","slug":"admin","avatar_urls":{"24": ...
Changelog dall'ultima versione:
La REST API esponeva i dati utente per tutti gli utenti che avevano creato un post di un tipo di post pubblico. WordPress 4.7.1 limita questo solo ai tipi di post che hanno specificato di dover essere mostrati all'interno della REST API. Segnalato da Krogsgard e Chris Jean.
Dopo aver installato il plugin Disable REST API
, sembra che tutto funzioni correttamente, ma non mi piace usare un plugin per ogni piccola cosa.
L'output dopo l'utilizzo del plugin è:
{"code":"rest_cannot_access","message":"Solo gli utenti autenticati possono accedere alla REST API.","data":{"status":401}}
Come posso risolvere questo problema senza utilizzare il plugin, o perché anche dopo l'aggiornamento questo problema persiste?
MODIFICA 30.9.2017
Mi sono reso conto che c'è un conflitto tra il plugin Contact Form 7
e Disable REST API
che ti darà un errore 401 non autorizzato
.
Quando provi a inviare un messaggio attraverso il modulo di Contact Form 7
, farà una richiesta
wp-json/contact-form-7/v1/contact-forms/258/feedback
e disabilitare questo non è una buona idea.
Questo snippet di codice nasconderà i risultati degli endpoint per utenti, articoli e commenti restituendo un errore 404, mentre tutte le altre chiamate API continueranno a funzionare normalmente.
::AGGIORNAMENTO::
add_filter('rest_endpoints', function(){
$toRemove = ['users', 'posts', 'comments'];
foreach($toRemove as $val)
{
if (isset($endpoints['/wp/v2/'.$val])) {
unset($endpoints['/wp/v2/'.$val]);
}
if(isset($endpoints['/wp/v2/'.$val.'/(?P<id>[\d]+)'])) {
unset($endpoints['/wp/v2/'.$val.'/(?P<id>[\d]+)']);
}
}
return $endpoints;
});
::AGGIORNAMENTO::
Questo snippet rimuoverà tutti gli endpoint predefiniti.
<?php remove_action('rest_api_init', 'create_initial_rest_routes', 99); ?>

come indicato nel link di riferimento puoi anche filtrare gli endpoint...

Dove va inserito questo codice personalizzato? Non menzioni dove questo deve essere salvato.

Questa soluzione disabilita tutte le operazioni CRUD sugli utenti, vedi questa implementazione solo per le richieste GET: https://github.com/szepeviktor/wordpress-fail2ban/commit/98eb4f292a5a6bdae637f9410eb7bb9d6dffd81c

Come disabilitare mydomain.com/wp-json/ dall'output di tutti questi dati json?

Questa soluzione non è compatibile con Gutenberg che restituisce:
Uncaught (in promise) Response { type: "basic", url: "/wp-json/wp/v2/users/?who=authors&per_page=100&_locale=user", redirected: false, status: 404, ok: false, statusText: "Not Found", headers: Headers, body: ReadableStream, bodyUsed: false }
Sarebbe meglio forzare l'autenticazione su questi endpoint.

/**
* Avvolge un callback esistente passato come parametro e crea
* un nuovo callback di permesso introducendo controlli preliminari
* e utilizzando il callback di default in caso di successo.
*/
function permission_callback_hardener ($existing_callback) {
return function ($request) use($existing_callback) {
if (! current_user_can('list_users')) {
return new WP_Error(
'rest_user_cannot_view',
__( 'Spiacente, non sei autorizzato ad accedere agli utenti.' ),
[ 'status' => rest_authorization_required_code() ]
);
}
return $existing_callback($request);
};
}
function api_users_endpoint_force_auth($endpoints)
{
$users_get_route = &$endpoints['/wp/v2/users'][0];
$users_get_route['permission_callback'] = permission_callback_hardener($users_get_route['permission_callback']);
$user_get_route = &$endpoints['/wp/v2/users/(?P<id>[\d]+)'][0];
$user_get_route['permission_callback'] = permission_callback_hardener($user_get_route['permission_callback']);
return $endpoints;
}
add_filter('rest_endpoints', 'api_users_endpoint_force_auth');
- L'endpoint non è bloccato per gli amministratori (Gutenberg continua a funzionare)
- L'endpoint rifiuta gli utenti anonimi in modo corretto.
- È abbastanza generico da supportare ulteriori endpoint.
- Il
current_user_can
potrebbe essere ulteriormente migliorato, reso più generico. - Si assume che il metodo
GET
sia il primo per una route registrata (finora è sempre stato vero)

Rimuovi il link API dall'head HTML se lo desideri.
// https://wordpress.stackexchange.com/a/211469/77054
// https://wordpress.stackexchange.com/a/212472
remove_action( 'wp_head', 'rest_output_link_wp_head', 10 );
Quindi richiedi l'autenticazione per tutte le richieste.
// Puoi richiedere l'autenticazione per tutte le richieste REST API aggiungendo un controllo is_user_logged_in al filtro rest_authentication_errors.
add_filter( 'rest_authentication_errors', function( $result ) {
if ( ! empty( $result ) ) {
return $result;
}
if ( ! is_user_logged_in() ) {
return new WP_Error( 'rest_not_logged_in', 'Solo gli utenti autenticati possono accedere alla REST API.', array( 'status' => 401 ) );
}
return $result;
});
Questo ti lascerà con il messaggio desiderato.
Ora per fermare l'enumerazione potresti usare qualcosa come questo.
// https://perishablepress.com/stop-user-enumeration-wordpress/
// blocca le scansioni di enumerazione WP
// https://m0n.co/enum
if (!is_admin()) {
// formato URL predefinito
if (preg_match('/author=([0-9]*)/i', $_SERVER['QUERY_STRING'])) die();
add_filter('redirect_canonical', 'shapeSpace_check_enum', 10, 2);
}
function shapeSpace_check_enum($redirect, $request) {
// formato URL permalink
if (preg_match('/\?author=([0-9]*)(\/*)/i', $request)) die();
else return $redirect;
}
Consulta l'intero post per ulteriori tecniche.

Codice .htaccess per bloccare tutti gli scan degli autori
# INIZIO blocco scan autori
RewriteEngine On
RewriteBase /
RewriteCond %{QUERY_STRING} (author=\d+) [NC]
RewriteRule .* - [F]
# FINE blocco scan autori
Puoi rimuoverlo tramite funzione come suggerito nella risposta accettata

Ho utilizzato questo piccolo codice in function.php :
/**
* Accesso all'API REST solo per amministratori
*
* @return void
*/
function api_rest_only_for_admin_users() {
$current_user = wp_get_current_user();
if ( in_array('administrator', $current_user->roles ) ) {
return;
} else {
wp_die('Spiacente, non sei autorizzato ad accedere a questi dati','API REST Vietata',403);
}
}
add_filter( 'rest_api_init', 'api_rest_only_for_admin_users', 99 );

Per completare la risposta di BlueSuiter del 2017, ecco una soluzione per filtrare gli utenti in base al ruolo che rende la sua soluzione compatibile con l'editor Gutenberg.
add_filter( 'rest_endpoints', function( $endpoints ) {
if(is_user_logged_in()) {
$user = wp_get_current_user();
$roles = array('editor', 'administrator', 'author');
if( array_intersect($roles, $user->roles ) ) return $endpoints;
}
if ( isset( $endpoints['/wp/v2/users'] ) ) unset( $endpoints['/wp/v2/users'] );
if ( isset( $endpoints['/wp/v2/users/(?P<id>[\d]+)'] ) ) unset( $endpoints['/wp/v2/users/(?P<id>[\d]+)'] );
return $endpoints;
});
L'editor Gutenberg ha bisogno di interrogare l'API REST per ottenere l'autore. È quindi necessario consentire il passaggio almeno a tutti i ruoli utente del tuo sito che hanno accesso a un editor Gutenberg, senza dimenticare i post personalizzati.
Sull'endpoint pubblico dell'API REST, viene restituito un errore 404 perché is_user_logged_in restituirà sempre false.

Se stai utilizzando un firewall per proteggere il tuo sito WordPress, l'opzione migliore è bloccare l'endpoint API che espone i dettagli degli utenti direttamente dal firewall.
Identifica l'URL https://example.com/wp-json/wp/v2/users
e bloccalo semplicemente dal firewall.
Modifica: blocca solo l'endpoint specifico dei dettagli utente e non l'intera API REST.

// Blocco API JSON per utenti WP
function disable_custom_rest_endpoints( $endpoints ) {
$routes = array( '/wp/v2/users', '/wp/v2/users/(?P<id>[\d]+)' );
foreach ( $routes as $route ) {
if ( empty( $endpoints[ $route ] ) ) {
continue;
}
foreach ( $endpoints[ $route ] as $i => $handlers ) {
if ( is_array( $handlers ) && isset( $handlers['methods'] ) &&
'GET' === $handlers['methods'] ) {
unset( $endpoints[ $route ][ $i ] );
}
}
}
return $endpoints;
}
add_filter( 'rest_endpoints', 'disable_custom_rest_endpoints' );

Ho creato un plugin completo che gestisce questo problema e lo ho pubblicato su GITHUB.
https://github.com/MRKWP/mrkwp-rest-permissions/
Ciò che ho fatto è stato assicurarmi di utilizzare INIT per l'avvio dell'azione, questo garantisce che le funzioni pluggabili funzionino come previsto.
Utilizzo inoltre la capacità 'Edit_posts' di WordPress per l'API. Questo assicura che l'amministratore e l'editor a blocchi funzionino correttamente.
Dai un'occhiata a quel plugin per risolvere eventuali problemi CVE-2017-5487 sollevati dagli auditor di sicurezza.
Utilizza anche PHP CS quindi funzionerà con un'installazione VIP o qualsiasi cosa che necessiti di standard implementati.
Spero che questo possa aiutare qualcuno. Mi ci è voluto un bel po' di tempo per farlo funzionare bene sui miei siti e server.

Leggendo velocemente il codice, il nome "hardener" è un po' fuorviante poiché non rende più sicuri gli endpoint, ma fa sì che rifiutino sempre le richieste - lo installi semplicemente in modo condizionale. Inoltre stai chiamando funzioni di attivazione e disattivazione che non esistono. Ma sì, sembra che questo possa fare il lavoro.

Per poter risolvere questo problema, è necessario prima individuare la fonte dell'issue.
- Utilizzi plugin SEO come: All in one SEO pack o Yoast? Prova a disabilitarli e verifica nuovamente.
- Utilizzi il plugin Jetpack? Prova a disabilitarlo e verifica nuovamente.
Fammi sapere se questi suggerimenti ti hanno indirizzato nella giusta direzione.
Una soluzione rapida (ma non elegante) è bloccare l'URL sottostante nel tuo file .htaccess. https://mywebsite.com/wp-json/wp/v2/users
