Manual: Instalación y configuración de PHPIDS
El otro día hablé de algunas buenas técnicas para proteger una aplicación PHP. Una de las herramientas que comenté, fue PHPIDS. PHPIDS es un software IDS para aplicaciones en PHP. Al contrario que modSecurity, no se requiere instalar módulos en Apache. El funcionamiento de PHPIDS será el siguiente: toda petición a nuestra aplicación pasará primero (después comentaremos cómo) por PHPIDS, de manera que se comprobará el contenido de dicha petición en busca de patrones (mediante reglas establecidas) que puedan comprometer la seguridad. PHPIDS nos devolverá el nivel de riesgo de la petición, y será labor del programador dedicir qué hacer a partir de ello, es decir, cómo actuará nuestra aplicación en función de ello.
A continuación vamos a explicar cómo instalar y configurarlo.
Las pruebas para este artículo se han realizado en Apache + PHP5 + MySQL bajo Ubuntu (instalación siguiendo este artículo). Lo primero que vamos a hacer es descargar PHPIDS de su web oficial. La última versión en el momento de escribir este artículo es la 0.7. Una vez lo hemos descargado crearemos una carpeta llamada «phpids» por ejemplo dentro de la carpeta «www» de nuestro Apache. Dentro de éste metemos la carpeta «lib» que encontramos dentro del PHPIDS que hemos descargado. Los directorios «docs» y «tests» no será necesario subirlos.
Ahora accedemos al fichero /lib/IDS/Config/Config.ini.php para modificar la configuración. Modificamos la línea 13:
<br /> base_path = /full/path/to/IDS/<br />
Debemos poner la ruta absoluta hasta el directorio IDS que encontramos dentro de la carpeta «lib».
Otra característica de PHPIDS es la de hacer log. Cuando se produce una petición que PHPIDS considera que puede se dañida, nos da la opción de registrarla en un log. Dentro de este fichero podemos configurar cómo queremos que haga el log. En la línea 48 podemos configurar el fichero plano donde queremos que guarde las entradas:
</p><p>path = tmp/phpids_log.txt</p><p>
Mi recomendación es que no modifiquemos esta línea. Es importante que le demos permisos de escritura a la carpeta «tmp» del directorio «IDS», de lo contrario tendremos errores de escritura.
Otra opción que nos da PHPIDS es la guardar los registros en una base de datos. Las líneas 62 a 67 nos permiten configurar la base de datos de MySQL:
<br /> ; database logging</p><p>wrapper = "mysql:host=localhost;port=3306;dbname=phpids"<br /> user = root<br /> password = root<br /> table = intrusions<br />
Deberemos configurar la base de datos, la tabla concreta, así como un usuario y contraseña con permisos para la base de datos.
Por último, podremos configurar avisos mediante email al administrador de la aplicación. Esta opción podremos configurarla en las líneas 50 a 60:
<br /> <br /> ; email logging</p><p>; note that enabling safemode you can prevent spam attempts,<br /> ; see documentation<br /> recipients[] = [email protected]<br /> subject = "PHPIDS detected an intrusion attempt!"<br /> header = "From: [email protected]"<br /> envelope = ""<br /> safemode = true<br /> urlencode = true<br /> allowed_rate = 15<br /><br />
Una vez que tenemos configurado PHPIDS vamos a crear una fichero php que compruebe la petición y nos devuelva si contiene patrones maliciosos o no. Además vamos a definir cómo queremos que actúe ante una petición maliciosa. Creamos una fichero llamado por ejemplo «phpids.php» dentro del directorio «www». El contenido del fichero es el siguiente:
</p><p>/**<br /> * PHPIDS<br /> * Requirements: PHP5, SimpleXML<br /> *<br /> * Copyright (c) 2010 PHPIDS group (https://phpids.org)<br /> *<br /> * This program is free software; you can redistribute it and/or modify<br /> * it under the terms of the GNU General Public License as published by<br /> * the Free Software Foundation; version 2 of the license.<br /> *<br /> * This program is distributed in the hope that it will be useful,<br /> * but WITHOUT ANY WARRANTY; without even the implied warranty of<br /> * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the<br /> * GNU General Public License for more details.<br /> */</p><p>// set the include path properly for PHPIDS<br /> set_include_path(<br /> get_include_path()<br /> . PATH_SEPARATOR<br /> . 'phpids/lib/'<br /> );</p><p>if (!session_id()) {<br /> session_start();<br /> }</p><p>require_once 'IDS/Init.php';</p><p>try {</p><p>/*<br /> * It's pretty easy to get the PHPIDS running<br /> * 1. Define what to scan<br /> *<br /> * Please keep in mind what array_merge does and how this might interfer<br /> * with your variables_order settings<br /> */<br /> $request = array(<br /> 'REQUEST' => $_REQUEST,<br /> 'GET' => $_GET,<br /> 'POST' => $_POST,<br /> 'COOKIE' => $_COOKIE<br /> );</p><p>$init = IDS_Init::init(dirname(__FILE__) . '/phpids/lib/IDS/Config/Config.ini.php');</p><p>/**<br /> * You can also reset the whole configuration<br /> * array or merge in own data<br /> *<br /> * This usage doesn't overwrite already existing values<br /> * $config->setConfig(array('General' => array('filter_type' => 'xml')));<br /> *<br /> * This does (see 2nd parameter)<br /> * $config->setConfig(array('General' => array('filter_type' => 'xml')), true);<br /> *<br /> * or you can access the config directly like here:<br /> */</p><p>$init->config['General']['base_path'] = dirname(__FILE__) . '/phpids/lib/IDS/';<br /> $init->config['General']['use_base_path'] = true;<br /> $init->config['Caching']['caching'] = 'none';</p><p>// 2. Initiate the PHPIDS and fetch the results<br /> $ids = new IDS_Monitor($request, $init);<br /> $result = $ids->run();</p><p>/*<br /> * That's it - now you can analyze the results:<br /> *<br /> * In the result object you will find any suspicious<br /> * fields of the passed array enriched with additional info<br /> *<br /> * Note: it is moreover possible to dump this information by<br /> * simply echoing the result object, since IDS_Report implemented<br /> * a __toString method.<br /> */<br /> if (!$result->isEmpty()) {<br /> //echo $result;</p><p>/*<br /> * The following steps are optional to log the results<br /> */<br /> require_once 'IDS/Log/File.php';<br /> require_once 'IDS/Log/Composite.php';</p><p>$compositeLog = new IDS_Log_Composite();<br /> $compositeLog->addLogger(IDS_Log_File::getInstance($init));</p><p>/*<br /> * Note that you might also use different logging facilities<br /> * such as IDS_Log_Email or IDS_Log_Database<br /> *<br /> * Just uncomment the following lines to test the wrappers<br /> */<br /> /*<br /> *<br /> require_once 'IDS/Log/Email.php';<br /> require_once 'IDS/Log/Database.php';</p><p>$compositeLog->addLogger(<br /> IDS_Log_Email::getInstance($init),<br /> IDS_Log_Database::getInstance($init)<br /> );<br /> */<br /> $compositeLog->execute($result);</p><p>header("Location:nohacking.php");</p><p>}<br /> } catch (Exception $e) {<br /> /*<br /> * sth went terribly wrong - maybe the<br /> * filter rules weren't found?<br /> */<br /> printf(<br /> 'An error occured: %s',<br /> $e->getMessage()<br /> );<br /> }</p><p>
Pasemos a explicar algunos puntos importantes de este código:
- En la línea 21 debéis poner la ruta absoluta hasta el directorio lib, de lo contrario es probable que tengáis varios problemas como tuve yo.
- Líneas 40 a 43: Indicaremos qué variables queremos que se analicen cada vez que se ejecute PHPIDS.
- Línea 46: dirección del fichero de configuración Config.ini.php
- Línea 61: dirección al directorio IDS
- Línea 79: Este if será True siempre y cuando PHPIDS haya detectado alguna entrada maliciosa. Recordemos que PHPIDS nos da un nivel de peligrosidad de unas entradas. Os recomiendo que descomentéis la línea siguiente, en la que se imprime por pantalla el valor de $result para que veáis cuál es el resultado que nos devuelve.
- Línea 109: Mi medida de seguridad ha sido que cuando se detecte algo malicioso (recordemos que estamos dentro del bloque if, redirija la aplicación a una página llamada nohacking.php en la cual se muestra un mensaje de alerta diciendo que se han detectado entradas maliciosas y que el caso de ser falso positivo se contacto con el administrador. En este caso, se habrá guardado una entrada en el log que proporciona PHPIDS.
Ahora viene una pregunta muy importante, ¿tengo ahora que modificar todas mis páginas php para primero pasen por esta página y se comprueben las entradas? Eso sería un engorro… podrías tener muchísimos scripts php que modificar. Por suerte php nos proporciona otra solución: anteponer un script php antes de que se ejecute cualquer scripts. Es decir, si visitamos por ejemplo index.php, antes de ejecutarse haremos que se ejecute nuestro phpids.php, de esta manera, si existe alguna petición maliciosa será redirigido a nohaking.php. ¿Cómo podemos hacer esto? Bien, esto podemos hacerlo de dos maneras: una solución global (para todo el servidor y todas las páginas, de todos los clientes por ejemplo) o sólo particular para una página web.
Para la solución global modificaremos el fichero php.ini y activaremos la variable auto_prepend_file y la igualaremos a la dirección donde se encuentre nuestro fichero phpids que acabamos de crear:
auto_prepend_file = /var/www/web1/web/phpids.php
La solución particular podemos hacerla mediante el fichero .htaccess de Apache (desconozco una solución similar para IIS). Crearemos un fichero llamado «.htaccess» en el directorio de nuestra web con el siguiente contenido:
php_value auto_prepend_file /var/www/web1/web/phpids.php
Ahora sólo tendremos que reiniciar el servidor y podréis comprobar que al entrar en cualquier página antes, se antepondrá este script. Podéis comprobar esto entrando a una página php de vuestra web. Por ejemplo:
index.php
Accederá primero a phpids.php (de manera transparente a nosotros) y al no encontrar nada malicioso seguirá ejecutando nuestro fichero index.php.
index.php?prueba=<script>alert(1)</script>
Al encontrar un intento de XSS por GET lo detectará como malicioso y nos redirige a nohacking.php. ¿Nada mal verdad?
Esto es todo, a disfrutar de PHPIDS.
¡Saludos!
Referencias:
Página oficial de PHPIDS: https://phpids.org/
Tutorial de configuración: http://www.howtoforge.com/intrusion-detection-for-php-applications-with-phpids
Foro de PHPIDS: http://forum.itratos.de/forum.php?tab=phpidsen&langid=2&styleid=2