PHP는 매우 강력한 언어이다.
이 인터프리터는 웹서버의 모듈로 되어 있건 CGI 바이너리로 되어 있건, 시스템 내의 파일을 사용할 수 있고,
여타의 명령이나 프로그램을 실행할 수 있을뿐 아니라, 네트웍을 통해 다른 서버로 연결을 할 수도 있다.
이런 기능들은 악의를 가진 사용자나 초보자가 어떤 프로그램이든 실행시켜 웹서버를 보안상 불안한 상태로 만들 수 있다.
PHP는 분명히 Perl이나 C보다 좀더 안전한 CGI 프로그램을 만들 수 있도록 되어 있다.
여러이 컴파일이나 실행시 설정 옵션들을 적절히 조합하여 사용하면, 원하는 정도의 자유로움과 보안상의 제약을 조화시킬 수 있다.
PHP는 수많은 방법으로 이용이 가능하고, 이에 따른 여러 가지 설정 옵션이 있을 수 있다.
많은 옵션 선택 사항들은 PHP를 보다 많은 목적으로 사용할 수 있도록 하지만,
이 옵션들의 조합과 서버의 설정에 따라 보안상의 허점을 노출시킬 수도 있다.
PHP에서 설정의 유연성은 코드의 유연성과 비견될 만큼 훌륭하다.
PHP는 쉘 사용자와 동일한 권한으로 완전한 서버 어플리케이션을 만드는데 사용될 수도 있고,
위험이 거의 없는 매우 제한된 환경에서 간단한 server-side include에도 사용될 수 있다.
환경을 어떻게 만드는가, 보안은 어떻게 설정하는가는 전적으로 PHP 개발자에게 달려있다.
이 장은 PHP를 안전하게 사용할수 있는 여러 다른 설정 옵션의 조합과 상황을 설명하는 것으로 시작한다.
그리고, 각각 상이한 보안 레벨에서의 코딩에 있어서 여러가지 고려사항을 설명한다.
Installed as CGI binary
가능한 공격 (Possible attacks)
CGI binary 형태의 PHP는 어떤 이유로 (아파치 같은) 서버 소프트웨어의 모듈로
사용되기를 원하지 않는 경우에 사용할 수 있고, PHP를 다른 종류의 CGI wrapper와 함께
안전한 chroot와 setuid 환경을 만들어 내는 곳에 때에 사용할 수 있다.
이 설정은 보통 실행할 PHP binary를 웹서버의 cgi-bin디렉토리에 설치해야 한다.
CERT 권고사항 에 따르면 모든 인터프리터들은 cgi-bin에 설치하도록 권고된다.
비록 PHP binary가 독립적인 인터프리터로 사용된다 할지라도,
PHP는 이런 설정에서 받을 수 있는 공격을 다음과 같이 막아내도록 설계되어 있다. :
시스템 파일로의 접근 : http://my.host/cgi-bin/php?/etc/passwd
이 url내의 물음표(?)이후에 있는 요청은 CGI 인터페이스에 의해 command line argument로 인터프리터에게 전달된다.
보통 인터프리터는 커맨드 라인의 첫 번째 argument의 파일을 읽어서 실행 한다.
CGI binary로 실행 된 PHP는 모든 command line argument들을 모두 무시한다.
서버의 웹 문서로의 접근 : http://my.host/cgi-bin/php/secret/doc.html
이 url의 PHP binary 이후의 path 정보(/secret/doc.html)는
일반적으로 CGI 프로그램에 의해 읽고 해석될 파일 이름으로 사용된다.
보통 웹서버는 설정 지시자(Apache의 경우 Action)를 사용하여 http://my.host/secret/script.php같은
문서 요청을 PHP 인터프리터로 바로 리디렉트 하도록 할 수 있다. 이렇게 설정하면,
웹서버는 우선 /secret 디렉토리의 접근 권한을 검사한 후에
http://my.host/cgi-bin/php/secret/script.php로 리디렉트를 한다.
불행히도, 이 요청이 원래의 form에서 주어진 것이라면, 웹서버는 /secret/script.php
파일에 대한 접근 권한 검사를 하지 않고, /cgi-bin/php 파일에 대한 검사만을 행한다.
이 방법을 사용하면 /cgi-bin/php에 접근 가능한 사용자는 웹서버상의 모든 보호된 문서들도 접근할 수 있다.
이런경우 서버 문서 tree에 접근 제약이 있는 디렉토리가 있다면,
컴파일시에 --enable-force-cgi-redirect 설정 옵션을 주고,
실행시에 doc_root와 user_dir 지시자를 사용하여서
이 공격을 막아낼 수 있다. 아래에 여러 가지 다른 조합 방법에 대한 자세한 설명이 나와 있다.
Case 1: 공용 파일만 제공하는 경우 (only public files served)
만약 여러분의 서버가 password 나 ip 기준의 접근 통제에 관한 어떠한 사항도 없다면,
이러한 설정 옵션들은 필요 없다.
만약 여러분의 웹서버가 리디렉트를 허용하지 않거나, 서버가 해당 요구가 안전하게 리디렉트된 요구인지 대한 정보를
PHP binary에 전달할 수 있는 방법이 없을 경우, config 스크립트에서
--enable-force-cgi-redirect 옵션을 주는 것이 좋다.
그러나 여전히 PHP 스크립트가, http://my.host/cgi-bin/php/dir/script.php같은 직접 접근 방법이나
http://my.host/dir/script.php같은 리디렉션의 방법 이외의,
다른 믿지 못할 방법에 의해 불려 질 때를 대비하여 대책을 강구해 두어야 합니다
리디렉션은 예를들어 Apache에서는 AddHandle과 Action 지시자를 사용하여 설정할 수 있습니다. (아래를 보세요.)
Case 2: --enable-force-cgi-redirect 옵션 사용
이 옵션을 주고 PHP를 컴파일 하면, 사용자가 http://my.host/cgi-bin/php/secretdir/script.php와
같이 PHP를 직접 호출하는 것을 막아준다.
대신, 이 모드에서 PHP는 사용자의 요구(request)가 웹서버의 리디렉트 규칙을 통과한 경우에만 동작 할 것이다.
일반적으로 아파치에서 리디렉트 설정은 다음과 같은 지시자를 사용하여 한다. :
Action php-script /cgi-bin/php
AddHandler php-script .php |
이 옵션은 아파치 웹서버에서만 검증되었다.
아파치는 리디렉트된 요구에 대해서는 REDIRECT_STATUS 라는 비 표준 CGI 환경 변수를 설정한다.
만약 웹서버가 해당 문서 요구가 직접접근(direct)인지 리디렉트(redirect)인지 구별할 수 있는 방법을 제공하지 않는다면
여러분은 이 옵션을 사용할 수 없으므로 이 문서에 있는 CGI 버전에 대한 다른 방법을 사용해야 할 것이다.
Case 3: doc_root나 user_dir의 설정
웹서버 문서 디렉토리에 script나 실행파일 같은 Active content를 포함하는 것은 때때로 불안한 상황을 만들 수 있다.
만약 약간의 설정 오류로 스크립트가 실행되지 않고 HTML 문서처럼 그대로 출력 된다면,
각종 지적 재산이나 Password같은 보안상의 정보가 누설되는 결과가 초래될 지 모른다.
그러므로 많은 시스템 관리자들은 스크립트를 위한 별도의 디렉토리를 만들어 놓고,
이 디렉토리는 PHP CGI를 통해서만 접근이 가능하도록 만들어 놓아 실행은 가능하지만 보여지지는 않도록 하고 있다.
또한, 위에서 말한 바와 같이, 문서 요구가 리디렉트된 것이 아니라고 확인해주는 방법이 없다면,
웹서버의 document root와는 별도의 script doc_root를 설정해 주는 것이 반드시 필요하다.
configuration file (php.ini)의 doc_root 지시자를 설정하거나, PHP_DOCUMENT_ROOT라는 환경변수를 설정하여, PHP script document root를 설정할 수 있다.
만약 이것이 설정되어 있다면, CGI 버전의 PHP는 접근하려는 파일의 이름을
이 doc_root와 request에 있는 경로 정보를 이용하여 만들어낸다.
따라서 여러분은 이 디렉토리 밖에서는 어떠한 스크립트도 실행될 수 없도록 만들게 된다. (아래에 있는 user_dir 만은 예외이다)
여기서 사용할 수 있는 또다른 옵션은 user_dir이다.
user_dir이 설정되어 있지 않으면 접근 가능한 파일은 doc_root 밑에 있는 것 뿐이다.
http://my.host/~user/doc.php같은 url로는 user home directory에 있는 파일을 열 수 없다.
단지 doc_root아래의 ~user/doc.php 파일을 부를 뿐이다.
(물론 틸데[~]로 시작하는 "~user"라는 디렉토리이다.)
예를 들어, 만약 user_dir이 public_php로 설정되어 있다면,
http://my.host/~user/doc.php와 같은 요구는 user의 home 디렉토리 밑의
public_php라는 디렉토리 밑에 있는 doc.php라는 파일을 호출 할 것이다.
만약 사용자의 home이 /home/user라면,
실행되는 파일은 /home/user/public_php/doc.php가 된다.
user_dir 확장은 doc_root 설정과 관련없으므로,
여러분은 document root와 user directory 접근 통제를 따로따로 할 수 있다.
Case 4: PHP parser를 웹 트리 밖에 두기 (parser outside of web tree)
매우 신뢰할수 있는 방법으로 PHP parser binary를 웹 트리 밖에다 두는 방법이다.
예를 들어 /usr/local/bin 같은 곳에 둔다.
이 옵션의 단점은, 모든 PHP 택을 포함하고 있는 파일의 첫번째 라인에 다음과 같이 적어주는 것 뿐이다. :
또한 파일을 실행 가능으로 만들어 주어야 한다.
즉, 자신의 실행을 위해서
#! shell-escape 메카니즘을 사용하는
Perl이나 sh 혹은 다른 스크립트 언어와 같은 방식으로 다루는 것이다.
이 설정에서 PHP가 PATH_INFO와 PATH_TRANSLATED정보를 정확히 다루려면,
php parser가 --enable-discard-path 설정 옵션으로 컴파일 되어야 한다.