[PHP] file_exists는 true로 나오지만 fopen, require/include 등이 불가능할 때 (file_exists return true but can't read)
PHP 2021. 4. 20. 15:35
디렉토리나 파일 권한 등도 문제 없고 file_exists()도 true를 반환하는데, 아래와 같이 파일 접근에 실패하는 경우.
$f = '/PATH/TO/FILENAME';
// true
var_dump(file_exists($f));
// false
var_dump(is_readable($f));
// error
require_once $f;
Warning: require_once(/PATH/TO/FILENAME): failed to open stream: Permission denied in /PATH/TO/HOSTFILE on line 10
Fatal error: require_once(): Failed opening required '/PATH/TO/FILENAME' (include_path='.:/usr/share/pear:/usr/share/php') in /PATH/TO/HOSTFILE on line 10
SELinux(Security Enhanced Linux)의 문제일 수 있음.
ls -l 했을 때 퍼미션 부분의 뒷쪽에 점(.)이 나오면 SELinux 보안 컨텍스트가 실행되고 있는 것.
[root@hostname]# ls -al
합계 16
drwxr-xr-x. 3 root root 4096 3월 23 15:58 .
drwxr-xr-x. 17 root root 4096 4월 7 10:15 ..
drwxr-xr-x. 3 root root 4096 4월 16 18:02 dir
-rw-r--r--. 1 root root 98 3월 23 15:58 index.php
SELinux의 보안 컨텍스트는 ls -Z 로 확인가능.
[root@hostname]# ls -Z
drwxr-xr-x. root root unconfined_u:object_r:etc_t:s0 dir
-rw-r--r--. root root unconfined_u:object_r:httpd_sys_content_t:s0 index.php
unconfined_u:object_r:httpd_sys_content_t:s0 <- 이 부분이 SELinux 보안 컨텍스트인데 자세한 내용은 생략하고,
: 를 기준으로 나눈 3번째 항목인 값인 Type Enforcement 부분이 중요함.
dir은 etc_t 이고 index.php는 httpd_sys_content_t 이다.
웹서버 프로세스의 SELinux 값을 출력해보면 아래처럼 나옴.
[root@hostname]# ps -ZC httpd
LABEL PID TTY TIME CMD
system_u:system_r:httpd_t:s0 11872 ? 00:00:00 httpd
system_u:system_r:httpd_t:s0 11873 ? 00:00:00 httpd
system_u:system_r:httpd_t:s0 11874 ? 00:00:00 httpd
system_u:system_r:httpd_t:s0 11875 ? 00:00:00 httpd
system_u:system_r:httpd_t:s0 11876 ? 00:00:00 httpd
system_u:system_r:httpd_t:s0 11878 ? 00:00:00 httpd
system_u:system_r:httpd_t:s0 11881 ? 00:00:00 httpd
system_u:system_r:httpd_t:s0 11882 ? 00:00:00 httpd
system_u:system_r:httpd_t:s0 11883 ? 00:00:00 httpd
httpd 프로세스의 Type Enforcement 값이 httpd_t 이므로 관련 권한만 접근이 가능함.
그러므로 httpd_sys_content_t인 index.php는 접근이 가능하지만 etc_t인 dir은 접근이 불가능함.
이 문제를 해결하려면 httpd가 접근이 가능하도록 해당 경로의 Type Enforcement를 수정해줘야 함.
// -R 옵션으로 하위 경로까지 전부 변경
[root@hostname]# chcon -R -t httpd_sys_content_t ./dir
// 확인
[root@hostname]# ls -Z
drwxr-xr-x. root root unconfined_u:object_r:httpd_sys_content_t:s0 dir
-rw-r--r--. root root unconfined_u:object_r:httpd_sys_content_t:s0 index.php
이제 PHP 에서 dir 경로의 파일들에 접근이 가능함.
참고로 httpd_sys_content_t 는 readonly 이며, 쓰기가 필요할 경우엔 httpd_sys_rw_content_t 로 변경해줘야 함.
자세한 타입들은 다음 페이지 참조.