코딩 스쿨 PHP

언어선택 : HTMLCSSJAVAJAVASCRIPTMYSQLSQL PHP

PHP Filesystem

PHP 파일 시스템 참조: 파일 및 디렉토리 관리


PHP 파일 시스템(File System) 기능은 웹 애플리케이션에서 파일과 디렉토리를 생성, 읽기, 수정, 삭제하는 등 다양한 작업을 수행할 수 있도록 도와줍니다. PHP는 강력한 내장 함수를 제공하여 파일과 디렉토리를 효율적으로 관리할 수 있게 합니다. 이 가이드는 PHP에서 파일 시스템을 다루는 방법과 관련 함수들을 상세히 설명하며, 실용적인 예제를 통해 효과적인 파일 및 디렉토리 관리를 도와드립니다.


1. PHP 파일 시스템 함수 개요

PHP는 파일과 디렉토리를 관리하기 위해 다양한 내장 함수를 제공합니다. 이러한 함수들은 파일 생성, 읽기, 쓰기, 삭제, 권한 변경, 디렉토리 생성 및 삭제 등을 가능하게 합니다. 주요 파일 시스템 관련 함수들은 다음과 같습니다:

  • 파일 작업 함수:
    • fopen(), fclose()
    • fread(), fwrite()
    • file_get_contents(), file_put_contents()
    • unlink(), rename(), copy()
    • file_exists(), is_readable(), is_writable()
  • 디렉토리 작업 함수:
    • opendir(), readdir(), closedir()
    • mkdir(), rmdir()
    • scandir(), glob()
    • is_dir(), file_exists()
  • 파일 권한 및 소유권 함수:
    • chmod(), chown(), chgrp()
  • 고급 파일 시스템 함수:
    • realpath()
    • basename(), dirname()
    • pathinfo()

2. 파일 작업

2.1 파일 열기 및 닫기: fopen()fclose()

파일을 열고 닫는 기본적인 방법은 fopen()fclose() 함수를 사용하는 것입니다. fopen()은 파일을 열고 파일 핸들을 반환하며, fclose()는 열린 파일을 닫습니다.

예시: 파일 열기 및 닫기

<?php
$filename = "example.txt";

// 파일 열기 (쓰기 모드)
$fileHandle = fopen($filename, "w");

if ($fileHandle) {
    echo "파일을 성공적으로 열었습니다.<br>";
    // 파일 작업 수행
    fclose($fileHandle);
    echo "파일을 성공적으로 닫았습니다.";
} else {
    echo "파일을 여는 데 실패했습니다.";
}
?>

설명:

  • "w" 모드는 쓰기 전용으로 파일을 열며, 파일이 존재하지 않으면 새로 생성합니다.
  • 파일을 연 후에는 반드시 fclose()를 사용하여 파일을 닫아야 합니다.

2.2 파일 읽기 및 쓰기: fread()fwrite()

파일에서 데이터를 읽고 쓰기 위해 fread()fwrite() 함수를 사용할 수 있습니다.

예시: 파일에 데이터 쓰기 및 읽기

<?php
$filename = "example.txt";

// 파일 열기 (쓰기 모드)
$fileHandle = fopen($filename, "w");

if ($fileHandle) {
    $data = "안녕하세요, PHP 파일 시스템!";
    fwrite($fileHandle, $data);
    fclose($fileHandle);
    echo "파일에 데이터를 성공적으로 썼습니다.<br>";
} else {
    echo "파일을 여는 데 실패했습니다.";
}

// 파일 열기 (읽기 모드)
$fileHandle = fopen($filename, "r");

if ($fileHandle) {
    $content = fread($fileHandle, filesize($filename));
    fclose($fileHandle);
    echo "파일 내용:<br>" . nl2br(htmlspecialchars($content));
} else {
    echo "파일을 여는 데 실패했습니다.";
}
?>

설명:

  • fwrite()는 지정된 파일 핸들에 데이터를 씁니다.
  • fread()는 파일 핸들로부터 데이터를 읽습니다. 두 번째 인자는 읽을 바이트 수를 지정합니다.
  • filesize() 함수는 파일의 크기를 바이트 단위로 반환합니다.

2.3 파일 전체 읽기 및 쓰기: file_get_contents()file_put_contents()

파일의 전체 내용을 한 번에 읽거나 쓰는 경우 file_get_contents()file_put_contents() 함수를 사용할 수 있습니다.

예시: 파일에 데이터 쓰기 및 전체 읽기

<?php
$filename = "example.txt";
$data = "안녕하세요, PHP 파일 시스템!\\\\n두 번째 줄입니다.";

// 파일에 데이터 쓰기
$result = file_put_contents($filename, $data);

if ($result !== false) {
    echo "파일에 데이터를 성공적으로 썼습니다.<br>";
} else {
    echo "파일에 데이터를 쓰는 데 실패했습니다.<br>";
}

// 파일 전체 읽기
$content = file_get_contents($filename);

if ($content !== false) {
    echo "파일 내용:<br>" . nl2br(htmlspecialchars($content));
} else {
    echo "파일을 읽는 데 실패했습니다.";
}
?>

설명:

  • file_put_contents()는 파일에 데이터를 쓰며, 기존 내용을 덮어씁니다. 추가 쓰기를 원할 경우 FILE_APPEND 플래그를 사용할 수 있습니다.
  • file_get_contents()는 파일의 전체 내용을 문자열로 반환합니다.

2.4 파일 삭제: unlink()

파일을 삭제하려면 unlink() 함수를 사용합니다.

예시: 파일 삭제

<?php
$filename = "example.txt";

if (file_exists($filename)) {
    if (unlink($filename)) {
        echo "파일을 성공적으로 삭제했습니다.";
    } else {
        echo "파일 삭제에 실패했습니다.";
    }
} else {
    echo "파일이 존재하지 않습니다.";
}
?>

설명:

  • file_exists() 함수는 파일의 존재 여부를 확인합니다.
  • unlink() 함수는 파일을 삭제하며, 성공 시 true를 반환합니다.

2.5 파일 복사 및 이동: copy()rename()

파일을 복사하거나 이동하려면 copy()rename() 함수를 사용할 수 있습니다.

예시: 파일 복사

<?php
$source = "source.txt";
$destination = "destination.txt";

// 파일 복사
if (copy($source, $destination)) {
    echo "파일을 성공적으로 복사했습니다.";
} else {
    echo "파일 복사에 실패했습니다.";
}
?>

예시: 파일 이동 또는 이름 변경

<?php
$source = "destination.txt";
$destination = "moved.txt";

// 파일 이동 또는 이름 변경
if (rename($source, $destination)) {
    echo "파일을 성공적으로 이동 또는 이름을 변경했습니다.";
} else {
    echo "파일 이동 또는 이름 변경에 실패했습니다.";
}
?>

설명:

  • copy() 함수는 소스 파일을 대상으로 복사합니다.
  • rename() 함수는 파일을 이동하거나 이름을 변경합니다.

3. 디렉토리 작업

3.1 디렉토리 생성: mkdir()

새로운 디렉토리를 생성하려면 mkdir() 함수를 사용합니다.

예시: 디렉토리 생성

<?php
$dirPath = "new_directory";
$permissions = 0755; // 읽기, 쓰기, 실행 권한

if (!is_dir($dirPath)) {
    if (mkdir($dirPath, $permissions, true)) { // true는 중첩 디렉토리 생성 허용
        echo "디렉토리를 성공적으로 생성했습니다.";
    } else {
        echo "디렉토리 생성에 실패했습니다.";
    }
} else {
    echo "디렉토리가 이미 존재합니다.";
}
?>

설명:

  • mkdir() 함수의 세 번째 인수는 재귀적으로 디렉토리를 생성할지 여부를 결정합니다.
  • 0755는 디렉토리 권한을 설정합니다. 이는 소유자에게 읽기, 쓰기, 실행 권한을 부여하고, 그룹 및 기타 사용자에게는 읽기 및 실행 권한을 부여합니다.

3.2 디렉토리 삭제: rmdir()

비어 있는 디렉토리를 삭제하려면 rmdir() 함수를 사용합니다.

예시: 비어 있는 디렉토리 삭제

<?php
$dirPath = "new_directory";

if (is_dir($dirPath)) {
    if (rmdir($dirPath)) {
        echo "디렉토리를 성공적으로 삭제했습니다.";
    } else {
        echo "디렉토리 삭제에 실패했습니다. 디렉토리가 비어 있지 않거나 권한이 부족할 수 있습니다.";
    }
} else {
    echo "디렉토리가 존재하지 않습니다.";
}
?>

비어 있지 않은 디렉토리 삭제:

비어 있지 않은 디렉토리를 삭제하려면, 먼저 디렉토리 내의 모든 파일과 서브디렉토리를 삭제해야 합니다.

예시: 비어 있지 않은 디렉토리 삭제

<?php
function deleteDirectory($dir) {
    if (!file_exists($dir)) {
        return true;
    }

    if (!is_dir($dir)) {
        return unlink($dir);
    }

    foreach (scandir($dir) as $item) {
        if ($item == '.' || $item == '..') {
            continue;
        }

        if (!deleteDirectory($dir . DIRECTORY_SEPARATOR . $item)) {
            return false;
        }
    }

    return rmdir($dir);
}

$dirPath = "non_empty_directory";

if (deleteDirectory($dirPath)) {
    echo "디렉토리를 성공적으로 삭제했습니다.";
} else {
    echo "디렉토리 삭제에 실패했습니다.";
}
?>

설명:

  • 재귀 함수를 사용하여 디렉토리 내의 모든 파일과 서브디렉토리를 삭제한 후, 최종적으로 디렉토리를 삭제합니다.
  • 이 방법은 디렉토리 트리 전체를 삭제할 때 유용합니다.

3.3 디렉토리 내용 읽기: scandir()glob()

디렉토리 내의 파일과 폴더 목록을 읽어들이기 위해 scandir()glob() 함수를 사용할 수 있습니다.

예시: scandir() 사용

<?php
$dirPath = "some_directory";

if (is_dir($dirPath)) {
    $files = scandir($dirPath);
    echo "디렉토리 목록:<br>";
    foreach ($files as $file) {
        if ($file != '.' && $file != '..') {
            echo "파일/폴더명: $file<br>";
        }
    }
} else {
    echo "디렉토리가 존재하지 않습니다.";
}
?>

예시: glob() 사용

<?php
$dirPath = "some_directory";
$pattern = "*.txt"; // .txt 확장자를 가진 파일들

$files = glob($dirPath . DIRECTORY_SEPARATOR . $pattern);

echo ".txt 파일 목록:<br>";
foreach ($files as $file) {
    echo "파일명: " . basename($file) . "<br>";
}
?>

설명:

  • scandir() 함수는 지정된 디렉토리의 모든 항목을 배열로 반환합니다.
  • glob() 함수는 특정 패턴과 일치하는 파일 경로 목록을 배열로 반환하며, 와일드카드를 사용하여 원하는 파일을 필터링할 수 있습니다.

4. 파일 권한 및 소유권

4.1 파일 권한 변경: chmod()

파일이나 디렉토리의 권한을 변경하려면 chmod() 함수를 사용합니다.

예시: 파일 권한 변경

<?php
$filePath = "example.txt";
$newPermissions = 0644; // 소유자에게는 읽기 및 쓰기 권한, 그룹 및 기타 사용자에게는 읽기 권한만

if (chmod($filePath, $newPermissions)) {
    echo "파일 권한을 성공적으로 변경했습니다.";
} else {
    echo "파일 권한 변경에 실패했습니다.";
}
?>

설명:

  • chmod() 함수는 파일의 권한을 변경하며, 권한은 4진수로 지정합니다.
  • 0644는 소유자에게 읽기 및 쓰기 권한을, 그룹 및 기타 사용자에게는 읽기 권한만 부여합니다.

4.2 파일 소유자 변경: chown()

파일이나 디렉토리의 소유자를 변경하려면 chown() 함수를 사용합니다.

예시: 파일 소유자 변경

<?php
$filePath = "example.txt";
$newOwner = "www-data"; // 예: Apache의 기본 사용자

if (chown($filePath, $newOwner)) {
    echo "파일 소유자를 성공적으로 변경했습니다.";
} else {
    echo "파일 소유자 변경에 실패했습니다.";
}
?>

설명:

  • chown() 함수는 파일의 소유자를 변경합니다.
  • 이 함수는 서버의 권한 설정에 따라 제한될 수 있으며, 대부분의 경우 웹 서버 사용자가 소유자를 변경할 수 있습니다.

4.3 파일 그룹 변경: chgrp()

파일이나 디렉토리의 그룹을 변경하려면 chgrp() 함수를 사용합니다.

예시: 파일 그룹 변경

<?php
$filePath = "example.txt";
$newGroup = "www-data"; // 예: Apache의 기본 그룹

if (chgrp($filePath, $newGroup)) {
    echo "파일 그룹을 성공적으로 변경했습니다.";
} else {
    echo "파일 그룹 변경에 실패했습니다.";
}
?>

설명:

  • chgrp() 함수는 파일의 그룹을 변경합니다.
  • 이 함수 역시 서버의 권한 설정에 따라 제한될 수 있습니다.

5. 파일 경로 다루기

5.1 절대 경로와 상대 경로

  • 절대 경로(Absolute Path): 파일 시스템의 루트부터 시작하는 전체 경로입니다. 예: /var/www/html/index.php
  • 상대 경로(Relative Path): 현재 스크립트의 위치를 기준으로 하는 경로입니다. 예: ../images/photo.jpg

예시: 절대 경로와 상대 경로 사용

<?php
// 절대 경로 사용
$absolutePath = "/var/www/html/index.php";

// 상대 경로 사용
$relativePath = "../images/photo.jpg";

echo "절대 경로: $absolutePath<br>";
echo "상대 경로: $relativePath";
?>

5.2 realpath() 함수

realpath() 함수는 경로의 실제 절대 경로를 반환하며, 심볼릭 링크를 해석하고, 상대 경로를 절대 경로로 변환합니다.

예시: realpath() 사용

<?php
$relativePath = "../images/photo.jpg";
$absolutePath = realpath($relativePath);

if ($absolutePath) {
    echo "실제 절대 경로: $absolutePath";
} else {
    echo "경로를 찾을 수 없습니다.";
}
?>

설명:

  • realpath() 함수는 경로가 유효하지 않으면 false를 반환합니다.
  • 경로를 검증하고, 안전하게 다루기 위해 유용합니다.

5.3 basename()dirname() 함수

  • basename(): 경로에서 파일명만 추출합니다.
  • dirname(): 경로에서 디렉토리 부분만 추출합니다.

예시: basename()dirname() 사용

<?php
$path = "/var/www/html/index.php";

echo "파일명: " . basename($path) . "<br>"; // index.php
echo "디렉토리: " . dirname($path) . "<br>"; // /var/www/html
?>

5.4 pathinfo() 함수

pathinfo() 함수는 파일 경로에 대한 정보를 배열로 반환합니다. 파일명, 확장자, 디렉토리 등을 포함합니다.

예시: pathinfo() 사용

<?php
$path = "/var/www/html/index.php";
$info = pathinfo($path);

echo "파일명: " . $info['basename'] . "<br>"; // index.php
echo "확장자: " . $info['extension'] . "<br>"; // php
echo "디렉토리: " . $info['dirname'] . "<br>"; // /var/www/html
?>


6. 파일 읽기 및 쓰기 고급 기능

6.1 바이너리 파일 처리

바이너리 파일(예: 이미지, PDF)을 처리할 때는 fopen()의 모드를 적절히 설정하고, 데이터를 바이너리 모드로 읽고 써야 합니다.

예시: 바이너리 파일 읽기 및 쓰기

<?php
$source = "image.jpg";
$destination = "copy_image.jpg";

// 바이너리 모드로 파일 열기
$sourceHandle = fopen($source, "rb");
$destinationHandle = fopen($destination, "wb");

if ($sourceHandle && $destinationHandle) {
    while (!feof($sourceHandle)) {
        $buffer = fread($sourceHandle, 4096);
        fwrite($destinationHandle, $buffer);
    }
    fclose($sourceHandle);
    fclose($destinationHandle);
    echo "바이너리 파일을 성공적으로 복사했습니다.";
} else {
    echo "파일을 여는 데 실패했습니다.";
}
?>

설명:

  • "rb": 읽기 전용 바이너리 모드
  • "wb": 쓰기 전용 바이너리 모드
  • 바이너리 파일은 텍스트 파일과 달리 데이터를 그대로 복사해야 합니다.

6.2 스트림 래퍼(Stream Wrappers)

PHP는 다양한 스트림 래퍼를 지원하여 원격 파일 시스템에 접근하거나, 압축된 파일을 다룰 수 있습니다. 예를 들어, http://, ftp://, php://memory 등의 스트림 래퍼가 있습니다.

예시: HTTP 스트림을 사용한 파일 읽기

<?php
$url = "<http://www.example.com/>";
$content = file_get_contents($url);

if ($content !== false) {
    echo "웹 페이지 내용:<br>" . htmlspecialchars($content);
} else {
    echo "웹 페이지를 읽는 데 실패했습니다.";
}
?>

예시: 메모리 스트림을 사용한 데이터 처리

<?php
// 메모리 스트림 열기
$fp = fopen('php://memory', 'w+');

fwrite($fp, "메모리 스트림에 쓰기");
rewind($fp);
$content = fread($fp, 1024);
fclose($fp);

echo "메모리 스트림 내용: " . htmlspecialchars($content);
?>


7. SPL 파일 시스템 클래스

PHP의 SPL(Standard PHP Library)은 파일 시스템을 객체 지향적으로 다룰 수 있는 클래스들을 제공합니다. 이를 통해 더 유연하고 강력한 파일 및 디렉토리 관리를 할 수 있습니다.

7.1 FilesystemIterator 클래스

FilesystemIterator 클래스는 디렉토리 내의 파일과 폴더를 반복적으로 처리할 수 있게 해줍니다.

예시: FilesystemIterator 사용

<?php
$dirPath = "some_directory";

if (is_dir($dirPath)) {
    $iterator = new FilesystemIterator($dirPath, FilesystemIterator::SKIP_DOTS);

    foreach ($iterator as $fileinfo) {
        echo $fileinfo->getFilename() . " - " . ($fileinfo->isDir() ? "디렉토리" : "파일") . "<br>";
    }
} else {
    echo "디렉토리가 존재하지 않습니다.";
}
?>

설명:

  • FilesystemIterator::SKIP_DOTS 옵션은 ... 항목을 건너뜁니다.
  • getFilename(), isDir(), isFile() 등의 메서드를 사용하여 파일 정보를 얻을 수 있습니다.

7.2 DirectoryIterator 클래스

DirectoryIterator 클래스는 디렉토리의 파일과 폴더를 순차적으로 반복할 수 있게 해줍니다.

예시: DirectoryIterator 사용

<?php
$dirPath = "some_directory";

if (is_dir($dirPath)) {
    $iterator = new DirectoryIterator($dirPath);

    foreach ($iterator as $fileinfo) {
        if (!$fileinfo->isDot()) {
            echo $fileinfo->getFilename() . " - " . ($fileinfo->isDir() ? "디렉토리" : "파일") . "<br>";
        }
    }
} else {
    echo "디렉토리가 존재하지 않습니다.";
}
?>

설명:

  • isDot() 메서드는 ... 항목을 확인합니다.
  • getFilename(), isDir(), isFile() 등의 메서드를 사용하여 파일 정보를 얻을 수 있습니다.

7.3 RecursiveDirectoryIterator 클래스

RecursiveDirectoryIterator 클래스는 재귀적으로 디렉토리를 탐색할 수 있게 해줍니다.

예시: RecursiveDirectoryIterator 사용

<?php
$dirPath = "some_directory";

if (is_dir($dirPath)) {
    $iterator = new RecursiveIteratorIterator(
        new RecursiveDirectoryIterator($dirPath, RecursiveDirectoryIterator::SKIP_DOTS),
        RecursiveIteratorIterator::SELF_FIRST
    );

    foreach ($iterator as $fileinfo) {
        echo str_repeat("-", $iterator->getDepth()) . " " . $fileinfo->getFilename() . " - " . ($fileinfo->isDir() ? "디렉토리" : "파일") . "<br>";
    }
} else {
    echo "디렉토리가 존재하지 않습니다.";
}
?>

설명:

  • RecursiveIteratorIteratorRecursiveDirectoryIterator를 결합하여 디렉토리 트리를 깊이 우선으로 탐색합니다.
  • getDepth() 메서드를 사용하여 현재 깊이를 알 수 있습니다.

8. 보안 고려사항

파일 시스템을 다룰 때는 보안에 대한 철저한 고려가 필요합니다. 잘못된 파일 접근이나 권한 설정은 웹 애플리케이션의 취약점을 초래할 수 있습니다.

8.1 파일 업로드 보안

사용자 입력을 기반으로 파일을 업로드할 때는 다음 사항을 유의해야 합니다:

  • 파일 타입 검증: 허용된 파일 형식만 업로드하도록 제한합니다.
  • 파일명 검증: 파일명이 유효하고 안전한지 확인합니다.
  • 디렉토리 트래버설 방지: ../ 같은 상대 경로를 사용하여 상위 디렉토리에 접근하는 것을 방지합니다.
  • 권한 설정: 업로드된 파일과 디렉토리의 권한을 적절히 설정합니다.

예시: 파일 업로드 시 보안 검증

<?php
$targetDir = "uploads/";
$targetFile = $targetDir . basename($_FILES["uploadedFile"]["name"]);
$uploadOk = 1;
$imageFileType = strtolower(pathinfo($targetFile, PATHINFO_EXTENSION));

// 파일이 실제 이미지인지 확인
if(isset($_POST["submit"])) {
    $check = getimagesize($_FILES["uploadedFile"]["tmp_name"]);
    if($check !== false) {
        echo "파일은 이미지입니다 - " . $check["mime"] . ".<br>";
        $uploadOk = 1;
    } else {
        echo "파일은 이미지가 아닙니다.<br>";
        $uploadOk = 0;
    }
}

// 파일이 이미 존재하는지 확인
if (file_exists($targetFile)) {
    echo "이미 존재하는 파일입니다.<br>";
    $uploadOk = 0;
}

// 파일 크기 제한 (예: 5MB)
if ($_FILES["uploadedFile"]["size"] > 5000000) {
    echo "파일 크기가 너무 큽니다.<br>";
    $uploadOk = 0;
}

// 허용된 파일 형식 확인
$allowedTypes = array("jpg", "jpeg", "png", "gif");
if(!in_array($imageFileType, $allowedTypes)) {
    echo "허용되지 않은 파일 형식입니다.<br>";
    $uploadOk = 0;
}

// 업로드 가능 여부 확인
if ($uploadOk == 0) {
    echo "파일이 업로드되지 않았습니다.<br>";
} else {
    if (move_uploaded_file($_FILES["uploadedFile"]["tmp_name"], $targetFile)) {
        echo "파일 ". htmlspecialchars(basename($_FILES["uploadedFile"]["name"])). " 이(가) 성공적으로 업로드되었습니다.<br>";
    } else {
        echo "파일 업로드 중 오류가 발생했습니다.<br>";
    }
}
?>

8.2 디렉토리 트래버설 방지

디렉토리 트래버설 공격은 악의적인 사용자가 상대 경로를 조작하여 시스템의 민감한 파일에 접근하려는 시도입니다. 이를 방지하기 위해 다음과 같은 방법을 사용할 수 있습니다:

  • 경로 정규화: realpath() 함수를 사용하여 경로를 정규화하고, 허용된 디렉토리 내에 있는지 확인합니다.
  • 화이트리스트 사용: 허용된 파일이나 디렉토리의 목록을 유지하고, 이 목록에 없는 경로는 접근을 차단합니다.

예시: 디렉토리 트래버설 방지

<?php
function sanitizePath($baseDir, $userInputPath) {
    // 기본 디렉토리의 절대 경로 가져오기
    $realBase = realpath($baseDir);
    // 사용자 입력 경로를 기본 디렉토리와 결합 후 절대 경로로 변환
    $realUserPath = realpath($baseDir . DIRECTORY_SEPARATOR . $userInputPath);

    // 경로가 기본 디렉토리 내부에 있는지 확인
    if ($realUserPath !== false && strpos($realUserPath, $realBase) === 0) {
        return $realUserPath;
    } else {
        return false;
    }
}

$baseDir = "/var/www/html/uploads";
$userInput = $_GET['file']; // 예: ../../etc/passwd

$sanitizedPath = sanitizePath($baseDir, $userInput);

if ($sanitizedPath) {
    // 안전하게 파일에 접근
    if (file_exists($sanitizedPath)) {
        echo "파일을 찾았습니다: " . htmlspecialchars($sanitizedPath);
    } else {
        echo "파일이 존재하지 않습니다.";
    }
} else {
    echo "유효하지 않은 파일 경로입니다.";
}
?>

설명:

  • sanitizePath() 함수는 사용자 입력 경로를 기본 디렉토리와 결합한 후, 실제 절대 경로를 확인하여 기본 디렉토리 내부에 있는지 검증합니다.
  • 이를 통해 ../../ 같은 상대 경로를 통한 디렉토리 트래버설 공격을 방지할 수 있습니다.

8.3 권한 설정 및 관리

파일과 디렉토리의 권한을 적절히 설정하여, 불필요한 접근을 제한해야 합니다. 최소 권한 원칙(Least Privilege Principle)을 준수하여, 필요한 최소한의 권한만 부여하는 것이 중요합니다.

예시: 파일 권한 설정

<?php
$filePath = "uploads/user_upload.txt";
$newPermissions = 0644; // 소유자에게는 읽기 및 쓰기 권한, 그룹 및 기타 사용자에게는 읽기 권한만

if (chmod($filePath, $newPermissions)) {
    echo "파일 권한을 성공적으로 변경했습니다.";
} else {
    echo "파일 권한 변경에 실패했습니다.";
}
?>

설명:

  • 0644 권한은 소유자에게 읽기 및 쓰기 권한을 부여하고, 그룹 및 기타 사용자에게는 읽기 권한만 부여합니다.
  • 디렉토리의 경우, 0755 권한을 사용하여 소유자에게는 읽기, 쓰기, 실행 권한을 부여하고, 그룹 및 기타 사용자에게는 읽기 및 실행 권한을 부여할 수 있습니다.

9. 실용적인 예제

9.1 디렉토리 목록 출력

웹 페이지에 특정 디렉토리의 파일과 폴더 목록을 출력하는 스크립트입니다.

예시: 디렉토리 목록 출력

<?php
$dirPath = "uploads/";

if (is_dir($dirPath)) {
    if ($dh = opendir($dirPath)) {
        echo "<h3>디렉토리 목록:</h3><ul>";
        while (($file = readdir($dh)) !== false) {
            if ($file != '.' && $file != '..') {
                echo "<li>$file</li>";
            }
        }
        echo "</ul>";
        closedir($dh);
    } else {
        echo "디렉토리를 여는 데 실패했습니다.";
    }
} else {
    echo "디렉토리가 존재하지 않습니다.";
}
?>

설명:

  • opendir()로 디렉토리를 열고, readdir()으로 각 항목을 읽어 목록으로 출력합니다.
  • ... 항목은 제외하여 사용자에게 불필요한 정보를 제공하지 않습니다.

9.2 파일 업로드 및 저장

사용자가 웹 폼을 통해 파일을 업로드하면, PHP 스크립트가 업로드된 파일을 특정 디렉토리에 저장하는 예제입니다.

HTML 폼: 파일 업로드

<!DOCTYPE html>
<html>
<head>
    <title>파일 업로드</title>
</head>
<body>
    <h2>파일 업로드</h2>
    <form action="upload.php" method="post" enctype="multipart/form-data">
        업로드할 파일 선택:
        <input type="file" name="uploadedFile"><br><br>
        <input type="submit" value="업로드">
    </form>
</body>
</html>

PHP 스크립트: upload.php

<?php
$targetDir = "uploads/";
$targetFile = $targetDir . basename($_FILES["uploadedFile"]["name"]);
$uploadOk = 1;
$imageFileType = strtolower(pathinfo($targetFile, PATHINFO_EXTENSION));

// 파일이 실제로 업로드되었는지 확인
if(isset($_POST["submit"])) {
    if ($_FILES["uploadedFile"]["error"] == UPLOAD_ERR_OK) {
        $uploadOk = 1;
    } else {
        echo "파일 업로드 중 오류가 발생했습니다.<br>";
        $uploadOk = 0;
    }
}

// 파일이 이미 존재하는지 확인
if (file_exists($targetFile)) {
    echo "이미 존재하는 파일입니다.<br>";
    $uploadOk = 0;
}

// 파일 크기 제한 (예: 5MB)
if ($_FILES["uploadedFile"]["size"] > 5000000) {
    echo "파일 크기가 너무 큽니다.<br>";
    $uploadOk = 0;
}

// 허용된 파일 형식 확인 (예: JPG, PNG, GIF)
$allowedTypes = array("jpg", "jpeg", "png", "gif");
if(!in_array($imageFileType, $allowedTypes)) {
    echo "허용되지 않은 파일 형식입니다.<br>";
    $uploadOk = 0;
}

// 업로드 가능 여부 확인
if ($uploadOk == 0) {
    echo "파일이 업로드되지 않았습니다.<br>";
} else {
    if (move_uploaded_file($_FILES["uploadedFile"]["tmp_name"], $targetFile)) {
        echo "파일 ". htmlspecialchars(basename($_FILES["uploadedFile"]["name"])). " 이(가) 성공적으로 업로드되었습니다.<br>";
    } else {
        echo "파일 업로드 중 오류가 발생했습니다.<br>";
    }
}
?>

설명:

  • 사용자로부터 업로드된 파일을 uploads/ 디렉토리에 저장합니다.
  • 파일의 존재 여부, 크기, 형식을 검증하여 보안을 강화합니다.
  • move_uploaded_file() 함수는 임시 위치에 있는 파일을 지정된 위치로 이동합니다.

9.3 파일 다운로드

웹 애플리케이션에서 사용자가 파일을 다운로드할 수 있도록 하는 예제입니다.

PHP 스크립트: download.php

<?php
$filename = "uploads/example.txt";

if (file_exists($filename)) {
    // 파일 크기 가져오기
    $fileSize = filesize($filename);

    // 헤더 설정
    header('Content-Description: File Transfer');
    header('Content-Type: application/octet-stream');
    header('Content-Disposition: attachment; filename="'.basename($filename).'"');
    header('Expires: 0');
    header('Cache-Control: must-revalidate');
    header('Pragma: public');
    header('Content-Length: ' . $fileSize);

    // 파일 읽기 및 출력
    readfile($filename);
    exit;
} else {
    echo "파일이 존재하지 않습니다.";
}
?>

설명:

  • header() 함수를 사용하여 브라우저에게 파일 다운로드를 지시합니다.
  • readfile() 함수는 파일의 내용을 읽어 출력 버퍼에 보냅니다.

10. 파일 시스템 Best Practices

효과적이고 안전한 파일 시스템 관리를 위해 다음과 같은 최선의 실천 방법을 따르는 것이 좋습니다.

10.1 입력 데이터 검증

사용자 입력을 기반으로 파일이나 디렉토리 작업을 수행할 때는 반드시 입력 데이터를 검증하여 보안 취약점을 방지합니다.

예시: 파일명 검증

<?php
function sanitizeFileName($filename) {
    // 파일명에서 위험한 문자 제거
    return preg_replace("/[^a-zA-Z0-9\\\\.\\\\-_]/", "", $filename);
}

$userInput = $_POST['filename'];
$sanitizedFilename = sanitizeFileName($userInput);

$filepath = "uploads/" . $sanitizedFilename;

// 파일 작업 수행
?>

설명:

  • 정규 표현식을 사용하여 파일명에서 알파벳, 숫자, 점, 대시, 언더스코어 외의 문자를 제거합니다.
  • 이를 통해 디렉토리 트래버설 공격이나 파일 이름 인젝션을 방지할 수 있습니다.

10.2 최소 권한 원칙 준수

파일과 디렉토리는 필요한 최소한의 권한만 부여하여, 불필요한 접근을 제한합니다.

예시: 최소 권한 설정

<?php
$dirPath = "uploads/";
$permissions = 0755; // 소유자에게는 읽기, 쓰기, 실행 권한을 부여하고, 그룹 및 기타 사용자에게는 읽기 및 실행 권한을 부여

if (mkdir($dirPath, $permissions, true)) {
    echo "디렉토리를 성공적으로 생성했습니다.";
} else {
    echo "디렉토리 생성에 실패했습니다.";
}
?>

10.3 예외 처리 및 오류 관리

파일 시스템 작업 중 발생할 수 있는 예외와 오류를 적절히 처리하여, 애플리케이션의 안정성을 높입니다.

예시: 예외 처리와 오류 관리

<?php
try {
    $filename = "uploads/example.txt";

    if (!file_exists($filename)) {
        throw new Exception("파일이 존재하지 않습니다: " . $filename);
    }

    $content = file_get_contents($filename);
    if ($content === false) {
        throw new Exception("파일을 읽는 데 실패했습니다: " . $filename);
    }

    echo "파일 내용:<br>" . nl2br(htmlspecialchars($content));

} catch (Exception $e) {
    // 오류 메시지를 로그에 기록
    error_log("파일 오류: " . $e->getMessage());
    // 사용자에게는 일반적인 메시지 표시
    echo "파일 처리 중 오류가 발생했습니다.";
}
?>

설명:

  • 예외를 사용하여 파일 존재 여부와 읽기 실패를 처리합니다.
  • 오류 메시지는 로그에 기록하고, 사용자에게는 일반적인 메시지만 표시하여 보안을 강화합니다.

10.4 경로 정규화 및 안전한 파일 접근

realpath()와 같은 함수를 사용하여 경로를 정규화하고, 안전한 파일 접근을 보장합니다.

예시: 안전한 파일 접근

<?php
$baseDir = "/var/www/html/uploads/";
$userInput = $_GET['file']; // 예: ../../etc/passwd

$realBase = realpath($baseDir);
$realUserPath = realpath($baseDir . $userInput);

// 경로가 기본 디렉토리 내부에 있는지 확인
if ($realUserPath && strpos($realUserPath, $realBase) === 0) {
    if (file_exists($realUserPath)) {
        echo "파일을 찾았습니다: " . htmlspecialchars($realUserPath);
        // 파일 작업 수행
    } else {
        echo "파일이 존재하지 않습니다.";
    }
} else {
    echo "유효하지 않은 파일 경로입니다.";
}
?>

설명:

  • realpath() 함수를 사용하여 실제 절대 경로를 얻고, 기본 디렉토리 내에 있는지 확인합니다.
  • 이를 통해 디렉토리 트래버설 공격을 방지합니다.

11. 결론

PHP 파일 시스템 참조는 웹 애플리케이션에서 파일과 디렉토리를 효율적이고 안전하게 관리하는 데 필수적인 기능을 제공합니다. PHP의 다양한 내장 함수와 SPL 클래스를 활용하면, 파일 생성, 읽기, 쓰기, 삭제, 권한 관리 등을 손쉽게 수행할 수 있습니다. 그러나 파일 시스템을 다룰 때는 보안과 권한 설정에 주의하여, 잠재적인 취약점을 예방하는 것이 중요합니다.

주요 포인트 요약:

  • 파일 및 디렉토리 작업: fopen(), fread(), fwrite(), unlink(), mkdir(), rmdir() 등의 함수를 활용하여 파일과 디렉토리를 관리합니다.
  • 파일 권한 및 소유권 관리: chmod(), chown(), chgrp() 함수를 사용하여 파일과 디렉토리의 권한과 소유권을 설정합니다.
  • 경로 관리: realpath(), basename(), dirname(), pathinfo() 함수를 사용하여 안전한 경로 관리를 수행합니다.
  • 보안 고려사항: 파일 업로드 시 파일 타입과 이름을 검증하고, 디렉토리 트래버설 공격을 방지하며, 최소 권한 원칙을 준수합니다.
  • SPL 파일 시스템 클래스: FilesystemIterator, DirectoryIterator, RecursiveDirectoryIterator 등을 사용하여 객체 지향적으로 파일 시스템을 관리합니다.
  • 예외 처리 및 오류 관리: 파일 시스템 작업 중 발생할 수 있는 예외와 오류를 적절히 처리하여 애플리케이션의 안정성을 높입니다.

PHP 파일 시스템 기능을 잘 활용하면, 웹 애플리케이션의 파일 관리가 더욱 효율적이고 안전하게 이루어질 수 있습니다. 다양한 예제를 통해 실습하고, 공식 문서를 참고하여 깊이 있는 이해를 쌓는 것이 중요합니다.


: PHP의 파일 시스템 기능을 마스터하려면, 다양한 파일 및 디렉토리 작업을 직접 구현해 보고, 보안 모범 사례를 준수하는 것이 좋습니다. 이를 통해 복잡한 파일 관리 작업에서도 안전하고 효율적으로 대처할 수 있는 능력을 키울 수 있습니다.


copyright ⓒ 스타트코딩 all rights reserved.
이메일 : startcodingim@gamil.com