Aislando las aplicaciones web

En este post me gustaría explicar la solución que he encontrado a uno de los problemas que me plantea el hecho de utilizar de Apache como servidor web por defecto, ya es que Apache no permite la ejecución de distintos VirtualHost con diferentes usuarios, lo cual puede suponer un grave problema de seguridad. Imaginemos que tenemos la siguiente situación en el mismo servidor:

  • Los Wookies tienen desplegada una aplicación cuyo funcionamiento consiste en incluir el contenido de un fichero del sistema especificado en un campo GET y mostrarlo al usuario. Por la sencillez de la aplicación y la no-criticidad de la misma, no se realiza ningún tipo de comprobación.
  • En el mismo servidor, Darth Vader tiene desplegada una aplicación que consulta de una base de datos, planos de infraestructuras críticas, como por ejemplo, el de la Estrella de la Muerte. Para ocultar el fallo de diseño de la misma, la aplicación se audita varias veces por un organismo externo sin encontrar ni un fallo de seguridad.
  • La fuerza Rebelde aprovecha el LFI de la web de los Wookies, y lee ficheros de sistema, entre los que se encuentra la configuración de la página web de Darth Vader.
  • Utilizando las credenciales obtenidas del fichero de configuración, son capaces de conectarse a la base de datos y obtener los planos de la Estrella de la Muerte, provocando la explosión que pudimos ver en Star Wars I (N.d.A.: no, no me he equivocado) – Una nueva esperanza.

En el ejemplo anterior, si cada aplicación se hubiese ejecutado con usuarios diferentes, las páginas web podrían haberse aislado aprovechando los permisos del sistema de ficheros. De manera que, a priori, el resto de aplicaciones del sistema no se habrían visto afectadas.

En la época de Apache 2.2, intenté solucionar este problema utilizando como MPM (Módulo de multi-procesamiento en inglés) Apache-ITK, que al estar basado en el MPM de Prefork, permite ejecutar aplicaciones PHP que no estén preparadas para su uso mediante threads. La peculiaridad de Apache-ITK es que permite añadir nuevos parámetros en la configuración de los VirtualHost, mediante los que podemos especificar el uid y el gid con el que se ejecutará la aplicación web.

El funcionamiento de este MPM es el siguiente: Apache se ejecuta como usuario root y cuando llega una petición, se crea un proceso con el uid y el gid indicados en la configuración del VirtualHost correspondiente. Sin embargo, cada vez que ejecuto el comando top y veo que el propietario de los procesos de Apache es el usuario root, me recorre un sudor frío por la espalda.

Ahora, con Apache 2.4, parece que el módulo mod_privileges permite realizar esta separación de privilegios, siempre y cuando el sistema base sea Solaris. Como no es el caso, el otro día descubrí una solución que me parece mucho más limpia: el uso de Nginx como proxy inverso hacia una aplicación ejecutada mediante uWSGI. Veamos esto en más detalle:

Por una parte, en la configuración de Nginx, creamos un server del siguiente modo:

server {
    location ~ ^(.+\.php)(.*)$ {
        include uwsgi_params;
        uwsgi_modifier1 14;
        uwsgi_pass unix:/run/uwsgi/app.sock; # o IP:puerto
    }
    location / {
        root /directorio de la app;
        index index.php;
    }
}

A su vez, crearemos un fichero de configuración de uwsgi para nuestra aplicación web. En este caso, /etc/uwsgi/app.ini:

[uwsgi]
master = true
socket = /run/uwsgi/%n.sock

uid = usuario
gid = grupo  

app_data_dir = /directorio app
chdir = %(app_data_dir)

plugins = php
php-docroot     = /directorio app
php-index       = index.php

# Limitamos que ficheros php se pueden ejecutar 
php-allowed-ext = /fichero-1.php
php-allowed-ext = /fichero-2.php

# Valores locales de PHP, sin necesidad de editar el php.ini global. Podemos poner 
# cualquier variable tras el “php-set=”
php-set = date.timezone=Europe/Madrid
php-set = open_basedir=%(app_data_dir):/tmp/:/usr/share/pear/
php-set = session.save_path=/tmp
php-set = post_max_size=1000M
php-set = upload_max_filesize=1000M

# Extensiones PHP
php-set = extension=pdo_sqlite.so
php-set = extension=sqlite3.so

processes = 10
cheaper = 2
cron = -3 -1 -1 -1 -1 /usr/bin/php -f /%(app_data_dir)/cron.php 1>/dev/null

Como podemos observar, con la configuración anterior no solo indicamos que privilegios utilizaremos en la ejecución de la aplicación, sino que también podemos indicar qué ficheros PHP se podrán ejecutar y cuáles no, así como establecer configuración específica para PHP, evitando de una manera más elegante el uso de ficheros .htaccess.

Comments

  1. Utiliza el módulo su_php en apache y listo.

  2. Sí, es una opción, siempre y cuando sólo se ejecuten webs programadas en PHP. En un entorno donde se ejecuten webs desarrolladas en diferentes tecnologías no serviría.