WordPress CSRF protection
The CSRF protection on WordPress is what allows us to create a secure form on a page, section, article, etc., and it is quite important. It’s been a long time since WordPress was used only for writing what we think or want to share with the reader.
Keep in mind that the user will need to perform at least one search, as in this site; this blog tends to publish things that I find interesting or that may help others, which is why it only has one search.
But if you are interested in creating forms without risking your WordPress being hacked or suffering from XSS attacks, you should use various WordPress functions in your plugin or theme.
These functions include wp_nonce_field
, wp_verify_nonce
, and check_admin_referer
.
wp_nonce_field
This function will return a hidden field for forms; it may or may not display it depending on the fourth parameter, whether it’s true
or false
. If this parameter is false
, we will need to display its result ourselves.
For example, if we use:
$form_nonce = wp_nonce_field('X_form', 'X_nonce', true, false);
We should display it using echo afterward.
Now, let’s look at its description.
Description / Descripciónwp_nonce_field( int|string $action = -1, string $name = '_wpnonce', bool $referer = true, bool $display = true ): string
Note that by default, the parameters will be as follows:
$action
: Its default value will be -1, indicating that there will be no action. In our case, we should give it a value, for example,"formA"
$name
: It will have the value"_wpnonce"
$referer
: It will have a value oftrue
.$display
: It will betrue
.
Therefore, if we use the wp_nonce_field
function with only the first parameter, it will automatically display on the screen. When we need to check the nonce, we can do so with $_POST['_wpnonce']
.
Let’s create a form: Suppose we need to create a newsletter form, for which we would require the user’s email and their preference for receiving our emails periodically, for example, daily, weekly, or monthly.
<html lang="es"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Formulario de Newsletter</title> <style> body { font-family: Arial, sans-serif; margin: 20px; } form { max-width: 400px; margin: auto; } label { display: block; margin-bottom: 8px; } input, select { width: 100%; padding: 10px; margin-bottom: 15px; box-sizing: border-box; } button { background-color: #4CAF50; color: white; padding: 10px 15px; border: none; border-radius: 4px; cursor: pointer; } button:hover { background-color: #45a049; } </style> </head> <body> <h2>Newsletter Form</h2> <p>Subscribe to our newsletter and choose the sending frequency:</p> <form action="procesar_formulario.php" method="post"> <label for="name">Name:</label> <input type="text" id="name" name="name" required> <label for="email">Correo electrónico:</label> <input type="email" id="email" name="email" required> <label for="frequency#34;>delivery Frequency:</label> <select id="frequency" name="frequency"> <option value="daily">Diaria</option> <option value="weekly">Semanal</option> <option value="monthly">Mensual</option> </select> <button type="submit" name="submit" value="Suscribirse" >Suscribirse</button> </form> </body> </html>
Now we should add our CSRF protection to our form. We can do this by adding the following below the
tag:
<?php echo wp_nonce_field('formA'); ?>
wp_verify_nonce
With this function, we will verify that a secure nonce has been used, which has a time limit.
Description / Descripciónwp_verify_nonce( string $nonce, string|int $action = -1 ): int|false
If we continue with the action we set in our wp_nonce_field
function, our action here should be "formA"
. Remember that we haven’t specified a nonce, so our nonce value will be in $_GET['_wpnonce']
or $_POST['_wpnonce']
, depending on the method we use in the form.
if (isset($_POST['submit'])) { // Check the nonce $nonce = $_POST['_wpnonce']; if (wp_verify_nonce($nonce, "formA")) { // Process the form data } else { // Handle the error } }
Note that in this example, we are using isset($_POST['submit'])
. This way, I know that I have clicked the button, saving time in directly checking the nonce. In essence, I already know that $_POST['_wpnonce']
exists.
Then, we check our nonce with the wp_verify_nonce
function. If it returns true, we proceed with the form process, which involves validation and data storage.
check_admin_referer
We will use the check_admin_referer
function if we are developing a plugin, as this function will check whether our page is being referred from another admin page and verify if the nonce is correct.
check_admin_referer( int|string $action = -1, string $query_arg = '_wpnonce' ): int|false
// chequeo el submit if (isset($_POST['submit'])) { check_admin_referer('accion', 'nonce_name'); // Si llegamos aquí, el nonce es válido, ya podemos porcesar los datos del formulario // ... }
If the nonce is invalid, in theory, the function will call die()
, printing "Are you sure?"
on the screen before die function.