2026年11月12日

2026年11月12日

ห้ามรัน PHP ในไดเรกทอรี uploads ของ WordPress ด้วย .htaccess และ Nginx

บทความนี้เป็นฉบับปรับให้เหมาะกับท้องถิ่นจากต้นฉบับภาษาญี่ปุ่น อ่านต้นฉบับภาษาญี่ปุ่น

บทนำ

wp-content/uploads/ คือไดเรกทอรีที่ WordPress ใช้เก็บสื่อ เช่น รูปภาพ หากมีไฟล์ PHP ถูกวางไว้ในไดเรกทอรีนี้และอยู่ในสภาพที่รันได้ ผู้โจมตีจะอัปโหลดเว็บเชลล์ (backdoor) แล้วสั่งรันโค้ดใดก็ได้

อาการและสาเหตุ

  • การสแกนมัลแวร์พบไฟล์ PHP ใน wp-content/uploads/
  • ปลั๊กอินด้านความปลอดภัยเตือนว่า "รัน PHP ได้ในไดเรกทอรี uploads"
  • พบไฟล์น่าสงสัยในเส้นทางเช่น 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 ตั้งแต่ต้น

// ① เพิ่มนามสกุล PHP ลงในรายการที่ห้ามอัปโหลด
add_filter('upload_mimes', function(array $mimes): array {
    // ลบ MIME type ที่เกี่ยวกับ PHP (เพื่อความปลอดภัย)
    unset($mimes['php'], $mimes['phtml'], $mimes['phar']);
    return $mimes;
});

// ② ตรวจจับและปฏิเสธไฟล์ 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 type ด้วย
    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 "คำเตือน: พบไฟล์ PHP ในไดเรกทอรี uploads:
$PHP_FILES" | mail -s "[ด่วน] อาจมีการวางเว็บเชลล์ใน WordPress" "$ADMIN_EMAIL"

    # ลบอัตโนมัติ (ใช้ด้วยความระมัดระวัง)
    # find "$UPLOADS_DIR" -name "*.php" -delete
fi
EOF
chmod +x /usr/local/bin/check-uploads-php.sh

# ตรวจทุก 1 ชั่วโมง
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 ทำได้ดังนี้ ① ตรวจหาและลบไฟล์น่าสงสัยเดิมด้วย find wp-content/uploads -name ".php" ② บน Apache เพิ่ม Deny from all ใน uploads/.htaccess ③ บน Nginx ใช้ location ~ /wp-content/uploads/.*\.php$ กับ deny all ④ ปฏิเสธการอัปโหลด PHP ตั้งแต่ต้นด้วยฮุก wp_handle_upload_prefilter ⑤ เฝ้าระวังไฟล์ PHP ใน uploads ด้วย cron ทุก 1 ชั่วโมง

มีคำถามเกี่ยวกับ WordPress ไหม ผู้เผยแพร่เว็บไซต์นี้อยู่ที่ญี่ปุ่นและตอบเป็นภาษาญี่ปุ่น ติดต่อ (ภาษาญี่ปุ่น)

お気軽にご相談ください

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