2026年11月12日

2026年11月12日

Chặn thực thi PHP trong thư mục uploads của WordPress

Đây là phiên bản được bản địa hóa từ bài viết gốc tiếng Nhật. Đọc bài gốc tiếng Nhật

Giới thiệu

wp-content/uploads/ là thư mục nơi WordPress lưu hình ảnh và các tệp media khác. Nếu tệp PHP có thể được đặt vào thư mục này và thực thi được, kẻ tấn công có thể tải lên một web shell (backdoor) và chạy mã tùy ý.

Triệu chứng và nguyên nhân

  • Quét mã độc phát hiện tệp PHP bên trong wp-content/uploads/
  • Plugin bảo mật cảnh báo "PHP có thể thực thi trong thư mục uploads"
  • Tìm thấy tệp đáng ngờ ở đường dẫn như wp-content/uploads/2024/01/shell.php
  • Web shell được cài đặt bằng cách khai thác lỗ hổng của chức năng tải tệp lên

Các bước khắc phục

Bước 1: Phát hiện các tệp PHP đáng ngờ đang có

# Tìm toàn bộ tệp PHP trong thư mục 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

# Kiểm tra các tệp PHP được tạo hoặc cập nhật gần đây (trong 7 ngày)
find /var/www/html/wp-content/uploads -name "*.php" -mtime -7

# Kiểm tra nội dung tệp (các mẫu đặc trưng của mã độc)
grep -rl "eval(base64_decode" /var/www/html/wp-content/uploads/
grep -rl "system\|exec\|passthru\|shell_exec" /var/www/html/wp-content/uploads/

# Xóa các tệp PHP đáng ngờ
find /var/www/html/wp-content/uploads -name "*.php" -delete
find /var/www/html/wp-content/uploads -name "*.phtml" -delete

Bước 2: Chặn thực thi PHP trên Apache (.htaccess)

# Tạo wp-content/uploads/.htaccess
cat > /var/www/html/wp-content/uploads/.htaccess << 'EOF'
# Cấm hoàn toàn việc thực thi tệp PHP
<FilesMatch "\.(php|phtml|php3|php4|php5|php7|phar|shtml)$">
    Order Deny,Allow
    Deny from all
</FilesMatch>

# Tắt chính PHP engine (khi dùng mod_php)
<IfModule mod_php.c>
    php_flag engine off
</IfModule>

# Cấm cả việc thực thi CGI
Options -ExecCGI
RemoveHandler .php .phtml .php5 .phar

# Tắt luôn việc liệt kê nội dung thư mục
Options -Indexes
EOF

chmod 644 /var/www/html/wp-content/uploads/.htaccess
# Kiểm tra sau khi thiết lập
# Tạo một tệp PHP thử nghiệm và thử truy cập
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
# Nhận được 403 là thành công
rm /var/www/html/wp-content/uploads/test_block.php

Bước 3: Chặn thực thi PHP trên Nginx

# /etc/nginx/sites-available/example.com
server {
    # ... cấu hình hiện có ...

    # Cấm thực thi PHP trong thư mục uploads
    location ~* /wp-content/uploads/.*\.(php|phtml|php5|phar)$ {
        deny all;
        access_log off;
        log_not_found off;
    }

    # Phải viết trước location của PHP-FPM
    location ~ \.php$ {
        # Xử lý PHP thông thường
        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;
    }
}
# Kiểm tra cấu hình Nginx rồi áp dụng
sudo nginx -t && sudo systemctl reload nginx

# Kiểm tra
curl -o /dev/null -s -w "%{http_code}" https://example.com/wp-content/uploads/shell.php
# Nhận được 403 là thành công

Bước 4: Chặn cả việc tải tệp PHP lên từ phía WordPress

// functions.php: cấm chính việc tải tệp PHP lên

// (1) Thêm phần mở rộng PHP vào danh sách cấm tải lên
add_filter('upload_mimes', function(array $mimes): array {
    // Loại bỏ các kiểu MIME liên quan đến PHP (để phòng ngừa)
    unset($mimes['php'], $mimes['phtml'], $mimes['phar']);
    return $mimes;
});

// (2) Phát hiện và từ chối PHP trong tệp tải lên
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'] = 'Không được phép tải tệp PHP lên.';
    }

    // Kiểm tra cả theo kiểu MIME
    if (str_contains($file['type'], 'php')) {
        $file['error'] = 'Không được phép tải tệp PHP lên.';
    }

    return $file;
});

Bước 5: Thiết lập giám sát định kỳ

# Script giám sát định kỳ các tệp PHP trong 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 "Cảnh báo: phát hiện tệp PHP trong thư mục uploads:
$PHP_FILES" | mail -s "[KHẨN CẤP] WordPress có thể đã bị cài web shell" "$ADMIN_EMAIL"

    # Tự động xóa (dùng thận trọng)
    # find "$UPLOADS_DIR" -name "*.php" -delete
fi
EOF
chmod +x /usr/local/bin/check-uploads-php.sh

# Kiểm tra mỗi giờ
echo "0 * * * * root /usr/local/bin/check-uploads-php.sh" > /etc/cron.d/wp-uploads-php-check

Lưu ý

  • php_flag engine off trong .htaccess chỉ có hiệu lực khi dùng mod_php. Nếu bạn dùng php-fpm (FastCGI), hãy xử lý bằng chỉ thị <FilesMatch> hoặc bằng khối location của Nginx
  • Với Nginx, nếu không viết quy tắc chặn cho uploads trước location ~ \.php$, có thể quy tắc sau sẽ được ưu tiên và PHP vẫn chạy được. Hãy luôn đặt quy tắc chặn ở phía trên
  • Một số plugin (ví dụ Slider Revolution) có thể đặt tệp .php trong uploads. Trước khi xóa, hãy kiểm tra mục đích của tệp, và gỡ các plugin không cần thiết trước rồi mới xử lý

Tổng kết

Để chặn thực thi PHP trong thư mục uploads: (1) phát hiện và xóa các tệp đáng ngờ có sẵn bằng find wp-content/uploads -name ".php"; (2) với Apache, thêm <FilesMatch "\.(php|phar)$"> Deny from all vào uploads/.htaccess; (3) với Nginx, đặt location ~ /wp-content/uploads/.*\.php$ là deny all; (4) từ chối chính việc tải PHP lên bằng hook wp_handle_upload_prefilter; (5) giám sát tệp PHP trong uploads bằng cron chạy mỗi giờ.

Có câu hỏi về WordPress? Đơn vị xuất bản trang này ở Nhật Bản và trả lời bằng tiếng Nhật. Liên hệ (tiếng Nhật)

お気軽にご相談ください

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