Skip to content

WordPress security hardening: protect your site

Last updated: 31 December 2025

Basis WordPress beveiliging is een goede start, maar serieuze sites vereisen geavanceerde maatregelen. Dit artikel behandelt security hardening op server-, applicatie- en codeniveau.

Why Extra Hardening?

WordPress is het meest aangevallen CMS ter wereld. Niet omdat het onveilig is, maar omdat het zo populair is. Een standaard beveiligde site stopt 90% van de aanvallen. Hardening stopt de overige 10%.

Dreigingslandschap

  • Brute force: Geautomatiseerd wachtwoorden raden
  • Plugin-kwetsbaarheden: Verouderde of slechte code
  • SQL injection: Database-manipulatie via invoervelden
  • XSS: Kwaadaardige scripts injecteren
  • File inclusion: Ongeautoriseerde bestanden uitvoeren

Server-Level Hardening

PHP Configuratie

Pas php.ini aan voor betere beveiliging:

; Verberg PHP-versie
expose_php = Off

; Beperk bestandsoperaties
open_basedir = /home/user/public_html:/tmp

; Schakel gevaarlijke functies uit
disable_functions = exec,passthru,shell_exec,system,proc_open,popen

; Beperk includes tot eigen directory
allow_url_include = Off
allow_url_fopen = Off

; Session beveiliging
session.cookie_httponly = 1
session.cookie_secure = 1
session.use_strict_mode = 1

Apache/Nginx Hardening

.htaccess (Apache):

# Blokkeer directe toegang tot gevoelige bestanden
<FilesMatch "^(wp-config\.php|readme\.html|license\.txt)">
    Order allow,deny
    Deny from all
</FilesMatch>

# Voorkom directory listing
Options -Indexes

# Blokkeer PHP-uitvoering in uploads
<Directory "/var/www/html/wp-content/uploads">
    <FilesMatch "\.php$">
        Order allow,deny
        Deny from all
    </FilesMatch>
</Directory>

# Bescherm .htaccess zelf
<Files .htaccess>
    Order allow,deny
    Deny from all
</Files>

# Security headers
Header always set X-Content-Type-Options "nosniff"
Header always set X-Frame-Options "SAMEORIGIN"
Header always set X-XSS-Protection "1; mode=block"
Header always set Referrer-Policy "strict-origin-when-cross-origin"

Nginx equivalent:

# Blokkeer gevoelige bestanden
location ~* /(wp-config\.php|readme\.html|license\.txt) {
    deny all;
}

# Geen PHP in uploads
location ~* /wp-content/uploads/.*\.php$ {
    deny all;
}

# Security headers
add_header X-Content-Type-Options "nosniff" always;
add_header X-Frame-Options "SAMEORIGIN" always;
add_header X-XSS-Protection "1; mode=block" always;

Database Beveiliging

Gebruik een unieke database prefix (niet wp_):

$table_prefix = 'hr7x_';

Beperk database-gebruikersrechten:

-- Alleen noodzakelijke rechten
GRANT SELECT, INSERT, UPDATE, DELETE ON database.* TO 'wpuser'@'localhost';
-- Geen DROP, ALTER, GRANT rechten

wp-config.php Hardening

// Forceer SSL voor admin
define('FORCE_SSL_ADMIN', true);

// Blokkeer bestandswijzigingen via admin
define('DISALLOW_FILE_EDIT', true);

// Blokkeer plugin/theme installatie via admin
define('DISALLOW_FILE_MODS', true);

// Unieke salts (genereer op api.wordpress.org/secret-key)
define('AUTH_KEY',         'unieke-waarde-hier');
define('SECURE_AUTH_KEY',  'unieke-waarde-hier');
define('LOGGED_IN_KEY',    'unieke-waarde-hier');
define('NONCE_KEY',        'unieke-waarde-hier');
define('AUTH_SALT',        'unieke-waarde-hier');
define('SECURE_AUTH_SALT', 'unieke-waarde-hier');
define('LOGGED_IN_SALT',   'unieke-waarde-hier');
define('NONCE_SALT',       'unieke-waarde-hier');

// Beperk revisies
define('WP_POST_REVISIONS', 5);

// Automatische updates voor security
define('WP_AUTO_UPDATE_CORE', 'minor');

Bestandspermissies

Correcte permissies zijn cruciaal:

# Directories: 755
find /var/www/html -type d -exec chmod 755 {} \;

# Bestanden: 644
find /var/www/html -type f -exec chmod 644 {} \;

# wp-config.php extra beveiligd
chmod 600 wp-config.php

# Uploads directory mag schrijfbaar zijn
chmod 755 wp-content/uploads

Login Beveiliging

Verplaats wp-admin

// In wp-config.php
define('WP_ADMIN_DIR', 'geheim-admin-pad');

// In functions.php
add_filter('site_url', function($url, $path, $scheme) {
    if ($path === 'wp-login.php' && !is_admin()) {
        return str_replace('wp-login.php', WP_ADMIN_DIR, $url);
    }
    return $url;
}, 10, 3);

Two-Factor Authentication

Verplicht 2FA voor alle admins. Gebruik plugins zoals:

  • Google Authenticator
  • Duo Security
  • Hardware keys (YubiKey)

Login Limiting

// Beperk inlogpogingen
function limit_login_attempts($user, $username, $password) {
    $ip = $_SERVER['REMOTE_ADDR'];
    $transient = 'login_attempts_' . md5($ip);
    $attempts = get_transient($transient) ?: 0;

    if ($attempts >= 5) {
        return new WP_Error('too_many_attempts',
            'Te veel inlogpogingen. Probeer over 15 minuten opnieuw.');
    }

    return $user;
}
add_filter('authenticate', 'limit_login_attempts', 30, 3);

function track_failed_login($username) {
    $ip = $_SERVER['REMOTE_ADDR'];
    $transient = 'login_attempts_' . md5($ip);
    $attempts = get_transient($transient) ?: 0;
    set_transient($transient, $attempts + 1, 15 * MINUTE_IN_SECONDS);
}
[add_action](/kennisbank/wordpress-hooks-uitleg)('wp_login_failed', 'track_failed_login');

Content Security Policy

CSP voorkomt XSS-aanvallen:

add_action('send_headers', function() {
    header("Content-Security-Policy:
        default-src 'self';
        script-src 'self' 'unsafe-inline' https://trusted-cdn.com;
        style-src 'self' 'unsafe-inline' https://fonts.googleapis.com;
        img-src 'self' data: https:;
        font-src 'self' https://fonts.gstatic.com;
        connect-src 'self';
        frame-ancestors 'self';
    ");
});

XML-RPC Uitschakelen

XML-RPC is een veelgebruikte aanvalsvector:

// Volledig uitschakelen
add_filter('xmlrpc_enabled', '__return_false');

// Of via .htaccess
<Files xmlrpc.php>
    Order deny,allow
    Deny from all
</Files>

REST API Beveiligen

Beperk de REST API voor niet-ingelogde gebruikers:

add_filter('rest_authentication_errors', function($result) {
    if (!is_user_logged_in()) {
        // Sta publieke endpoints toe
        $public_routes = ['/wp/v2/posts', '/wp/v2/pages'];
        $route = $_SERVER['REQUEST_URI'];

        foreach ($public_routes as $public) {
            if (strpos($route, $public) !== false) {
                return $result;
            }
        }

        return new WP_Error('rest_forbidden',
            'Authenticatie vereist', ['status' => 401]);
    }
    return $result;
});

Monitoring en Logging

Activiteitenlog

function log_security_event($message, $level = 'info') {
    $log_file = WP_CONTENT_DIR . '/security.log';
    $timestamp = date('Y-m-d H:i:s');
    $ip = $_SERVER['REMOTE_ADDR'];
    $user = wp_get_current_user();
    $username = $user->ID ? $user->user_login : 'anonymous';

    $entry = "[{$timestamp}] [{$level}] [{$ip}] [{$username}] {$message}\n";
    file_put_contents($log_file, $entry, FILE_APPEND | LOCK_EX);
}

// Log belangrijke acties
add_action('wp_login', function($username) {
    log_security_event("Successful login: {$username}");
});

add_action('wp_login_failed', function($username) {
    log_security_event("Failed login attempt: {$username}", 'warning');
});

File Integrity Monitoring

Controleer regelmatig of kernbestanden gewijzigd zijn:

# Genereer checksums
find /var/www/html/wp-includes -type f -name "*.php" \
    -exec md5sum {} \; > /root/wp-checksums.txt

# Vergelijk later
md5sum -c /root/wp-checksums.txt 2>&1 | grep FAILED

Security Headers Testen

Test je configuratie op:

  • securityheaders.com
  • observatory.mozilla.org
  • ssllabs.com/ssltest

Incident Response Plan

Bereid je voor op het ergste:

  1. Detectie: Monitoring alerts configureren
  2. Isolatie: Site offline halen indien nodig
  3. Analyse: Logs onderzoeken, besmetting identificeren
  4. Herstel: Schone backup terugzetten
  5. Preventie: Kwetsbaarheid dichten, hardening verbeteren

More information: WordPress.org documentatie

Frequently Asked Questions

How long does it take to implement this?

Implementation time varies per situation. Simple configurations can be done within an hour, more complex setups may take several hours to a day.

What are the costs?

Costs depend on your hosting provider and package. Many basic features are included for free, advanced features may incur additional costs.

Do I need technical knowledge?

You need little technical knowledge for the basics. Most hosting providers offer extensive documentation and support to help you.

Was this article helpful?

Compare hosting packages directly to find the best choice for your situation.