Menyembunyikan titik akhir WordPress REST API v2 dari tampilan publik

14

Saya ingin mulai menggunakan WordPress REST API v2 untuk menanyakan informasi dari situs saya. Saya perhatikan bahwa ketika saya mengunjungi URL titik akhir secara langsung, saya dapat melihat semua data secara publik. Saya juga melihat bahwa banyak tutorial menyebutkan penggunaan server uji atau lokal daripada situs langsung.

Pertanyaan saya adalah:

  • Apakah ini dimaksudkan untuk digunakan di lokasi produksi?
  • Apakah ada risiko keamanan untuk mengizinkan titik akhir dilihat oleh siapa pun, seperti /wp-json/wp/v2/users/yang menunjukkan semua pengguna yang terdaftar di situs?
  • Apakah mungkin untuk mengizinkan hanya pengguna yang berwenang untuk mengakses titik akhir?

Saya ingin memastikan bahwa saya mengikuti praktik terbaik terkait keamanan, jadi tip apa pun akan membantu. The docs api menyebutkan otentikasi, tapi aku tidak yakin bagaimana untuk mencegah URL dari yang diakses secara langsung. Bagaimana orang lain biasanya mengatur data ini untuk diakses oleh aplikasi eksternal tanpa memaparkan terlalu banyak informasi?

Morgan
sumber
1
Pertanyaan sebenarnya adalah, apakah Anda menggunakan sisi klien titik akhir (yaitu dalam panggilan AJAX), atau sisi server (mungkin dari aplikasi lain)?
TheDeadMedic
1
Catatan: Versi terbaru dari plugin WordFence memiliki opsi untuk "Mencegah penemuan nama pengguna melalui pemindaian '/? Penulis = N, API oEmbed, dan API REST WordPress"
squarecandy

Jawaban:

18

Apakah ini dimaksudkan untuk digunakan di lokasi produksi?

Iya. Banyak situs sudah menggunakannya .

Apakah ada risiko keamanan untuk mengizinkan titik akhir dilihat oleh siapa pun, seperti / wp-json / wp / v2 / pengguna / yang menunjukkan semua pengguna yang terdaftar di situs?

Tidak. Respons server tidak ada hubungannya dengan keamanan, apa yang dapat Anda lakukan dengan layar kosong / akses hanya baca? Tidak ada!

Namun, Jika situs Anda mengizinkan kata sandi yang lemah, ada beberapa masalah . Tapi itu kebijakan situs Anda, REST API tidak tahu apa-apa tentang itu.

Apakah mungkin untuk mengizinkan hanya pengguna yang berwenang untuk mengakses titik akhir?

Iya. Anda dapat melakukannya dengan menggunakan izin panggilan balik .

Sebagai contoh:

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

Bagaimana orang lain biasanya mengatur data ini untuk diakses oleh aplikasi eksternal tanpa memaparkan terlalu banyak informasi?

Pertanyaan ini sulit dijawab karena kita tidak tahu apa / kapan terlalu banyak informasi . Tapi kita semua menggunakan referensi dan cheatsheet .

MinhTri
sumber
1
Penting untuk diperhatikan: "Eksposur menjadi terbatas untuk pengguna yang telah menulis jenis posting yang diatur untuk diekspos melalui REST API." - jadi jika Anda mengatakan, toko online tempat setiap pelanggan memiliki pengguna, pengguna ini tidak akan diekspos melalui /wp-json/wp/v2/users/. (Referensi wordpress.stackexchange.com/q/252328/41488 @JHoffmann komentar)
squarecandy
Perlu dicatat bahwa Anda perlu memiliki nonce berbasis REST wp_create_nonce ('wp_rest') di header 'X-WP-Nonce', atau tidak satu pun dari hal ini akan bekerja sama sekali, dan akan selalu mengembalikan 403.
Andrew Killen
5

Apakah mungkin untuk mengizinkan hanya pengguna yang berwenang untuk mengakses titik akhir?

Dimungkinkan untuk menambahkan panggilan balik izin khusus ke titik akhir API Anda yang memerlukan otentikasi untuk melihat konten. Pengguna yang tidak sah akan menerima respons kesalahan"code": "rest_forbidden"

Cara paling sederhana untuk melakukan ini adalah memperluas WP_REST_Posts_Controller. Berikut ini contoh yang sangat sederhana:

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

}

Anda akan melihat bahwa panggilan balik izin function get_items_permissions_checkdigunakan current_user_canuntuk menentukan apakah akan mengizinkan akses. Bergantung pada bagaimana Anda menggunakan API, Anda mungkin perlu mempelajari lebih lanjut tentang otentikasi klien.

Anda kemudian dapat mendaftarkan jenis pos kustom Anda dengan dukungan REST API dengan menambahkan argumen berikut di 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 );
}

Anda akan melihat rest_controller_classpenggunaan My_Private_Posts_Controlleralih-alih pengontrol default.

Saya kesulitan menemukan contoh dan penjelasan yang baik untuk menggunakan REST API di luar dokumentasi . Saya memang menemukan penjelasan yang bagus untuk memperluas controller default , dan inilah panduan yang sangat menyeluruh untuk menambahkan titik akhir .

Dalton
sumber
2

Inilah yang saya gunakan untuk memblokir semua pengguna yang tidak masuk menggunakan REST API sama sekali:

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
sumber
Karena penggunaan titik akhir sisanya akan berkembang, strategi semacam ini akan menjadi masalah. Pada akhirnya titik akhir wp-json akan menggantikan satu admin-ajax, yang berarti bahwa juga akan ada semua jenis permintaan front end yang sah. Lagi pula, lebih baik mati dengan 403 daripada sesuatu yang mungkin ditafsirkan sebagai konten.
Mark Kaplun
@MarkKaplun - ya, Anda benar tentang itu. Saya menggunakan ini dalam konteks situs yang pada dasarnya tidak menawarkan data publik sama sekali dan data yang kami simpan termasuk pengguna, meta pengguna, data jenis pos khusus, dll. Adalah data eksklusif yang tidak boleh diakses oleh publik. . Ini menyebalkan ketika Anda melakukan banyak pekerjaan dalam struktur template WP klasik untuk memastikan data tertentu bersifat pribadi dan kemudian tiba-tiba menyadari bahwa semuanya dapat diakses secara publik melalui REST API.
Ngomong
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
sumber
1
Bisakah Anda menguraikan dalam teks mengapa dan bagaimana ini menjawab pertanyaan OP?
Kero
Ini bukan jawaban op dan saya hanya memberikan kode untuk menunjukkan cara bekerja secara praktis dan saya telah mencoba untuk Anda agar mudah dipahami secara terprogram Jika Anda memahaminya
dipen patel