wpapp.ru wordpress wpapp.ru

Как ограничить доступ к REST API WordPress по токену

REST API в WordPress часто оставляют открытым «по умолчанию», а потом удивляются лишним запросам, мусорным интеграциям и попыткам дергать эндпоинты снаружи. Если сайт использует API только для своих приложений, админки, фронтенда или пары доверенных сервисов, доступ можно ограничить по токену на уровне WordPress. Это не замена полноценной авторизации для публичного API, но для закрытого сценария — рабочее решение.

Ниже разберем, как понять, что проблема именно в открытом REST API, как добавить проверку токена через стандартные хуки WordPress, как не сломать редактор и встроенные запросы, и как проверить, что ограничение реально работает.

Когда REST API нужно ограничивать

Типичный сценарий: на сайте есть кастомное приложение, внешняя форма, мобильный клиент или внутренний сервис, который ходит в WordPress по REST API. При этом любые другие запросы к /wp-json/ вам не нужны. В логах начинают появляться обращения к стандартным эндпоинтам, а иногда и к кастомным маршрутам, которые не должны быть доступны без ключа.

Ограничение по токену особенно уместно, если:

  • API используется только для одного или нескольких доверенных клиентов;
  • нужно быстро отсечь случайные и скриптовые запросы;
  • вы не хотите открывать авторизацию через cookies или OAuth для простого внутреннего сценария;
  • эндпоинты возвращают чувствительные данные, но не должны быть полностью публичными.

Что именно будем ограничивать

В WordPress REST API есть маршруты ядра, маршруты плагинов и ваши собственные endpoints. Если закрыть все подряд, можно сломать:

  • редактор блоков, который использует REST API для сохранения и загрузки данных;
  • встроенные запросы темы или плагинов;
  • интеграции, которые уже завязаны на cookie-авторизацию.

Поэтому разумнее ограничивать только нужные маршруты, а не весь /wp-json/ целиком.

Диагностика проблемы: какие запросы реально идут к API

Перед изменениями посмотрите, кто и что дергает. Это можно сделать через логи веб-сервера, DevTools в браузере или временный лог в WordPress. Если вы видите запросы без ожидаемых заголовков, без авторизации или с подозрительных IP, значит ограничение имеет смысл.

Для быстрой проверки удобно открыть вкладку Network в браузере и отфильтровать запросы по wp-json. Если сайт уже использует REST API в админке или на фронтенде, вы увидите, какие маршруты нужны легитимно.

Минимальный чек-лист перед внедрением

  • Список нужных REST-маршрутов составлен.
  • Понятно, кто будет передавать токен: приложение, сервер, форма, cron.
  • Проверено, не завязан ли фронтенд или редактор на те же endpoints.
  • Есть доступ к тестовому окружению или хотя бы к staging.

Пошаговое решение: проверка токена через rest_authentication_errors

Самый практичный вариант — повесить проверку на хук rest_authentication_errors. Он срабатывает до обработки маршрута и позволяет вернуть ошибку, если токен отсутствует или неверный. Такой подход не требует выдуманных API и работает на стандартном WordPress.

Ниже пример, который ограничивает доступ ко всем REST-запросам, кроме нескольких разрешенных маршрутов, если не передан правильный токен в заголовке X-API-Token.

<?php
add_filter( 'rest_authentication_errors', function( $result ) {
    if ( ! empty( $result ) ) {
        return $result;
    }

    $request_uri = isset( $_SERVER['REQUEST_URI'] ) ? wp_unslash( $_SERVER['REQUEST_URI'] ) : '';

    // Разрешаем служебные маршруты, если нужно.
    $allowed_routes = array(
        '/wp-json/wp/v2/users/me',
    );

    foreach ( $allowed_routes as $allowed_route ) {
        if ( strpos( $request_uri, $allowed_route ) !== false ) {
            return $result;
        }
    }

    $token = isset( $_SERVER['HTTP_X_API_TOKEN'] ) ? sanitize_text_field( wp_unslash( $_SERVER['HTTP_X_API_TOKEN'] ) ) : '';
    $expected_token = defined( 'MY_REST_API_TOKEN' ) ? MY_REST_API_TOKEN : '';

    if ( empty( $expected_token ) ) {
        return new WP_Error( 'rest_token_not_configured', 'REST API token is not configured.', array( 'status' => 500 ) );
    }

    if ( ! hash_equals( $expected_token, $token ) ) {
        return new WP_Error( 'rest_forbidden', 'Invalid API token.', array( 'status' => 403 ) );
    }

    return $result;
} );

Токен лучше хранить не в коде темы, а в wp-config.php или в переменных окружения. Например:

define( 'MY_REST_API_TOKEN', 'replace-with-a-long-random-string' );

Если у вас есть собственный плагин, логичнее держать проверку там, а не в теме. Тогда ограничение не исчезнет при смене шаблона.

Как не сломать редактор и внутренние запросы WordPress

Полностью закрывать весь REST API без исключений — плохая идея для обычного сайта. Gutenberg, некоторые плагины и даже отдельные части админки могут обращаться к API от имени авторизованного пользователя. Если вы поставите жесткую проверку на все маршруты, получите ошибки сохранения записей, проблемы с медиа и странное поведение в редакторе.

Поэтому есть три рабочих сценария:

ПодходКогда использоватьКомпромисс
Проверка токена на всех маршрутахЗакрытый API для внешнего клиентаНужно явно исключить служебные запросы
Проверка только на своих маршрутахЕсть кастомные endpointsЯдро и плагины остаются открытыми
Проверка токена + авторизация WordPressСмешанный сценарий для админов и сервисовСложнее поддерживать, но гибче

Если вам нужно ограничить только свои маршруты, лучше использовать register_rest_route() с permission_callback. Это чище, чем глобальная проверка.

<?php
add_action( 'rest_api_init', function () {
    register_rest_route( 'myapp/v1', '/stats', array(
        'methods'             => 'GET',
        'callback'            => 'myapp_get_stats',
        'permission_callback' => function () {
            $token = isset( $_SERVER['HTTP_X_API_TOKEN'] ) ? sanitize_text_field( wp_unslash( $_SERVER['HTTP_X_API_TOKEN'] ) ) : '';
            return defined( 'MY_REST_API_TOKEN' ) && hash_equals( MY_REST_API_TOKEN, $token );
        },
    ) );
} );

function myapp_get_stats( WP_REST_Request $request ) {
    return rest_ensure_response( array(
        'ok' => true,
        'time' => current_time( 'mysql' ),
    ) );
}

Проверка результата после внедрения

После добавления кода проверьте поведение не только в браузере, но и через curl. Это самый быстрый способ убедиться, что защита работает предсказуемо.

Запрос без токена должен вернуть ошибку 403 или ваш кастомный ответ:

curl -i https://example.com/wp-json/myapp/v1/stats

Запрос с правильным токеном должен вернуть JSON-ответ:

curl -i \
  -H 'X-API-Token: replace-with-a-long-random-string' \
  https://example.com/wp-json/myapp/v1/stats

Что смотреть в ответе:

  • код статуса: 403 без токена, 200 с токеном;
  • нет ли лишних редиректов;
  • не ломаются ли запросы в админке;
  • не появились ли ошибки в debug.log.

Если нужен быстрый лог для отладки

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

<?php
add_action( 'rest_api_init', function () {
    add_filter( 'rest_pre_dispatch', function ( $result, $server, $request ) {
        error_log( 'REST request: ' . $request->get_route() );
        return $result;
    }, 10, 3 );
} );

Частые ошибки и как их исправить

Проверяют токен только по GET-параметру

Токен в URL быстро утекает в логи, историю браузера и рефереры. Для закрытого API безопаснее передавать его в заголовке. Если по архитектуре иначе нельзя, хотя бы не используйте короткий и предсказуемый ключ.

Закрывают весь /wp-json/ без исключений

Это ломает легитимные запросы WordPress и плагинов. Если задача — защитить только свои endpoints, ограничивайте только их. Если задача — закрыть все, заранее проверьте админку, редактор и интеграции.

Хранят токен в теме

При смене темы защита исчезает. Для такой логики лучше использовать mu-plugin, обычный плагин или хотя бы отдельный мини-плагин.

Не используют hash_equals()

Сравнение строк через == или === в этом сценарии хуже, чем hash_equals(). Для токенов это стандартная практика, потому что она снижает риск проблем с timing attacks.

Безопасность и производительность: что учесть

Токеновая защита не должна становиться единственным барьером. Если API публично доступен из интернета, добавьте еще и базовые меры: ограничение по IP для внутренних сервисов, HTTPS, нормальные логи и ротацию токена. Если токен утек, его нужно быстро заменить.

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

  • Используйте длинный случайный токен.
  • Храните его вне репозитория.
  • Не логируйте токен целиком.
  • Ограничивайте только нужные маршруты.
  • Проверяйте работу после обновлений плагинов и ядра.

Если задача шире и вам нужно одновременно чистить сайт от лишних служебных сущностей, дубликатов и технического мусора, можно посмотреть в сторону инструментов вроде Clearfy Pro: https://wpshop.ru/plugins/clearfy?utm_source=wpapp.ru&utm_medium=article&utm_campaign=ogranichit-dostup-k-rest-api-wordpress-po-tokenu. Но для самой токеновой защиты все равно нужен понятный код и контроль маршрутов.

Когда лучше не использовать токен в REST API

Если API должен быть публичным, а доступ к данным зависит от пользователя, токен в заголовке может оказаться неудобным. В таком случае лучше смотреть в сторону штатной авторизации WordPress, application passwords или отдельной схемы аутентификации, которую вы сможете сопровождать без костылей.

Токеновый вариант хорош там, где есть один или несколько доверенных клиентов и понятная граница доступа. Если границы нет, сначала спроектируйте модель авторизации, а уже потом закрывайте endpoints.

×
Прокачай свой сайт WordPress!

WordPress

-20% на премиум темы и плагины

Создай сайт своей мечты ⋙