📚 PHP 중급 - 4주차: 파일 업로드 및 이미지 처리 - 03 업로드된 파일 유효성 검사 (확장자, 크기)

2025. 7. 26. 11:48프로그램/PHP 중급

📚 PHP 중급 - 4주차: 파일 업로드 및 이미지 처리 - 03 업로드된 파일 유효성 검사 (확장자, 크기)

📚 PHP 중급 - 4주차: 파일 업로드 및 이미지 처리 - 03 업로드된 파일 유효성 검사 (확장자, 크기)



파일 #업로드 기능은 웹 애플리케이션에서 매우 흔하게 사용됩니다. 사용자로부터 파일을 받아 서버에 저장하는 것은 편리하지만, 동시에 보안과 안정성 측면에서 중요한 고려 사항을 안고 있습니다. 특히, 악의적인 파일 업로드를 통해 서버가 손상되거나 예측 불가능한 문제가 발생하는 것을 막기 위해 철저한 #유효성_검사(#validation)가 필수적입니다.

 


 

왜 파일 유효성 검사가 중요한가요?

파일 업로드 시 유효성 검사를 소홀히 하면 다음과 같은 심각한 문제가 발생할 수 있습니다.

  • 보안 취약점: 실행 가능한 스크립트 파일(.php, .asp, .exe 등)이 업로드되어 서버에서 실행될 경우, 웹쉘(webshell) 공격 등으로 이어져 서버가 완전히 장악될 수 있습니다.
  • 서비스 거부 (DoS) 공격: 매우 큰 파일이 지속적으로 업로드되어 서버의 디스크 공간이 고갈되거나, 네트워크 대역폭을 소모시켜 정상적인 서비스가 불가능해질 수 있습니다.
  • 데이터 오염: 예상치 못한 형식의 파일이 업로드되어 애플리케이션 로직에 오류를 발생시키거나, 데이터베이스에 잘못된 정보를 기록할 수 있습니다.
  • 사용자 경험 저하: 잘못된 파일 형식이나 너무 큰 파일 때문에 업로드에 실패했을 때, 적절한 피드백이 없으면 사용자가 혼란을 겪을 수 있습니다.

따라서 #파일_업로드 기능을 구현할 때는 서버 측에서 반드시 #확장자, #파일_크기, #MIME_타입 등을 꼼꼼하게 확인해야 합니다.

 


 

업로드된 파일 정보 확인하기

PHP에서 파일 업로드가 이루어지면, #$_FILES 전역 변수에 업로드된 파일의 정보가 배열 형태로 저장됩니다. 이 배열을 통해 파일의 이름, 타입, 크기, 임시 저장 경로 등을 확인할 수 있습니다.

예를 들어, <input type="file" name="my_file"> 형태로 파일을 업로드했다면, $_FILES['my_file'] 배열에는 다음과 같은 정보가 담깁니다.

PHP
 
$_FILES['my_file'] = [
    'name' => '원본_파일명.jpg',    // 클라이언트가 전송한 원본 파일 이름
    'type' => 'image/jpeg',      // 파일의 MIME 타입 (브라우저가 전송)
    'tmp_name' => '/tmp/phpXyZ123', // 서버에 임시로 저장된 파일의 경로
    'error' => 0,                // 파일 업로드 오류 코드 (0은 오류 없음)
    'size' => 123456             // 파일의 크기 (바이트 단위)
];

우리는 이 정보들을 활용하여 유효성 검사를 진행할 것입니다.

 


 

1. 확장자 유효성 검사

#확장자 검사는 파일의 종류를 제한하여 잠재적인 위협을 줄이는 중요한 단계입니다. 하지만 단순히 파일 이름의 확장자만 확인하는 것은 안전하지 않습니다. 악의적인 사용자는 파일 이름만 image.jpg.php와 같이 속일 수 있기 때문입니다. 따라서 #MIME_타입($_FILES['my_file']['type'])과 실제 파일 내용을 기반으로 한 검사를 병행하는 것이 좋습니다.

 

1-1. 허용된 확장자 리스트 정의

 

업로드를 허용할 확장자를 배열로 미리 정의합니다.

PHP
 
<?php
$allowed_extensions = ['jpg', 'jpeg', 'png', 'gif', 'pdf'];
$max_file_size = 5 * 1024 * 1024; // 5MB

if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_FILES['upload_file'])) {
    $file = $_FILES['upload_file'];

    // 파일 업로드 오류 확인
    if ($file['error'] !== UPLOAD_ERR_OK) {
        echo "파일 업로드 중 오류가 발생했습니다: " . $file['error'];
        exit;
    }.

 

1-2. 파일 이름에서 확장자 추출 및 검사

 

pathinfo() 함수를 사용하여 파일 이름에서 확장자를 추출하고, 허용된 리스트에 포함되는지 확인합니다.

PHP
 
    $file_extension = strtolower(pathinfo($file['name'], PATHINFO_EXTENSION));

    if (!in_array($file_extension, $allowed_extensions)) {
        echo "허용되지 않는 파일 확장자입니다. 허용되는 확장자: " . implode(', ', $allowed_extensions);
        exit;
    }.

 

1-3. MIME 타입 검사 (더 강력한 방법)

 

브라우저가 제공하는 #MIME_타입($_FILES['upload_file']['type'])을 확인하는 것은 일차적인 보안에 도움이 됩니다.

PHP
 
    // 브라우저가 제공하는 MIME 타입 검사 (일차적)
    $allowed_mime_types = [
        'image/jpeg',
        'image/png',
        'image/gif',
        'application/pdf'
    ];

    if (!in_array($file['type'], $allowed_mime_types)) {
        echo "허용되지 않는 파일 타입입니다. 실제 MIME 타입: " . $file['type'];
        exit;
    }.

더욱 강력한 검사를 위해서는 PHP의 finfo_open() 함수를 사용하여 파일의 실제 내용을 기반으로 MIME 타입을 확인하는 것이 좋습니다. 이는 파일 확장자를 속인 경우에도 실제 타입을 파악할 수 있게 해줍니다.

PHP
 
    // 실제 파일 내용 기반 MIME 타입 검사 (강력 추천)
    $finfo = finfo_open(FILEINFO_MIME_TYPE);
    $real_mime_type = finfo_file($finfo, $file['tmp_name']);
    finfo_close($finfo);

    if (!in_array($real_mime_type, $allowed_mime_types)) {
        echo "파일 내용과 일치하지 않는 타입입니다. 실제 타입: " . $real_mime_type;
        exit;
    }.

 


 

2. 파일 크기 유효성 검사

#파일_크기 검사는 서버의 디스크 공간을 보호하고 서비스 거부 공격을 방지하는 데 필수적입니다. PHP 설정(php.ini)에서 upload_max_filesize와 post_max_size로 최대 업로드 크기를 제한할 수 있지만, 애플리케이션 레벨에서 더 세밀하게 제어하는 것이 좋습니다.

 

2-1. 최대 파일 크기 정의

허용할 최대 파일 크기를 바이트 단위로 정의합니다.

PHP
 
    $max_file_size = 5 * 1024 * 1024; // 5MB로 제한

    if ($file['size'] > $max_file_size) {
        echo "파일 크기가 너무 큽니다. 최대 " . ($max_file_size / (1024 * 1024)) . "MB까지 업로드 가능합니다.";
        exit;
    }.

 


 

3. 최종 코드 예시 및 파일 이동

모든 유효성 검사를 통과한 파일은 원하는 서버 디렉토리로 이동시켜야 합니다. 이 때 move_uploaded_file() 함수를 사용하며, 파일 이름 충돌을 피하기 위해 유니크한 파일 이름을 생성하는 것이 일반적입니다.

PHP
 
<?php
// 업로드 설정
$upload_dir = 'uploads/'; // 파일을 저장할 디렉토리 (웹 서버가 쓰기 권한이 있어야 함)
$allowed_extensions = ['jpg', 'jpeg', 'png', 'gif', 'pdf'];
$allowed_mime_types = ['image/jpeg', 'image/png', 'image/gif', 'application/pdf'];
$max_file_size = 5 * 1024 * 1024; // 5MB

// 업로드 디렉토리가 없으면 생성 (권한 0755 또는 0777에 주의)
if (!is_dir($upload_dir)) {
    mkdir($upload_dir, 0755, true);
}

if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_FILES['upload_file'])) {
    $file = $_FILES['upload_file'];

    // 1. 파일 업로드 오류 확인
    if ($file['error'] !== UPLOAD_ERR_OK) {
        switch ($file['error']) {
            case UPLOAD_ERR_INI_SIZE:
            case UPLOAD_ERR_FORM_SIZE:
                echo "업로드된 파일이 PHP 설정된 최대 크기를 초과했습니다.";
                break;
            case UPLOAD_ERR_PARTIAL:
                echo "파일이 부분적으로만 업로드되었습니다.";
                break;
            case UPLOAD_ERR_NO_FILE:
                echo "업로드된 파일이 없습니다.";
                break;
            case UPLOAD_ERR_NO_TMP_DIR:
                echo "임시 디렉토리가 없습니다.";
                break;
            case UPLOAD_ERR_CANT_WRITE:
                echo "디스크에 파일을 쓸 수 없습니다.";
                break;
            case UPLOAD_ERR_EXTENSION:
                echo "PHP 확장 기능에 의해 파일 업로드가 중지되었습니다.";
                break;
            default:
                echo "알 수 없는 파일 업로드 오류가 발생했습니다.";
        }
        exit;
    }

    // 2. 파일 크기 유효성 검사
    if ($file['size'] > $max_file_size) {
        echo "파일 크기가 너무 큽니다. 최대 " . ($max_file_size / (1024 * 1024)) . "MB까지 업로드 가능합니다.";
        exit;
    }

    // 3. 확장자 검사
    $file_extension = strtolower(pathinfo($file['name'], PATHINFO_EXTENSION));
    if (!in_array($file_extension, $allowed_extensions)) {
        echo "허용되지 않는 파일 확장자입니다. 허용되는 확장자: " . implode(', ', $allowed_extensions);
        exit;
    }

    // 4. MIME 타입 검사 (가장 중요)
    $finfo = finfo_open(FILEINFO_MIME_TYPE);
    $real_mime_type = finfo_file($finfo, $file['tmp_name']);
    finfo_close($finfo);

    if (!in_array($real_mime_type, $allowed_mime_types)) {
        echo "파일 내용과 일치하지 않는 타입입니다. 실제 타입: " . $real_mime_type;
        exit;
    }

    // 모든 유효성 검사 통과 후 파일 이동
    // 파일 이름 충돌 방지를 위해 고유한 파일 이름 생성
    $new_file_name = uniqid() . '.' . $file_extension;
    $destination_path = $upload_dir . $new_file_name;

    if (move_uploaded_file($file['tmp_name'], $destination_path)) {
        echo "파일 업로드 및 유효성 검사 성공! 파일명: " . $new_file_name;
        // 이제 $destination_path에 저장된 파일에 대한 추가 작업을 할 수 있습니다 (예: 이미지 처리, DB 저장).
    } else {
        echo "파일을 지정된 디렉토리로 이동하는 데 실패했습니다.";
    }
} else {
    // 최초 페이지 로드 시 또는 잘못된 요청 시
    echo "
        <form action='' method='post' enctype='multipart/form-data'>
            <label for='upload_file'>파일 선택 (JPG, JPEG, PNG, GIF, PDF / 최대 5MB):</label><br>
            <input type='file' name='upload_file' id='upload_file'><br><br>
            <input type='submit' value='업로드'>
        </form>
    ";
}
?>

 


 

결론

 

#PHP 파일 #업로드 시 #유효성_검사는 웹 애플리케이션의 #보안과 #안정성을 지키는 핵심 요소입니다. 단순히 클라이언트 측 검사에 의존하지 않고, 항상 #서버_측에서 #확장자, #파일_크기, #MIME_타입 등을 꼼꼼하게 검증하는 습관을 들여야 합니다. 특히 finfo_open()을 사용한 #실제_MIME_타입 검사는 파일 #확장자_위변조 공격을 막는 데 매우 효과적입니다. 이러한 방어적인 프로그래밍 습관을 통해 더 견고하고 안전한 웹 서비스를 구축할 수 있습니다.

 

 

 

빠른속도, 간편한사용, 장애없는VPN, 사용이력없는 깨끗한 아이피

https://xn--299ao67b9qbmsf04c.net/

 

VPN 5,500원 / IP교체 1,100원 / 유동프록시 22,000원 | LuzenVPN 루젠VPN

국내최저가 고정IP서비스,유동프록시(IP4000개이상제공),PPTP,L2TP,IPSec,OpenVPNVPN,통신사VPN,VPN프로그램,고정IP,고정아이피,PPTP,저렴한VPN,리니지MVPN,리니지VPN,아이온VPN,던파VPN,유동프록시,유동PROXY,바이

vpn.luzensoft.com