File Download

파일 다운로드 취약점 대응 방안

ragdo11 2024. 8. 24. 20:55

파일 다운로드 취약점이 발생하는 가장 큰 이유는 파일 다운로드가 허용되는 디렉터리를 벗어나 다른 곳으로 접근하기 때문이다. 이를 막기 위해서 경로의 변경을 가능하게 해주는 ../ 같은 경로 변경 문자열을 필터링해주거나 사용자가 접근을 요청하는 파일이 허용하는 경로 혹은 디렉터리가 맞는지 검사해주는 방법이 있다.

 

우선 사용자가 접근을 요청하는 파일이 허용하는 경로인지 검사해주는 방법이다.

$filename = urldecode($_GET['file']);
$filepath = $_SERVER['DOCUMENT_ROOT'] . "/upload/" . $filename;

download.php의 일부분을 들고 왔다. file 파라미터로 받은 파일명을 urlencode하여 download.php로 보낸 뒤 decode하여 filename 변수에 저장하고 있다. 저 부분이 문제가 되는 부분인데 아무런 검사없이 파라미터로 들어온 파일명을 단지 인코딩 디코딩만 수행하여 저장하고 있기 때문이다.

 

정말 간단하게 이에 대응하는 방법은 urldecode 함수를 basename 함수로 교체하는 것이다. basename은 path 끝 요소만 반환하는 함수이다. basename 함수로 교체하기만 해도 방어가 되는 이유는 아래와 같다.

$filename = urldecode($_GET['file']);
~?file=../../../../etc/passwd -> filepath = document_root/upload/../../../../etc/passwd
-> /etc/passwd file download success!
$filename = basename($_GET['file']);
~?file=../../../../etc/passwd -> filepath = document_root/upload/passwd
-> if only passwd file exists in upload dir can download successfully

 

방어를 제대로 구현하려면 realpath 함수를 이용해 파일의 절대 경로를 확인하여 허용하는 디렉터리 내에 있는지 확인하면 된다.

 

urldecode -> basename 함수로 바꾼 후 /etc/passwd 파일 다운로드 시도

파일 다운로드 공격 실패

 

 

두 번째로 다운로드할 파일명에서 파일 경로 변경 문자열을 확인하고 차단하는 것이다.

파일명은 당연히 다양하고 예측할 수 없고, 파일 경로 변경 문자열은 정해져 있으므로 블랙 리스트 필터링을 해야 한다.

if(preg_match('/\.\.\/|\.\//i',$file_name)){
        exit('경로 조작 문자열을 사용할 수 없습니다.');
}

다운로드 스크립트에 preg_match 함수로 ../ 문자열과 ./ 문자열을 필터링하여 exit 해주었다.

여담으로 eregi 함수가 사용이 안된다 하더라요.. 대체 함수로 preg_match 함수를 사용해봤는데 확실히 정규식이 익숙치가 않아서 어렵기도 하고,,, 예전에 los에서 문제 풀면서 preg_match 함수로 필터링해준것이 기억나서 거기서 참고하면서 작성해봤는데 일단 원하는 대로 필터링이 되긴 하는데 정규식이 아쉬운 부분이 있을 수도 있다.

 

경로 조작 문자열을 사용하여 다운로드 시도

필터링이 잘 이루어진다. 잘 먹히진 않지만 우회하는 방법이 있는데 이건 다음에 공부할 Directory Traversal 취약점에서 알아봐야겠다. 대응 방안이 파일 다운로드 취약점과 거의 동일해 작성할게 없을거 같아서 ㅎㅎ;

 

chroot 사용

유닉스/리눅스 시스템에서 사용할 수 있는 chroot 기능은 임의의 디렉터리를 가상의 루트 디렉터리로 설정해 주는 기능이다. 만일 웹 루트 디렉터리를 chroot로 가상의 루트 디렉터리로 설정해준다면 루트 디렉터리로 설정되었기 때문에 상위 디렉터리에 접근하는 것이 불가능해 시스템 루트 디렉터리에 대해 접근이 불가능해진다.

 

예를 들어 일반적인 document_root인 /var/www/html을 chroot로 설정해준다면 html이 임의의 루트 디렉터리가 되어 상위 디렉터리에 접근이 불가능해진다. 그래서 아무리 경로 조작 문자열로 시스템 루트 디렉터리에 접근을 시도해도 html을 벗어나지 못한다.

 

임의의 디렉터리를 루트 디렉터리로 만들어 상위 디렉터리에 접근하지 못하게 하는 방법은 굉장히 흥미롭기도 하고 좋은 방어가 될 수 있지만 chroot는 설정하면 관리하기가 굉장히 어려워진다고 한다. 현업에서도 chroot를 사용하는 경우는 잘 없다고 들었고, 대체할 기술이 있기도 해서 이러한 방어법이 있다고 알기만 하면 좋을 것 같다.