Ausblenden von WordPress REST API v2-Endpunkten aus der öffentlichen Anzeige

14

Ich möchte die WordPress REST API v2 zum Abfragen von Informationen von meiner Site verwenden. Ich habe festgestellt, dass ich beim direkten Aufrufen einer Endpunkt-URL alle Daten öffentlich sehen kann. Ich habe auch gesehen, dass in vielen Tutorials die Verwendung von Test- oder lokalen Servern anstelle von Live-Sites erwähnt wird.

Meine Fragen sind:

  • Soll dies an Standorten in der Produktion eingesetzt werden?
  • Besteht ein Sicherheitsrisiko darin, dass Endpunkte von jedermann angezeigt werden, z. B., wenn /wp-json/wp/v2/users/alle auf der Site registrierten Benutzer angezeigt werden ?
  • Ist es möglich, nur autorisierten Benutzern den Zugriff auf einen Endpunkt zu erlauben?

Ich möchte sicherstellen, dass ich die Best Practices in Bezug auf die Sicherheit befolge. Daher sind Tipps hilfreich. Die API-Dokumente erwähnen die Authentifizierung, aber ich bin nicht sicher, wie ich den direkten Zugriff auf die URL verhindern soll. Wie richten andere Benutzer diese Daten für den Zugriff durch externe Anwendungen ein, ohne zu viele Informationen preiszugeben?

Morgan
quelle
1
Die eigentliche Frage ist, ob Sie die Endpunkt- Clientseite (dh bei AJAX-Aufrufen) oder die Serverseite (möglicherweise von einer anderen Anwendung) verwenden.
TheDeadMedic
1
Hinweis: Die neueste Version des WordFence-Plugins bietet die Option "Erkennung von Benutzernamen durch '/? Author = N' Scans, die oEmbed-API und die WordPress-REST-API verhindern"
squarecandy

Antworten:

18

Soll dies an Standorten in der Produktion eingesetzt werden?

Ja. Viele Websites haben es bereits verwendet .

Besteht ein Sicherheitsrisiko, wenn Endpunkte von beliebigen Benutzern angezeigt werden, z. B. / wp-json / wp / v2 / users /, die alle auf der Site registrierten Benutzer anzeigen?

Nein. Die Antworten des Servers haben nichts mit Sicherheit zu tun. Was können Sie mit einem leeren Bildschirm / Nur-Lese-Zugriff tun? Nichts!

Wenn Ihre Websites jedoch schwache Kennwörter zulassen, treten einige Probleme auf . Die REST-API weiß nichts darüber.

Ist es möglich, nur autorisierten Benutzern den Zugriff auf einen Endpunkt zu erlauben?

Ja. Sie können dies mithilfe des Berechtigungsrückrufs tun .

Beispielsweise:

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

Wie richten andere Benutzer diese Daten für den Zugriff durch externe Anwendungen ein, ohne zu viele Informationen preiszugeben?

Diese Frage ist schwer zu beantworten, da wir nicht wissen, wann und wie viele Informationen vorliegen . Aber wir verwenden alle Referenzen und Cheatsheets .

MinhTri
quelle
1
Wichtig zu beachten: "Die Verfügbarkeit ist auf Benutzer beschränkt, die Beitragstypen verfasst haben, deren Verfügbarkeit über die REST-API festgelegt wurde." - Wenn Sie also sagen, ein Online-Shop, in dem jeder Kunde einen Benutzer hat, sind diese Benutzer nicht über ausgesetzt /wp-json/wp/v2/users/. (Referenz wordpress.stackexchange.com/q/252328/41488 @JHoffmann Kommentar)
squarecandy
Es sollte beachtet werden, dass Sie ein REST-basiertes nonce wp_create_nonce ('wp_rest') im 'X-WP-Nonce'-Header haben müssen, da sonst keines dieser Dinge funktioniert und immer ein 403 zurückgibt.
Andrew Killen
5

Ist es möglich, nur autorisierten Benutzern den Zugriff auf einen Endpunkt zu erlauben?

Es ist möglich, Ihrem API-Endpunkt einen benutzerdefinierten Berechtigungsrückruf hinzuzufügen, für den eine Authentifizierung erforderlich ist, um den Inhalt anzuzeigen. Nicht autorisierte Benutzer erhalten eine Fehlerantwort"code": "rest_forbidden"

Am einfachsten ist es, den WP_REST_Posts_Controller zu erweitern. Hier ist ein sehr einfaches Beispiel dafür:

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

}

Sie werden feststellen, dass der Berechtigungsrückruf function get_items_permissions_checkverwendet wird current_user_can, um zu bestimmen, ob der Zugriff zugelassen werden soll. Je nachdem, wie Sie die API verwenden, müssen Sie möglicherweise mehr über die Clientauthentifizierung erfahren.

Anschließend können Sie Ihren benutzerdefinierten Beitragstyp bei der REST-API-Unterstützung registrieren, indem Sie die folgenden Argumente in hinzufügen 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 );
}

Sie werden sehen , rest_controller_classAnwendungen My_Private_Posts_Controlleranstelle der Standard - Controller.

Ich fand es schwierig, gute Beispiele und Erklärungen für die Verwendung der REST-API außerhalb der Dokumentation zu finden . Ich fand diese großartige Erklärung für die Erweiterung des Standard-Controllers und hier ist eine sehr gründliche Anleitung zum Hinzufügen von Endpunkten .

Dalton
quelle
1

Ich habe Folgendes verwendet, um zu verhindern, dass alle nicht angemeldeten Benutzer die REST-API verwenden:

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);
    }
}
squarecandy
quelle
Da die Verwendung des Restendpunkts zunehmen wird, wird diese Art von Strategie problematisch. Am Ende wird der Endpunkt wp-json den Endpunkt admin-ajax ersetzen, was bedeutet, dass es auch alle Arten von legitimen Front-End-Anforderungen gibt. Jedenfalls ist es besser, mit einer 403 zu sterben, als mit etwas, das als Inhalt interpretiert werden könnte.
Mark Kaplun
@ MarkKaplun - ja, das ist richtig. Ich verwende dies im Zusammenhang mit einer Website, die im Wesentlichen überhaupt keine öffentlichen Daten bietet, und die Daten, die wir speichern, einschließlich Benutzer, Benutzer-Meta, benutzerdefinierte Post-Typ-Daten usw., sind geschützte Daten, auf die die Öffentlichkeit niemals zugreifen sollte . Es ist nicht einfach, wenn Sie eine Menge Arbeit in der klassischen WP-Vorlagenstruktur erledigen, um sicherzustellen, dass bestimmte Daten privat sind, und dann plötzlich zu erkennen, dass über die REST-API öffentlich auf alle zugegriffen werden kann. Wie auch immer, ein guter Punkt, um einen 403 zu
servieren
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 );
Dipen Patel
quelle
Könnten Sie im Text erläutern, warum und wie dies die Fragen des OP beantwortet?
kero
Dies ist nicht op Antwort und ich habe Code nur , wie man Arbeit zu zeigen , in praktisch und ich habe versucht, Sie leicht in programmatisch zu verstehen, wenn man es versteht
DIPEN Patel