Documentation menuWordPress

WordPress

A small plugin that protects the login, registration and comment forms.

On this page

Install #

Create the folder wp-content/plugins/blockalt/ with the two files below, add your keys to wp-config.php, then activate BlockAlt protection under Plugins. Use the hostname of your WordPress site as an origin and verified hostname of the BlockAlt site, with the actions login, register and comment.

wp-config.phpPHP
define('BLOCKALT_SITE_KEY', 'ba_pk_your_public_site_key');
define('BLOCKALT_SECRET', 'ba_sk_…'); // keep out of version control
define('BLOCKALT_CONFIG_KEYS', 'MCowBQYDK2VwAyEAXmYdcBh/jf+z+0H+hs2QvZ9UG4PBgkRvHX/JuXyjIL8=,MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE34qdEtOeD21+CmmfTmFyRr/vySsY1TD6BJZFtwF37pkelvN2vRYWWHdLrUouzkQXEfnA7V50xK2QcmnBCGsxbA==,MCowBQYDK2VwAyEAtkfghYcdvSRf2wA01gjQtgTqdYGjaDUZsOPXzgLnnT0=,MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEYYmQNAMnVKK7G3VVguAk3SC/RFT5SDjCoxcFMOPFx0vmsGycJWOK8hCdxuTCHlvShng1fo0y4Q9PJHPIGQ9EfA=='); // every key from GET /v1/keys

Plugin file #

wp-content/plugins/blockalt/blockalt.phpPHP
<?php
/**
 * Plugin Name: BlockAlt protection
 * Description: Verifies BlockAlt tokens on the login, registration and comment forms.
 * Version: 1.0.0
 */

if (!defined('ABSPATH') || !defined('BLOCKALT_SITE_KEY') || !defined('BLOCKALT_SECRET')) {
    return;
}

const BLOCKALT_API = 'https://blockalt.click';
const BLOCKALT_SCRIPT = 'https://blockalt.click/v1/assets/blockalt.c8b2d9539dbbc932999adb0f51a36bddd4e99899f543e1505818841878cf9813.js';
const BLOCKALT_INTEGRITY = 'sha384-pblByFDqcJxRfhDh18GH3gRsWy9ZaS4AjDc6OpozzQqvkuYvyin+4BWcKXxA+bN8';

function blockalt_enqueue()
{
    wp_enqueue_script('blockalt', BLOCKALT_SCRIPT, [], null, ['in_footer' => true]);
    wp_enqueue_script('blockalt-forms', plugins_url('blockalt.js', __FILE__), ['blockalt'], '1.0.0', ['in_footer' => true]);
    wp_localize_script('blockalt-forms', 'BlockAltSettings', [
        'siteKey' => BLOCKALT_SITE_KEY,
        'apiUrl' => BLOCKALT_API,
        'configPublicKeys' => array_map('trim', explode(',', BLOCKALT_CONFIG_KEYS)),
    ]);
}
add_action('login_enqueue_scripts', 'blockalt_enqueue');
add_action('wp_enqueue_scripts', function () {
    if (is_singular() && comments_open()) {
        blockalt_enqueue();
    }
});

add_filter('script_loader_tag', function ($tag, $handle) {
    if ($handle !== 'blockalt') {
        return $tag;
    }
    return str_replace(' src=', ' integrity="' . esc_attr(BLOCKALT_INTEGRITY) . '" crossorigin="anonymous" src=', $tag);
}, 10, 2);

function blockalt_field()
{
    echo '<input type="hidden" name="blockalt-token" value="">';
}
add_action('login_form', 'blockalt_field');
add_action('register_form', 'blockalt_field');
add_action('comment_form', 'blockalt_field');

function blockalt_verify(string $action): bool
{
    $token = isset($_POST['blockalt-token']) ? sanitize_text_field(wp_unslash($_POST['blockalt-token'])) : '';
    $response = wp_remote_post(BLOCKALT_API . '/v1/siteverify', [
        'timeout' => 3,
        'redirection' => 0,
        'headers' => [
            'Authorization' => 'Bearer ' . BLOCKALT_SECRET,
            'Content-Type' => 'application/json',
        ],
        'body' => wp_json_encode([
            'token' => $token,
            'hostname' => strtolower((string) wp_parse_url(home_url(), PHP_URL_HOST)),
            'action' => $action,
        ]),
    ]);
    if (is_wp_error($response) || wp_remote_retrieve_response_code($response) !== 200) {
        return false;
    }
    $result = json_decode(wp_remote_retrieve_body($response), true);
    return is_array($result) && ($result['success'] ?? false) === true;
}

// Login form on wp-login.php only; XML-RPC and application passwords are not affected.
add_filter('authenticate', function ($user, $username) {
    if (!did_action('login_init') || $_SERVER['REQUEST_METHOD'] !== 'POST' || empty($username)) {
        return $user;
    }
    return blockalt_verify('login') ? $user : new WP_Error('blockalt', __('Verification failed. Please try again.'));
}, 30, 2);

add_filter('registration_errors', function ($errors) {
    if (!blockalt_verify('register')) {
        $errors->add('blockalt', __('Verification failed. Please try again.'));
    }
    return $errors;
});

add_filter('preprocess_comment', function ($comment) {
    if (is_admin() || wp_doing_ajax() || (defined('REST_REQUEST') && REST_REQUEST)) {
        return $comment;
    }
    if (!blockalt_verify('comment')) {
        wp_die(esc_html__('Verification failed. Please go back and try again.'), 403);
    }
    return $comment;
});

Script #

wp-content/plugins/blockalt/blockalt.jsJavaScript
(() => {
  const settings = window.BlockAltSettings;
  if (!settings || !window.BlockAlt) return;
  const protection = BlockAlt.create(settings);
  const forms = { loginform: "login", registerform: "register", commentform: "comment" };
  for (const [id, action] of Object.entries(forms)) {
    const form = document.getElementById(id);
    if (!form) continue;
    form.addEventListener("submit", async (event) => {
      event.preventDefault();
      const button = form.querySelector("[type=submit]");
      if (button) button.disabled = true;
      try {
        form.elements["blockalt-token"].value = await protection.execute(action);
        HTMLFormElement.prototype.submit.call(form); // the comment form has a field named "submit"
      } catch {
        if (button) button.disabled = false;
        alert("We could not verify this browser. Please try again.");
      }
    });
  }
})();

If a security plugin sets a Content-Security-Policy, add the CSP entries. Caching plugins can cache the pages: the token is requested at submit time, so cached pages keep working.

Something unclear or missing on this page? Write to support@blockalt.click.