Verify nonce in REST API?

时间:2018-12-22 作者:Lucas Bustamante

我想了解RESTAPI中关于nonce验证的最佳实践。

我看到很多人在谈论wp_rest REST请求的nonce。但在查看WordPress核心代码时,我发现wp_rest 只是验证登录用户状态的一个nonce,如果它不存在,则只作为guest运行请求。

也就是说,我应该在向RESTAPI发送POST请求时提交两个nonce吗?一个用于身份验证wp_rest 还有一个动作foo_action?

如果是,我应该如何发送wp_restfoo_action JavaScript中的nonce,以及PHP中验证这些nonce的正确位置是什么?(我的意思是验证arg?permission\\u回调的validate\\u回调?)

2 个回复
SO网友:Lucas Bustamante

你应该通过特别考试wp_rest nonce作为请求的一部分。没有它global $current_user 对象在REST类中不可用。您可以通过多种方式传递,从$\\u GET到$\\u POST to headers。

操作nonce是可选的。如果添加了REST端点,则不能从外部服务器使用REST端点,只能从WordPress本身内部调度的请求使用REST端点。用户可以使用Basic Auth, OAuth2, 或JWT 即使没有wp_rest nonce,但如果同时添加操作nonce,它将无法工作。

因此,操作nonce是可选的。如果希望端点仅在本地工作,请添加它。

示例:

/**
* First step, registering, localizing and enqueueing the JavaScript
*/
wp_register_script( \'main-js\', get_template_directory_uri() . \'/js/main.js\', [ \'jquery\' ] );
wp_localize_script( \'main-js\', \'data\', [
    \'rest\' => [
        \'endpoints\' => [
            \'my_endpoint\'       => esc_url_raw( rest_url( \'my_plugin/v1/my_endpoint\' ) ),
        ],
        \'timeout\'   => (int) apply_filters( "my_plugin_rest_timeout", 60 ),
        \'nonce\'     => wp_create_nonce( \'wp_rest\' ),
        //\'action_nonce\'     => wp_create_nonce( \'action_nonce\' ), 
    ],
] );
wp_enqueue_script( \'main-js\' );

/**
* Second step, the request on the JavaScript file
*/
jQuery(document).on(\'click\', \'#some_element\', function () {
    let ajax_data = {
        \'some_value\': jQuery( ".some_value" ).val(),
        //\'action_nonce\': data.rest.action_nonce
    };

    jQuery.ajax({
        url: data.rest.endpoints.my_endpoint,
        method: "GET",
        dataType: "json",
        timeout: data.rest.timeout,
        data: ajax_data,
        beforeSend: function (xhr) {
            xhr.setRequestHeader(\'X-WP-Nonce\', data.rest.nonce);
        }
    }).done(function (results) {
        console.log(results);
        alert("Success!");
    }).fail(function (xhr) {
        console.log(results);
        alert("Error!");
    });
});

/**
* Third step, the REST endpoint itself
*/
class My_Endpoint {
        public function registerRoutes() {
        register_rest_route( \'my_plugin\', \'v1/my_endpoint\', [
            \'methods\'             => WP_REST_Server::READABLE,
            \'callback\'            => [ $this, \'get_something\' ],
            \'args\'                => [
                \'some_value\'     => [
                    \'required\' => true,
                ],
            ],
            \'permission_callback\' => function ( WP_REST_Request $request ) {
                return true;
            },
        ] );
    }

    /**
    *   @return WP_REST_Response
    */
    private function get_something( WP_REST_Request $request ) {
        //if ( ! wp_verify_nonce( $request[\'nonce\'], \'action_nonce\' ) ) {
        //  return false;
        //}

        $some_value        = $request[\'some_value\'];

        if ( strlen( $some_value ) < 5 ) {
            return new WP_REST_Response( \'Sorry, Some Value must be at least 5 characters long.\', 400 );
        }

        // Since we are passing the "X-WP-Nonce" header, this will work:
        $user = wp_get_current_user();

        if ( $user instanceof WP_User ) {
            return new WP_REST_Response( \'Sorry, could not get the name.\', 400 );
        } else {
            return new WP_REST_Response( \'Your username name is: \' . $user->display_name, 200 );
        }
    }
}

SO网友:Danny Watts

在卢卡斯·布斯塔曼特(LucasBustamante)所写内容的基础上(这对我帮助很大!),在自定义管线中设置X-WP-Nonce标头后,可以执行以下操作:

    register_rest_route(\'v1\', \'/my_post\', [
        \'methods\' => WP_REST_Server::CREATABLE,
        \'callback\' => [$this, \'create_post\'],
        \'args\' => [
            \'post_title\' => [
                \'required\' => true,
            ],
            \'post_excerpt\' => [
                \'required\' => true,
            ]
        ],
        \'permission_callback\' => function ( ) {
            return current_user_can( \'publish_posts\' );
        },
    ]);
请注意permission_callback 位于根级别,不在args下(documented here) 我已经删除了额外的nonce 检查发件人args 因为如果nonce无效或未提供,单独检查权限将失败(我已经对此进行了广泛的测试,可以确认在未提供nonce或nonce无效时出现错误)。

相关推荐

Handling expired nonces

我有一个有几个按钮的页面。当人们单击按钮时,会发出AJAX请求并更新页面的各个部分。为了安全起见,AJAX请求使用nonce。我注意到,如果有人打开页面的时间超过了nonce过期所需的时间,那么AJAX请求将按预期停止工作。然而,我想要一个更健壮的行为。我想做的是-检测nonce何时过期/失败,然后在该点以某种方式生成一个新的nonce。这是一个由两部分组成的问题:如何检测nonce已过期</在过期的时候,最好的做法是整页刷新还是其他什么?我对安全考虑不太在行。在页面刷新之后,我想继续保持当前页面状