Уразливості класів XSS (Cross-Site Scripting) та CSRF (Cross-Site Request Forgery) є критичними загрозами клієнт-серверної безпеки у веб-застосунках на базі WordPress. В архітектурі корпоративних рішень 2026 року безпека будується за принципом ешелонованої оборони (Defense-in-Depth) та моделі нульової довіри (Zero-Trust Architecture).
XSS-атаки спрямовані на впровадження та виконання довільного клієнтського коду (переважно JavaScript) у контексті довіреного домену жертви. Уразливість поділяється на чотири фундаментальні типи:
wp_posts, wp_comments, wp_postmeta) та віддається сервером усім авторизованим чи публічним відвідувачам.$_GET, $_POST, $_SERVER['REQUEST_URI']) і негайно відображається сервером у тілі відповіді без належної контекстної санітизації.location.hash, window.name, document.referrer) і передає їх у небезпечний приймач (Sink: element.innerHTML, document.write(), eval()), оминаючи серверний рендеринг.CSRF-атаки експлуатують довіру веб-сервера до браузера автентифікованого користувача. Браузер автоматично прикріплює сесійні куки (включно з куками автентифікації WordPress wordpress_logged_in_*) до будь-яких міжсайтових запитів до цільового домену. Зловмисник може ініціювати несанкціоновану зміну стану системи (зміну пароля адміністратора, генерацію Application Password, встановлення бекдору через плагіни) за допомогою прихованих HTML-форм, тегів <img> або міжсайтових AJAX-запитів.
Сучасний стек захисту нейтралізує ці загрози на всіх рівнях: сувора типізація та пізнє екранування контексту виведення в PHP 8.4+, криптографічні одноразові токени (WordPress Nonces), політика безпеки контенту третього покоління (CSP Level 3 з динамічними криптографічними нонсами), API довірених типів (W3C Trusted Types) та ізоляція міждоменних куки через атрибути SameSite і механізм CHIPS (Cookies Having Independent Partitioned State).
Вектори атак та механізми захисту у вигляді багатошарового периметра ізоляції потоків даних:
+-----------------------------------------------------------------------------------------+
| БРАУЗЕР КЛІЄНТА (User Context) |
+-----------------------------------------------------------------------------------------+
| [Вхідний Payload: URL/DOM] ----> [Trusted Types Policy] ----> DOM Sink (innerHTML) |
| | (Блокування DOM XSS) |
| v |
| [Safe DOM Tree] |
+-----------------------------------------------------------------------------------------+
| ^
1. HTTP Request| | 4. Protected Response
(SameSite/CORS) | (CSP Level 3 + Nonce)
v |
+-----------------------------------------------------------------------------------------+
| СЕРВЕР WORDPRESS (Core & REST API) |
+-----------------------------------------------------------------------------------------+
| [CORS Firewall] ----------> [CSRF Gate: Nonce Check] ----> [Access Control: Capability] |
| (Strict Origin Match) (Time-bucketed HMAC hash) (current_user_can) |
| | |
| v |
| [Business Logic Layer] |
| | |
| [Late Escaping: esc_html/wp_kses] <--- [wp_json_encode Flags] <---+ |
+-----------------------------------------------------------------------------------------+Впровадження виконуваного шкідливого коду в базу даних застосунку, за якого заражений контент персистентно віддається клієнтам і виконується в їхніх браузерах із правами поточної користувацької сесії.
Тип уразливості, що виникає безпосередньо в DOM-дереві браузера при передачі недовірених рядкових даних у небезпечні приймачі (Sinks). Нейтралізується впровадженням стандарту W3C Trusted Types, що забороняє прямий запис рядків у innerHTML.
Атака з підробкою міжсайтових запитів, що змушує браузер жертви виконати ненавмисну дію на довіреному сервері, використовуючи автоматично переслані сесійні куки авторизації.
Механізм глибокого захисту на базі HTTP-заголовка, що забороняє виконання будь-яких вбудованих скриптів, окрім тих, які підписані криптографічно стійким псевдовипадковим нонсом поточного запиту (nonce-${random}).
Атрибут директиви Set-Cookie, що вказує браузеру умови передачі куки при міжсайтових запитах (Strict, Lax, None). У поєднанні з механізмом CHIPS запобігає використанню сесійних куки в нелегітимних сторонніх контекстах.
Браузер надсилає HTTP-запит до WordPress. Сесійні куки wordpress_logged_in_* містять атрибути SameSite=Lax та Secure, запобігаючи надсиланню авторизаційних даних зі сторонніх сайтів.
Серверний модуль перехоплює запит і валідує одноразовий токен (WordPress Nonce) за допомогою wp_verify_nonce() або check_ajax_referer(). Токен прив'язаний до ID користувача, конкретної дії та часового вікна.
Сервер перевіряє гранулярні права поточного користувача через current_user_can(). Без цієї перевірки верифікація Nonce марна, оскільки будь-який авторизований користувач міг би надіслати валідний токен.
Сервер генерує криптографічно стійкий нонс (random_bytes) для заголовка Content-Security-Policy. Дані для фронтенду безпечно серіалізуються через wp_json_encode() з бітовими масками екранування HTML.
Виведення даних у шаблони огортається в контекстні функції екранування, а клієнтський JavaScript обробляє відповіді REST API через політики window.trustedTypes, виключаючи мутаційний та DOM XSS.
Access-Control-Allow-Origin: https://frontend.company.com), валідація попередніх запитів OPTIONS та безпечна передача JWT-токенів чи куки.wp_kses() із забороною небезпечних протоколів (javascript:, data:) та inline-атрибутів (onerror, onload).check_admin_referer() у зв'язці з current_user_can('manage_options') перед оновленням критичних налаштувань у таблиці wp_options.'unsafe-inline' на користь генерації криптографічних нонсів на хуку send_headers та прив'язка їх до хука wp_enqueue_scripts.Захист від XSS та CSRF ґрунтується на чіткому розділенні потоків довірених та недовірених даних.
Токен WordPress Nonce є не класичним одноразовим числом (Number Used Once), а HMAC-хешем з обмеженим терміном дії, прив'язаним до 12-годинних квантів часу (Time-bucketed hash). Формула обчислення токена всередині ядра:
HMAC-MD5( Action | User_ID | Token | Tick, Secret_Salt )Оскільки зловмисник на сторонньому домені не має доступу до локальної сесії користувача та секретних солей NONCE_SALT у wp-config.php, він не може згенерувати валідний токен. Сучасні браузери за замовчуванням призначають кукам статус SameSite=Lax, блокуючи передачу куки при автоматичних POST-запитах зі сторонніх сайтів. Проте покладатися виключно на SameSite=Lax неприпустимо, оскільки перехід за посиланням (GET-запит) або атаки через піддомени можуть обійти цей захист.
Поширена вразливість у темах і плагінах — спроба передати дані з PHP у JavaScript за допомогою простого виклику echo json_encode($data). Якщо масив містить користувацькі рядки зі знаками <script> або </script>, HTML-парсер браузера передчасно перериває виконання блоку <script>, передаючи керування пейлоаду зловмисника.
Стандарт 2026 року вимагає використання функції wp_json_encode() з обов'язковим набором бітових масок екранування HTML-сутностей:
// Безпечна серіалізація з екрануванням кутових дужок, лапок та амперсандів
$safe_json = wp_json_encode(
$client_data,
JSON_HEX_TAG | JSON_HEX_AMP | JSON_HEX_APOS | JSON_HEX_QUOT | JSON_UNESCAPED_UNICODE | JSON_THROW_ON_ERROR
);Нижче наведено завершений модуль безпеки для ядра та REST API, що реалізує динамічний CSP Level 3 з генерацією нонсів, безпечну ізоляцію CORS для Headless-архітектур, автоматичне встановлення прапорців SameSite та захист від XSS.
<?php
declare(strict_types=1);
namespace AgenticLab\Security;
use WP_Error;
use WP_REST_Request;
use WP_REST_Response;
/**
* Enterprise Security Shield.
*
* Комплексний модуль захисту від XSS, CSRF та небезпечного CORS.
* Сумісний із WordPress 7.0+ та PHP 8.4+.
*
* Author: Artur Yusupov (Fullstack Agentic Lab)
*/
final class AgenticSecurityShield {
private static ?self $instance = null;
private string $csp_nonce;
/**
* Дозволені довірені домени для Headless REST API.
* @var list<string>
*/
private const array ALLOWED_ORIGINS = [
'https://frontend.agenticlab.com',
'https://staging-frontend.agenticlab.com',
];
private function __construct() {
// Генерація криптографічно стійкого нонса для поточного HTTP-запиту
$this->csp_nonce = base64_encode(random_bytes(16));
$this->register_hooks();
}
public static function get_instance(): self {
return self::$instance ??= new self();
}
public function get_csp_nonce(): string {
return $this->csp_nonce;
}
private function register_hooks(): void {
// Заголовки безпеки HTTP
add_action('send_headers', [$this, 'inject_security_headers'], 1);
// Налаштування SameSite прапорців для авторизаційних кук
add_action('set_auth_cookie', [$this, 'enforce_samesite_cookies'], 10, 5);
// Захист і гранулярне налаштування CORS для REST API
add_action('rest_api_init', [$this, 'configure_rest_cors'], 15);
// Ін'єкція CSP-нонса в системні скрипти WordPress
add_filter('script_loader_tag', [$this, 'attach_nonce_to_script_tag'], 10, 3);
}
/**
* Впровадження корпоративних заголовків безпеки.
*/
public function inject_security_headers(): void {
if (headers_sent()) {
return;
}
// Захист від MIME-sniffing
header('X-Content-Type-Options: nosniff');
// Захист від Clickjacking (заборона відображення сайту в зовнішніх iframe)
header('X-Frame-Options: SAMEORIGIN');
// Ізоляція контекстів перегляду вікон верхнього рівня
header('Cross-Origin-Opener-Policy: same-origin');
header('Referrer-Policy: strict-origin-when-cross-origin');
// Примусовий HTTPS (HSTS) на 1 рік із піддоменами
if (is_ssl()) {
header('Strict-Transport-Security: max-age=31536000; includeSubDomains; preload');
}
// Content Security Policy Level 3 зі строгим нонсом для скриптів
$csp_directives = [
"default-src 'self'",
"script-src 'self' 'nonce-{$this->csp_nonce}' 'strict-dynamic' https:",
"style-src 'self' 'unsafe-inline' https://fonts.googleapis.com",
"font-src 'self' data: https://fonts.gstatic.com",
"img-src 'self' data: https: blob:",
"connect-src 'self' https://api.agenticlab.com",
"frame-ancestors 'self'",
"base-uri 'self'",
"form-action 'self'",
"object-src 'none'",
];
header('Content-Security-Policy: ' . implode('; ', $csp_directives));
}
/**
* Ін'єкція криптографічного нонса в усі теги <script>, зареєстровані WordPress.
*/
public function attach_nonce_to_script_tag(string $tag, string $handle, string $src): string {
if (empty($tag)) {
return $tag;
}
if (!str_contains($tag, 'nonce=')) {
return str_replace('<script ', sprintf('<script nonce="%s" ', esc_attr($this->csp_nonce)), $tag);
}
return $tag;
}
/**
* Примусове встановлення атрибута SameSite=Lax та прапорця Secure для кук сесії.
*/
public function enforce_samesite_cookies(string $cookie, int $expire, int $expiration, int $user_id, string $scheme): void {
if (!headers_sent() && is_ssl()) {
header_remove('Set-Cookie');
setcookie(
LOGGED_IN_COOKIE,
$cookie,
[
'expires' => $expire,
'path' => COOKIEPATH,
'domain' => COOKIE_DOMAIN,
'secure' => true,
'httponly' => true,
'samesite' => 'Lax',
]
);
}
}
/**
* Конфігурація безпечного CORS для Headless REST API.
*/
public function configure_rest_cors(): void {
remove_filter('rest_pre_serve_request', 'rest_send_cors_headers');
add_filter('rest_pre_serve_request', function ($value) {
$origin = get_http_origin();
// Перевіряємо вхідний Origin за суворим білим списком
if ($origin !== '' && in_array($origin, self::ALLOWED_ORIGINS, true)) {
header("Access-Control-Allow-Origin: {$origin}");
header('Access-Control-Allow-Methods: GET, POST, PUT, PATCH, DELETE, OPTIONS');
header('Access-Control-Allow-Credentials: true');
header('Access-Control-Allow-Headers: Authorization, X-WP-Nonce, Content-Type, X-Requested-With');
header('Access-Control-Max-Age: 86400');
}
// Обробка Preflight-запиту OPTIONS
if (isset($_SERVER['REQUEST_METHOD']) && $_SERVER['REQUEST_METHOD'] === 'OPTIONS') {
status_header(204);
exit;
}
return $value;
});
}
/**
* Безпечна серіалізація даних для виведення в клієнтський JS-контекст.
*
* @param array<string, mixed> $data
* @throws \JsonException
*/
public static function serialize_for_script(array $data): string {
return wp_json_encode(
$data,
JSON_HEX_TAG | JSON_HEX_AMP | JSON_HEX_APOS | JSON_HEX_QUOT | JSON_UNESCAPED_UNICODE | JSON_THROW_ON_ERROR
);
}
}
// Ініціалізація модуля
add_action('init', [AgenticSecurityShield::class, 'get_instance'], 0);
Запуск лінтерів із правилами WordPress-Coding-Standards та Psalm Security Plugin. Автоматичне виявлення неекранованих викликів echo, прямих звернень до суперглобальних масивів ($_GET, $_POST) та відсутності валідації Nonce.
Перевіряє 100% вихідних файлів плагінів перед збиранням релізів.
Аналіз HTTP-відповідей сервера утилітами типу curl та Mozilla Observatory. Контроль наявності заголовків CSP Level 3, HSTS, X-Content-Type-Options: nosniff та директиви SameSite для кук.
Цільова оцінка за шкалою Observatory — A+.
Аудит усіх кастомних роутів REST API на наявність функції permission_callback. Будь-який ендпоінт, що змінює стан даних, зобов'язаний вимагати автентифікацію та токен X-WP-Nonce.
Виключає анонімний виклик мутацій і витік приватних сутностей.
Інспекція фронтенд-бандлів Gutenberg та React-застосунків на предмет використання innerHTML, outerHTML та dangerouslySetInnerHTML. Впровадження DOMPurify та реєстрація Trusted Types Policy.
Гарантує захист від мутаційних та DOM-based XSS атак.
Захист від XSS та CSRF у сучасній розробці на WordPress 7.0+ спирається на парадигму Zero-Trust:
current_user_can().wp_json_encode з усіма масками JSON_HEX_* для JavaScript та пізнє контекстне екранування (esc_html, esc_attr, esc_url).Content-Security-Policy Level 3 зі строгими псевдовипадковими нонсами, суворий білий список CORS для Headless-архітектур та атрибути куки SameSite=Lax.