WordPress 4.7.1 REST API sigue exponiendo usuarios
He actualizado mi WordPress a 4.7.1
, y después de eso intenté enumerar usuarios a través de la REST API, que debería estar corregido, pero pude recuperar los usuarios.
https://mywebsite.com/wp-json/wp/v2/users
Salida:
[{"id":1,"name":"admin","url":"","description":"","link":"https:\/\/mywebsite\/author\/admin\/","slug":"admin","avatar_urls":{"24": ...
Registro de cambios de la última versión:
La REST API exponía datos de usuario para todos los usuarios que habían escrito una entrada de un tipo de publicación pública. WordPress 4.7.1 limita esto solo a los tipos de publicación que han especificado que deberían mostrarse dentro de la REST API. Reportado por Krogsgard y Chris Jean.
Después de instalar el plugin Disable REST API
, parece que todo funciona bien, pero no me gusta usar un plugin para cada pequeña cosa.
La salida después de usar el plugin es:
{"code":"rest_cannot_access","message":"Solo los usuarios autenticados pueden acceder a la REST API.","data":{"status":401}}
¿Cómo puedo arreglar este problema sin usar plugin, o por qué incluso después de actualizar esto sigue existiendo?
EDICIÓN 30.9.2017
Me di cuenta de que hay un conflicto entre el plugin Contact Form 7
y Disable REST API
que te dará un error 401 no autorizado
.
Cuando intentas enviar un mensaje a través del formulario de Contact Form 7
, realizará una petición
wp-json/contact-form-7/v1/contact-forms/258/feedback
y deshabilitar eso no es una buena idea.
Este fragmento de código ocultará los resultados de los endpoints de usuarios, publicaciones y comentarios, devolviendo un error 404 como resultado, mientras que el resto de llamadas a la API seguirán funcionando como antes.
::ACTUALIZACIÓN::
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;
});
::ACTUALIZACIÓN::
Este fragmento eliminará todos los endpoints predeterminados.
<?php remove_action('rest_api_init', 'create_initial_rest_routes', 99); ?>

¿Dónde va este código personalizado? No mencionas dónde se debe guardar esto.

Esta solución deshabilita todas las operaciones CRUD en usuarios, mira esta implementación solo para solicitudes GET: https://github.com/szepeviktor/wordpress-fail2ban/commit/98eb4f292a5a6bdae637f9410eb7bb9d6dffd81c

¿Cómo deshabilitar mydomain.com/wp-json/ para que no muestre todos estos datos en formato json?

Esta solución es incompatible con Gutenberg que muestra:
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 }
Sería mejor forzar la autenticación en estos endpoints.

/**
* Envuelve un callback existente pasado como parámetro y crea
* un nuevo callback de permisos que realiza verificaciones preliminares
* y recurre al callback original en caso de éxito.
*/
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',
__( 'Lo sentimos, no tienes permiso para acceder a los usuarios.' ),
[ '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');
- El/los endpoint(s) no están bloqueados para administradores (Gutenberg sigue funcionando)
- El endpoint rechaza usuarios anónimos de forma adecuada.
- Es lo suficientemente genérico para soportar más endpoints.
- El
current_user_can
podría mejorarse, haciéndolo más genérico. - Se asume que el método
GET
es el primero para una ruta registrada (lo cual hasta ahora siempre ha sido cierto)

Elimina el enlace de la API del encabezado HTML si lo deseas.
// https://wordpress.stackexchange.com/a/211469/77054
// https://wordpress.stackexchange.com/a/212472
remove_action( 'wp_head', 'rest_output_link_wp_head', 10 );
Luego, requiere autenticación para todas las solicitudes.
// Puedes requerir autenticación para todas las solicitudes de la API REST agregando una verificación 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 los usuarios autenticados pueden acceder a la API REST.', array( 'status' => 401 ) );
}
return $result;
});
Esto te dejará con el mensaje deseado.
Ahora, para detener la enumeración, podrías usar algo como esto.
// https://perishablepress.com/stop-user-enumeration-wordpress/
// bloquear escaneos de enumeración en WP
// https://m0n.co/enum
if (!is_admin()) {
// formato de URL por defecto
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 de URL de enlace permanente
if (preg_match('/\?author=([0-9]*)(\/*)/i', $request)) die();
else return $redirect;
}
Consulta la publicación completa para más técnicas.

Código .htaccess para bloquear todos los escaneos de autores
# BEGIN block author scans
RewriteEngine On
RewriteBase /
RewriteCond %{QUERY_STRING} (author=\d+) [NC]
RewriteRule .* - [F]
# END block author scans
Puedes eliminarlo mediante función como sugiere la respuesta aceptada

Usé este pequeño código en functions.php:
/**
* Acceso a la API REST solo para administradores
*
* @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('Lo sentimos, no tienes permiso para acceder a estos datos','API REST Prohibido',403);
}
}
add_filter( 'rest_api_init', 'api_rest_only_for_admin_users', 99 );

Para completar la respuesta de BlueSuiter en 2017, aquí hay una solución para filtrar usuarios por rol que hace que su solución sea compatible con el 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;
});
El editor Gutenberg necesita consultar la API REST para obtener el autor. Por lo tanto, es necesario permitir al menos todos los roles de usuario de tu sitio que tienen acceso a un editor Gutenberg, no olvides las publicaciones personalizadas.
En el endpoint público de la API REST, se devuelve un error 404 porque is_user_logged_in siempre devolverá falso.

Si estás utilizando un firewall para proteger tu sitio web de WordPress, la mejor opción es bloquear el endpoint de la API que expone los detalles de los usuarios desde el firewall.
Detecta la URL https://ejemplo.com/wp-json/wp/v2/users
y simplemente bloquéala desde el firewall.
Edición: solo bloquea el endpoint específico de detalles de usuarios y no toda la API REST.

// Bloqueo de la API JSON de usuarios de WordPress
function disable_custom_rest_endpoints( $endpoints ) {
// Definir las rutas de la API que queremos deshabilitar
$routes = array( '/wp/v2/users', '/wp/v2/users/(?P<id>[\d]+)' );
// Recorrer cada ruta definida
foreach ( $routes as $route ) {
// Si la ruta no existe en los endpoints, continuar
if ( empty( $endpoints[ $route ] ) ) {
continue;
}
// Recorrer los manejadores de cada ruta
foreach ( $endpoints[ $route ] as $i => $handlers ) {
// Si es un manejador válido con método GET, eliminarlo
if ( is_array( $handlers ) && isset( $handlers['methods'] ) &&
'GET' === $handlers['methods'] ) {
unset( $endpoints[ $route ][ $i ] );
}
}
}
// Devolver los endpoints modificados
return $endpoints;
}
// Aplicar el filtro a los endpoints REST
add_filter( 'rest_endpoints', 'disable_custom_rest_endpoints' );

Creé un plugin completo que funciona para esto dentro de GITHUB.
https://github.com/MRKWP/mrkwp-rest-permissions/
Lo que hice fue asegurarme de usar INIT para el inicio de la acción, esto garantiza que las funciones pluggables funcionen como se espera.
También uso la capacidad 'Edit_posts' de WordPress para la API. Esto asegura que el administrador y el editor de bloques funcionen correctamente.
Echa un vistazo a ese plugin para resolver cualquier problema CVE-2017-5487 señalado por los auditores de seguridad.
También utiliza PHP CS, por lo que funcionará con una instalación VIP o algo que necesite estándares implementados.
Espero que esto ayude a alguien. Me tomó bastante tiempo hacer que esto funcionara bien en mis propios sitios y servidores.

Al revisar el código, el nombre "hardener" es un poco engañoso ya que no refuerza los endpoints, sino que hace que siempre rechacen las solicitudes. Simplemente lo instalas condicionalmente. Además, estás llamando funciones de activación y desactivación que no existen. Pero sí, parece que esto haría el trabajo.

Para poder solucionar esto, primero necesitas conocer la fuente del problema.
- ¿Utilizas plugins de SEO como: All in one SEO pack o Yoast? Intenta desactivarlos y verifica nuevamente.
- ¿Utilizas el plugin Jetpack? Intenta desactivarlo y verifica nuevamente.
Por favor, házme saber si esto te orientó en la dirección correcta.
Una forma rápida de solucionar esto es simplemente bloquear la URL en tu archivo .htaccess. https://miweb.com/wp-json/wp/v2/users
