lunes, 27 de febrero de 2012

Basado en hechos reales: Cómo proteger tu SQL Server en Internet.

Recientemente hemos sufrido un ataque informático contra nuestra base de datos SQL Server 2008 R2 expuesta en Internet.

Por casualidad nos pusimos a mirar el log de errores y descubrimos que había más de 2 millones y medio de intentos de inicio de sesión erróneos del usuario sa desde hace más de 2 meses.

El cómo no nos dimos cuenta en todo ese tiempo de que estábamos siendo atacados por fuerza bruta contra el usuario sa daría para otro post, pero ahora lo más importante es intentar incrementar drásticamente la seguridad de nuestro servidor SQL Server en Internet y dejar de buscar culpables… sobre todo porque yo mismo soy el principal culpable!

“Cabe resaltar que este post asume un escenario con un servidor VPS donde se tienen plenos derechos de administración accediendo por escritorio remoto, es decir, no estoy hablando de Windows Azure ni de un hosting compartido”

La medida más efectiva para evitar ataques contra tu servidor SQL Server en Internet es NO exponerlo a Internet. Esto parece de perogrullo, pero a veces no es tan obvio. ¿Por qué expones tu servidor SQL Server en Internet? Normalmente será porque los desarrolladores argumentan que necesitan acceder a la base de datos para consultar datos, realizar cambios, etc. Yo creo que los desarrolladores (entre los que yo me incluyo) deberíamos pensar que un sistema en producción no es nuestra banco de pruebas personal y que si realmente necesitamos acceder a los datos del servidor hay otras herramientas para darnos acceso sin comprometer la seguridad del servidor. Por ejemplo, escritorio remoto, una VPN, etc.

La verdad es que para un sistema SQL Server en producción no se me ocurre ninguna buena razón para exponerlo directamente en Internet.

Antes de ver que medidas tomamos para garantizar la seguridad de nuestro servidor, la pregunta es ¿Cómo me doy cuenta de que estoy siendo atacado? Pues mirando el log de errores de SQL Server. El log de errores de SQL Server lo podemos consultar de 2 formas distintas:

  • De forma manual, accediendo a Administración > Registros de SQL Server
  • A través de T-SQL, con el comando xp_ReadErrorLog

En cualquiera de los 2 casos podrás ver una información como la siguiente (aquí optamos por el comando xp_ReadErrorLog que es más directo)

xp_ReadErrorLog 0, 1, 'failed'

LogDate

ProcessInfo

Text

2012-02-23 10:39:25.710

NULL

Login failed for user 'sa'. Motivo: la contraseña no es válida para el inicio de sesión proporcionado. [CLIENTE: <local machine>]

Ahora imagina este registro pero multiplicado por 2.5 millones de veces con distintas IP y desde hace 2 meses…y 2.5 GB de log de regalo… ¡terror!

Lo primero es cortar de raíz el acceso al servidor SQL Server desde Internet, y esto es tan sencillo cómo deshabilitar las siguientes reglas del firewall (puede que en tu equipo no se llamen igual, pero básicamente responderán al mismo propósito):

  • MS SQL over TCP protocol. Puerto 1433 sobre TCP.
  • MS SQL Probe. Puerto 1434 sobre UDP.

En este momento ya estamos seguros porque NO exponemos nuestro servidor SQL Server a Internet, pero ¿Qué pasa si tuviéramos necesidad de hacerlo por algún motivo que ahora mismo se me escapa?

Pues te cuento que según mis investigaciones en google y con la ayuda del sentido común (el menos común de los sentidos por otra parte) las medidas que vamos a tomar son las siguientes:

1. Asegurarnos que todas las claves de los login de SQL Server responden a una política de contraseñas fuerte que impiden poner nombres como “Pepe” o “God”. Esto es porque así los diccionarios de ataques por fuerza bruta no encontrarán nuestra contraseña a la primera de turno y además, cuando empiecen a generar contraseñas de forma aleatoria, les cueste más… Gracias a esto después de 2 meses, nuestro servidor no ha sido hackeado (esta es la única triste medalla que podemos lucir a día de hoy).

A este respecto podemos instruir a SQL Server para garantizar una política de contraseñas e incluso forzar la expiración de los login de SQL Server. Más info aquí. Yo por mi parte no voy a activar esta configuración pero prometo que mi contraseña será de muuuchos caracteres, con números, mayúsculas y minúsculas y algún carácter especial como la arroba o el ampersand.

2. Cambiar el puerto por defecto donde escucha SQL Server. Por defecto, SQL Server atiende al puerto 1433 (puerto bien conocido o como dicen en inglés, well known port). Esto significa que cualquier escáner de puertos que detecte que esté abierto el puerto 1433 automáticamente nos dirá que tenemos a SQL Server a la escucha, luego seremos una victima pidiendo a voces un verdugo.

Una lista completa de estos puertos “bien conocidos” la puedes encontrar en la Wikipedia, que por ejemplo nos dice que los siguientes puertos son “bien conocidos”:

  • 1433/tcp Microsoft-SQL-Server
  • 1434/tcp Microsoft-SQL-Monitor
  • 1434/udp Microsoft-SQL-Monitor

Primero vamos a hacer un ejemplo con los puertos abiertos para ver que información nos da un escáner de puertos on-line como http://www.internautas.org/w-scanonline.php

Con los puertos abiertos (sin haber desactivado las reglas del firewall):

clip_image001

Con los puertos cerrados (habiendo desactivado las reglas del firewall):

clip_image002

Aunque en este post nos estamos centrando en los puertos 1433 TCP (puerto por defecto para SQL Server) y 1434 UDP (puerto por defecto para el servicio SQL Server Browser), hay otros puertos menos habituales pero también relacionados con SQL Server que también podrían estar abiertos. Puedes encontrar más información sobre estos puertos aquí.

Además y aunque no es el propósito de este post, si utilizamos instancias con nombre podríamos estar utilizando puertos dinámicos. Un excelente post que habla sobre ello y lo deja muy claro SQL Server runs on which port? Y otro post más que indica como averiguar que puerto concreto está utilizando SQL Server si estamos utilizando los puertos dinámicos How to Find the Dynamic Port reserved by SQL Server?

En cualquier caso, nosotros no tenemos instancias con nombre y la instancia por defecto utiliza el puerto 1433, así que lo cambiaremos a cualquier otro puerto en el rango 49152-65535 (este es el rango para puertos personalizados que seguro no nos darán problemas mañana con la instalación de otros programas). Imaginemos que lo cambio por ejemplo al puerto 50215. El procedimiento para cambiar el puerto está muy bien explicado aquí.

Después de esto ya sólo nos queda crear una regla en el firewall de Windows, en la que incluso podríamos acotar que direcciones IP podrán conectar (yo por mi parte sólo daré acceso a la IP pública de mi oficina).

3. Deshabilitar la cuenta del usuario sa. Esta medida responde a que es por todos conocidos que cualquier instalación de SQL Server tendrá un usuario sa con privilegios administrativos sobre el servidor. Pero claro está que si deshabilitamos esta cuenta, nuestro atacante no sabrá con que login de SQL Server llevar a cabo su ataque. Lógicamente, antes de deshabilitar la cuenta sa estate seguro de que has creado otro login con los mismos privilegios que el usuario sa o que tienes agregada alguna cuenta de usuario de Windows al role de servidor sysadmin.

Hasta aquí hemos llegado con el propósito de intentar poner las cosas un poco más difíciles a nuestros queridos “amigos de lo ajeno”.

Un saludo!

6 comentarios:

  1. Un artículo muy interesante.

    ¿Qué opinas de crear un trigger en el evento LOGON para que mande un mail (o avise de alguna otra manera) cuando se produzcan estos ataques?

    ¿Se puede conseguir lo mismo de una forma más sencilla?

    Un saludo.

    ResponderEliminar
  2. Hola Jonathan:
    La verdad es que, efectivamente, el tema de estar mirando el log cada 2x3 para ver si has sido atacado no parece muy efectivo.
    Voy a mirar el tema de los triggers porque veo que ha evolucionado mucho y no estoy al día.
    Por otro lado, en Domitienda me pusieron en pista de Win2FailBan que tiene muy buena pinta. Un tutorial sencillo http://antivirusonline.es/blog/index.php/2009/05/29/bloquear-ip-de-ataques-fuerza-bruta-fail2ban/ y la página del proyecto http://winfail2ban.sourceforge.net/
    En cuanto tengo algo más claro te cuento, porque estamos "rallados" con este tema y estamos dispuestos a intentar poner las cosas difíciles a los "amigos de lo ajeno".
    Un saludo.

    ResponderEliminar
  3. Nos hemos encontrado con el mismo problema, menos mal que nuestras claves no son muy obvias....
    Pero nosotros estábamos intentando solucionarlo a través de certificados o claves simétricas y forzar que el SQL Server encripte las comunicaciones, pero de momento en las pruebas no han dado los resultados esperados.

    Saber si alguien también pensó en lo mismo y pudo aplicarlo, o es un callejón sin salida.

    Gracias.

    ResponderEliminar
  4. Hola Sergio, he leído algo al respecto de encriptar las comunicaciones de SQL con SSL, pero no lo he puesto nunca en práctica. Entiendo que esta medida sería adicional (no cambiará nada del post, pero sí quizás metería el SSL). De todas formas, entiendo que habría que instalar un certificado válido, que los clientes .NET lo supieran interpretar, uff, ahí tela que cortar ;-)

    ResponderEliminar
  5. Teóricamente no es muy complicado.
    http://msdn.microsoft.com/es-es/library/ms191192%28v=sql.105%29.aspx
    hasta hay algunos foros que complementan la anterior página, con el añadido de como generar un certificado viable. Pero a mí de momento no me ha funcionado, será porque lo he probado en un Express, mañana lo probaré en una versión Developer a ver si funciona.

    ResponderEliminar
  6. Excelente post, tengo el mismo problema y he instalado Win2FailBan, lo malo es que este no reconoce la IP de donde proviene el ataque y ahora tengo por un lado el log incrementandose y win2failban tratando en cada ataque bloquearlo y banearlo. Mi pregunta es, si bloqueo los puertos de SQL Server ¿podre acceder a la base de datos si mi conexion es por escritorio remoto, ya que supongo yo sería localmente...?

    ResponderEliminar