Руководство разработчика плагинов для WordPress (часть 14) 10 Марта 2018

Руководство разработчика плагинов для WordPress (часть 14)

Защищенные данные (nonces)

Nonces являются сгенерированными числами, используемыми для верификации источника и запросов с целью обеспечения безопасности. Каждый nonce может быть использован только один раз. Если ваш плагин позволяет пользователям отправлять данные, будь то из консоли Администратора, либо из общедоступных страниц, вы должны убедиться в том, что данный пользователь является тем, за кого он себя выдает, и что он имеет необоходимые полномочия для выполнения данного действия. Выполнение сразу двух этих условий означает, что данные только лишь меняются, когда пользователь ожидает их изменения.

Использование nonces

Следуя нашему примеру проверки возможностей пользователя, следующим шагом проверки безопасности при отправке пользователем данных является использование защищенных данных (nonces). Данная проверка удостоверяет, что только те пользователи, которые имеют права на удаление записей, способны удалять какие-либо записи. Но что, если кто-то решит подшутить над вами, нажав данную ссылку? У вас есть такая необходимая возможность, так что вы можете невольно удалить запись. Nonces могут быть использованы для проверки того, что текущий пользователь действительно собирается выполнить данное действие. Когда вы генерируете ссылку для удаления, вам захочется использовать функцию wp_create_nonce() для добавления nonce к данной ссылке, переданный в функцию аргумент удостоверяет, что был создан уникальный nonce для данного конкретного действия. Затем, когда вы выполняете запрос на удаление ссылки, вы можете проверить, что это тот nonce, который вы ожидаете увидеть. Для получения дальнейшей информации: пост Mark Jaquith о WordPress nonces.

Полный пример

Полный пример использования проверок возможностей, валидации данных, безопасного ввода, безопасного вывода и nonces:
 <?php
             /**
             * generate a Delete link based on the homepage url
             */
             function wporg_generate_delete_link($content)
             {
                // run only for single post page
                if (is_single() && in_the_loop() && is_main_query()) {
                    // add query arguments: action, post, nonce
                    $url = add_query_arg(
                        [
                            "action" => "wporg_frontend_delete",
                            "post"   => get_the_ID(),
                            "nonce"  => wp_create_nonce("wporg_frontend_delete"),
                        ],
                        home_url()
                    );
                    return $content . " <a href="" . esc_url($url) . "">" . esc_html__("Delete Post", "wporg") . "</a>";
                }
                return null;
             }

             /**
             * request handler
             */
             function wporg_delete_post()
             {
                if (
                    isset($_GET["action"]) &&
                    isset($_GET["nonce"]) &&
                    $_GET["action"] === "wporg_frontend_delete" &&
                    wp_verify_nonce($_GET["nonce"], "wporg_frontend_delete")
                ) {
                    // verify we have a post id
                    $post_id = (isset($_GET["post"])) ? ($_GET["post"]) : (null);

                    // verify there is a post with such a number
                    $post = get_post((int)$post_id);
                    if (empty($post)) {
                        return;
                    }

                    // delete the post
                    wp_trash_post($post_id);

                    // redirect to admin page
                    $redirect = admin_url("edit.php");
                    wp_safe_redirect($redirect);

                    // we are done
                    die;
                }
             }

             if (current_user_can("edit_others_posts")) {
                /**
                 * add the delete link to the end of the post content
                 */
                add_filter("the_content", "wporg_generate_delete_link");

                /**
                 * register our request handler with the init hook
                 */
                add_action("init", "wporg_delete_post");
             }