Komplexní zabezpečení komentářů ve WordPressu - Affinite.io CZ

Úvod: Proč je zabezpečení komentářů důležité

WordPress komentáře jsou jedním z nejčastěji napadaných vektorů na webových stránkách. Podle statistik Wordfence tvoří útoky na komentářové systémy více než 35% všech automatizovaných útoků na WordPress. Spam komentáře nejen zhoršují uživatelskou zkušenost, ale mohou také negativně ovlivnit SEO hodnocení a v extrémních případech vést k blacklistingu domény.

Jak fungují útoky na komentáře

Technické pozadí útoků

Moderní spamovací boti používají sofistikované metody pro obcházení standardních ochran:

1. Přímé HTTP požadavky

  • Boti nepoužívají webový formulář na stránce, ale posílají přímé POST požadavky
  • Obcházejí tak JavaScript validace, CSRF tokeny a jiné klientské ochrany
  • Dokáží simulovat legitimní user-agenty a HTTP hlavičky

2. Cílené endpointy:

  • wp-comments-post.php – hlavní endpoint pro zpracování komentářů
  • /wp-json/wp/v2/comments – REST API endpoint (WordPress 4.7+)
  • xmlrpc.php – XML-RPC metody pro pingbacky a trackbacky
  • /wp-json/wp/v2/posts/{id}/comments – specifické REST endpointy pro jednotlivé příspěvky

3. Pokročilé techniky:

  • Rate limiting evasion – rotace IP adres přes proxy sítě
  • Fingerprint spoofing – napodobování legitímních browserů
  • Content spinning – automatické generování variací textů
  • Honeypot detection – rozpoznávání a obcházení past pro boty

Dopady úspěšných útoků

Typ dopaduPopisZávažnost
PerformancePřetížení databáze, zpomalení webuVysoká
SEOSpam odkazy, degradace v SERPVysoká
BezpečnostPotenciální XSS, injection útokyKritická
NákladyVyšší server nároky, čištěníStřední
ReputaceZtráta důvěry návštěvníkůStřední

Komplexní zabezpečení – krok za krokem

1. Základní nastavení v administraci

Diskuzní nastavení (wp-admin/options-discussion.php)

Kritická nastavení:

Nastavení → Diskuze

Doporučená konfigurace:

  1. Výchozí nastavení článků:
    • Povolit návštěvníkům webu přidávat komentáře k novým příspěvkům
    • Přidávat komentáře mohou pouze registrovaní a přihlášení uživatelé
  2. Další nastavení komentářů:
    • Autor komentáře musí vyplnit jméno a e-mail
    • Uživatelé musí být registrováni a přihlášeni pro přidání komentáře
    • Automaticky uzavřít komentáře u článků starších než 30 dní
  3. Oznámení a moderace:
    • Komentář musí být schválen ručně
    • Komentář musí být schválen, pokud obsahuje 2 nebo více odkazů
    • Poslat e-mail při každém komentáři
    • Poslat e-mail při čekání komentáře na schválení
  4. Blacklist nastavení:
    • Přidat běžná spam slova do seznamu zakázaných výrazů
    • Nastavit automatické označení komentářů s podezřelými klíčovými slovy

Pokročilé WordPress hooks

Pre-comment validace:

// functions.php nebo MU plugin
add_action('pre_comment_on_post', 'custom_comment_security_check');
function custom_comment_security_check($comment_post_id) {
    // Kontrola, zda je uživatel přihlášen
    if (!is_user_logged_in()) {
        wp_die(
            'Komentáře jsou povoleny pouze přihlášeným uživatelům.', 
            'Přístup zamítnut', 
            ['response' => 403]
        );
    }
    
    // Kontrola rychlosti přidávání komentářů
    $user_id = get_current_user_id();
    $last_comment = get_transient('last_comment_time_' . $user_id);
    
    if ($last_comment && (time() - $last_comment) < 30) {
        wp_die(
            'Příliš rychlé přidávání komentářů. Zkuste to za chvíli.', 
            'Rate limit exceeded', 
            ['response' => 429]
        );
    }
    
    set_transient('last_comment_time_' . $user_id, time(), 3600);
}

2. Technické zabezpečení endpoints

REST API ochrany

Kompletní blokace anonymních komentářů:

// Zakáže anonymní komentáře přes REST API
add_filter('rest_allow_anonymous_comments', '__return_false');

// Přidá dodatečnou autentifikaci
add_filter('rest_pre_insert_comment', 'secure_rest_comments', 10, 2);
function secure_rest_comments($prepared_comment, $request) {
    if (!is_user_logged_in()) {
        return new WP_Error(
            'rest_comment_login_required',
            'Pro přidání komentáře se musíte přihlásit.',
            ['status' => 401]
        );
    }
    return $prepared_comment;
}

// Volitelně - odstranění celého comments endpointu
add_filter('rest_endpoints', 'remove_comments_endpoints');
function remove_comments_endpoints($endpoints) {
    if (isset($endpoints['/wp/v2/comments'])) {
        unset($endpoints['/wp/v2/comments']);
    }
    if (isset($endpoints['/wp/v2/comments/(?P<id>[\d]+)'])) {
        unset($endpoints['/wp/v2/comments/(?P<id>[\d]+)']);
    }
    return $endpoints;
}

Selective REST API protection:

// Pokročilejší ochrana s logováním
add_filter('rest_pre_dispatch', 'monitor_comments_api', 10, 3);
function monitor_comments_api($result, $server, $request) {
    $route = $request->get_route();
    
    if (strpos($route, '/wp/v2/comments') !== false) {
        // Logování pokusů o přístup
        error_log(sprintf(
            'Comments API access attempt - IP: %s, User-Agent: %s, Route: %s',
            $_SERVER['REMOTE_ADDR'] ?? 'unknown',
            $_SERVER['HTTP_USER_AGENT'] ?? 'unknown',
            $route
        ));
        
        // Blokace pro nepřihlášené
        if (!is_user_logged_in()) {
            return new WP_Error(
                'rest_comments_forbidden', 
                'Comments API access forbidden',
                ['status' => 403]
            );
        }
    }
    
    return $result;
}

XML-RPC zabezpečení

XML-RPC je častým cílem útoků, zejména pingback metody:

// Kompletní vypnutí XML-RPC
add_filter('xmlrpc_enabled', '__return_false');

// Nebo selektivní blokace nebezpečných metod
add_filter('xmlrpc_methods', 'secure_xmlrpc_methods');
function secure_xmlrpc_methods($methods) {
    // Odstranění nebezpečných metod
    unset($methods['pingback.ping']);
    unset($methods['pingback.extensions.getPingbacks']);
    unset($methods['wp.newComment']);
    
    return $methods;
}

// Dodatečná ochrana XML-RPC pomocí .htaccess
// Přidat do .htaccess:
/*
<Files "xmlrpc.php">
    Order Allow,Deny
    Deny from all
</Files>
*/

wp-comments-post.php zabezpečení

PHP metoda (doporučená):

add_action('init', 'secure_comments_post_endpoint');
function secure_comments_post_endpoint() {
    if (strpos($_SERVER['REQUEST_URI'], 'wp-comments-post.php') !== false) {
        // Kontrola referer
        if (!wp_verify_nonce($_POST['_wp_http_referer'] ?? '', 'comment_nonce')) {
            // Logování podezřelé aktivity
            error_log(sprintf(
                'Suspicious comment attempt - IP: %s, User-Agent: %s, Referer: %s',
                $_SERVER['REMOTE_ADDR'] ?? 'unknown',
                $_SERVER['HTTP_USER_AGENT'] ?? 'unknown',
                $_SERVER['HTTP_REFERER'] ?? 'none'
            ));
        }
        
        if (!is_user_logged_in()) {
            status_header(403);
            wp_die('Komentáře jsou povoleny pouze přihlášeným uživatelům.');
        }
        
        // Rate limiting
        $ip = $_SERVER['REMOTE_ADDR'] ?? '';
        $attempts = get_transient('comment_attempts_' . md5($ip));
        
        if ($attempts && $attempts > 5) {
            status_header(429);
            wp_die('Příliš mnoho pokusů. Zkuste to později.');
        }
        
        set_transient('comment_attempts_' . md5($ip), ($attempts + 1), 300);
    }
}

.htaccess metoda (Apache):

<IfModule mod_rewrite.c>
    RewriteEngine On
    
    # Blokace wp-comments-post.php pro nepřihlášené
    RewriteCond %{REQUEST_URI} ^/wp-comments-post\.php$ [NC]
    RewriteCond %{HTTP_COOKIE} !wordpress_logged_in_ [NC]
    RewriteCond %{REQUEST_METHOD} POST [NC]
    RewriteRule .* - [R=403,L]
    
    # Blokace podezřelých User-Agents
    RewriteCond %{HTTP_USER_AGENT} ^$ [NC,OR]
    RewriteCond %{HTTP_USER_AGENT} (bot|crawler|spider) [NC]
    RewriteCond %{REQUEST_URI} wp-comments-post\.php$ [NC]
    RewriteRule .* - [R=403,L]
</IfModule>

Nginx varianta:

location = /wp-comments-post.php {
    # Kontrola přihlášení přes cookie
    if ($http_cookie !~* "wordpress_logged_in_") {
        return 403;
    }
    
    # Rate limiting
    limit_req zone=comments burst=2 nodelay;
    
    # Blokace prázdných User-Agentů
    if ($http_user_agent = "") {
        return 403;
    }
    
    try_files $uri =404;
    fastcgi_pass php;
}

# Rate limiting definice (přidat do http bloku)
limit_req_zone $binary_remote_addr zone=comments:10m rate=1r/m;

3. Pokročilá ochrana proti spamu

Anti-spam pluginy – srovnání

PluginCenaMetody detekceVýhodyNevýhody
Akismet$5-50/měsAI, blacklisty, communityOficiální, přesnýPlacený pro komerční
Antispam BeeZdarmaHoneypot, geolokaceZdarma, GDPR compliantMéně funkcí
CleanTalk$8/rokAI, behavior analysisPokročilé funkceZávislost na API
WP ArmourZdarmareCAPTCHA, honeypotJednoduchéZákladní funkce

Vlastní antispam řešení

Honeypot implementace:

// Přidání honeypot pole do comment formu
add_action('comment_form_after_fields', 'add_honeypot_field');
function add_honeypot_field() {
    echo '<p style="display:none;">
        <label for="url-extra">Leave this field empty:</label>
        <input type="text" name="url-extra" id="url-extra" value="" />
    </p>';
}

// Kontrola honeypot při submit
add_action('pre_comment_on_post', 'check_honeypot');
function check_honeypot() {
    if (!empty($_POST['url-extra'])) {
        wp_die('Spam detected.', 'Error', ['response' => 403]);
    }
}

Behavioral analysis:

// Analýza času vyplnění formuláře
add_action('comment_form', 'add_timing_check');
function add_timing_check() {
    echo '<input type="hidden" name="comment_timestamp" value="' . time() . '" />';
}

add_filter('pre_comment_approved', 'timing_spam_check', 99, 2);
function timing_spam_check($approved, $commentdata) {
    $timestamp = $_POST['comment_timestamp'] ?? 0;
    $time_spent = time() - $timestamp;
    
    // Příliš rychlé vyplnění (bot) nebo příliš pomalé (opuštěný formulář)
    if ($time_spent < 5 || $time_spent > 3600) {
        return 'spam';
    }
    
    return $approved;
}

CAPTCHA implementace

Google reCAPTCHA v3:

// Přidání reCAPTCHA do comment formu
add_action('comment_form_after_fields', 'add_recaptcha_v3');
function add_recaptcha_v3() {
    $site_key = 'YOUR_RECAPTCHA_SITE_KEY';
    
    echo '<script src="https://www.google.com/recaptcha/api.js?render=' . $site_key . '"></script>';
    echo '<script>
        grecaptcha.ready(function() {
            grecaptcha.execute("' . $site_key . '", {action: "comment"}).then(function(token) {
                document.getElementById("g-recaptcha-response").value = token;
            });
        });
    </script>';
    echo '<input type="hidden" id="g-recaptcha-response" name="g-recaptcha-response" />';
}

// Validace reCAPTCHA
add_filter('pre_comment_approved', 'validate_recaptcha', 10, 2);
function validate_recaptcha($approved, $commentdata) {
    $secret_key = 'YOUR_RECAPTCHA_SECRET_KEY';
    $response = $_POST['g-recaptcha-response'] ?? '';
    
    if (empty($response)) {
        return new WP_Error('recaptcha_required', 'reCAPTCHA verification required.');
    }
    
    $verify = wp_remote_post('https://www.google.com/recaptcha/api/siteverify', [
        'body' => [
            'secret' => $secret_key,
            'response' => $response,
            'remoteip' => $_SERVER['REMOTE_ADDR']
        ]
    ]);
    
    $verify_body = wp_remote_retrieve_body($verify);
    $result = json_decode($verify_body, true);
    
    if (!$result['success'] || $result['score'] < 0.5) {
        return 'spam';
    }
    
    return $approved;
}

Cloudflare Turnstile (alternativa k reCAPTCHA):

// Cloudflare Turnstile implementace
add_action('comment_form_after_fields', 'add_turnstile');
function add_turnstile() {
    echo '<script src="https://challenges.cloudflare.com/turnstile/v0/api.js" async defer></script>';
    echo '<div class="cf-turnstile" data-sitekey="YOUR_TURNSTILE_SITE_KEY"></div>';
}

add_filter('pre_comment_approved', 'validate_turnstile', 10, 2);
function validate_turnstile($approved, $commentdata) {
    $secret = 'YOUR_TURNSTILE_SECRET_KEY';
    $token = $_POST['cf-turnstile-response'] ?? '';
    
    if (empty($token)) {
        return new WP_Error('turnstile_required', 'Turnstile verification required.');
    }
    
    $response = wp_remote_post('https://challenges.cloudflare.com/turnstile/v0/siteverify', [
        'body' => [
            'secret' => $secret,
            'response' => $token,
            'remoteip' => $_SERVER['REMOTE_ADDR']
        ]
    ]);
    
    $body = json_decode(wp_remote_retrieve_body($response), true);
    
    if (!$body['success']) {
        return 'spam';
    }
    
    return $approved;
}

4. Monitoring a forensní analýza

Logování pokusů o spam

// Komplexní logování comment aktivit
add_action('wp_insert_comment', 'log_comment_activity', 10, 2);
function log_comment_activity($id, $comment) {
    $log_data = [
        'comment_id' => $id,
        'post_id' => $comment->comment_post_ID,
        'author_ip' => $comment->comment_author_IP,
        'user_agent' => $_SERVER['HTTP_USER_AGENT'] ?? '',
        'referer' => $_SERVER['HTTP_REFERER'] ?? '',
        'timestamp' => current_time('mysql'),
        'status' => $comment->comment_approved
    ];
    
    // Ukládání do custom log tabulky nebo file
    error_log('COMMENT_LOG: ' . json_encode($log_data));
    
    // Detekce podezřelých vzorů
    if (is_comment_suspicious($comment)) {
        // Poslání alertu administrátorům
        wp_mail(
            get_option('admin_email'),
            'Suspicious comment detected',
            sprintf('Suspicious comment detected from IP %s on post %d', 
                $comment->comment_author_IP, 
                $comment->comment_post_ID
            )
        );
    }
}

function is_comment_suspicious($comment) {
    // Kontrola na známé spam patterns
    $spam_indicators = [
        strlen($comment->comment_content) < 5, // Příliš krátký
        preg_match('/https?:\/\//', $comment->comment_content) > 2, // Mnoho linků
        preg_match('/\[url=|\[link=/', $comment->comment_content), // BBcode linky
        empty($comment->comment_author_email), // Bez emailu
    ];
    
    return array_sum($spam_indicators) >= 2;
}

Databázová analýza spamu

// Funkce pro analýzu spam komentářů
function analyze_comment_patterns() {
    global $wpdb;
    
    // Top IP adresy s odmítnutými komentáři
    $spam_ips = $wpdb->get_results("
        SELECT comment_author_IP, COUNT(*) as count 
        FROM {$wpdb->comments} 
        WHERE comment_approved = 'spam' 
        AND comment_date > DATE_SUB(NOW(), INTERVAL 30 DAY)
        GROUP BY comment_author_IP 
        ORDER BY count DESC 
        LIMIT 20
    ");
    
    // Časové vzory útoků
    $time_patterns = $wpdb->get_results("
        SELECT HOUR(comment_date) as hour, COUNT(*) as count
        FROM {$wpdb->comments} 
        WHERE comment_approved = 'spam'
        AND comment_date > DATE_SUB(NOW(), INTERVAL 7 DAY)
        GROUP BY HOUR(comment_date)
        ORDER BY count DESC
    ");
    
    return [
        'spam_ips' => $spam_ips,
        'time_patterns' => $time_patterns
    ];
}

5. Úplné vypnutí komentářů

Plugin Disable Comments

Nejjednodušší způsob je použití pluginu Disable Comments:

Funkce:

  • Kompletní odstranění UI pro komentáře
  • Blokace všech komentářových endpoints
  • Čištění existujících komentářů
  • Podpora pro custom post types

Manuální vypnutí

// Kompletní odstranění comment systému
add_action('admin_init', 'disable_comments_admin');
function disable_comments_admin() {
    // Odstranění menu items
    remove_menu_page('edit-comments.php');
    remove_submenu_page('options-general.php', 'options-discussion.php');
}

add_action('init', 'disable_comments_frontend');
function disable_comments_frontend() {
    // Odstranění podpory pro komentáře ze všech post types
    $post_types = get_post_types();
    foreach ($post_types as $post_type) {
        if (post_type_supports($post_type, 'comments')) {
            remove_post_type_support($post_type, 'comments');
            remove_post_type_support($post_type, 'trackbacks');
        }
    }
}

// Blokace wp-comments-post.php
add_action('wp_loaded', 'block_comments_completely');
function block_comments_completely() {
    if (strpos($_SERVER['REQUEST_URI'], 'wp-comments-post.php') !== false) {
        wp_die('Comments are completely disabled.', 'Comments Disabled', 410);
    }
}

// Odstranění z feeds
add_filter('comments_open', '__return_false', 20, 2);
add_filter('pings_open', '__return_false', 20, 2);
add_filter('comments_array', '__return_empty_array', 10, 2);

6. Optimalizace performance

Databázové optimalizace

-- Vyčištění spam komentářů starších než 30 dní
DELETE FROM wp_comments 
WHERE comment_approved = 'spam' 
AND comment_date < DATE_SUB(NOW(), INTERVAL 30 DAY);

-- Optimalizace comment meta tabulky
OPTIMIZE TABLE wp_commentmeta;

-- Index pro rychlejší dotazy
CREATE INDEX idx_comment_approved_date ON wp_comments(comment_approved, comment_date);

Caching considerations

// Invalidace cache při nových komentářích
add_action('comment_post', 'clear_comment_cache');
function clear_comment_cache($comment_id) {
    $comment = get_comment($comment_id);
    
    // Vyčištění page cache
    if (function_exists('wp_cache_flush')) {
        wp_cache_flush();
    }
    
    // LiteSpeed Cache
    if (class_exists('LiteSpeed_Cache_API')) {
        LiteSpeed_Cache_API::purge_post($comment->comment_post_ID);
    }
    
    // W3 Total Cache
    if (function_exists('w3tc_flush_post')) {
        w3tc_flush_post($comment->comment_post_ID);
    }
}

7. Bezpečnostní monitoring

Server-level monitoring

.htaccess advanced rules:

<IfModule mod_security.c>
    # Blokace společných spam patterns v komentářích
    SecRule ARGS:comment "@detectSQLi" \
        "id:1001,phase:2,block,msg:'SQL Injection in comment'"
    
    SecRule ARGS:comment "@detectXSS" \
        "id:1002,phase:2,block,msg:'XSS in comment'"
    
    # Rate limiting pro comment submissions
    SecRule IP:comment_rate "@gt 5" \
        "id:1003,phase:2,deny,status:429"
    
    SecAction "id:1004,phase:2,setvar:IP.comment_rate=+1,expirevar:IP.comment_rate=60"
</IfModule>

Fail2Ban konfigurace

# /etc/fail2ban/jail.local

[wordpress-comment-spam]

enabled = true filter = wordpress-comment-spam action = iptables-multiport[name=wp-comment, port=“http,https“] logpath = /var/log/apache2/access.log maxretry = 5 bantime = 3600 findtime = 300 # Filter: /etc/fail2ban/filter.d/wordpress-comment-spam.conf [Definition] failregex = ^<HOST> .* „POST .*wp-comments-post\.php.*“ 403 ^<HOST> .* „POST .*/wp-json/wp/v2/comments.*“ 40[13]

Bezpečnostní checklist

Základní ochrana (must-have)

  • [ ] Komentáře pouze pro přihlášené uživatele
  • [ ] Vypnutí pingbacks a trackbacks
  • [ ] XML-RPC vypnuto nebo omezeno
  • [ ] Antispam plugin (Akismet/Antispam Bee)
  • [ ] Automatické zavření starých komentářů
  • [ ] Moderace nových komentářů

Pokročilá ochrana (recommended)

  • [ ] REST API endpoints zabezpečené
  • [ ] wp-comments-post.php chráněno
  • [ ] CAPTCHA/Turnstile implementováno
  • [ ] Rate limiting nastaveno
  • [ ] Honeypot pole přidána
  • [ ] Behavioral analysis aktivní

Expert level (optional)

  • [ ] Server-level filtering (.htaccess/Nginx)
  • [ ] Fail2Ban konfigurace
  • [ ] Custom logging implementováno
  • [ ] Database optimization provedena
  • [ ] Performance monitoring nastaveno
  • [ ] Security headers konfigurovány

Závěr a doporučení

Zabezpečení WordPress komentářů vyžaduje vícevrstvý přístup kombinující správné nastavení administrace, technické ochrany a aktivní monitoring. Nejefektivnější je začít se základními opatřeními a postupně přidávat pokročilejší funkce podle potřeb konkrétního webu.

Klíčové body:

  1. Prevention over cleanup – lepší zabránit spamu než ho později odstraňovat
  2. Layered security – kombinace více obranných mechanismů
  3. Regular monitoring – aktivní sledování a vyhodnocování útoků
  4. Performance impact – vždy zvážit dopad na rychlost webu

Pro většinu webů postačí kombinace správného nastavení v administraci, kvalitního antispam pluginu a základní technické ochrany. Pokročilejší metody jsou vhodné pro weby s vysokým provozem nebo specifickými bezpečnostními požadavky.

Užitečné externí zdroje:

Americký patentový úřad zamítl žádosti WordPress Foundation o ochranné známky
Americký patentový úřad zamítl žádosti WordPress Foundation o ochranné známky
28 Srp, 2025

Looking for something?