wp_create_nonce doesn't verify when using WP_List_Table

I've created an admin page where I'm displaying a list of a MySQL Table using WP_List_Table. In this table I need to be able to delete a record when I want to. And that's where I have a problem.

To delete a record I've set up the following code.

class Genres_List extends WP_List_Table {

    [...]

    public static function delete_genre( $id ) {
        global $wpdb;
        $wpdb-delete(
            "{$wpdb-prefix}genres",
            [ 'id' = $id ],
            [ '%d' ]
            );
    }

    function column_cb( $item ) {
        return sprintf('input type="checkbox" name="id[]" value="%s" /', $item['id']);
    }

    function column_name( $item ) {
        $delete_nonce = wp_create_nonce( 'sp_delete_genre' );
        $title = 'strong' . stripslashes($item['name']) . '/strong';
        $actions = [
            'edit' = sprintf( 'a href="?page=%saction=%sid=%s"Bewerken/a', esc_attr( $_REQUEST['page'] ), 'edit', absint( $item['id'] ) ),
            'delete' = sprintf( 'a href="?page=%saction=deleteid=%s_wpnonce=%s"Verwijderen/a', esc_attr( $_REQUEST['page'] ), absint( $item['id'] ), $delete_nonce )
            ];
        return 'a href="?page=genresaction=editid=' . $item['id'] . '"strong' . stripslashes($item['name']) . '/strong' . $this-row_actions( $actions );
    }

    function get_columns() {
        $columns = [
            'cb' = 'input type="checkbox" /',
            'name' = 'Naam'
            ];

        return $columns;
    }

    public function get_bulk_actions() {
        $actions = [
            'delete' = 'Verwijderen'
            ];

        return $actions;
    }

    public function process_bulk_action() {
        if ( $this-current_action() === 'delete' ) {
            if ( ! wp_verify_nonce( $_REQUEST['_wpnonce'], 'sp_delete_genre' ) ) {
                die('This is a secure website. Your nonce did not verify. Go get a coffee.');
            } else {
                self::delete_genre( absint( $_GET['id'] ) );
                wp_redirect( esc_url( add_query_arg() ) );
                exit;
            }

            if ( $_POST['action'] === 'delete' || $_POST['action2'] === 'delete') {
                $delete_ids = esc_sql( $_POST['id'] );

                foreach ( $delete_ids as $id ) { self::delete_genre( $id ); }

                wp_redirect( esc_url( add_query_arg() ) );
                exit;
            }
        }
    }
}

The problem is that wp_verify_nonce( $_REQUEST['_wpnonce'], 'sp_delete_genre' ) returns false when trying to delete a record. I can't seem to find what I'm doing wrong, since I'm exactly following the Wordpress Codex:

  1. Creating a nonce using wp_create_nonce( 'sp_delete_genre' ).
  2. Using the nonce in a _wpnonce= parameter.
  3. Verifying the nonce using wp_verify_nonce( $_REQUEST['_wpnonce'], 'sp_delete_genre' )

Topic nonce wp-list-table Wordpress

Category Web


This question still unanswered for long time. I answer it if anyone reaches here, just in case.

When you are using WP_List_Table (perhaps by extending the class), the generated code for single delete and bulk delete differs. The single delete is sent over a GET request by setting query params, but the bulk delete is sent over a POST request by setting form-data. (You can investigate it by checking the network tab of developer tools.)

So, generated nonce also differs in these situations, and IMO it is a bad design by Wordpress that both requests call process_bulk_action method. BTW you must check the conditions, like:

if (is_array($_REQUEST['id'])) {
    $true = wp_verify_nonce($_REQUEST['_wpnonce'], 'bulk-' . $this->_args['plural']);
} else if (is_string($_REQUEST['id'])) {
    $true = wp_verify_nonce($_REQUEST['_wpnonce'], 'sp_delete_genre');
}

You could also check the request method, but I guess $_REQUEST['id'] is more reliable.

P.S. Wordpress official website notices that extending WP_List_Table is not safe enough!

Note: This class’s access is marked as private. That means it is not intended for use by plugin and theme developers as it is subject to change without warning in any future WordPress release. If you would still like to make use of the class, you should make a copy to use and distribute with your own project, or else use it at your own risk.

P.S. Two resources that I used and I'd like to mention.
https://gist.github.com/petenelson/8981536
https://stackoverflow.com/questions/25000403/verifying-nonce-from-wp-list-table

About

Geeks Mental is a community that publishes articles and tutorials about Web, Android, Data Science, new techniques and Linux security.