ซ่อนจุดสิ้นสุด WordPress REST API v2 จากการดูสาธารณะ


15

ฉันต้องการเริ่มใช้WordPress REST API v2เพื่อค้นหาข้อมูลจากเว็บไซต์ของฉัน ฉันสังเกตเห็นว่าเมื่อฉันเยี่ยมชม URL ปลายทางโดยตรงฉันสามารถดูข้อมูลทั้งหมดแบบสาธารณะ ฉันเคยเห็นด้วยเช่นกันว่าบทเรียนจำนวนมากพูดถึงการใช้เซิร์ฟเวอร์ทดสอบหรือเซิร์ฟเวอร์ในพื้นที่มากกว่าในเว็บไซต์

คำถามของฉันคือ:

  • สิ่งนี้มีไว้เพื่อใช้กับไซต์ที่ผลิตหรือไม่
  • มีความเสี่ยงด้านความปลอดภัยหรือไม่ที่จะอนุญาตให้ทุกคนสามารถดูจุดปลายได้เช่น /wp-json/wp/v2/users/ที่แสดงให้ผู้ใช้ทุกคนลงทะเบียนกับเว็บไซต์หรือไม่
  • เป็นไปได้หรือไม่ที่จะอนุญาตให้ผู้ใช้ที่ได้รับอนุญาตเท่านั้นเข้าถึงจุดปลายได้

ฉันต้องการตรวจสอบให้แน่ใจว่าฉันได้ปฏิบัติตามแนวทางปฏิบัติที่ดีที่สุดเกี่ยวกับความปลอดภัยดังนั้นเคล็ดลับใด ๆ จะเป็นประโยชน์ เอกสาร APIพูดถึงการตรวจสอบ แต่ผมไม่แน่ใจว่าวิธีการป้องกัน URL จากการเข้าถึงโดยตรง คนอื่น ๆ มักจะตั้งค่าข้อมูลนี้ให้เข้าถึงได้โดยแอปพลิเคชันภายนอกโดยไม่เปิดเผยข้อมูลมากเกินไปหรือไม่


1
คำถามที่แท้จริงคือคุณใช้ฝั่งไคลเอ็นต์ปลายทาง(เช่นในการโทร AJAX) หรือฝั่งเซิร์ฟเวอร์ (อาจมาจากแอปพลิเคชันอื่น)
TheDeadMedic

1
หมายเหตุ: ปลั๊กอิน WordFence รุ่นล่าสุดมีตัวเลือกในการ "ป้องกันการค้นพบชื่อผู้ใช้ผ่านการสแกน '/? ผู้เขียน = N', oEmbed API และ WordPress REST API"
squarecandy

คำตอบ:


18

สิ่งนี้มีไว้เพื่อใช้กับไซต์ที่ผลิตหรือไม่

ใช่. เว็บไซต์หลายแห่งได้มีการใช้มัน

มีความเสี่ยงด้านความปลอดภัยหรือไม่ที่จะอนุญาตให้บุคคลทั่วไปสามารถดูปลายทางเช่น / wp-json / wp / v2 / users / ซึ่งแสดงผู้ใช้ทั้งหมดที่ลงทะเบียนกับเว็บไซต์

ไม่คำตอบของเซิร์ฟเวอร์ไม่เกี่ยวกับความปลอดภัยคุณสามารถทำอะไรกับหน้าจอว่าง / เข้าถึงแบบอ่านอย่างเดียว ไม่มีอะไร!

อย่างไรก็ตามหากเว็บไซต์ของคุณช่วยให้รหัสผ่านที่อ่อนแอมีกำลังปัญหาบางอย่าง แต่เป็นนโยบายของไซต์ของคุณ REST API ไม่รู้อะไรเลย

เป็นไปได้หรือไม่ที่จะอนุญาตให้ผู้ใช้ที่ได้รับอนุญาตเท่านั้นเข้าถึงจุดปลายได้

ใช่. คุณสามารถทำมันโดยใช้การเรียกกลับได้รับอนุญาต

ตัวอย่างเช่น:

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

คนอื่น ๆ มักจะตั้งค่าข้อมูลนี้ให้เข้าถึงได้โดยแอปพลิเคชันภายนอกโดยไม่เปิดเผยข้อมูลมากเกินไปหรือไม่

คำถามนี้ตอบยากเพราะเราไม่รู้ว่าข้อมูลอะไรมากเกินไป / เมื่อใด แต่เรากำลังทั้งหมดที่ใช้อ้างอิงและcheatsheets


1
สิ่งสำคัญที่ควรทราบ: "การเปิดเผยจะ จำกัด เฉพาะผู้ใช้ที่มีประเภทโพสต์ที่สร้างขึ้นซึ่งจะถูกเปิดเผยผ่าน REST API" - /wp-json/wp/v2/users/ดังนั้นถ้าคุณได้บอกว่าเป็นร้านค้าออนไลน์ที่ลูกค้าทุกคนมีผู้ใช้ผู้ใช้เหล่านี้ไม่ได้สัมผัสผ่านทาง (อ้างอิงwordpress.stackexchange.com/q/252328/41488ความคิดเห็น @JHoffmann)
squarecandy

ควรสังเกตว่าคุณต้องมีส่วนที่ไม่ใช่ RCE ที่ใช้ wp_create_nonce ('wp_rest') ในส่วนหัว 'X-WP-Nonce' หรือไม่มีสิ่งใดที่จะทำงานได้เลยและจะคืนค่า 403 เสมอ
Andrew Killen

5

เป็นไปได้หรือไม่ที่จะอนุญาตให้ผู้ใช้ที่ได้รับอนุญาตเท่านั้นเข้าถึงจุดปลายได้

เป็นไปได้ที่จะเพิ่มการอนุญาตให้โทรกลับที่กำหนดเองไปยังปลายทาง API ของคุณซึ่งต้องมีการตรวจสอบเพื่อดูเนื้อหา ผู้ใช้ที่ไม่ได้รับอนุญาตจะได้รับการตอบกลับข้อผิดพลาด"code": "rest_forbidden"

วิธีที่ง่ายที่สุดในการทำเช่นนี้คือการขยาย WP_REST_Posts_Controller นี่คือตัวอย่างง่ายๆของการที่:

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

}

คุณจะสังเกตเห็นว่าการโทรกลับการอนุญาตfunction get_items_permissions_checkใช้current_user_canเพื่อพิจารณาว่าจะอนุญาตการเข้าถึงหรือไม่ คุณอาจต้องเรียนรู้เพิ่มเติมเกี่ยวกับการรับรองความถูกต้องของไคลเอ็นต์ทั้งนี้ขึ้นอยู่กับวิธีที่คุณใช้ API

จากนั้นคุณสามารถลงทะเบียนประเภทโพสต์ที่กำหนดเองของคุณด้วยการสนับสนุน REST API โดยการเพิ่มอาร์กิวเมนต์ต่อไปนี้ 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 );
}

คุณจะเห็นการrest_controller_classใช้งานMy_Private_Posts_Controllerแทนที่จะเป็นตัวควบคุมเริ่มต้น

ฉันได้รับพบว่ามันยากที่จะหาตัวอย่างที่ดีและคำอธิบายสำหรับการใช้ REST API นอกเอกสาร ฉันไม่พบที่ยิ่งใหญ่นี้คำอธิบายของการขยายตัวควบคุมเริ่มต้นและที่นี่เป็นคำแนะนำอย่างละเอียดมากที่จะเพิ่มปลายทาง


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('sorry you are not allowed to access this data','cheatin eh?',403);
    }
}

เนื่องจากการใช้จุดสิ้นสุดที่เหลือกำลังขยายตัวกลยุทธ์ประเภทนี้จะกลายเป็นปัญหาได้ ในตอนท้ายจุดสิ้นสุดของ wp-json จะแทนที่ admin-ajax หนึ่งซึ่งหมายความว่าจะมีคำขอส่วนหน้าที่ถูกต้องตามกฎหมายทุกชนิด อย่างไรก็ตามดีกว่าที่จะตายด้วย 403 กว่าสิ่งที่อาจตีความได้ว่าเป็นเนื้อหา
Mark Kaplun

@ MarkKaplun - ใช่คุณพูดถูกแล้ว ฉันใช้สิ่งนี้ในบริบทของเว็บไซต์ที่ไม่มีข้อมูลสาธารณะเป็นหลักและข้อมูลที่เราจัดเก็บรวมถึงผู้ใช้เมตาของผู้ใช้ข้อมูลประเภทโพสต์ที่กำหนดเอง ฯลฯ เป็นข้อมูลที่เป็นกรรมสิทธิ์ที่ไม่ควรเปิดเผยต่อสาธารณะ . มันแย่มากเมื่อคุณทำงานหลายอย่างภายในโครงสร้างเทมเพลต WP คลาสสิกเพื่อให้แน่ใจว่าข้อมูลบางอย่างเป็นส่วนตัวและจากนั้นก็รู้ทันทีว่าทุกคนสามารถเข้าถึงได้แบบสาธารณะผ่าน REST API ยังไงก็ตามจุดที่ดีเกี่ยวกับการให้บริการ 403 ...
squarecandy

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

1
คุณช่วยอธิบายเนื้อหาในข้อความได้อย่างไรและตอบคำถามของ OP อย่างไร
kero

นี่ไม่ใช่คำตอบของ op และฉันให้รหัสเพียงเพื่อแสดงวิธีการทำงานในทางปฏิบัติและฉันได้พยายามให้คุณเข้าใจได้ง่ายในเชิงโปรแกรมถ้าคุณเข้าใจ
dipen patel
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.