Masquage des points de terminaison de l'API REST WordPress v2 de la vue publique


14

Je voudrais commencer à utiliser l' API REST WordPress v2 pour interroger les informations de mon site. J'ai remarqué que lorsque je visite directement une URL de point de terminaison, je peux voir toutes les données publiquement. J'ai également vu que de nombreux tutoriels mentionnent l'utilisation de serveurs de test ou locaux plutôt que de sites en direct.

Mes questions sont:

  • Est-ce destiné à être utilisé sur des sites en production?
  • Existe-t-il un risque pour la sécurité d'autoriser la visualisation de points de terminaison par quiconque, par exemple, /wp-json/wp/v2/users/qui montre tous les utilisateurs enregistrés sur le site?
  • Est-il possible d'autoriser uniquement les utilisateurs autorisés à accéder à un point de terminaison?

Je veux m'assurer de suivre les meilleures pratiques en matière de sécurité, donc tous les conseils seraient utiles. Les documents de l'API mentionnent l'authentification, mais je ne sais pas comment empêcher l'accès direct à l'URL. Comment les autres configurent-ils généralement ces données pour qu'elles soient accessibles par des applications externes sans exposer trop d'informations?


1
La vraie question est, utilisez-vous le côté client des points de terminaison (c'est-à-dire dans les appels AJAX), ou le côté serveur (peut-être d'une autre application)?
TheDeadMedic

1
Remarque: La version la plus récente du plugin WordFence a une option pour "Empêcher la découverte des noms d'utilisateurs via les analyses '/? Author = N', l'API oEmbed et l'API WordPress REST"
squarecandy

Réponses:


18

Est-ce destiné à être utilisé sur des sites en production?

Oui. De nombreux sites l'utilisent déjà .

Existe-t-il un risque pour la sécurité d'autoriser la visualisation de points de terminaison par quiconque, tel que / wp-json / wp / v2 / users / qui montre tous les utilisateurs enregistrés sur le site?

Non. Les réponses du serveur n'ont rien à voir avec la sécurité, que pouvez-vous faire avec un écran vide / un accès en lecture seule? Rien!

Cependant, si vos sites autorisent des mots de passe faibles, il y a des problèmes . Mais c'est la politique de vos sites, l'API REST n'en sait rien.

Est-il possible d'autoriser uniquement les utilisateurs autorisés à accéder à un point de terminaison?

Oui. Vous pouvez le faire en utilisant le rappel d'autorisation .

Par exemple:

if ( 'edit' === $request['context'] && ! current_user_can( 'list_users' ) ) {
    return new WP_Error( 'rest_forbidden_context', __( 'Sorry, you cannot view this resource with edit context.' ), array( 'status' => rest_authorization_required_code() ) );
}

Comment les autres configurent-ils généralement ces données pour qu'elles soient accessibles par des applications externes sans exposer trop d'informations?

Il est difficile de répondre à cette question parce que nous ne savons pas ce qui / quand est trop d'informations . Mais nous utilisons tous des références et des cheatsheets .


1
Important à noter: "L'exposition est limitée aux utilisateurs qui ont créé des types de publication qui doivent être exposés via l'API REST." - donc si vous avez dit, une boutique en ligne où chaque client a un utilisateur, ces utilisateurs ne sont pas exposés via /wp-json/wp/v2/users/. (Référence wordpress.stackexchange.com/q/252328/41488 @JHoffmann comment)
squarecandy

Il convient de noter que vous devez avoir un nonce basé sur REST wp_create_nonce ('wp_rest') dans l'en-tête 'X-WP-Nonce', ou rien de tout cela ne fonctionnera du tout, et retournera toujours un 403.
Andrew Killen

5

Est-il possible d'autoriser uniquement les utilisateurs autorisés à accéder à un point de terminaison?

Il est possible d'ajouter un rappel d'autorisation personnalisé à votre point de terminaison API qui nécessite une authentification pour afficher le contenu. Les utilisateurs non autorisés recevront une réponse d'erreur"code": "rest_forbidden"

La façon la plus simple de procéder consiste à étendre WP_REST_Posts_Controller. Voici un exemple très simple de cela:

class My_Private_Posts_Controller extends WP_REST_Posts_Controller {

   /**
   * The namespace.
   *
   * @var string
   */
   protected $namespace;

   /**
   * The post type for the current object.
   *
   * @var string
   */
   protected $post_type;

   /**
   * Rest base for the current object.
   *
   * @var string
   */
   protected $rest_base;

  /**
   * Register the routes for the objects of the controller.
   * Nearly the same as WP_REST_Posts_Controller::register_routes(), but with a 
   * custom permission callback.
   */
  public function register_routes() {
    register_rest_route( $this->namespace, '/' . $this->rest_base, array(
        array(
            'methods'             => WP_REST_Server::READABLE,
            'callback'            => array( $this, 'get_items' ),
            'permission_callback' => array( $this, 'get_items_permissions_check' ),
            'args'                => $this->get_collection_params(),
            'show_in_index'       => true,
        ),
        array(
            'methods'             => WP_REST_Server::CREATABLE,
            'callback'            => array( $this, 'create_item' ),
            'permission_callback' => array( $this, 'create_item_permissions_check' ),
            'args'                => $this->get_endpoint_args_for_item_schema( WP_REST_Server::CREATABLE ),
            'show_in_index'       => true,
        ),
        'schema' => array( $this, 'get_public_item_schema' ),
    ) );

    register_rest_route( $this->namespace, '/' . $this->rest_base . '/(?P<id>[\d]+)', array(
        array(
            'methods'             => WP_REST_Server::READABLE,
            'callback'            => array( $this, 'get_item' ),
            'permission_callback' => array( $this, 'get_item_permissions_check' ),
            'args'                => array(
                'context' => $this->get_context_param( array( 'default' => 'view' ) ),
            ),
            'show_in_index'       => true,
        ),
        array(
            'methods'             => WP_REST_Server::EDITABLE,
            'callback'            => array( $this, 'update_item' ),
            'permission_callback' => array( $this, 'update_item_permissions_check' ),
            'args'                => $this->get_endpoint_args_for_item_schema( WP_REST_Server::EDITABLE ),
            'show_in_index'       => true,
        ),
        array(
            'methods'             => WP_REST_Server::DELETABLE,
            'callback'            => array( $this, 'delete_item' ),
            'permission_callback' => array( $this, 'delete_item_permissions_check' ),
            'args'                => array(
                'force' => array(
                    'default'     => true,
                    'description' => __( 'Whether to bypass trash and force deletion.' ),
                ),
            ),
            'show_in_index'       => false,
        ),
        'schema' => array( $this, 'get_public_item_schema' ),
    ) );     
  }

  /**
   * Check if a given request has access to get items
   *
   * @param WP_REST_Request $request Full data about the request.
   * @return WP_Error|bool
   */
  public function get_items_permissions_check( $request ) {
    return current_user_can( 'edit_posts' );
  }

}

Vous remarquerez que le rappel des autorisations function get_items_permissions_checkutilise current_user_canpour déterminer s'il faut autoriser l'accès. Selon la façon dont vous utilisez l'API, vous devrez peut-être en savoir plus sur l'authentification client.

Vous pouvez ensuite enregistrer votre type de publication personnalisé avec le support de l'API REST en ajoutant les arguments suivants dans register_post_type

  /**
   * Register a book post type, with REST API support
   *
   * Based on example at: http://codex.wordpress.org/Function_Reference/register_post_type
   */
  add_action( 'init', 'my_book_cpt' );
  function my_book_cpt() {
    $labels = array(
        'name'               => _x( 'Books', 'post type general name', 'your-plugin-textdomain' ),
        'singular_name'      => _x( 'Book', 'post type singular name', 'your-plugin-textdomain' ),
        'menu_name'          => _x( 'Books', 'admin menu', 'your-plugin-textdomain' ),
        'name_admin_bar'     => _x( 'Book', 'add new on admin bar', 'your-plugin-textdomain' ),
        'add_new'            => _x( 'Add New', 'book', 'your-plugin-textdomain' ),
        'add_new_item'       => __( 'Add New Book', 'your-plugin-textdomain' ),
        'new_item'           => __( 'New Book', 'your-plugin-textdomain' ),
        'edit_item'          => __( 'Edit Book', 'your-plugin-textdomain' ),
        'view_item'          => __( 'View Book', 'your-plugin-textdomain' ),
        'all_items'          => __( 'All Books', 'your-plugin-textdomain' ),
        'search_items'       => __( 'Search Books', 'your-plugin-textdomain' ),
        'parent_item_colon'  => __( 'Parent Books:', 'your-plugin-textdomain' ),
        'not_found'          => __( 'No books found.', 'your-plugin-textdomain' ),
        'not_found_in_trash' => __( 'No books found in Trash.', 'your-plugin-textdomain' )
    );

    $args = array(
        'labels'             => $labels,
        'description'        => __( 'Description.', 'your-plugin-textdomain' ),
        'public'             => true,
        'publicly_queryable' => true,
        'show_ui'            => true,
        'show_in_menu'       => true,
        'query_var'          => true,
        'rewrite'            => array( 'slug' => 'book' ),
        'capability_type'    => 'post',
        'has_archive'        => true,
        'hierarchical'       => false,
        'menu_position'      => null,
        'show_in_rest'       => true,
        'rest_base'          => 'books-api',
        'rest_controller_class' => 'My_Private_Posts_Controller',
        'supports'           => array( 'title', 'editor', 'author', 'thumbnail', 'excerpt', 'comments' )
    );

    register_post_type( 'book', $args );
}

Vous verrez des rest_controller_classutilisations My_Private_Posts_Controllerau lieu du contrôleur par défaut.

J'ai eu du mal à trouver de bons exemples et des explications pour utiliser l'API REST en dehors de la documentation . J'ai trouvé cette excellente explication de l'extension du contrôleur par défaut , et voici un guide très complet pour ajouter des points de terminaison .


2

Voici ce que j'ai utilisé pour empêcher tous les utilisateurs non connectés d'utiliser l'API REST:

add_filter( 'rest_api_init', 'rest_only_for_authorized_users', 99 );
function rest_only_for_authorized_users($wp_rest_server){
    if ( !is_user_logged_in() ) {
        wp_die('sorry you are not allowed to access this data','cheatin eh?',403);
    }
}

À mesure que l'utilisation du point final de repos va se développer, ce type de stratégie deviendra problématique. À la fin, le point de terminaison wp-json remplacera celui admin-ajax, ce qui signifie qu'il y aura également toutes sortes de demandes frontales légitimes. Quoi qu'il en soit, mieux vaut mourir avec un 403 que quelque chose qui pourrait être interprété comme du contenu.
Mark Kaplun

@MarkKaplun - oui, vous avez raison à ce sujet. J'utilise ceci dans le contexte d'un site qui n'offre essentiellement aucune donnée publique du tout et les données que nous stockons, y compris les utilisateurs, les métadonnées utilisateur, les données de type de message personnalisé, etc. sont des données propriétaires auxquelles le public ne devrait jamais avoir accès. . Cela craint lorsque vous effectuez un tas de travail dans la structure de modèle WP classique pour vous assurer que certaines données sont privées et que vous réalisez soudainement qu'elles sont toutes accessibles au public via l'API REST. Quoi qu'il en soit, bon point à propos de servir un 403 ...
squarecandy

0
add_filter( 'rest_api_init', 'rest_only_for_authorized_users', 99 );
function rest_only_for_authorized_users($wp_rest_server)
{
if( !is_user_logged_in() ) 

    wp_die('sorry you are not allowed to access this data','Require Authentication',403);
} } 
function json_authenticate_handler( $user ) {

global $wp_json_basic_auth_error;

$wp_json_basic_auth_error = null;

// Don't authenticate twice
if ( ! empty( $user ) ) {
    return $user;
}

if ( !isset( $_SERVER['PHP_AUTH_USER'] ) ) {
    return $user;
}

$username = $_SERVER['PHP_AUTH_USER'];
$password = $_SERVER['PHP_AUTH_PW'];


remove_filter( 'determine_current_user', 'json_authenticate_handler', 20 );

$user = wp_authenticate( $username, $password );

add_filter( 'determine_current_user', 'json_authenticate_handler', 20 );

if ( is_wp_error( $user ) ) {
    $wp_json_basic_auth_error = $user;
    return null;
}

$wp_json_basic_auth_error = true;

return $user->ID;}add_filter( 'determine_current_user', 'json_authenticate_handler', 20 );

1
Pourriez-vous expliquer dans le texte pourquoi et comment cela répond aux questions du PO?
kero

Ce n'est pas la réponse de l'op et je n'ai donné que du code pour montrer comment travailler pratiquement et j'ai essayé de vous faire comprendre facilement par programme Si vous l'avez compris
dipen patel
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.