09 febrero 2011

Hardening PHP mediante php.ini

En algunas ocasiones una buena configuración nos puede salvar de un fallo de programación que pueda desembocar en una brecha de seguridad. Es el caso de PHP, cuya configuración tiene algunas medidas de seguridad que pueden ser útiles.

Vamos a ver estas opciones centrándonos en las versiones actuales de PHP5 y con PHP6 ya en mente.

El fichero de configuración de PHP es php.ini. Puede estar localizado en diferentes directorios según plataforma o distribución, por ejemplo en Ubuntu está en /etc/php5/apache2/php.ini


Si está definido, limita todas las operaciones con ficheros a la ruta especificada (root de PHP) y las contenidas por ella. Nos ayuda a evitar vulnerabilidades del tipo Directory Traversal. Esta directiva no está afectada por el hecho de que el modo seguro (Safe Mode) esté On u Off.

Ejemplo:
open_basedir = /var/www/


Deshabilita funciones de PHP. Podemos quitar funciones peligrosas como system() o exec() para restringirlas en el proceso de desarrollo, o prevenirnos de ellas en el entorno final. Si un usuario consigue subir y ejecutar un script PHP (por ejemplo una shell) no podrá ejecutar las funciones definidas en la directiva. No es una garantía, ya que aunque no se puedan usar comandos del sistema se pueden utilizar otras funciones de PHP, pero puede servirnos para hacerle perder tiempo a un atacante, evitar ataques genéricos, e incluso defendernos de un atacante con pocas habilidades técnicas en PHP.

Ejemplo:
disable_functions = system, exec, php_uname


Habilita o deshabilita que se muestren los errores de PHP (el log no se verá afectado). En el proceso de desarrollo es algo tedioso tenerlo deshabilitado, pero en el entorno final nos ayuda a que un atacante no descubra la estructura de nuestra aplicación, lo que le hará más complicada la detección y explotación de un fallo.

Ejemplo:
display_errors = Off

- register_globals [Obsoleto desde PHP 5.3.0]

Habilita o deshabilita que las variables EGPCS (Environment, GET, POST, Cookie, Server) sean globales o no. Si está habilitado permite el uso de variables sin especificar cual es su origen. Desde PHP 4.2.0 la directiva está fijada en Off por defecto.

Ejemplo práctico:

Cuando nosotros le pasamos a un script una variable por GET (/script.php?nombre=Pepe), si tenemos activado register_globals la variable en el código podrá ser utilizada como $name, en cambio, si lo tenemos desactivado tendremos que usar la variable como $_GET["name"]. La segunda forma es más segura (y a nivel de desarrollo opino que mejor), ya que un atacante no podría inyectarnos variables en un origen desde otro ($_POST vía $_GET por ejemplo).

El único motivo para habilitar esta característica es que nuestra aplicación sea antigua y el uso de variables sea el obsoleto. Aún así, es recomendable actualizar el código al segundo modelo.

Ejemplo (recomendado):
register_globals = Off

- Deshabilitar Remote File Includes

En algunas ocasiones los desarrolladores hacen inclusiones de código incluyendo en ellas datos que introduce el usuario. Si un atacante identifica una, puede utilizarla para incluir y ejecutar su propio código de forma remota, sin tener permisos de escritura en el servidor (RFI). Más información en php-security.

Ejemplo (recomendado):
allow_url_fopen = Off
allow_url_include = Off

- file_uploads

Habilita o deshabilita la subida de ficheros mediante HTTP. Si nuestra aplicación no usa subida de ficheros es una buena idea deshabilitarlo.

Ejemplo:
file_uploads = Off

- magic_quotes_gpc y magic_quotes_runtime [Obsoleto desde PHP 5.3.0]

Con estas funciones podemos escapar caracteres que pueden causar problemas con la interacción de bases de datos y permitir inyección de sentencias (como ' o "). No es una protección adecuada, por lo que es necesario utilizar las funciones específicas para solucionar estos fallos.

Ejemplo:
magic_quotes_gpc = On
magic_quotes_runtime = On

- safe_mode [Obsoleto desde PHP 5.3.0]

Es un intento de solucionar los problemas de hosting compartido a nivel de PHP. Por defecto compara el UID de ficheros cuando se intentan abrir. Permite algunas características adicionales.

Ejemplo:
safe_mode = On

Referencias:

8 comments :

Santi Saez dijo...

Hola,

Mucha gente desconoce que todas estas directivas se pueden redefinir a nivel de vhost con un .htaccess o bien modificando los valores desde PHP con ini_set(), de modo que si solo las definimos en el php.ini no sirven para nada, se pueden sobre-escribir.

Si ejecutamos PHP como módulo DSO de Apache para evitarlo debemos definir las variables con las directivas php_admin_flag y php_admin_value o bien utilizar una extensión específica como Suhosin que bloquea este tipo de cambios en variables, ejemplo suhosin.memory_limit controlaría/bloquearía el burst de memoria.

Algunas pequeñas aportaciones a las recomendaciones de Alberto:

- Si permitimos subir ficheros (file_uploads = On) sería recomendable activar la directiva max_file_uploads que limita el número de uploads de forma concurrente, útil para evitar DoS (está disponible a partir de la versión 5.2.12).

- enable_dl = Off para evitar que los usuarios puedan cargar sus propias extensiones PHP, recordar el histórico exploit que permitía inyectar código en todos los vhosts: flame.so

- expose_php = Off la documentación lo deja bien claro: no constituye un riesgo de seguridad, pero hace posible determinar si están usando PHP así como su versión a través de la cabecera "Server" del servidor web.

- Limitar el consumo de memoria de scripts PHP para evitar un DoS con "memory_limit = 64M", cruzándolo con el max_input_time.

La gran mayoría de intrusiones en scripts PHP se pueden frenar -como ya ha comentado Alberto- con disable_functions y allow_url_fopen, sin embargo este tiene un problema: la implementación de PHP es muy básica y solo permite activar/desactivar globalmente estas variables, no se permite el control a nivel de vhost. Si nos interesa jugar con listas blancas/negras a nivel de dominio para funciones tipo disable_functions y allow_url_fopen podemos utilizar Suhosin.. en general pensar en instalar esta extensión es un buen punto de partida para securizar PHP.

Saludos,

Jesús dijo...

Buenas Alberto,

Comentas que el magic_quotes_runtime está obsoleto y es mejor no usarlo, sin embargo has puesto:

Ejemplo:
magic_quotes_gpc = On
magic_quotes_runtime = On

Debería ir en off no?
----
buguroo team

Alberto Ortega dijo...

@Santi Saez
Muy buen aporte!

@Jesús
Está obsoleto, pero si nuestra instalación de PHP lo soporta no está de más tenerlos activados :)

Aún así, incluso activados, debemos usar las funciones específicas, por ejemplo en mysql mysql_real_escape().

coder dijo...

Una cosa. No sé si lo he pasado por alto al leerlo, pero nadie comenta la posibilidad de desactivar el uso de .htaccess (AllowOverride a None), no solo por seguridad sino también por rendimiento.

En una empresa que ofrezca managed hosting con sistemas de ticketing -vamos, lo normal-, el cliente puede pedir determinadas directivas y se le aplican en su vhost.conf concreto. Incluso con Plesk de por medio se puede.

Lo comento aunque igual es algo tan evidente que no merecía mencionarse.

Fulton dijo...

Muy interesante.
Acabo de aplicar los cambios en mi portal, pero he tenido que reconfigurar miles de carpetas, porque el safe-mode solo permite el acceso al archivo al dueño.

Fulton dijo...

Muy interesante.
Acabo de aplicar los cambios en mi portal, pero he tenido que reconfigurar miles de carpetas, porque el safe-mode solo permite el acceso al archivo al dueño.

coder dijo...

Una cosa. No sé si lo he pasado por alto al leerlo, pero nadie comenta la posibilidad de desactivar el uso de .htaccess (AllowOverride a None), no solo por seguridad sino también por rendimiento.

En una empresa que ofrezca managed hosting con sistemas de ticketing -vamos, lo normal-, el cliente puede pedir determinadas directivas y se le aplican en su vhost.conf concreto. Incluso con Plesk de por medio se puede.

Lo comento aunque igual es algo tan evidente que no merecía mencionarse.

Santi Saez dijo...

Hola,

Mucha gente desconoce que todas estas directivas se pueden redefinir a nivel de vhost con un .htaccess o bien modificando los valores desde PHP con ini_set(), de modo que si solo las definimos en el php.ini no sirven para nada, se pueden sobre-escribir.

Si ejecutamos PHP como módulo DSO de Apache para evitarlo debemos definir las variables con las directivas php_admin_flag y php_admin_value o bien utilizar una extensión específica como Suhosin que bloquea este tipo de cambios en variables, ejemplo suhosin.memory_limit controlaría/bloquearía el burst de memoria.

Algunas pequeñas aportaciones a las recomendaciones de Alberto:

- Si permitimos subir ficheros (file_uploads = On) sería recomendable activar la directiva max_file_uploads que limita el número de uploads de forma concurrente, útil para evitar DoS (está disponible a partir de la versión 5.2.12).

- enable_dl = Off para evitar que los usuarios puedan cargar sus propias extensiones PHP, recordar el histórico exploit que permitía inyectar código en todos los vhosts: flame.so

- expose_php = Off la documentación lo deja bien claro: no constituye un riesgo de seguridad, pero hace posible determinar si están usando PHP así como su versión a través de la cabecera "Server" del servidor web.

- Limitar el consumo de memoria de scripts PHP para evitar un DoS con "memory_limit = 64M", cruzándolo con el max_input_time.

La gran mayoría de intrusiones en scripts PHP se pueden frenar -como ya ha comentado Alberto- con disable_functions y allow_url_fopen, sin embargo este tiene un problema: la implementación de PHP es muy básica y solo permite activar/desactivar globalmente estas variables, no se permite el control a nivel de vhost. Si nos interesa jugar con listas blancas/negras a nivel de dominio para funciones tipo disable_functions y allow_url_fopen podemos utilizar Suhosin.. en general pensar en instalar esta extensión es un buen punto de partida para securizar PHP.

Saludos,