13 consejos para mejorar la seguridad de tu aplicación web

Hace algunas semanas asistí a un (excelente) curso de seguridad en aplicaciones web impartido por @raulsiles en el Centro Criptológico Nacional. Tras terminarlo programé una pequeña auditoría de varias aplicaciones. Este post trata de ennumerar una serie de consejos básicos para mejorar la seguridad de una aplicación web con relativamente poco esfuerzo

0.-Formación, formación, formación

Todos debemos tener unas nociones básicas. Hay muchos cursos (incluso gratuitos) y libros. No te fíes exclusivamente de lo que te cuenta en un blog alguien no experto en la materia 😉

1.-Estar al tanto de las actualizaciones de los productos utilizados

Una aplicación está sustentada en una serie de componentes software. Todos los días se descubren vulnerabilidades que afectan a esos componentes y se publican actualizaciones para parchearlas. Si no aplicamos esas actualizaciones tenemos componentes con vulnerabilidades conocidas y publicadas. Por ejemplo, si un atacante averigua que en una aplicación se utiliza PHP 5.3.X (sin soporte, actualmente están vivas las ramas 5.4.X y 5.5.X) sabe las vulnerabilidades no parcheadas desde el fin de soporte de PHP 5.3 y por lo tanto puede explotarlas.

Por ejemplo, supongamos una aplicación basada en Drupal corriendo en un servidor web Apache en Ubuntu Server. Pues habría que estar al tanto de:

  • Drupal y sus módulo que utilicemos.
  • Apache y sus módulos.
  • PHP
  • Ubuntu Server
  • ….

Para evitar estar consultando páginas, yo trato de suscribirme a feeds rss y newsletter de las releases de cada componente.

2.-Comprobar la información que se obtiene de las cabeceras HTTP

Las cabeceras HTTP suelen aportar más información de lo deseable acerca del sistema operativo, versiones de software, productos, etc. Para ver las cabeceras utilicé nmap y Netcat

nmap -O -Pn -n NOMBRE_DEL_HOST

La respuesta:

Starting Nmap 5.21 ( http://nmap.org ) at 2014-05-06 20:34 CEST
Nmap scan report for NOMBRE_DEL_HOST (X.X.X.X)
Host is up (0.049s latency).
Not shown: 998 filtered ports
PORT     STATE  SERVICE
80/tcp   open   http
Device type: general purpose|WAP|specialized
Running (JUST GUESSING) : Linux 2.6.X|2.4.X (87%), Linksys Linux 2.4.X (86%), Crestron 2-Series (85%)
Aggressive OS guesses: Linux 2.6.22 (87%), Linux 2.6.23 (87%), OpenWrt White Russian 0.9 (Linux 2.4.30) (86%), OpenWrt 0.9 - 7.09 (Linux 2.4.30 - 2.4.34) (86%), OpenWrt Kamikaze 7.09 (Linux 2.6.22) (86%), Crestron XPanel control system (85%)
No exact OS matches for host (test conditions non-ideal).

OS detection performed. Please report any incorrect results at http://nmap.org/submit/ .
Nmap done: 1 IP address (1 host up) scanned in 13.12 seconds
nc -w 10 NOMBRE_DEL_HOST 80
HEAD / HTTP/1.0

La respuesta:

HTTP/1.1 200 OK
Server: nginx
Date: Tue, 06 May 2014 18:39:17 GMT
Content-Type: text/html; charset=UTF-8
Connection: close
Vary: Accept-Encoding
X-Powered-By: PHP/VERSION+NOMBRE_DE_REPOSITORIO~NOMBRE_SO+1
X-Pingback: http://NOMBRE_DEL_HOST/xmlrpc.php
Link: <http://wp.me/3dPWl>; rel=shortlink
X-Frame-Options: SAMEORIGIN
X-Content-Type-Options: nosniff
X-XSS-Protection: 1; mode=block
X-Page-Speed: 1.7.30.4-3847
Cache-Control: max-age=0, no-cache

Como podéis ver, se ve tanto el servidor (cabecera “Server”) como la versión de PHP utilizada (cabecera “X-Powered-By”), de la que se podía deducir el repositorio del que se instaló y el sistema operativo del servidor.

Esta segunda cabecera es la que más me preocupó, ya que sabiendo la versión de PHP y el sistema operativo utilizada en el servidor es más sencillo explotar vulnerabilidades. Investigando un poco averigüé que esa cabecera la introduce PHP y desactivarla es tan sencillo como cambiar una linea del archivo de configuración php.ini.

Basta con buscar la linea “expose_php=On” y cambiarlo a “expose_php=Off”. Tras ello, hay que reiniciar php-fpm y el servidor web

En cuanto a la cabecera “Server”, lo más preocupante hubiera sido que mostrara la versión, pero no me termina de gusta que muestre el servidor web utilizado. Mi primer impulso fue ofuscarla, pero tras leer alguna discusión al respecto y leer las implicaciones me convencí de que:

  1. Seguramente ese dato se pueda obtener de otros modos.
  2. Al ocultar la versión tampoco es una información especialmente interesante.

Por lo que decidí olvidarme de ello.

3.-Verificar los servicios visibles

El siguiente paso es comprobar que sólo estamos exponiendo los servicios deseados (típicamente, el servidor web por los puertos 80 y 443). Para comprobarlo podemos usar Nmap:

sudo nmap -sV -n -Pn -p 80,443 NOMBRE_DEL_HOST
Starting Nmap 5.21 ( http://nmap.org ) at 2014-05-06 20:52 CEST
Nmap scan report for NOMBRE_DEL_HOST (XXX.XXX.XXX.XXX)
Host is up (0.051s latency).
PORT    STATE    SERVICE VERSION
80/tcp  open     http    nginx
443/tcp filtered https

Service detection performed. Please report any incorrect results at http://nmap.org/submit/ .
Nmap done: 1 IP address (1 host up) scanned in 8.68 seconds

Vemos que sólo están accesibles los puertos del servidor web.

4.-Buscar vulnerabilidades

Utilizando herramientas más avanzadas como Nikto y W3AF podemos automatizar la búsqueda de vulnerabilidades e información interesante:

larry@sobremesa ~ $ nikto -h NOMBRE_DEL_HOST
- Nikto v2.1.4
---------------------------------------------------------------------------
+ Target IP:          XXX.XXX.XXX.XXX
+ Target Hostname:    NOMBRE_DEL_HOST
+ Target Port:        80
+ Start Time:         2014-05-07 21:14:16
---------------------------------------------------------------------------
+ Server: nginx
+ No CGI Directories found (use '-C all' to force check all possible dirs)
+ robots.txt contains 1 entry which should be manually viewed.
+ ETag header found on server, fields: 0x52af7c7c 0x21 
+ OSVDB-3093: /.htaccess: Contains authorization information
+ OSVDB-3092: /xmlrpc.php: xmlrpc.php was found.
+ /wp-content/plugins/akismet/readme.txt: The WordPress Akismet plugin 'Tested up to' version usually matches the WordPress version
+ OSVDB-3092: /license.txt: License file found may identify site software.
+ /wordpress/: A WordPress installation was found.
+ 6448 items checked: 41 error(s) and 7 item(s) reported on remote host
+ End Time:           2014-05-07 22:05:36 (3080 seconds)
---------------------------------------------------------------------------
+ 1 host(s) tested

La salida de w3af es demasiado extensa como para ponerla aquí, pero utilizando los perfiles predeterminados podemos obtener información muy valiosa para mejorar la seguridad de la aplicación.

Hay que tener en cuenta que herramientas como Nikto o W3af realizan muchas peticiones HTTP para tratar de obtener información. Es muy adecuado tener instalado algún Web Application Firewall o al menos un plugin que bloquee las direcciones IP que realicen demasiadas peticiones en poco tiempo. De cara a obtener la mayor cantidad de información posible, puede ser interesante desactivar estas herramientas antes de lanzar las peticiones.

5.- Bloquear puertos de entrada y salida

Es imprescindible utilizar un Firewall para bloquear todos los puertos que no sean necesarios. Si no se dispone de un Firewall corporativo, se puede utilizar Iptables con el front-end ufw, cuya configuración es muy sencilla.

6.- Ajustar los usuarios y permisos

Ficheros sensibles del sistema operativo

Aunque suene evidente, insistir en que todos los ficheros sensibles del sistema operativo deben estar lo más restringidos posible. Por ejemplo, un VirtualHost sólo debe ser leído y escrito por root.

Ficheros de la aplicación web

En muchas ocasiones tendemos a asignar los archivos de una aplicación web al usuario y grupo que ejecuta el servidor web ( en Linux suele ser www-data). Sin embargo, esto no es realmente necesario y tiene sus riesgos desde el punto de vista de la seguridad. No es necesario porque el usuario www-data debe tener permiso para leer y, dependiendo de la tecnología que utilicemos, ejecutar determinados archivos. Es decir, salvo determinadas excepciones el usuario www-data no tiene por qué tener permisos de escritura.

Por ello, se puede optar por separar el usuario de publicación y el del servidor web. Por ejemplo, los archivos podrían pertenecer al usuario publicador y el grupo www-data, teniendo el grupo permisos de lectura y ejecución. De este modo, si un atacante obtuviera el control del usuario www-data expotando una vulnerabilidad de nuestra aplicación no podría cambiar el contenido.

No obstante, hay situaciones en las que es interesante que el usuario www-data tenga permisos de escritura sobre los archivos. Por ejemplo, WordPress permite actualizar tanto el CMS como los plugins a través de su interfaz web. Esta funcionalidad es muy cómoda, pero lógicamente requiere que el usuario www-data tenga permisos de escritura. En ese caso, cada responsable deberá elegir entre funcionalidad y mayor seguridad.

7.- Revisar la configuración de HTTPS

Aunque todos usamos deberíamos usar HTTPS, en muchas ocasiones aceptamos la configuración por defecto del software que utilicemos sin dedicarle mayor tiempo. Sin embargo, conviene prestarle atención a aspectos como:

  • Pedir a la CA que emite nuestro certificado que utilice SHA-2, ya que SHA-1 quedará obsoleto en 2017. Además, el certificado debe ser de 2048b.
  • Revisar los algoritmos utilizados. Para ello, es interesante utilizar TLSSLed.
  • Usar las cabeceras STS.
  • Evitar las redirecciones HTTP -> HTTPS, ya que son vulnerables a SSL Strip.
  • Asegurarse de que el certificado utilizado es designado como confiable por los navegadores. Esto implica utilizar autoridades de certificación (AC) cuyo certificado raíz está incluido por defecto y comprobar que el servidor web incluye los certificados de las AC intermedias.

8.- Habilitar la conexión en dos pasos

Si el software utilizado lo soporta o si es factible desarrollarlo, es interesante exigir autenticación en dos pasos al menos para los perfiles más privilegiados. En el caso de WordPress está disponible el plugin Google Authenticator.

9.- Limitar la información que ofrece tu aplicación en lo relativo a contraseñas

Pensemos en tres funcionalidades que tienen casi todas las aplicaciones públicas:

  1. Crear una cuenta.
  2. Tratar de autenticarse con una cuenta.
  3. Olvidé la contraseña

Varias de ellas (o todas) permiten a un hipotético atacante saber si un correo electrónico determinado está asociado a una cuenta:

  • Intento crear una cuenta => “Ese correo electrónico ya está asociado con una cuenta”
  • Tratar de autenticarse con una cuenta => “Esa cuenta no existe” / Error genérico
  • Olvidé la contraseña => “Se ha enviado un enlace etc etc ” / “Esa cuenta no existe”

Para evitar dar más información de la necesaria, se pueden configurar los mensajes de error de modo que den la menor información posible:

  • Crear una cuenta => Responder siempre “Se ha enviado un correo para confirmar la creación”. Si ya existe, no hacer nada, o enviar un correo al propietario legítimo de la cuenta avisando de la circunstancia.
  • Tratar de autenticarse con una cuenta => Si la autenticación falla, devolver un error genérico, no decir si la cuenta existe o no.
  • Olvidé la contraseña => Decir siempre “Se ha enviado un enlace para resetear la contraseña”, aunque la cuenta no exista.

Es cierto que estas medidas pueden disminuir la usabilidad de la aplicación. Un usuario legítimo podría pinchar en olvidé mi contraseña pero equivocarse y meter su correo electrónico mal. En ese caso, como la aplicación ya no le avisa de que el correo electrónico introducido es incorrecto, puede esperar indefinidamente a ese correo. Como siempre, habrá que valorar la importancia de la seguridad de la aplicación frente a su usabilidad.

10.- Configurar las cookies para que sean httponly y secure

Al configurar la cookie como secure sólo se intercambian entre el navegador y tu aplicación por https. Al marcarlas como httponly evitas que scripts accedan a la misma, limitando las posibilidades de los ataques vía cross-site scripting.

11.- Filtrar todo lo que venga del usuario y utilizar consultas SQL “compiladas”

Todo campo de formulario puede ser utilizado para explorar vulnerabilidades. SIEMPRE conviene filtrar el contenido introducido por el usuario mediante expresiones regulares.

Además, para tratar de evitar ataques por inyección SQL conviene utilizar consultas compiladas (prepared statements). En este post de StackOverflow lo explica muy bien.

12.- Utilizar un WAF

Un Web Application Firewall es una aplicación que intercepta las peticiones dirigidas a una aplicación web. De este modo, antes de derivar la petición se ejecutan una serie de filtros que tratan de encontrar patrones comunes en ataques. Por ejemplo, si un atacante envía muchas peticiones en poco tiempo, su IP será bloqueada temporalmente.

El más popular en el mundo del software libre es mod_security. Sin llegar a su nivel de potencia, en WordPress hay plugins como iThemes Security que tratan de cubrir los puntos básicos.

13.- Repetir las comprobaciones cada cierto tiempo

Aunque tras un repaso se haya mejorado la seguridad de una aplicación, en un tiempo la situación se habrá vuelto a degradar. Esto es debido a actualizaciones de software, vulnerabilidades que se descubran, el hecho de que a lo largo de la vida de una aplicación acceden a ella diferentes técnicos, falta de recursos, etc.

Por ello es conveniente volver a auditar la aplicación cada cierto tiempo. La frecuencia dependerá de la criticidad de la aplicación y de los recursos disponibles, pero siempre se puede hacer una comprobación rápida utilizando herramientas que automaticen parte de las comprobaciones.

Herramientas utilizadas

Nmap y Netcat para examinar las cabeceras HTTP.

W3AF y Nikto para hacer un escáner general de vulnerabilidades.

Phpsecinfo para comprobar la configuración de una aplicación PHP.

TLSSLed para verificar la configuración de HTTPS.

Brutus para evaluar la calidad de las contraseñas.

Sqlmap para buscar vulnerabilidades de tipo SQL Injection.

Fuentes:

http://www.raulsiles.com/
https://www.ccn.cni.es/
http://stackoverflow.com/questions/962230/hide-x-powered-by-nginx
http://serverfault.com/questions/214242/can-i-hide-all-server-os-info
https://gist.github.com/plentz/6737338
http://www.cyberciti.biz/tips/linux-unix-bsd-nginx-webserver-security.html
https://www.modsecurity.org/

Publicado en proyectos, tutoriales Etiquetado con: , ,
2 Comentarios en “13 consejos para mejorar la seguridad de tu aplicación web
  1. Miguel J Guevara dice:

    Sos un capo Gracias por compartirlo, sos una herramienta imprescindible para los que vivimos entre los vpss

  2. Ross LH dice:

    Excelentes consejos para mejorar la seguridad de mis aplicativos 🙂

Deja un comentario

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *

*