Сховання кінцевих точок WordPress REST API v2 від загального перегляду


15

Я хотів би почати використовувати WordPress REST API v2 для запиту інформації з мого сайту. Я помітив, що коли я відвідую URL-адресу кінцевої точки безпосередньо, я можу бачити всі дані публічно. Я також бачив, що в багатьох навчальних посібниках згадується використання тестових або локальних серверів, а не живих сайтів.

Мої запитання:

  • Це призначено для використання на майданчиках у виробництві?
  • Чи існує ризик безпеки для того, щоб дозволити переглядати кінцеві точки будь-хто, наприклад, /wp-json/wp/v2/users/який показує всіх зареєстрованих на сайті користувачів?
  • Чи можна дозволити лише кінцевим користувачам отримати доступ до кінцевої точки?

Я хочу переконатися, що я дотримуюся найкращих практик щодо безпеки, тому будь-які поради будуть корисними. Документи api згадують автентифікацію, але я не впевнений, як запобігти прямому доступу до URL-адреси. Як інші люди налаштовують доступ до цих даних зовнішніми програмами, не піддаючи занадто багато інформації?


1
Справжнє запитання полягає в тому, чи використовуєте ви клієнтську сторону кінцевих точок (тобто для дзвінків AJAX) або серверну сторону (можливо, з іншої програми)?
TheDeadMedic

1
Примітка. В останній версії плагіна WordFence є опція "Запобігти виявленню імен користувачів через '/? Author = N' сканування, API oEmbed та API WordPress REST"
squarecandy

Відповіді:


18

Це призначено для використання на майданчиках у виробництві?

Так. Багато сайтів вже користуються ним .

Чи існує ризик безпеки для дозволу перегляду кінцевих точок ким-небудь, наприклад / wp-json / wp / v2 / users /, який показує всіх користувачів, зареєстрованих на сайті?

Ні. Відповіді сервера не мають нічого спільного з безпекою. Що ви можете зробити з порожнім екраном / доступом лише для читання? Нічого!

Однак якщо ваші сайти дозволяють слабкі паролі, є деякі проблеми . Але це політика ваших сайтів, REST API нічого про це не знає.

Чи можна дозволити лише кінцевим користувачам отримати доступ до кінцевої точки?

Так. Це можна зробити, використовуючи зворотний дзвінок дозволу .

Наприклад:

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() ) );
}

Як інші люди налаштовують доступ до цих даних зовнішніми програмами, не піддаючи занадто багато інформації?

На це питання важко відповісти, оскільки ми не знаємо, що / коли надто багато інформації . Але ми всі використовуємо посилання та шаблони .


1
Важливо відзначити: "Експозиція обмежується користувачами, які мають авторські типи публікацій, які встановлені для викриття через API REST." - тож, якщо ви скажете, інтернет-магазині, де кожен клієнт має користувача, ці користувачі не піддаються впливу /wp-json/wp/v2/users/. (Довідкова wordpress.stackexchange.com/q/252328/41488 @ коментар Дж. Джоффмана)
squarecandy

Слід зазначити, що у заголовку "X-WP-Nonce" вам потрібно мати REST, який не базується на REST, або жоден із цих матеріалів не працюватиме взагалі і завжди повертатиме 403.
Ендрю Кіллен

5

Чи можна дозволити лише кінцевим користувачам отримати доступ до кінцевої точки?

Можна додати спеціальний зворотний дзвінок дозволу до кінцевої точки API, який вимагає автентифікації для перегляду вмісту. Несанкціоновані користувачі отримають відповідь про помилку"code": "rest_forbidden"

Найпростіший спосіб зробити це - розширити WP_REST_Posts_Controller. Ось дуже простий приклад цього:

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' );
  }

}

Ви помітите, що зворотний виклик дозволів function get_items_permissions_checkвикористовується, current_user_canщоб визначити, чи дозволяти доступ. Залежно від способу використання API, вам може знадобитися дізнатися більше про автентифікацію клієнта.

Потім можна зареєструвати власний тип публікації за допомогою підтримки REST API, додавши наступні аргументи в 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 );
}

Ви побачите rest_controller_classвикористання My_Private_Posts_Controllerзамість контролера за замовчуванням.

Мені важко було знайти хороші приклади та пояснення щодо використання API REST поза документацією . Я знайшов це чудове пояснення щодо розширення контролера за замовчуванням , і ось дуже ретельне керівництво щодо додавання кінцевих точок .


2

Ось що я використав, щоб взагалі блокувати користувачів, які не ввійшли в систему, використовувати 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);
    }
}

Оскільки використання кінцевої точки відпочинку буде розширюватися, така стратегія стане проблематичною. Зрештою, кінцева точка wp-json замінить адмін-ajax, що означає, що також будуть всілякі законні запити на передньому кінці. У будь-якому випадку, краще померти з 403, ніж щось, що може трактуватися як зміст.
Марк Каплун

@MarkKaplun - так, ти прав. Я використовую це в контексті веб-сайту, який фактично не пропонує загальнодоступних даних, а дані, які ми зберігаємо, включаючи користувачів, мета користувача, користувацькі дані про публікацію тощо, - це власні дані, до яких громадськість не має доступу. . Це дуже добре, коли ви виконуєте велику роботу в рамках класичної структури шаблонів WP, щоб переконатися, що певні дані є приватними, а потім раптом зрозумієте, що всі вони доступні публічно через API REST. У будь-якому разі, хороший момент щодо обслуговування 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
Не могли б ви детально розібратися, чому і як це відповідає на питання ОП?
керо

Це не відповідь оп, і я дав лише код, щоб показати, як практично працювати, і я намагався вас легко зрозуміти в програмі Якщо ви це зрозуміли
dipen patel
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.