title: 'Безпека ядра: Nonces, Sanitization, Late Escaping та фільтрація wp_kses' description: >- Глибокий інженерний розбір захисного конвеєра WordPress 7.0+ (PHP 8.4+): математична будова Nonce-токенів (wp_nonce_tick), контекстно-залежна дезінфекція вхідних даних, Late Escaping безпосередньо перед виведенням і продуктивна фільтрація HTML через wp_kses. module: security order: 1 difficulty: advanced estimatedTime: 40 хв topics:
Безпека серверного моноліта WordPress та його REST-інтерфейсів базується на Тріаді Глибокого Захисту (Defense-in-Depth):
$_POST, $_GET, REST payload) перед їх збереженням у базу даних.Поширена інженерна омана — вважати Nonces у WordPress «одноразовими криптографічними числами» (Number Used Once). В архітектурі ядра WordPress нонс — це сесійний хеш-токен з ковзним часовим вікном (12–24 години), прив'язаний до ID користувача, токена сесії та назви дії. Він не запобігає повторним відправкам однакових запитів (Replay Attacks), а захищає суто від міжсайтової підробки запитів (CSRF).
Другий критичний рубіж — Late Escaping (екранування в останній момент). Дані в базі даних (wp_posts, wp_postmeta) повинні зберігатися в канонічному (незаекранованому в HTML-сутності) вигляді, а трансформація через esc_html(), esc_attr(), esc_url() або гранулярний wp_kses() зобов'язана відбуватися суто в точці виведення (echo). Це гарантує, що якщо сторонній плагін або прямий SQL-запит змінить значення в базі, браузер клієнта все одно отримає безпечний текст.
Уявіть конвеєр безпеки WordPress як систему контролю доступу на закритий хімічний завод:
wp_verify_nonce / check_admin_referer): якщо кур'єр намагається пронести розпорядження директора без свіжого бейджа дня (CSRF-атака зі сторонньої вкладки), ворота блокуються.sanitize_text_field, absint) перед тим, як покласти її на склад (MySQL).esc_html), щоб робітник під час розпакування не обпік руки (браузер відрендерив текст як літери, а не як шкідливий <script>).ВХІДНІ ДАНІ (HTTP POST / REST)
│
▼
[1. CSRF ПЕРЕВІРКА]: wp_verify_nonce($nonce, 'action')
├── НЕВАЛІДНИЙ ──► wp_die('Security Check Failed', 403)
└── ВАЛІДНИЙ
│
▼
[2. SANITIZATION]: sanitize_text_field(), absint(), sanitize_email()
│
▼
[3. БАЗА ДАНИХ]: Чисті дані в MySQL (wp_postmeta, wp_options)
│
▼
[4. ТОЧКА ВИВЕДЕННЯ (LATE ESCAPING)]:
├── У текст тега ──────► esc_html($val)
├── В атрибут тега ────► esc_attr($val)
├── В URL / посилання ─► esc_url($val)
└── У багату розмітку ─► wp_kses($val, $allowed)Функція wp_create_nonce() хешує wp_nonce_tick() (вікно 12 годин), UID користувача, session_token та сіль сайту через wp_hash().
Обробник перевіряє токен через check_admin_referer() (для POST-форм) або check_ajax_referer() / rest_cookie_check_errors() (для API).
Параметри очищаються: цілі числа через absint(), рядки через sanitize_text_field() (видаляє null-байти, теги та зайві пробіли).
Дані записуються в БД без HTML-сутностей (&, "), зберігаючи канонічний вигляд для пошуку та експорту.
Безпосередньо перед викликом echo дані проганяються через esc_html/esc_attr або компілюються через whitelist-фільтр wp_kses().
В інженерному стеку Fullstack Agentic Lab тріада безпеки застосовується для:
X-WP-Nonce або cookie сесії.wp_kses() для відгуків, технічних специфікацій та профілів інженерів.WordPress-Extra (PHP_CodeSniffer) із забороною прямого echo $variable без esc_*.sanitize_callback при виклику register_setting() та register_post_meta().Порівняємо три підходи до обробки даних у кодовій базі WordPress:
Нижче наведено еталонний виробничий артефакт для WordPress 7.0+ та PHP 8.4+, що демонструє реалізацію захищеного сервісу прийому архітектурних специфікацій із повною тріадою захисту: перевіркою Nonce, суворою дезінфекцією, кешованим білим списком wp_kses та безпечним виведенням.
<?php
/**
* Plugin Name: Fullstack Agentic Lab — Enterprise Security Gateway
* Description: Тріада безпеки: Nonce верифікація, Input Sanitization та Late Escaping.
* Version: 3.0.0
* Requires at least: 6.8
* Requires PHP: 8.4
* Author: Artur Yusupov (Fullstack Agentic Lab)
*/
declare(strict_types=1);
namespace AgenticLab\Security;
use WP_Error;
defined('ABSPATH') || exit;
final readonly class SecureSubmissionGateway
{
private const string NONCE_ACTION = 'agentic_lab_submit_spec_v1';
private const string NONCE_FIELD = '_agentic_sec_token';
public function registerHooks(): void
{
add_action('admin_post_agentic_submit_spec', $this->handleFormSubmission(...));
}
/**
* Обробник вхідної POST-форми з повним конвеєром перевірки безпеки.
*/
public function handleFormSubmission(): void
{
// 1. ПЕРШИЙ РУБІЖ (CSRF): Перевірка Nonce-токена
// check_admin_referer зупиняє виконання з wp_die() при невалідному токені
check_admin_referer(self::NONCE_ACTION, self::NONCE_FIELD);
// 2. ДРУГИЙ РУБІЖ (RBAC): Перевірка прав доступу поточного користувача
if (!current_user_can('edit_posts')) {
wp_die(
esc_html__('Недостатньо привілеїв для виконання операції.', 'agentic-lab'),
esc_html__('Помилка авторизації', 'agentic-lab'),
['response' => 403]
);
}
// 3. ТРЕТІЙ РУБІЖ (SANITIZATION): Сувора дезінфекція вхідного потоку
$rawTitle = (string) ($_POST['spec_title'] ?? '');
$rawEmail = (string) ($_POST['contact_email'] ?? '');
$rawTargetId = $_POST['target_cluster_id'] ?? 0;
$rawHtmlBody = (string) ($_POST['spec_architecture_html'] ?? '');
// Очищаємо кожне поле суворо відповідно до його семантичного типу
$cleanTitle = sanitize_text_field($rawTitle);
$cleanEmail = sanitize_email($rawEmail);
$cleanTargetId = absint($rawTargetId);
// Для форматованого HTML-тексту застосовуємо суворий білий список wp_kses
$cleanHtmlBody = wp_kses($rawHtmlBody, $this->getAllowedHtmlSchema());
if ($cleanTitle === '' || $cleanEmail === '' || $cleanTargetId === 0) {
wp_die(
esc_html__('Некоректні параметри запиту.', 'agentic-lab'),
esc_html__('Помилка валідації', 'agentic-lab'),
['response' => 400]
);
}
// Зберігаємо в канонічному вигляді в базу даних (MySQL)
$recordId = wp_insert_post([
'post_title' => $cleanTitle,
'post_content' => $cleanHtmlBody,
'post_status' => 'pending',
'post_type' => 'spec_submission',
'meta_input' => [
'_agentic_contact_email' => $cleanEmail,
'_agentic_cluster_id' => $cleanTargetId,
'_agentic_submitted_at' => gmdate('c'),
],
]);
if (is_wp_error($recordId)) {
wp_die(esc_html($recordId->get_error_message()), 500);
}
// Безпечний редирект на екран успіху
wp_safe_redirect(add_query_arg('status', 'submitted', wp_get_referer() ?: admin_url()));
exit;
}
/**
* Білий список дозволеної розмітки (White List) для безпечного HTML.
* Забороняє <script>, <iframe>, <style>, <svg>, <object> та будь-які inline-події on*.
*
* @return array<string, array<string, bool>>
*/
private function getAllowedHtmlSchema(): array
{
return [
'h3' => ['class' => true],
'h4' => ['class' => true],
'p' => ['class' => true],
'strong' => [],
'em' => [],
'code' => ['class' => true],
'pre' => ['class' => true],
'ul' => ['class' => true],
'ol' => ['class' => true],
'li' => ['class' => true],
'a' => [
'href' => true,
'title' => true,
'target' => true,
'rel' => true,
'class' => true,
],
];
}
/**
* 4. ЧЕТВЕРТИЙ РУБІЖ (LATE ESCAPING): Безпечний рендеринг у поданні.
*/
public static function renderSubmissionCard(int $submissionId): void
{
$submission = get_post($submissionId);
if (!$submission) {
return;
}
$email = (string) get_post_meta($submissionId, '_agentic_contact_email', true);
$clusterId = (int) get_post_meta($submissionId, '_agentic_cluster_id', true);
$statusUrl = admin_url("admin.php?page=cluster-view&id={$clusterId}");
// КРИТИЧНО: Екрануємо змінні суворо за їхнім контекстом у момент виклику echo!
?>
<article class="agentic-spec-card" id="submission-<?php echo esc_attr((string) $submissionId); ?>">
<header class="agentic-spec-card__header">
<h3><?php echo esc_html($submission->post_title); ?></h3>
<span class="badge"><?php echo esc_html("Кластер #{$clusterId}"); ?></span>
</header>
<div class="agentic-spec-card__body">
<?php
// Для форматованого HTML викликаємо wp_kses_post
echo wp_kses_post($submission->post_content);
?>
</div>
<footer class="agentic-spec-card__footer">
<a href="<?php echo esc_url("mailto:{$email}"); ?>" class="email-link">
<?php echo esc_html($email); ?>
</a>
<a href="<?php echo esc_url($statusUrl); ?>" class="button button-secondary">
<?php echo esc_html__('Відкрити в консолі', 'agentic-lab'); ?>
</a>
</footer>
</article>
<?php
}
}
(new SecureSubmissionGateway())->registerHooks();
Перевірте токен через check_admin_referer() (POST-форми), check_ajax_referer() (AJAX) або заголовок X-WP-Nonce (REST API).
Переконайтеся через current_user_can(), що поточний користувач має конкретне право (capability), а не просто авторизований.
Пропустіть параметри через sanitize_text_field(), sanitize_email(), absint() і запишіть чисті канонічні дані в БД.
При рендері застосуйте esc_html() для тексту, esc_attr() для атрибутів, esc_url() для посилань і wp_kses() для складної верстки.