Скрытие конечных точек WordPress REST API v2 от публичного просмотра

2 июн. 2016 г., 18:39:44
Просмотры: 36.5K
Голосов: 24

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

Мои вопросы:

  • Предназначен ли он для использования на сайтах в продакшене?
  • Существует ли риск безопасности при разрешении просмотра конечных точек кому угодно, например /wp-json/wp/v2/users/, который показывает всех зарегистрированных пользователей сайта?
  • Возможно ли разрешить доступ к конечной точке только авторизованным пользователям?

Я хочу убедиться, что следую лучшим практикам в отношении безопасности, поэтому любые советы будут полезны. В документации API упоминается аутентификация, но я не уверен, как предотвратить прямой доступ к URL. Как обычно другие настраивают доступ к этим данным для внешних приложений, не раскрывая слишком много информации?

2
Комментарии

Главный вопрос в том, используете ли вы конечные точки на стороне клиента (например, в AJAX-запросах) или на стороне сервера (возможно, из другого приложения)?

TheDeadMedic TheDeadMedic
2 июн. 2016 г. 21:32:50

Примечание: В последней версии плагина WordFence есть опция "Предотвращать обнаружение имен пользователей через сканирование '/?author=N', через API oEmbed и через WordPress REST API"

squarecandy squarecandy
11 янв. 2018 г. 05:37:42
Все ответы на вопрос 5
3
22

Предназначено ли это для использования на работающих сайтах?

Да. Многие сайты уже используют его.

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

Нет. Ответы сервера не имеют отношения к безопасности, вы ничего не можете поделать с пустым экраном или ответом только для чтения.

Однако, если ваш сайт разрешает слабые пароли, могут возникнуть проблемы. Но это политика вашего сайта, REST API ничего об этом не знает.

Можно ли разрешить доступ к конечной точке только авторизованным пользователям?

Да. Это можно сделать с помощью функции проверки прав доступа.

Например:

if ( 'edit' === $request['context'] && ! current_user_can( 'list_users' ) ) {
    return new WP_Error( 'rest_forbidden_context', __( 'Извините, вы не можете просматривать этот ресурс в контексте редактирования.' ), array( 'status' => rest_authorization_required_code() ) );
}

Как другие обычно настраивают доступ к этим данным для внешних приложений, не раскрывая слишком много информации?

На этот вопрос сложно ответить, потому что мы не знаем, что/когда является слишком большой информацией. Но мы можем строго следовать документации API и рекомендациям по безопасности, чтобы избежать нежелательных ситуаций.

3 июн. 2016 г. 08:08:10
Комментарии

Важно отметить: "Доступ ограничен пользователями, которые являются авторами типов записей, настроенных для отображения через REST API." - то есть, если у вас, например, интернет-магазин, где у каждого клиента есть учетная запись, эти пользователи не будут доступны через /wp-json/wp/v2/users/. (Ссылка https://wordpress.stackexchange.com/q/252328/41488 @JHoffmann комментарий)

squarecandy squarecandy
10 нояб. 2017 г. 18:48:18

Следует отметить, что вам необходимо использовать REST-based nonce wp_create_nonce('wp_rest') в заголовке 'X-WP-Nonce', иначе ничего не будет работать и всегда будет возвращаться ошибка 403.

Andrew Killen Andrew Killen
12 дек. 2018 г. 11:17:10

Точно. Я только что получил отчет об уязвимости через Open Bug Bounty на своем сайте на WP 5.x. Даже script kiddies (openbugbounty.org/researchers/Cyber_World) неправильно понимают суть CVE для этой уязвимости (cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2017-5487). Это было исправлено для всех, кто обеспокоен. Не копируйте и не вставляйте все эти данные, не понимая, что действительно используется и нужно ли вам это.

Tony-Caffe Tony-Caffe
19 нояб. 2021 г. 22:00:16
1

Можно ли разрешить доступ к конечной точке API только авторизованным пользователям?

Да, вы можете добавить пользовательский callback проверки прав для вашей конечной точки API, который требует аутентификации для просмотра контента. Неавторизованные пользователи получат ответ с ошибкой "code": "rest_forbidden"

Самый простой способ - расширить класс WP_REST_Posts_Controller. Вот очень простой пример:

class My_Private_Posts_Controller extends WP_REST_Posts_Controller {

   /**
   * Пространство имен.
   *
   * @var string
   */
   protected $namespace;

   /**
   * Тип записи для текущего объекта.
   *
   * @var string
   */
   protected $post_type;

   /**
   * Базовый путь REST для текущего объекта.
   *
   * @var string
   */
   protected $rest_base;

  /**
   * Регистрация маршрутов для объектов контроллера.
   * Почти идентично WP_REST_Posts_Controller::register_routes(), но с 
   * пользовательской проверкой прав.
   */
  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' => __( 'Удалить навсегда, минуя корзину.' ),
                ),
            ),
            'show_in_index'       => false,
        ),
        'schema' => array( $this, 'get_public_item_schema' ),
    ) );     
  }

  /**
   * Проверка прав на доступ к списку элементов
   *
   * @param WP_REST_Request $request Полные данные о запросе.
   * @return WP_Error|bool
   */
  public function get_items_permissions_check( $request ) {
    return current_user_can( 'edit_posts' );
  }

}

Обратите внимание, что callback проверки прав function get_items_permissions_check использует current_user_can для определения прав доступа. В зависимости от того, как вы используете API, вам может понадобиться больше узнать о клиентской аутентификации.

Затем вы можете зарегистрировать свой пользовательский тип записи с поддержкой REST API, добавив следующие аргументы в register_post_type:

  /**
   * Регистрация типа записи "Книга" с поддержкой REST API
   *
   * На основе примера из: http://codex.wordpress.org/Function_Reference/register_post_type
   */
  add_action( 'init', 'my_book_cpt' );
  function my_book_cpt() {
    $labels = array(
        'name'               => _x( 'Книги', 'общее название типа записи', 'your-plugin-textdomain' ),
        'singular_name'      => _x( 'Книга', 'название одной записи этого типа', 'your-plugin-textdomain' ),
        'menu_name'          => _x( 'Книги', 'админ-меню', 'your-plugin-textdomain' ),
        'name_admin_bar'     => _x( 'Книга', 'добавить новую в админ-баре', 'your-plugin-textdomain' ),
        'add_new'            => _x( 'Добавить новую', 'книгу', 'your-plugin-textdomain' ),
        'add_new_item'       => __( 'Добавить новую книгу', 'your-plugin-textdomain' ),
        'new_item'           => __( 'Новая книга', 'your-plugin-textdomain' ),
        'edit_item'          => __( 'Редактировать книгу', 'your-plugin-textdomain' ),
        'view_item'          => __( 'Просмотреть книгу', 'your-plugin-textdomain' ),
        'all_items'          => __( 'Все книги', 'your-plugin-textdomain' ),
        'search_items'       => __( 'Искать книги', 'your-plugin-textdomain' ),
        'parent_item_colon'  => __( 'Родительские книги:', 'your-plugin-textdomain' ),
        'not_found'          => __( 'Книги не найдены.', 'your-plugin-textdomain' ),
        'not_found_in_trash' => __( 'В корзине книг не найдено.', 'your-plugin-textdomain' )
    );

    $args = array(
        'labels'             => $labels,
        '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 вместо стандартного контроллера.

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

19 июл. 2016 г. 20:49:33
Комментарии

Спасибо, отлично сработало для моих нужд - хотя не забудьте добавить пространство имен в класс контроллера, если вы используете пространства имен - так как нет ошибки, если класс контроллера не найден (да, я забыл!)

Alan Fuller Alan Fuller
4 июн. 2020 г. 17:06:01
2

Вот что я использовал для полной блокировки REST API для всех пользователей без авторизации:

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('Извините, у вас нет доступа к этим данным', 'Нечестная игра?', 403);
    }
}
1 дек. 2017 г. 04:48:27
Комментарии

Поскольку использование REST API будет расширяться, такой подход станет проблематичным. В конечном итоге конечная точка wp-json заменит admin-ajax, что означает, что через нее будут проходить все виды легитимных фронтенд-запросов. В любом случае, лучше вернуть 403 ошибку, чем что-то, что может быть интерпретировано как контент.

Mark Kaplun Mark Kaplun
1 дек. 2017 г. 07:22:41

@MarkKaplun - да, вы правы. Я использую это в контексте сайта, который практически не предоставляет публичных данных, а хранимая информация, включая пользователей, метаданные пользователей, данные пользовательских типов записей и т.д., является собственными данными, которые никогда не должны быть доступны публично. Очень неприятно, когда вы проделываете огромную работу в рамках классической структуры шаблонов WP, чтобы обеспечить приватность определенных данных, а потом внезапно осознаете, что все это доступно публично через REST API. В любом случае, хорошее замечание насчет возврата 403...

squarecandy squarecandy
1 дек. 2017 г. 16:29:53
1

Лучший вариант — отключить новый редактор V5, а затем отключить API JSON, как объясняется здесь.

https://codber.com/2020/05/01/how-to-disable-wordpress-rest-api-to-not-logged-in-user-without-a-plugin/

9 мая 2020 г. 18:52:07
Комментарии

Ответ, содержащий только ссылку, устареет, если ссылка перестанет работать. Вы можете [отредактировать] свой ответ, добавив его краткое содержание, а затем оставить ссылку для более подробной информации. :)

Mayeenul Islam Mayeenul Islam
10 мая 2020 г. 21:56:48
2
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('Извините, у вас нет доступа к этим данным','Требуется аутентификация',403);
} } 
function json_authenticate_handler( $user ) {

global $wp_json_basic_auth_error;

$wp_json_basic_auth_error = null;

// Не аутентифицировать дважды
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 );
8 янв. 2018 г. 13:56:56
Комментарии

Можете подробнее объяснить в тексте, почему и как это отвечает на вопросы автора?

kero kero
8 янв. 2018 г. 14:32:59

Это не ответ автора, я лишь привел код, чтобы показать, как это работает на практике, и попытался помочь вам легче понять это программно. Если вы это поняли

dipen patel dipen patel
9 янв. 2018 г. 15:17:02