Los DBAs son criaturas paranoicas. No, en serio, los amo de corazón, pero son la cosa más desconfiada que hay en el mundo; y si no lo es el DBA, lo será su jefe, o el jefe de su jefe, o alguien en su línea de reporte directo que considera que la seguridad de su empresa (ya sea ésta un Banco internacional o la Agrupación de Tamborileros y Triangulistas Amateur de Cerecinos de Campo) es crítica y de mayor importancia. Qué curioso, sobre todo si tenemos en cuenta su reticencia innata para actualizar de nivel de Service Pack, o para tener una cuidadosa política de permisos en el servidor que minimicen los riesgos de los ataques de inyección SQL, que no nos engañemos, si bien la vulnerabilidad de una inyección SQL la pone en bandeja el desarrollador, su explotabilidad viene determinada por la configuración y administración de la misma. Aquí estamos todos en el mismo barco, o como diría el gran Fran Peula, «Aquí, o f******* todos o la p*** al rio» :)).
Hoy he tenido un momento flashback. Como algunos de vosotros sabéis, hasta hace poco trabajaba en Microsoft dando soporte a SQL Server, y hoy me he acordado de dos clientes en particular. Uno de ellos, un chico gallego muy majo, de una empresa de desarrollo de La Coruña, que llamo a soporte de Microsoft porque habían sufrido una corrupción en uno de sus archivos de datos y era un tema crítico para ellos. La verdad es que trabajamos muy bien en ese caso; esfuerzos por los dos lados, nos quedamos bastante más tarde del horario establecido, y además hubo mucha suerte porque se pudo recuperar lo que necesitaban. Al final de la llamada, tras la recuperación de los datos, pude oír como pocas veces risas de satisfacción de éste chico y sus compañeros, se les notaba el tremendo alivio en todas las palabras de agradecimiento (realmente esa parte del trabajo era impagable… :_) ) y ¡¡¡hasta su compañero ‘linuxero’ alabó el producto y la calidad del soporte!!! ¿Creéis que me he acordado de él por todo esto? Pues no, me acordé de él porque tenía hambre, estoy en La Coruña y me prometió una mariscada xDDD Realmente no es el momento, porque no tengo casi nada de tiempo libre, pero la próxima vez que venga le llamaré; yo pondré las cervezas.
El otro cliente si tiene que ver con la parrafada inicial. Se trata de un chico de UK, un tal Greg, que estaba planeando el despliegue de una nueva aplicación, destinada a ser usada con SQL Server 2005, y que iba a contener información muy sensible. Lo que éste chico me preguntaba era básicamente si se podían cifrar los datos de una columna de SQL Server de tal modo que solo un usuario especial pudiera acceder a ellos, impidiendo el acceso a todos los demás usuarios, incluidos los sysadmin. Lo recordaré siempre porque fue mi primer caso con SQL Server 2005… ¿Veis? En el fondo soy un romanticón 🙂
Como me parece un tema interesante me gustaría compartir con vosotros una versión light del resumen que le hice, que a su vez estaba basada en dos geniales artículos de la MSDN, ya que estoy seguro que muchos de vosotros lo encontrareis útil tanto a nivel de desarrollo como de consultoría, etc.
Cifrado de Datos en SQL Server Dosmilcinc… Yukon
Como suele suceder, escoger el nivel de protección adecuado consiste en buscar el balance entre la seguridad y la usabilidad (en el sentido de lo que se complique el empleo y administración de la base de datos). Para tomar esta decisión, primeramente debemos determinar de quien queremos proteger nuestros datos. Para ello vamos a considerar tres niveles de protección básicos:
- Nivel 2: Protección frente a atacantes que pueden acceder a los ficheros físicos de la base de datos.
- Nivel 1: Nivel 2 + protección frente al Sysadmin
- Nivel 0: Nivel 1 + protección frente al propietario de la base de datos (dbo)
Ahora voy a proceder a explicar la aproximación que deberíamos tomar para securizar nuestros datos sensibles en cada uno de esos niveles, desde el más seguro (Nivel 0) hasta el menos seguro, pero más usable (Nivel 2).
Todos estos niveles se basan en el hecho de que los datos sean protegidos por una clave simétrica, ya que es la manera más eficiente de cifrar los datos, pero emplean diferentes mecanismos para proteger ésta clave simétrica. A partir de aquí surgen las diferencias:
Nivel 0: Solo el usuario autorizado puede ver sus datos
Para lograr este grado de protección de nuestros datos, la clave simétrica debe ser protegida por una clave que solo será conocida por el usuario que ha cifrado los datos. Éste es el modo más seguro, ya que no hay manera en la cual el propietario de la base de datos, o el sysadmin, puedan acceder a la clave simétrica, ya que ésta no está almacenada en el servidor.
Los ficheros de datos se aseguran de este modo también, ya que aún en el supuesto de que un atacante consiga obtener acceso a los ficheros y adjuntarlos a su servidor, no podrá acceder a las columnas cifradas mientras no tenga a su disposición la contraseña de cifrado de la clave simétrica.
Evidentemente, hay una seria contrapartida en términos de usabilidad: para acceder a estos datos, necesitamos asegurarnos de que la clave sea abierta, y para ello debemos suministrar explícitamente la clave con la que se cifró la clave simétrica.
Podemos comprobar en cualquier momento las claves abiertas en una sesión dada simplemente echándole un vistazo a la vista de administración del sistema sys.open_keys. Si la clave no estuviera abierta, entonces la siguiente sentencia DDL sería necesaria para abrirla:
OPEN SYMMETRIC KEY <nombre_clave> DECRYPTION BY PASSWORD = ‘<contraseña>’
Nivel 1: Otorgándole acceso al DBO
En éste escenario suponemos que el propietario de la base de datos es de fiar y puede tener acceso a las columnas sensibles, de modo que nos podemos permitir relajar un poco las restricciones de seguridad; lo que haremos en este caso es proteger la clave simétrica con un certificado (o una clave asimétrica). La clave privada del certificado (o de la clave asimétrica) necesita ser protegida por la Database Master Key o llave maestra de la base de datos. Para lograr esto en el momento de creación del certificado, debemos simplemente omitir la opción ENCRYPTION BY PASSWORD:
CREATE CERTIFICATE cert WITH SUBJECT = ‘EncryptionCert’
Si ya disponemos de un certificado existente (o clave asimétrica) protegido por una contraseña, y queremos que la protección la realice la Database Master Key, deberemos alterar éste certificado. Me gustaría recalcar aquí que la sintaxis para esta labor puede parecer muy poco intuitiva, así que como siempre, en caso de duda deberemos recurrir a los Books On Line del producto.
ALTER CERTIFICATE cert WITH PRIVATE KEY DECRYPTION BY PASSWORD = ‘<password>’
Y aquí viene la parte importante, el momento donde realmente protegemos los datos del sysadmin. El usuario dbo, o el usuario con privilegios, debe asegurarse de que la Database Master Key esté protegida solo mediante una contraseña. En el momento de creación de ésta Database Master Key se establece que, por defecto, sea cifrada por la Service Master Key, de modo que debemos eliminar ésta protección con una sentencia DROP como la siguiente:
ALTER MASTER KEY DROP ENCRYPTION BY SERVICE MASTER KEY
De éste modo, el propietario de la base de datos tiene acceso a los datos del usuario privilegiado, ya que puede usar la Database Master Key para descifrar la clave privada en el certificado, de modo que pueda ser luego usada para abrir la clave simétrica y descifrar los datos protegidos. No obstante, estamos protegidos del sysadmin ya que no será capaz de abrir la Database Master Key sin la clave apropiada.
Nivel 2: Otorgándole al Sysadmin acceso a los datos
En éste escenario vamos a confiar también en el usuario sysadmin; solo nos preocupa la posibilidad de que nuestros datos sean robados y queremos prevenir que los atacantes puedan acceder a los datos sensibles en caso de que se hagan con el fichero de datos y puedan adjuntarlo a su base de datos.
Debemos cifrar los datos con una clave simétrica, y proteger ésta clave simétrica con un certificado (o clave asimétrica) tal y como vimos en el nivel 1 de protección. Éste ultimo certificado es protegido, como demostramos antes, por la Database Master Key.
La diferencia importante es que el propietario de la base de datos NO debería hacer un drop del cifrado de la Database Master Key por la Service Master Key. Es decir, debemos dejarlo tal como se crea por defecto, o bien, en caso de que haya sido modificado, volver a establecerla con la siguiente sentencia DDL:
ALTER MASTER KEY ADD ENCRYPTION BY SERVICE MASTER KEY
El sysadmin, de éste modo, tendrá acceso a la Database Master Key y por tanto, a cualquier objeto cifrado por la misma. Esto, evidentemente, implica que los datos no están protegidos ni frente al propietario de la base de datos ni frente al administrador del sistema, pero hemos conseguido que los datos estén protegidos en el fichero de datos. De este modo, si alguien robara los ficheros de la base de datos y fuera capaz de adjuntarlos a su propio SQL Server, no sería capaz de leer los datos cifrados ya que no tiene acceso a la contraseña que cifra la clave simétrica, ni acceso a ninguna de las claves de la jerarquía que protege la clave simétrica.
En resumen, el empleo de la jerarquía de claves en SQL Server 2005 facilita las áreas administrativas y de programación ya que no necesitamos abrir explícitamente las claves usadas para proteger los datos. No obstante, si queremos una securización adicional de los datos, debemos escoger la altura en la jerarquía a partir de la cual queremos cifrar la clave con una clave simétrica. Esto nos permite aumentar dramáticamente la seguridad, a costa, como ya vengo comentandoos, de una mayor complejidad administrativa debido al hecho de tener que abrir y cerrar las claves manualmente.
Desde un punto de vista del rendimiento, es evidente que se producirá una sobrecarga en el sistema. No obstante, no podemos estimarla a priori, ya que depende inmensamente del algoritmo de cifrado empleado, así como de la cantidad de los datos.
Ejemplo
Os dejo aquí un ejemplillo que muestra algunos de los conceptos que os comenté en el breve artículo. Es un script SQL para ir ejecutando pasito a pasito, leyendo los comentarios. Espero que el cacharro este lo formatee medio bien.
CREATE DATABASE PruebaBaseDatosSegura
GO
USE PruebaBaseDatosSegura
— Creamos una nueva tabla donde vamos a almacenar el nombre del empleado (no
— cifrado) y el numero de la seguridad social del mismo. Vamos a cifrar el número de
— la seguridad social durante la inserción, de modo que tenemos que tener esto en
— mente cuando establezcamos el tipo de datos, que deberá ser un varbinary de
— modo que pueda contener la cadena resultante por el algoritmo de cifrado.
CREATE TABLE empleados (emp_nombre varchar(50), nss varbinary(256) primary key clustered)
— Ahora creamos la clave simétrica que vamos a usar para cifrar/descifrar los datos
CREATE SYMMETRIC KEY skey WITH ALGORITHM = triple_des ENCRYPTION BY PASSWORD = ‘C1av3’
— Ahora un fragmento interesante… debemos tener en mente que si queremos usar
— una clave, debemos abrirla primero. Para abrir una clave debemos usar la sentencia
— OPEN. La clave podría estar protegida por un certificado, por la “Service Master Key”,
— por la “Database Master Key” o por una contraseña. En éste ejemplo, y para hacerlo
— coincidir con el escenario en el cual queremos evitar que el propietario de la base de
— datos y el sysadmin puedan acceder a los datos, debemos suministrar la contraseña
— en la sentencia open.
OPEN SYMMETRIC KEY skey DECRYPTION BY PASSWORD = ‘C1av3’
— Vamos a insertar ahora algunos valores, usando la función encryptbykey() para generar
— un stream cifrado de la cadena que le pasamos como parámetro.
INSERT INTO empleados values (‘Tom’, encryptbykey(key_guid(‘skey’), ‘111-11-1111’))
INSERT INTO empleados values (‘John’, encryptbykey(key_guid(‘skey’), ‘222-22-2222’))
INSERT INTO empleados values (‘Bob’, encryptbykey(key_guid(‘skey’), ‘333-33-3333’))
— Si ahora hacemos la select, podemos ver que el campo nss contiene solo un stream de
— bytes ilegible.
SELECT * FROM empleados
— Para poder leer los datos, necesitamos convertir desde el varbinary donde almacenamos
— los datos, al tipo de datos de destino deseado, en este caso, varchar.
SELECT CONVERT(varchar(256), decryptbykey(nss)) FROM empleados
— En el SSMS, usamos Ctrl-L o desde el menu: QueryDisplay Estimated Execution Plan,
— para comprobar el plan de ejecución estimado. Podemos ver que se realiza un descifrado
— por cada fila escaneada.
SELECT CONVERT(varchar(256), decryptbykey(nss)) FROM empleados WHERE CONVERT(varchar(256), decryptbykey(ssn) ) = ‘111-11-1111’
— … a ver que pasa con la clave cerrada…
CLOSE SYMMETRIC KEY skey
GO
SELECT CONVERT(varchar(256), decryptbykey(nss)) FROM empleados
— Limpieza final… Ahora borramos la base de datos de ejemplo
USE Master
DROP DATABASE PruebaBaseDatosSegura
Como se puede ver en el ejemplo, cada vez que queremos acceder a un campo cifrado debemos descifrarlo con una función, y después hacer un cast al tipo de datos deseado. Esto tiene un impacto sobre el rendimiento de la aplicación, pero es algo que deberíamos medir y probar en nuestros sistemas.
Respecto a los aspectos de seguridad, obviamente esta implementación es mucho más robusta de lo que nunca haya sido SQL Server 2000, pero eso no quiere decir que podamos descansar tranquilos simplemente por utilizar las características criptográficas del producto. Hay otros modos de atacar los datos; por ejemplo, si el administrador de la base de datos quisiera acceder a los mismos de modo no autorizado y fuera suficientemente hábil, podría recurrir a múltiples trucos para obtener los datos; desde sniffers para capturar contraseñas a ataques de fuerza bruta, pasando por el empleo malintencionado de un depurador sobre el producto.
Con esto quiero haceros consciente de que la seguridad no es solo un asunto de cómo almacenemos los datos; todos los detalles del entorno juegan su papel. Desde la configuración de permisos de Windows, al almacenamiento físico de los datos, todo ha de ser tenido en cuenta.
Ya terminoooo…
Me gustaría terminar con una reflexión que le hacía a Greg cuando estaba decidiendo su implementación final: a simple vista parece que la opción de cifrar con una clave que no se almacene en el SQL Server y solo conozca una persona parece la más segura, pero como un día el propietario de esta clave sea atropellado por el camión de los pollos(tm), abducido por los Vogones, o se vaya a cenar al Restaurante del Fin del Mundo nos podemos olvidar de todos los datos que estuvieran cifrados con su clave. Así de crudo.