Ascunderea endpoint-urilor WordPress REST API v2 de la vizualizarea publică

2 iun. 2016, 18:39:44
Vizualizări: 36.5K
Voturi: 24

Aș dori să încep să folosesc WordPress REST API v2 pentru a interoga informații de pe site-ul meu. Am observat că atunci când accesez direct URL-ul unui endpoint, pot vedea toate datele în mod public. De asemenea, am observat că multe tutoriale menționează utilizarea serverelor de test sau locale în loc de site-uri live.

Întrebările mele sunt:

  • Este destinat utilizării pe site-urile în producție?
  • Există vreun risc de securitate în a permite endpoint-urilor să fie vizualizate de oricine, cum ar fi /wp-json/wp/v2/users/ care arată toți utilizatorii înregistrați pe site?
  • Este posibil să permitem accesul la un endpoint doar utilizatorilor autorizați?

Vreau să mă asigur că urmez cele mai bune practici în ceea ce privește securitatea, așa că orice sfaturi ar fi de ajutor. Documentația API menționează autentificarea, dar nu sunt sigur cum să împiedic accesarea directă a URL-ului. Cum configurează de obicei alții acest tip de date pentru a fi accesate de aplicații externe fără a expune prea multe informații?

2
Comentarii

Întrebarea reală este, folosești endpoint-urile pe partea de client (adică în apelurile AJAX) sau pe partea de server (poate de la o altă aplicație)?

TheDeadMedic TheDeadMedic
2 iun. 2016 21:32:50

Notă: Cea mai recentă versiune a plugin-ului WordFence are o opțiune pentru a „Preveni descoperirea numelor de utilizator prin scanări '/?author=N', API-ul oEmbed și API-ul REST WordPress”

squarecandy squarecandy
11 ian. 2018 05:37:42
Toate răspunsurile la întrebare 5
3
22

Este destinat să fie folosit pe site-uri în producție?

Da. Multe site-uri îl folosesc deja.

Există un risc de securitate prin permiterea afișării endpoint-urilor de către oricine, cum ar fi /wp-json/wp/v2/users/ care afișează toți utilizatorii înregistrați pe site?

Nu. Răspunsurile serverului nu au nicio legătură cu securitatea, nu poți face nimic împotriva unui ecran gol sau a unui răspuns doar în citire.

Totuși, dacă site-urile dumneavoastră permit parole slabe, există unele probleme. Dar aceasta este politica site-ului dumneavoastră, REST API nu știe nimic despre acest lucru.

Este posibil să permitem accesul la un endpoint doar pentru utilizatorii autorizați?

Da. Puteți face acest lucru folosind callback-ul de permisiune.

De exemplu:

if ( 'edit' === $request['context'] && ! current_user_can( 'list_users' ) ) {
    return new WP_Error( 'rest_forbidden_context', __( 'Ne pare rău, nu puteți vizualiza această resursă cu contextul de editare.' ), array( 'status' => rest_authorization_required_code() ) );
}

Cum configurează de obicei alții aceste date pentru a fi accesate de aplicații externe fără a expune prea multe informații?

Această întrebare este dificil de răspuns pentru că nu știm ce/când este prea multe informații. Dar putem urma strict referințele API și ghidurile de securitate pentru a evita situații nedorite.

3 iun. 2016 08:08:10
Comentarii

Important de știut: "Accesul este limitat la utilizatorii care au creat tipuri de postări care sunt setate să fie expuse prin REST API." - deci dacă aveți, de exemplu, un magazin online unde fiecare client are un utilizator, acești utilizatori nu sunt expuși prin /wp-json/wp/v2/users/. (Referință https://wordpress.stackexchange.com/q/252328/41488 @JHoffmann comentariu)

squarecandy squarecandy
10 nov. 2017 18:48:18

Trebuie menționat că trebuie să aveți un nonce bazat pe REST wp_create_nonce('wp_rest') în antetul 'X-WP-Nonce', altfel nimic din aceste lucruri nu va funcționa și va returna mereu un 403.

Andrew Killen Andrew Killen
12 dec. 2018 11:17:10

Exact. Tocmai am primit un raport Open Bug Bounty despre aceasta pe site-ul meu WP pe versiunea 5.x. Chiar și script kiddies (openbugbounty.org/researchers/Cyber_World) înțeleg greșit CVE-ul pentru aceasta (cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2017-5487). Această problemă a fost rezolvată pentru cei care sunt îngrijorați. Nu copiați și lipiți toate acestea fără a înțelege ce folosește cu adevărat și dacă aveți nevoie de ele.

Tony-Caffe Tony-Caffe
19 nov. 2021 22:00:16
1

Este posibil să permiti accesul la un endpoint doar pentru utilizatorii autorizați?

Este posibil să adaugi un callback personalizat de permisiuni la endpoint-ul tău API care necesită autentificare pentru a vizualiza conținutul. Utilizatorii neautorizați vor primi un răspuns de eroare "code": "rest_forbidden"

Cea mai simplă metodă este să extinzi WP_REST_Posts_Controller. Iată un exemplu foarte simplu:

class My_Private_Posts_Controller extends WP_REST_Posts_Controller {

   /**
   * Namespace-ul.
   *
   * @var string
   */
   protected $namespace;

   /**
   * Tipul de postare pentru obiectul curent.
   *
   * @var string
   */
   protected $post_type;

   /**
   * Baza REST pentru obiectul curent.
   *
   * @var string
   */
   protected $rest_base;

  /**
   * Înregistrează rutele pentru obiectele controlerului.
   * Aproape identic cu WP_REST_Posts_Controller::register_routes(), dar cu un
   * callback de permisiuni personalizat.
   */
  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' ),
    ) );     
  }

  /**
   * Verifică dacă o cerere dată are acces pentru a obține articole
   *
   * @param WP_REST_Request $request Date complete despre cerere.
   * @return WP_Error|bool
   */
  public function get_items_permissions_check( $request ) {
    return current_user_can( 'edit_posts' );
  }

}

Veți observa că callback-ul de permisiuni function get_items_permissions_check folosește current_user_can pentru a determina dacă să permită accesul. În funcție de cum folosiți API-ul, poate fi necesar să aflați mai multe despre autentificarea clientului.

Apoi puteți înregistra tipul personalizat de postare cu suport pentru REST API adăugând următoarele argumente în register_post_type

  /**
   * Înregistrează un tip de postare 'book', cu suport pentru REST API
   *
   * Bazat pe exemplul de la: http://codex.wordpress.org/Function_Reference/register_post_type
   */
  add_action( 'init', 'my_book_cpt' );
  function my_book_cpt() {
    $labels = array(
        'name'               => _x( 'Cărți', 'nume general tip postare', 'your-plugin-textdomain' ),
        'singular_name'      => _x( 'Carte', 'nume singular tip postare', 'your-plugin-textdomain' ),
        'menu_name'          => _x( 'Cărți', 'meniu admin', 'your-plugin-textdomain' ),
        'name_admin_bar'     => _x( 'Carte', 'adaugă nou în bara admin', 'your-plugin-textdomain' ),
        'add_new'            => _x( 'Adaugă nou', 'carte', 'your-plugin-textdomain' ),
        'add_new_item'       => __( 'Adaugă carte nouă', 'your-plugin-textdomain' ),
        'new_item'           => __( 'Carte nouă', 'your-plugin-textdomain' ),
        'edit_item'          => __( 'Editează carte', 'your-plugin-textdomain' ),
        'view_item'          => __( 'Vezi carte', 'your-plugin-textdomain' ),
        'all_items'          => __( 'Toate cărțile', 'your-plugin-textdomain' ),
        'search_items'       => __( 'Caută cărți', 'your-plugin-textdomain' ),
        'parent_item_colon'  => __( 'Cărți părinte:', 'your-plugin-textdomain' ),
        'not_found'          => __( 'Nicio carte găsită.', 'your-plugin-textdomain' ),
        'not_found_in_trash' => __( 'Nicio carte găsită în Coșul de gunoi.', 'your-plugin-textdomain' )
    );

    $args = array(
        'labels'             => $labels,
        'description'        => __( 'Descriere.', '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 );
}

Veți observa că rest_controller_class folosește My_Private_Posts_Controller în loc de controlerul implicit.

Am avut dificultăți în a găsi exemple bune și explicații pentru utilizarea REST API în afara documentației. Am găsit însă această explicație excelentă despre extinderea controlerului implicit și iată un ghid foarte detaliat pentru adăugarea de endpoint-uri.

19 iul. 2016 20:49:33
Comentarii

Mulțumesc, a funcționat bine pentru nevoile mele - deși nu uitați să includeți un namespace în clasa controller dacă folosiți namespaces - deoarece nu apare nicio eroare dacă clasa controller nu este găsită (da, eu am uitat!)

Alan Fuller Alan Fuller
4 iun. 2020 17:06:01
2

Iată ce am folosit pentru a bloca toți utilizatorii nelogati să folosească API-ul 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('Ne pare rău, nu aveți permisiunea de a accesa aceste date','Încercați să trișați?',403);
    }
}
1 dec. 2017 04:48:27
Comentarii

Pe măsură ce utilizarea punctului final REST va crește, acest tip de strategie va deveni problematic. În final, punctul final wp-json va înlocui cel admin-ajax, ceea ce înseamnă că vor exista și tot felul de cereri legitime din partea front-end-ului. Oricum, mai bine să răspundem cu un 403 decât cu ceva care ar putea fi interpretat drept conținut.

Mark Kaplun Mark Kaplun
1 dec. 2017 07:22:41

@MarkKaplun - da, ai dreptate în privința asta. Eu folosesc acest lucru în contextul unui site care practic nu oferă date publice deloc, iar datele pe care le stocăm, inclusiv utilizatori, meta date ale utilizatorilor, date de tip custom post type etc., sunt date proprietare care nu ar trebui să fie accesate niciodată de public. E frustrant când depui o grămadă de efort în structura clasică de template WordPress pentru a te asigura că anumite date sunt private și apoi realizezi brusc că toate sunt accesibile public prin API-ul REST. Oricum, bun punct în legătură cu returnarea unui 403...

squarecandy squarecandy
1 dec. 2017 16:29:53
1

Cea mai bună opțiune este să dezactivezi noul editor V5 și apoi să dezactivezi API-ul JSON, așa cum este explicat aici.

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

9 mai 2020 18:52:07
Comentarii

Răspunsurile care conțin doar un link vor deveni inutile odată cu expirarea linkurilor. Puteți [edita] răspunsul dvs. pentru a include esența răspunsului și apoi puteți adăuga linkul pentru mai multe detalii. :)

Mayeenul Islam Mayeenul Islam
10 mai 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('Ne pare rău, nu aveți permisiunea să accesați aceste date','Autentificare necesară',403);
} } 
function json_authenticate_handler( $user ) {

global $wp_json_basic_auth_error;

$wp_json_basic_auth_error = null;

// Nu autentifica de două ori
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 ian. 2018 13:56:56
Comentarii

Poți să elaborezi în text de ce și cum acest lucru răspunde la întrebările OP?

kero kero
8 ian. 2018 14:32:59

Acesta nu este răspunsul OP și am dat doar cod pentru a arăta cum se lucrează în practică și am încercat să te ajut să înțelegi mai ușor din punct de vedere programatic dacă l-ai înțeles

dipen patel dipen patel
9 ian. 2018 15:17:02