Аудит безопасности сайта на Drupal
Drupal исторически имеет хорошую репутацию в безопасности — выделенная Security Team, SA (Security Advisory) система, регулярные патчи. Тем не менее устаревшие модули, неправильная конфигурация и кастомный код создают уязвимости.
Инструменты аудита
# Проверка обновлений безопасности
drush pm:security
# Детальный список уязвимых пакетов
composer audit
# Drupal Security Scanner (внешний)
# https://www.drupal.org/security/advisories
Контрольный список
Ядро и модули:
# Все ли обновления безопасности применены?
drush pm:security --format=table
# PHP-версия
php -v # минимум 8.1, рекомендуется 8.3
Права доступа к файлам:
# settings.php должен быть только на чтение
stat web/sites/default/settings.php
# 444 или 400 — правильно
# 644 или 755 — слишком открыто
# Директория files
find web/sites/default/files -name "*.php" -type f
# PHP-файлы в /files — признак взлома
# Директории с правильными правами
find web -type d -perm /o+w -not -path "*/\.*"
settings.php аудит:
// Должно быть:
$settings['hash_salt'] = 'длинная-случайная-строка'; // уникальна для каждого сайта
$config['system.logging']['error_level'] = 'hide'; // не показывать ошибки
$settings['trusted_host_patterns'] = [ // список разрешённых хостов
'^yourdomain\.com$',
'^www\.yourdomain\.com$',
];
// Не должно быть:
// $settings['rebuild_access'] = TRUE; // только для разработки
// $config['system.performance']['cache']['page']['max_age'] = 0; // не в production
Trusted Host Patterns — критично. Без этой настройки сайт уязвим к Host header injection attacks.
Пользователи и роли:
# Список администраторов
drush sql:query "SELECT name, mail FROM users u JOIN user__roles ur ON u.uid = ur.entity_id WHERE ur.roles_target_id = 'administrator'"
# Проверить анонимные разрешения
drush role:list --filter=anonymous
Конфигурация безопасности:
# Включён ли Security Review?
composer require drupal/security_review
drush en security_review -y
drush secrev --store # запустить проверку
Security Review проверяет: права файлов, разрешения ролей, критические настройки PHP, input filters, приватные файлы.
Анализ кастомного кода
XSS-уязвимости:
# Поиск небезопасного вывода в Twig
grep -r "raw" web/themes/custom/ web/modules/custom/
# {{ var | raw }} без предварительной санитизации — уязвимо
# В PHP
grep -r "print_r\(\$_\|echo \$_\|print \$_" web/modules/custom/
SQL Injection:
// Небезопасно (не делать так):
$result = \Drupal::database()->query("SELECT * FROM node WHERE title = '" . $input . "'");
// Правильно:
$result = \Drupal::database()->query(
"SELECT * FROM node WHERE title = :title",
[':title' => $input]
);
CSRF защита:
// Все формы должны использовать FormAPI или явную проверку токена
$form['#token'] = 'my_operation';
// Или в кастомном контроллере
$this->csrfToken->validate($token, 'my-route');
File upload:
# Разрешённые расширения файлов в полях
drush config:get field.field.node.article.field_attachment | grep "file_extensions"
# Никогда не разрешать: php, phtml, phar, js, html
Заголовки безопасности
// services.yml или кастомный EventSubscriber
class SecurityHeadersSubscriber implements EventSubscriberInterface {
public function onResponse(ResponseEvent $event): void {
$response = $event->getResponse();
$response->headers->set('X-Content-Type-Options', 'nosniff');
$response->headers->set('X-Frame-Options', 'SAMEORIGIN');
$response->headers->set('Referrer-Policy', 'strict-origin-when-cross-origin');
$response->headers->set('Permissions-Policy', 'camera=(), microphone=(), geolocation=()');
}
}
Приватные файлы
// settings.php: хранить приватные файлы вне web root
$settings['file_private_path'] = '/var/private/drupal-files';
Документы, доступные только авторизованным — никогда не в sites/default/files (публичная директория).
Сроки
Аудит безопасности Drupal с отчётом — 1–2 дня. Устранение критических уязвимостей — дополнительно 1–3 дня.







