Заплахата

Ако някога сте виждали странна заявка като тази ?действие=запазване_настройки&данни=… В лог файловете си вероятно сте се натъквали на най-честия сценарий през 2026 г. в WordPress: „практическо“ AJAX или REST действие, което в крайна сметка отваря задна врата.

Проблемът рядко идва от ядрото на WordPress 6.9.4. Според моя опит, инцидентът почти винаги започва от „бързо“ добавен фрагмент (functions.php, snippet plugin, mu-plugin) или малък домашно приготвен плъгин: липса на nonce, забравена проверка на капацитета или прекалено разрешителна проверка на входа.

На практика, ако чувствително действие (актуализиране на опции, запис на файлове, импортиране на съдържание, изпълнение на заявки) бъде разкрито без предпазни мерки, нападателят може:

  • променяне на опции (имейл администратор(URL адрес на сайта, SEO настройки, API ключове) за отвличане на сайта;
  • създайте администраторски акаунт чрез лошо защитена крайна точка;
  • инжектиране на съдържание (спам, линкове, скриптове), което влошава SEO и репутацията;
  • задействане на непряка RCE (напр. писане на PHP файл чрез нефилтриран импорт или зареждане на контролиран „шаблон“);
  • извличане на данни (имейли, поръчки от WooCommerce, формуляри), ако крайната точка върне твърде много информация.

Действителната честота варира в зависимост от повърхността на атаката (плъгини, публични крайни точки, формуляри). Автоматизираните сканирания са насочени предимно към:

  • персонализирани REST маршрути, които са неправилно удостоверени;
  • действията wp_ajax_nopriv_* които правят повече от това да „четат“;
  • front-end формуляри, които пишат в базата данни без еднократно запитване;
  • Импортирането на CSV/JSON файлове е „само за администратор“, но е достъпно без контрол на капацитета.

Рискът, казано по-просто: Мислите, че сте създали бутон „запазване“но понякога си създавал публичен формуляр за конфигурация достъпен за всеки, включително бот.

Бързо обобщение

  • Nonce + капацитет Действие, което променя нещо, трябва да удовлетворява еднократен номер (nonce). et капацитет (напр. manage_options).
  • REST > admin-ajax За нови разработки: ясно оторизиране, схема на аргументи, стандартизирани отговори.
  • Дезинфекция на входа, строга валидация, бягство на изхода три различни стъпки, които да се приложат на правилното място.
  • Никога не съхранявайте опционален „безплатен“ HTML/JS ако не контролирате кой може да пише (класически съхранен XSS).
  • Подсилете сървъра : деактивиране на редактирането на файлове, блокиране на изпълнението на PHP в uploadsДобавете заглавки.
  • Одит на крайните ви точки : търси wp_ajax_nopriv, register_rest_route et update_option във вашия код.

Уязвим код — какво НЕ трябва да правите

Реалистичен пример: разработчик добавя опция „saver“ чрез admin-ajax.php да контролирате промоционален банер (текст + URL адрес) от предния край. Работи... докато някой не открие действието и не презапише настройките ви.

<?php
/**
 * Exemple VULNÉRABLE : ne copiez pas ça.
 * Problèmes :
 * - action accessible aux non-connectés (nopriv)
 * - pas de nonce
 * - pas de contrôle de capacité
 * - validation insuffisante
 */
add_action( 'wp_ajax_nopriv_bpcab_save_promo', 'bpcab_save_promo_vulnerable' );
add_action( 'wp_ajax_bpcab_save_promo', 'bpcab_save_promo_vulnerable' );

function bpcab_save_promo_vulnerable() {
	// Mauvaise idée : on fait confiance à $_POST
	$text = isset( $_POST['text'] ) ? $_POST['text'] : '';
	$url  = isset( $_POST['url'] ) ? $_POST['url'] : '';

	// Mauvaise idée : update_option sans garde-fous
	update_option( 'bpcab_promo_text', $text );
	update_option( 'bpcab_promo_url', $url );

	wp_send_json_success( array(
		'message' => 'OK',
	) );
}

Ето какво се случва зад кулисите:

  • Куката wp_ajax_nopriv_... прави действието достъпно без да сте влезли в системата.
  • без нунцийВсеки сайт може да задейства заявката от браузъра на посетителя (CSRF) и всеки бот може да я извика директно.
  • без проверка на капацитета, дори абонатен акаунт (или компрометиран акаунт) може да променя настройки, които би трябвало да останат само за администратор.
  • Без валидиране отваряте вратата към неочаквани ценности и често към... Съхранен XSS ако след това покажете bpcab_promo_text без строго избягване.

Често срещан недостатък, който виждам, е, че кодът е копиран в functions.phpСлед това кодът се нарушава от липсваща запетая или точка и запетая. Резултатът: бял екран и сайтът се връща в режим „отстраняване на неизправности“, ако имате опцията за възстановяване. В WordPress 6.9.4 режимът на възстановяване помага, но не замества валидирането в режим на подготовка.

Сигурен код — правилната имплементация

За WordPress 6.9.4 предпочитам REST маршрут, а не admin-ajax.php Веднага щом коригирате настройките, получавате структуриран аргумент, последователни отговори и по-чисто удостоверяване.

Цел: същата функционалност (запазване на текст + URL адрес), но с:

  • заверка (потребителят е влязъл);
  • разрешението за (способност);
  • анти-CSRF (REST еднократен номер);
  • утвърждаване (видове, дължини, URL адрес);
  • саниране (почистване);
  • отговори за грешки използваем.

1) Регистрирайте REST маршрута с permission_callback

<?php
/**
 * Implémentation sécurisée (WordPress 6.9.4+, PHP 8.1+).
 * À placer dans un plugin (recommandé) ou un mu-plugin.
 */

add_action( 'rest_api_init', function () {
	register_rest_route(
		'bpcab/v1',
		'/promo',
		array(
			array(
				'methods'             => 'POST',
				'callback'            => 'bpcab_rest_save_promo',
				'permission_callback' => 'bpcab_rest_can_manage_promo',
				'args'                => array(
					'text' => array(
						'type'              => 'string',
						'required'          => true,
						'sanitize_callback' => 'sanitize_text_field',
						'validate_callback' => function ( $value ) {
							// Validation stricte : évite les payloads énormes et les surprises
							return is_string( $value ) && mb_strlen( $value ) >= 1 && mb_strlen( $value ) <= 140;
						},
					),
					'url'  => array(
						'type'              => 'string',
						'required'          => true,
						'sanitize_callback' => 'esc_url_raw',
						'validate_callback' => function ( $value ) {
							// Autorise uniquement http(s)
							if ( ! is_string( $value ) ) {
								return false;
							}
							$scheme = wp_parse_url( $value, PHP_URL_SCHEME );
							return in_array( $scheme, array( 'http', 'https' ), true );
						},
					),
				),
			),
		)
	);
} );

function bpcab_rest_can_manage_promo( WP_REST_Request $request ) : bool {
	// 1) L'utilisateur doit être connecté
	if ( ! is_user_logged_in() ) {
		return false;
	}

	// 2) Capacité : adaptez selon votre besoin (manage_options est un classique)
	return current_user_can( 'manage_options' );
}

Просто обяснение: пътят съществуваWordPress обаче ще отхвърли заявката, ако потребителят не е влязъл в системата или няма правилния капацитет.

Техническо обяснение: permission_callback се оценява преди callbackТова е основната ви бариера от страна на приложението. args Те налагат договор: тип, дезинфекция, валидиране. Това значително намалява грешките и заобиколните решения.

2) Обратно извикване: актуализиране на опциите с обработка на грешки

<?php
function bpcab_rest_save_promo( WP_REST_Request $request ) : WP_REST_Response|WP_Error {
	$text = (string) $request->get_param( 'text' );
	$url  = (string) $request->get_param( 'url' );

	// Défense en profondeur : même si args valide, on reste prudent
	$text = sanitize_text_field( $text );
	$url  = esc_url_raw( $url );

	if ( '' === $text ) {
		return new WP_Error(
			'bpcab_invalid_text',
			'Le texte ne peut pas être vide.',
			array( 'status' => 400 )
		);
	}

	if ( empty( $url ) ) {
		return new WP_Error(
			'bpcab_invalid_url',
			'URL invalide.',
			array( 'status' => 400 )
		);
	}

	update_option( 'bpcab_promo_text', $text, false );
	update_option( 'bpcab_promo_url', $url, false );

	return new WP_REST_Response(
		array(
			'success' => true,
			'data'    => array(
				'text' => $text,
				'url'  => $url,
			),
		),
		200
	);
}

Важен детайл: третият параметър на update_option ($autoload) е фиксиран на falseВиждал съм уебсайтове да се забавят, защото плъгин е съхранявал твърде много опции при автоматично зареждане. На страници с висок трафик това има значително влияние върху TTFB (време до първия байт).

3) От страна на JavaScript: REST nonce и fetch

Не е необходимо да публикувате „работещ“ пример. Но ви е необходимо правилно извикване от страна на front-end/администратора. REST nonce се извлича чрез wp_create_nonce( 'wp_rest' ) и се изпраща в заглавката X-WP-Nonce.

<?php
/**
 * Enqueue : charge un script et lui passe l'URL REST + nonce.
 * À utiliser dans l'admin, ou sur le front si nécessaire.
 */
add_action( 'admin_enqueue_scripts', function ( $hook ) {
	// Exemple : ne charger que sur une page d'options (à adapter)
	if ( 'settings_page_bpcab-promo' !== $hook ) {
		return;
	}

	wp_enqueue_script(
		'bpcab-promo',
		plugins_url( 'assets/promo.js', __FILE__ ),
		array(),
		'1.0.0',
		true
	);

	wp_localize_script(
		'bpcab-promo',
		'BPCAB_PROMO',
		array(
			'restUrl' => esc_url_raw( rest_url( 'bpcab/v1/promo' ) ),
			'nonce'   => wp_create_nonce( 'wp_rest' ),
		)
	);
} );
async function savePromo(text, url) {
  const res = await fetch(BPCAB_PROMO.restUrl, {
    method: 'POST',
    headers: {
      'Content-Type': 'application/json',
      'X-WP-Nonce': BPCAB_PROMO.nonce
    },
    body: JSON.stringify({ text, url })
  });

  const data = await res.json();
  if (!res.ok) {
    throw new Error(data?.message || 'Erreur lors de la sauvegarde');
  }
  return data;
}

Често срещани капани:

  • употреба admin-ajax.php „по навик“ и забравяйки това nopriv разкрива действието.
  • Поставете кода в грешен файл (напр. functions.php на родителската тема) и след това да го загубите при следващата актуализация.
  • Забравяте да изчистите кеша (плъгин за кеширане, кеш на сървъра, Cloudflare). Тествате поправка... но все още използвате стария JS.
  • Тествайте директно в продукцията, без да запазвате. В сайт на Elementor/Divi/Avada, една проста фатална грешка в PHP може също да повреди редактора.

Съвместимост Divi 5, Elementor, Avada

Този REST шаблон работи добре с конструкторите на страници, защото не зависи от специфично рендиране:

  • Диви 5 Използвайте персонализиран модул или „Кодов модул“ за потребителския интерфейс, но запазете резервното копие от REST страната. Избягвайте съхраняването на незадължителен безплатен HTML код, ако потребителският интерфейс е достъпен за лица без администраторски права.
  • Elementor : ако създавате уиджет, запазете конфигурацията чрез контролите на Elementor (които преминават през администраторския панел и функциите) и използвайте REST само за динамични действия.
  • Avada Същата логика важи и за Fusion Builder. „Активните“ действия трябва да останат само за администратор и защитени от nonce.

Конфигурация на сървъра

Сигурният код намалява риска, но повечето компромиси, които трябваше да отстраня, произтичат от верижна реакция: уязвим плъгин + разрешителен сървър. Укрепването на сървъра ограничава въздействието при повреда на компонент.

.htaccess (Apache): Блокиране на изпълнението на PHP при качвания

Ако вашият доставчик на хостинг услуги използва Apache и това .htaccess е активно, поставете го вътре wp-content/uploads/.htaccessЦелта: да се предотврати изпълнението на PHP скриптове, съхранени в качените файлове.

# Créez/éditez wp-content/uploads/.htaccess (Apache)
# Bloque l'exécution des fichiers PHP dans uploads
<FilesMatch ".(php|phtml|phar)$">
  Require all denied
</FilesMatch>

Краен случай: Някои много стари плъгини качват „помощници“ (лоша практика). С WordPress 6.9.4 избягвайте този тип плъгини. Ако нямате друга опция, изолирайте го и документирайте изключението.

.htaccess: Защита на wp-config.php и чувствителни файлове

# Dans le .htaccess à la racine WordPress (Apache)
<Files wp-config.php>
  Require all denied
</Files>

<FilesMatch "^(readme.html|license.txt)$">
  Require all denied
</FilesMatch>

wp-config.php: деактивиране на редактора на файлове

Това не блокира нападател, който вече има пълен администраторски достъп, но намалява щетите в сценарии, при които висока роля се получава чрез слабост.

<?php
// Désactive l'éditeur de fichiers dans l'admin (recommandé)
define( 'DISALLOW_FILE_EDIT', true );

// Optionnel : bloque aussi l'installation/MAJ via l'admin (à évaluer selon votre workflow)
// define( 'DISALLOW_FILE_MODS', true );

HTTP заглавки (Apache): намаляване на XSS/clickjacking

Тези заглавки не предотвратяват логически уязвимости, но ограничават някои въздействия. Винаги тествайте след добавянето им, особено ако имате легитимни iframe-ове (напр. интеграции).

# Dans .htaccess (si mod_headers est actif)
<IfModule mod_headers.c>
  Header always set X-Content-Type-Options "nosniff"
  Header always set Referrer-Policy "strict-origin-when-cross-origin"
  Header always set X-Frame-Options "SAMEORIGIN"
  Header always set Permissions-Policy "geolocation=(), microphone=(), camera=()"
</IfModule>

HTTPS и бисквитки

Ако вашият сайт е 100% HTTPS (което би трябвало да е така до 2026 г.), наложете SSL за администраторската област:

<?php
// Force SSL sur /wp-admin (si votre HTTPS est correctement configuré)
define( 'FORCE_SSL_ADMIN', true );

Проверете дали вашият сайт е уязвим

Започвам с едно просто правило: ако имате персонализиран код, имате крайни точки. А ако имате крайни точки, трябва да ги инвентаризирате.

Бърз одит чрез WP-CLI: търсене на опасни модели

На сървър с WP-CLI, започнете с намирането на публичните AJAX действия и актуализации на опциите.

# Rechercher des actions AJAX accessibles aux non-connectés
wp eval 'echo "Mu-plugins:n";' && grep -R --line-number "wp_ajax_nopriv_" wp-content/mu-plugins wp-content/plugins wp-content/themes 2>/dev/null

# Rechercher des endpoints REST custom
grep -R --line-number "register_rest_route" wp-content/mu-plugins wp-content/plugins wp-content/themes 2>/dev/null

# Rechercher des écritures sensibles
grep -R --line-number "update_option|add_option|set_transient|wp_insert_user|wp_update_user" wp-content/mu-plugins wp-content/plugins wp-content/themes 2>/dev/null

Какво търсите:

  • писане на обратни извиквания (update_option, wp_insert_user(запис на файл), достъпен от wp_ajax_nopriv ;
  • REST маршрути с permission_callback = __return_true (Виждал съм го в продукция, повече от веднъж);
  • слаби валидации (напр. sanitize_text_field използва се вместо проверка на формата).

Проверете лог файловете: типични сигнали

В лог файловете за достъп (Nginx/Apache) следете:

  • снимки на /wp-admin/admin-ajax.php с параметри на повтарящи се действия;
  • заявки ПУСНИ от /wp-json/ към неочаквани пространства от имена;
  • 401/403 кодове в пакети (опит за достъп);
  • празни или непоследователни потребителски агенти.

Диагностична диаграма

симптом Вероятна причина проверка Решение
Опции на сайта, които се променят „сами“ Незащитена AJAX/REST крайна точка, компрометиран администраторски акаунт ТЪРСИ update_option + публични действия; проверете лог файловете на admin-ajax.php Добавяне на nonce + capacity; премахване на nopriv нулиране на пароли и ключове
Спам, инжектиран в страниците/членове XSS атака, съхранена чрез нефилтрирана опция/мета данни или компрометиран акаунт на издател Сравняване на редакции, проверка на HTML съдържание; сканиране на опции за автоматично зареждане Почистване на съдържанието; ограничаване на разрешения HTML код; затягане на ролите/възможностите
Пренасочва към външен сайт Опция siteurl/home модифициран, злонамерен плъгин, инжектиран JS Контрол home/siteurl в базата данни; проверете wp_options Възстановяване на стойности; премахване на задната вратичка; добавяне на контроли и WAF
Натоварване на процесора admin-ajax.php Злоупотреба с публична крайна точка (DoS за приложение) Най-често срещани URL адреси (GoAccess), регистрационни файлове; профилиране на заявки Ограничаване на публичните действия; кеш; ограничение на скоростта (сървър/WAF)

Често срещани грешки в сигурността

Грешка малко неприличен Решение
употреба wp_ajax_nopriv_* за писмено действие Неавтентична модификация, ботове, CSRF Изтрий nopriv или ограничение до четене; изисква auth + nonce
Проверете еднократния случай, но не и възможностите Основен акаунт може да извършва администраторски действия current_user_can() систематично за всички чувствителни действия
„Общо“ дезинфекциране вместо валидиране Непоследователни данни, заобиколни решения, логически грешки Валидиране на формат/дължина/бял списък + подходяща дезинфекция
По желание съхранявайте HTML/JS и го показвайте без екраниране. Съхранен XSS Избягвайте свободния HTML; в противен случай wp_kses() + esc_html()/esc_attr()
Копирайте откъс от стар урок Остарял API, уязвимости, несъвместимости между WP 6.9.4 и PHP 8.1 Проверете на developer.wordpress.org; тествайте в режим на подготовка
Поставете кода в родителската тема Неуспешна актуализация, неработещ уебсайт Специализиран плъгин или mu-плъгин; дъщерна тема, ако е необходимо
Лошо закачане (напр. REST регистриран твърде рано или твърде късно) Маршрутът е недостъпен, заобиколни решения чрез резервен вариант употреба rest_api_init За REST, специални куки за администратор
Забравяне да се изчисти кешът след поправката Мислиш, че си го поправил, но старият JS все още работи Почистване на кеша на плъгина/CDN/браузъра; ресурси за версията
Тестване в производствена среда без резервно копие Прекъсване, загуба на данни План за подготовка + архивиране + връщане към предишни етапи

Контролен списък за закаляване

  • Актуализация WordPress 6.9.4+, теми и плъгини (и премахнете тези, които вече не са необходими).
  • PHP 8.1 + (в идеалния случай 8.2/8.3, ако вашият стек го поддържа) и актуални разширения.
  • Деактивиране на редактора : DISALLOW_FILE_EDIT.
  • Блокиране на PHP при качвания (еквивалент на Apache/Nginx) и проверете разрешенията за файлове.
  • Принудително HTTPS + FORCE_SSL_ADMIN ако е приложимо.
  • Крайни точки на инвентара REST маршрути, AJAX действия, входящи уеб куки.
  • За всички писания : nonce + капацитет + стриктна валидация + лог файлове.
  • Ограничете ролите без споделено администриране, поименни акаунти, 2FA, ако е възможно.
  • архивиране Ежедневни задачи, тестване на реставрации, съхранение извън обекта.
  • Дневник : лог файлове на сървъра + лог файлове на приложенията за чувствителни действия.

Ами ако сайтът вече е компрометиран?

Ако подозирате компрометиране, грешният подход е „просто да промените администраторската парола“. Ако има задна вратичка, тя ще пресъздаде достъпа.

  1. Превключете сайта в режим на поддръжка (или поне блокирайте администратора), докато нещата се стабилизират. Ако имате CDN/WAF, активирайте режим на подобрена защита.
  2. Създайте криминалистично резервно копие (файлове + база данни) преди почистване. Това ще ви е необходимо, за да разберете входните данни и да докажете какво се е променило.
  3. Идентифицирайте входната точка :
    • наскоро добавени/актуализирани плъгини;
    • фрагменти в плъгина functions.php / snippets;
    • неизвестни администраторски акаунти;
    • Персонализирани REST/AJAX маршрути без permission_callback/nonce.
  4. Преинсталирайте ядрото на WordPress правилно (сменете wp-admin et wp-includes (от официален източник), без презаписване wp-content за момента.
  5. Чисто wp-съдържание :
    • премахнете неизвестни теми/плъгини;
    • преинсталирайте от wordpress.org/plugins необходимите плъгини;
    • търсене на скорошни PHP файлове в uploads.
  6. Одит на базата данни :
    • администраторски акаунти;
    • подозрителни опции (огромно автоматично зареждане, скриптове);
    • инжектирано съдържание (статии, джаджи, шаблони).
  7. Нулиране на всички тайни :
    • пароли (WP, FTP/SSH, база данни, хостинг);
    • Ключове за сигурност на WordPress (AUTH_KEYи др.);
    • API токени (SMTP, CDN, плащане, reCAPTCHA).
  8. Внедряване на контроли WAF, 2FA, ограничение на опитите, известия за създаване от администратор.
  9. Проверете доставката : почистване на кешове, сканиране на предния край (HTML код) за инжектирани скриптове, повторно тестване на критични пътища (вход, формуляри, плащане).

Полезен съвет: ако имате WP-CLI, избройте администраторите и потърсете скорошни акаунти.

wp user list --role=administrator --fields=ID,user_login,user_email,user_registered

Съвети за поддръжка и съвместимост

В WordPress 6.9.4, ядрото осигурява солидна основа, но сигурността ви зависи от вашата дисциплина на разработка.

Предпочитайте плъгин (или mu-плъгин) за кода за сигурност.

Поставянето на вашите REST/AJAX крайни точки в тема (дори в дъщерна тема) е лоша идея. Често съм виждал как редизайнът на Divi/Elementor нарушава защитна корекция, защото темата се е променила. Малък плъгин „site“ е по-стабилен и с по-мощни версии.

Производителност: внимавайте за опциите за автоматично зареждане

Всяка опция за автоматично зареждане се зарежда при повечето заявки. Ако съхранявате полезни товари (JSON, HTML, списъци), увеличавате размера на файла. alloptionsНа сайтове на Avada с много опции това може да се превърне в пречка.

SEO: Компромисите оставят следи

След почистване, наблюдавайте:

  • Search Console (спам страници, пренасочвания);
  • генерирани карти на сайтове;
  • инжектирани изходящи връзки;
  • Време за реакция (зловреден софтуер = процесор).

Бъдеща съвместимост

Избягвайте фрагменти, които зависят от вътрешни имплементации. Придържайте се към документирани API: REST API, Настройки API, API опции. Когато мигрирате остарял AJAX код към REST, предвидете преходен период, но премахнете старата крайна точка възможно най-скоро.


ресурси

ЧЗВ

Трябва ли да се откажа от admin-ajax.php?

Не. За прости, вътрешни администраторски действия, wp_ajax_* остава валидно. Но за структурирани и поддържаеми крайни точки, REST API обикновено е по-чист. Неподлежащият на обсъждане момент: еднократно число + капацитет за всеки акт на писане.

Достатъчен ли е nonce в WordPress, за да се осигури действие?

Не. Еднократният номер основно ограничава CSRF. Той не замества контрола на капацитета. В реални инциденти съм виждал правилно имплементирани еднократни номера... но достъпни за роли с твърде ниско ниво на достъп чрез front-end страница.

Защо да валидирам дължината на текста, ако вече съм го „санифицирал“?

Защото санитизацията не налага спазването на бизнес правилата ви и не предпазва от огромни полезни товари. Валидирането на дължината предотвратява злоупотреби (съхранение, лог файлове, производителност) и неочаквано поведение.

Къде трябва да се постави този код: functions.php, плъгин за snippets, персонализиран плъгин?

От съображения за безопасност, избягвайте functions.php на родителската тема. Едно персонализирани добавки (или mu-plugin) е по-надежден, с възможност за промяна на версиите и не изчезва при смяна на теми (Divi/Elementor/Avada).

Получавам грешки 403 по REST маршрута след добавяне на nonce. Нормално ли е това?

Често „да“: липсващ/изтекъл nonce, неизпратени „бисквитки“ или междудомейно повикване. Проверете дали изпращате X-WP-Nonceче потребителят е правилно свързан и че повикването е насочено към същия домейн (внимавайте със среди с www/без www).

Как мога да предотвратя нарушаването на тестовете ми за сигурност от кеш?

Версия на вашите активи (настройка на версията в wp_enqueue_script), изчистете кеша на плъгина/CDN и тествайте в режим на частно сърфиране. Често съм виждал „неефективни“ поправки, които са били просто изчистване на кеша.

DISALLOW_FILE_EDIT блокира ли хакер?

Това блокира често срещана техника (редактиране на теми/плъгини от администраторския панел). Ако атакуващият вече има FTP/SSH достъп или RCE, това не е достатъчно. Но в сценарий на защита в дълбочина, това е проста и полезна настройка.

Как бързо да идентифицирам XSS, съхранени чрез опции?

Внимавайте за големи опции за автоматично зареждане и търсете фрагменти <script, onerror=, javascript: в базата данни. След това, коригирайте в източника: кой може да пише тези опции и как се показват (екраниране)?

Какво е минималното изискване за крайна точка, която променя базата данни?

Удостоверяване (влязъл потребител), оторизация (възможност), еднократен номер (anti-CSRF), стриктно валидиране на параметри и регистриране на чувствителни действия (поне при debug/staging).

Моят плъгин на трета страна предоставя публичен REST маршрут. Трябва ли да се паникьосвам?

Не е задължително. Обществен път на видно място може да е нормален. Опасният е обществен път, който писмен или което разкрива чувствителни данни. Одит permission_callback и разрешените методи (POST/PUT/DELETE).