2026年11月12日

2026年11月12日

Запрет выполнения PHP в каталоге uploads WordPress

Эта статья — локализованная версия японского оригинала. Читать японский оригинал

Введение

wp-content/uploads/ — каталог, где WordPress хранит изображения и другие медиафайлы. Если в нём можно разместить и запустить PHP-файл, злоумышленник может загрузить веб-шелл (бэкдор) и выполнять произвольный код.

Симптомы и причины

  • Сканер вредоносного ПО обнаружил PHP-файлы в wp-content/uploads/
  • Плагин безопасности предупредил: «В каталоге uploads можно выполнять PHP»
  • Подозрительный файл найден по пути вроде wp-content/uploads/2024/01/shell.php
  • Веб-шелл установили через уязвимость функции загрузки файлов

Порядок решения

Шаг 1. Найдите уже существующие подозрительные PHP-файлы

# Найти все PHP-файлы в каталоге uploads
find /var/www/html/wp-content/uploads -name "*.php" -type f
find /var/www/html/wp-content/uploads -name "*.phtml" -type f
find /var/www/html/wp-content/uploads -name "*.php5" -type f

# Проверить PHP-файлы, созданные или изменённые за последние 7 дней
find /var/www/html/wp-content/uploads -name "*.php" -mtime -7

# Проверить содержимое (характерные признаки вредоносного кода)
grep -rl "eval(base64_decode" /var/www/html/wp-content/uploads/
grep -rl "system\|exec\|passthru\|shell_exec" /var/www/html/wp-content/uploads/

# Удалить подозрительные PHP-файлы
find /var/www/html/wp-content/uploads -name "*.php" -delete
find /var/www/html/wp-content/uploads -name "*.phtml" -delete

Шаг 2. Запретите выполнение PHP в Apache (.htaccess)

# Создать wp-content/uploads/.htaccess
cat > /var/www/html/wp-content/uploads/.htaccess << 'EOF'
# Полностью запретить выполнение PHP-файлов
<FilesMatch "\.(php|phtml|php3|php4|php5|php7|phar|shtml)$">
    Order Deny,Allow
    Deny from all
</FilesMatch>

# Отключить сам движок PHP (при использовании mod_php)
<IfModule mod_php.c>
    php_flag engine off
</IfModule>

# Запретить и выполнение CGI
Options -ExecCGI
RemoveHandler .php .phtml .php5 .phar

# Отключить и листинг каталогов
Options -Indexes
EOF

chmod 644 /var/www/html/wp-content/uploads/.htaccess
# Проверка после настройки
# Создать тестовый PHP-файл и попробовать обратиться к нему
echo "<?php echo 'test'; ?>" > /var/www/html/wp-content/uploads/test_block.php
curl -o /dev/null -s -w "%{http_code}" https://example.com/wp-content/uploads/test_block.php
# Если возвращается 403, всё работает
rm /var/www/html/wp-content/uploads/test_block.php

Шаг 3. Запретите выполнение PHP в Nginx

# /etc/nginx/sites-available/example.com
server {
    # ... существующие настройки ...

    # Запретить выполнение PHP в каталоге uploads
    location ~* /wp-content/uploads/.*\.(php|phtml|php5|phar)$ {
        deny all;
        access_log off;
        log_not_found off;
    }

    # Писать выше location для PHP-FPM
    location ~ \.php$ {
        # Обычная обработка PHP
        fastcgi_pass unix:/var/run/php/php8.2-fpm.sock;
        fastcgi_index index.php;
        include fastcgi_params;
        fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
    }
}
# Проверить конфигурацию Nginx и применить её
sudo nginx -t && sudo systemctl reload nginx

# Проверка
curl -o /dev/null -s -w "%{http_code}" https://example.com/wp-content/uploads/shell.php
# Если возвращается 403, всё работает

Шаг 4. Заблокируйте загрузку PHP и на стороне WordPress

// functions.php: запретить саму загрузку PHP-файлов

// 1) Добавить расширения PHP в список запрещённых для загрузки
add_filter('upload_mimes', function(array $mimes): array {
    // Убрать MIME-типы, связанные с PHP (на всякий случай)
    unset($mimes['php'], $mimes['phtml'], $mimes['phar']);
    return $mimes;
});

// 2) Обнаружить PHP в загружаемом файле и отклонить его
add_filter('wp_handle_upload_prefilter', function(array $file): array {
    $dangerous_extensions = ['php', 'phtml', 'php3', 'php4', 'php5', 'phar', 'shtml'];
    $ext = strtolower(pathinfo($file['name'], PATHINFO_EXTENSION));

    if (in_array($ext, $dangerous_extensions, true)) {
        $file['error'] = 'Загрузка PHP-файлов запрещена.';
    }

    // Проверить и по MIME-типу
    if (str_contains($file['type'], 'php')) {
        $file['error'] = 'Загрузка PHP-файлов запрещена.';
    }

    return $file;
});

Шаг 5. Настройте регулярный контроль

# Скрипт для регулярного контроля PHP-файлов в uploads
cat > /usr/local/bin/check-uploads-php.sh << 'EOF'
#!/bin/bash
UPLOADS_DIR="/var/www/html/wp-content/uploads"
ADMIN_EMAIL="admin@example.com"

PHP_FILES=$(find "$UPLOADS_DIR" -name "*.php" -o -name "*.phtml" -o -name "*.phar" 2>/dev/null)

if [ -n "$PHP_FILES" ]; then
    echo "Внимание: в каталоге uploads обнаружены PHP-файлы:
$PHP_FILES" | mail -s "[СРОЧНО] Возможно, в WordPress установлен веб-шелл" "$ADMIN_EMAIL"

    # Автоматическое удаление (использовать осторожно)
    # find "$UPLOADS_DIR" -name "*.php" -delete
fi
EOF
chmod +x /usr/local/bin/check-uploads-php.sh

# Проверять каждый час
echo "0 * * * * root /usr/local/bin/check-uploads-php.sh" > /etc/cron.d/wp-uploads-php-check

Важные замечания

  • php_flag engine off в .htaccess действует только при использовании mod_php. Если вы используете php-fpm (FastCGI), применяйте директиву или блок location в Nginx
  • В Nginx, если правило блокировки для uploads не расположить перед location ~ \.php$, приоритет может получить второй блок, и файлы смогут выполняться. Обязательно размещайте правило блокировки выше
  • Некоторые плагины (например, Slider Revolution) могут размещать файлы .php внутри uploads. Перед удалением выясните их назначение и действуйте после удаления ненужных плагинов

Итоги

Запрет выполнения PHP в каталоге uploads состоит из пяти шагов: (1) найдите и удалите уже существующие подозрительные файлы командой find wp-content/uploads -name ".php"; (2) в Apache добавьте в uploads/.htaccess правило Deny from all; (3) в Nginx используйте deny all в location ~ /wp-content/uploads/.*\.php$; (4) отклоняйте саму загрузку PHP хуком wp_handle_upload_prefilter; (5) проверяйте PHP-файлы в uploads через cron раз в час.

Есть вопросы по WordPress? Издатель сайта находится в Японии и отвечает по-японски. Связаться (по-японски)

お気軽にご相談ください

お見積りへ お問い合わせへ
▲