Encodera

Thanks for reading!

Donate to the site — we work hard for you.

Contents

Security

WordPress is the most widespread system and therefore the most attacked. Most break-ins happen not through the core but through themes and plugins with careless code. The rules are few, but they must always be followed: sanitise input, escape output, check the token and the rights.

Escaping output

Any value is escaped before it is printed on a page — with the function that fits the place:

<?php
// card.php
?>
<h2><?php echo esc_html($title); ?></h2>
<a href="<?php echo esc_url($link); ?>" title="<?php echo esc_attr($hint); ?>">Open</a>
<div class="content"><?php echo wp_kses_post($html); ?></div>
FunctionWhere to use it
esc_html()Text between tags
esc_attr()An attribute value
esc_url()An address in href and src
esc_js()A string inside JavaScript
wp_kses_post()HTML in which only safe tags are allowed

Escape as late as possible — right in the output line.

Sanitising input

Data from a visitor is cleaned before it is saved:

$name = sanitize_text_field($_POST['name'] ?? '');
$email = sanitize_email($_POST['email'] ?? '');
$age = absint($_POST['age'] ?? 0);
$slug = sanitize_title($_POST['slug'] ?? '');
$comment = sanitize_textarea_field($_POST['comment'] ?? '');
$page = esc_url_raw($_POST['website'] ?? '');

sanitize_text_field() removes tags, line breaks and extra spaces.

Nonces

A nonce confirms that a request was sent from a page of your site rather than staged by someone else's. One function adds it to a form:

<?php
// form.php
?>
<form method="post">
    <?php wp_nonce_field('mytheme_feedback', 'feedback_nonce'); ?>
    <textarea name="message"></textarea>
    <button type="submit">Send</button>
</form>

The nonce is verified when the form is handled:

// functions.php
 
add_action('init', function () {
    if (!isset($_POST['feedback_nonce'])) {
        return;
    }
 
    if (!wp_verify_nonce($_POST['feedback_nonce'], 'mytheme_feedback')) {
        wp_die('The form has expired. Please refresh the page.');
    }
 
    $message = sanitize_textarea_field($_POST['message'] ?? '');
 
    // save the message
});

Links have wp_nonce_url(), and dashboard pages check_admin_referer().

Checking capabilities

A nonce answers "where did the request come from"; capabilities answer "may this user do it":

if (!current_user_can('edit_post', $postId)) {
    wp_die('You do not have permission.', 403);
}
 
if (!current_user_can('manage_options')) {
    return;
}

Check the capability for the action, not the role: roles on a site can be changed.

Database queries

Values go into SQL queries only through $wpdb->prepare() — the previous lesson covered that.

Protecting files

<?php
// includes/helpers.php
 
defined('ABSPATH') || exit;

In the wp-config.php of a live site the dashboard code editor and error display are turned off:

// wp-config.php
 
define('DISALLOW_FILE_EDIT', true);
define('WP_DEBUG', false);

What else matters

MeasureWhy
Update the core, themes and pluginsMost break-ins use vulnerabilities fixed long ago
Delete unused pluginsA deactivated plugin can still be vulnerable
HTTPSThe password and cookies cannot be intercepted on the network
BackupsThe only way to recover after a break-in

Common mistake

Sanitising on save does not replace escaping on output. Data may have reached the database another way — through an import, another plugin, an old version of the code. The rule to remember: sanitise on the way in, escape on the way out — both, always.

Thanks for reading!

Donate to the site — we work hard for you.