2026年11月12日

2026年11月12日

WordPress uploads 디렉터리에서 PHP 실행을 금지하는 방법

이 글은 일본어 원문을 현지화한 버전입니다. 일본어 원문 보기

들어가며

wp-content/uploads/는 WordPress가 이미지 등 미디어를 저장하는 디렉터리입니다. 이 디렉터리에 PHP 파일이 설치되어 실행 가능한 상태라면, 공격자가 웹 셸(백도어)을 업로드해 임의의 코드를 실행할 수 있습니다.

증상·원인

  • 멀웨어 스캔에서 wp-content/uploads/ 안에 PHP 파일이 검출되었다
  • 보안 플러그인이 "uploads 디렉터리에서 PHP를 실행할 수 있습니다"라고 경고했다
  • wp-content/uploads/2024/01/shell.php 같은 경로에서 수상한 파일이 발견되었다
  • 파일 업로드 기능의 취약점을 이용해 웹 셸이 설치되었다

해결 절차

1단계: 기존의 수상한 PHP 파일 검출하기

# uploads 디렉터리 안의 PHP 파일을 모두 검색
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

# 최근(7일 이내) 생성·수정된 PHP 파일 확인
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단계: Apache에서 PHP 실행 금지하기(.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단계: Nginx에서 PHP 실행 금지하기

# /etc/nginx/sites-available/example.com
server {
    # ... 기존 설정 ...

    # uploads 디렉터리에서의 PHP 실행을 금지
    location ~* /wp-content/uploads/.*\.(php|phtml|php5|phar)$ {
        deny all;
        access_log off;
        log_not_found off;
    }

    # PHP-FPM의 location보다 앞에 작성할 것
    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단계: WordPress에서도 PHP 업로드 차단하기

// functions.php: PHP 파일의 업로드 자체를 금지

// ① PHP 확장자를 업로드 금지 목록에 추가
add_filter('upload_mimes', function(array $mimes): array {
    // PHP 관련 MIME 타입을 제거(만약을 위해)
    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 타입으로도 확인
    if (str_contains($file['type'], 'php')) {
        $file['error'] = 'PHP 파일 업로드는 금지되어 있습니다.';
    }

    return $file;
});

5단계: 정기 감시 설정하기

# uploads 안의 PHP 파일을 정기적으로 감시하는 스크립트
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

# 1시간마다 확인
echo "0 * * * * root /usr/local/bin/check-uploads-php.sh" > /etc/cron.d/wp-uploads-php-check

주의 사항

  • .htaccess의 php_flag engine off는 mod_php를 사용할 때만 유효합니다. php-fpm(FastCGI)을 사용 중이라면 지시자 또는 Nginx의 location 블록으로 대응하세요
  • Nginx에서는 location ~ \.php$보다 앞에 uploads 차단 규칙을 작성하지 않으면 뒤쪽 규칙이 우선되어 실행될 수 있습니다. 차단 규칙은 반드시 위쪽에 배치하세요
  • 일부 플러그인(예: Slider Revolution 등)은 uploads 안에 .php 파일을 설치하는 경우가 있습니다. 삭제하기 전에 용도를 확인하고, 불필요한 플러그인을 삭제한 뒤에 대응하세요

정리

uploads 디렉터리의 PHP 실행 금지는 ① find wp-content/uploads -name ".php"로 기존의 수상한 파일을 검출·삭제, ② Apache는 uploads/.htaccess에 Deny from all을 추가, ③ Nginx는 location ~ /wp-content/uploads/.*\.php$로 deny all, ④ wp_handle_upload_prefilter 훅으로 PHP 업로드 자체를 거부, ⑤ 1시간마다 cron으로 uploads 안의 PHP 파일을 감시하는 순서로 진행합니다.

WordPress에 대해 궁금한 점이 있으신가요? 이 사이트의 운영사는 일본에 있으며 일본어로 답변합니다. 문의하기(일본어)

お気軽にご相談ください

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