Archivo del Autor: Alberto Gonzalez Iniesta
DNSSEC. Asegurando las respuestas de nuestro dominio. La teoría
¿Qué es?
DNSSEC es una extensión al sistema de resolución de nombres (DNS) que permite aumentar la seguridad de éste, evitando ataques como el envenenamiento de cache (respuestas falsas con el fin de dirigir el tráfico a un destino diferente al real).
Básicamente añade autenticación a las respuestas DNS mediante firma digital, permitiendo a los clientes validar la veracidad de dichas respuestas. DNSSEC no cifra el tráfico entre clientes y/o servidores, que sigue siendo «en claro». Ni evita ataques de denegación de servicio.
¿Cómo funciona DNSSEC?
El funcionamiento se basa en una delegación de confianza entre las zonas padre e hijas y el uso de criptografía asimétrica (clave pública/privada). La zona raíz (.) está firmada con una clave privada y su pública es de conocimiento global (normalmente viene incluida en los servidores DNS). En dicha zona (.) se encuentra un registro DS (Delegation Signer) por cada zona hija (.com, .net, .org, .es,…) que contiene un hash de la clave pública correspondiente a cada una de ellas.
Es decir, en la zona raíz (.) hay un registro DS que indica cual es la clave pública de la zona .net. En la zona .net hay un registro DS para cada dominio (que soporte DNSSEC) que cuelga de ella. Por ejemplo, en la zona .net, además de los servidores DNS (NS) que sirven el dominio inittab.net, también hay un registro DS que indica con que clave (por su hash) hay que verificar las respuestas de esa zona.
Cuando usamos DNSSEC, las respuestas a una pregunta DNS (sea un registro A, MX, DS, CNAME, …) vendrán acompañadas de un registro «extra» de tipo RRSIG (Resource Record SIGnature) que consiste en la firma digital (con la clave privada de la zona) del registro que se solicitó. Este registro RRSIG puede ser verificado con la clave pública de inittab.net, que además puede ser validada al ser un registro DS en la zona padre (.net), que a su vez está firmado por la clave privada de ésta,….
De forma que si solicitamos el registro MX del dominio inittab.net, el proceso (que seguiría normalmente el servidor DNS que nos presta el servicio de resolución de nombres) sería:
- Solicitar a los servidores raíz la lista de DNS (registros NS) que sirven el dominio .net. La respuesta llevaría dichos registros (NS), además de los correspondientes RRSIG para validarlos. Además habría que solicitar el registro DS (hash de la clave pública) del dominio .net, para poder validar la respuesta que nos den los siguientes servidores DNS. Por supuesto este registro (DS) será verificable con su correspondiente registro RRSIG (firma creada con la clave privada del dominio raíz).
- Nuestro servidor DNS validaría con la clave pública de la zona raíz (que ya tiene, por ejemplo en /etc/bind/bind.keys) que la firma (RRSIG) de los registros NS y DS es correcta y por tanto la información válida.
- Lo siguiente sería pedir a alguno de los servidores encargados de .net su clave pública (registro DNSKEY) para poder validar las siguientes respuestas que nos dé. Este registro debe contener una clave cuyo hash coincida con el que venía en el registro DS que nos entregó la zona raíz y que ya validamos. (Aquí he simplificado un poco el proceso, luego hablaremos de los dos tipos de claves de firmado, KSK y ZSK)
- Ahora, con la clave pública (ya validada) del dominio .net, podemos pedir a sus DNS los registros NS del dominio inittab.net y el registro DS con el hash de su clave pública. Por supuesto las respuestas a estas preguntas vendrán acompañadas de los correspondientes registros RRSIG que permitirán comprobar la validez de las mismas con la clave pública de .net.
- El proceso se repetiría con los DNS de inittab.net. Es decir, pedimos la clave pública de inittab.net (DNSKEY) verificamos que su hash coincide con el registro validado DS que nos entregaron los servidores de la zona padre (.net) y con ella validaremos el resto de peticiones que hagamos del dominio y que vendrán acompañadas de sus correspondientes RRSIG.
Claves de firma de clave y claves de firma de zona (KSK vs ZSK)
Como se puede observar del proceso anteriormente descrito, las zonas padre deben contener una referencia (DS) a la clave con la que las zonas hijas firman sus registros. En el caso de un dominio tradicional (p.e. inittab.net), eso requiere de informar en el registrador, junto con los DNS que lo servirán, el valor de dicho registro DS para el domino. Esto es un proceso manual y poco automatizable.
Además la información que se firma de los registros de un dominio es muy pequeña (una IP, un nombre de máquina, un registro SPF, etc..), lo que «facilita» el criptoanálisis de la clave con la que están firmados. Por ello es conveniente cambiar la clave con la que se firman de forma periódica.
De hecho los registros RRSIG tienen una validez temporal (independiente del TTL del dominio) y tienen que ser recreados (firmados) de forma periódica. El motivo es evitar ataques de repetición (Replay Attacks) que permitirían reutilizar una respuesta vieja (firmada hace meses o años) por un atacante para engañar a un cliente.
Por estos motivos, y para facilitar el cambio de la clave con la que se firman los registros de la zona, se recomienda el uso de dos claves: KSK y ZSK. La clave KSK (Key Signing Key) es aquella que estará «informada» como DS en la zona superior. Su función es la de firmar la clave de zona (ZSK) con la que están firmados el resto de registros del dominio. Es decir, la «exposición» de la KSK es mínima.
Por el contrario la ZSK (Zone Signing Key) es la que firma todos los registros de la zona. Puede ser algo más «pequeña/corta» que la KSK ya que su rotación periódica es más sencilla que la de una sola clave o la de la KSK. Es decir, no hay que modificar la zona padre (cambios en el registrador) cuando se quiera cambiar de clave de firmado de zona (ZSK), basta con generar una nueva y firmarla con la KSK, únicamente informando los cambios en nuestra zona DNS.
Desde el punto de vista técnico no hay diferencias (importantes) entre un tipo de clave y otro. Sólo es la forma en la que se usan lo que las diferencia. De hecho puede usarse una única clave, informando a la zona padre de ésta, y firmando todo con ella. El problema es que el trabajo de cambio de clave será más complejo. Mientras que el de cambio de una ZSK usando una KSK está prácticamente automatizado con las herramientas DNSSEC que usaremos y no
requiere de cambios en el registrador del dominio.
Así que aunque parezca una solución más complicada inicialmente, el uso de una clave para cada función a la larga es más seguro y más sencillo de gestionar.
En la próxima entrada veremos todos los pasos necesarios para implementar DNSSEC en nuestro dominio.
$ exit
Cifrando discos, particiones o ficheros con LUKS
Uno de los mecanismos más eficaces para proteger nuestra información es el cifrado. Hoy no se me pasa por la cabeza tener un portátil sin el disco cifrado. Lo grave no sería la pérdida física del mismo, sino la de la información que lleve y en las manos que pueda acabar. Pero esto no es sólo aplicable a portátiles, discos USB o pendrives con información sensible también deberían estar cifrados para evitar que dicha información sea accesible por terceros no autorizados.
En GNU/Linux tenemos múltiples opciones a la hora de cifrar nuestra información. GPG sobre ficheros, eCryptfs o EncFS sobre directorios, TrueCrypt o dm-crypt sobre dispositivos, loop-AES para ficheros loop, etc. De todas las opciones mi preferida es LUKS (Linux Unified Key Setup), que podemos usar sobre discos, particiones, volúmenes lógicos (LVs) o ficheros loop. Entre sus ventajas están: su sencillez de uso, incluido en el propio kernel y la capacidad de asignar, cambiar y revocar varias claves para un mismo dispositivo.
La instalación de muchas distribuciones actuales hace muy sencillo incorporar el cifrado del sistema completo desde el principio. Mi recomendación en un portátil es: crear una pequeña partición para /boot (que no irá cifrada) y dejar el resto del disco para ser cifrado con LUKS. Sobre dicho cifrado crear un PV para LVM y todo lo que vaya sobre el LVM ya estará cifrado.
Pero en la entrada de hoy veremos como usar LUKS en un sistema que ya está corriendo, por ejemplo para cifrar un nuevo volumen lógico, un fichero loop donde guardar información sensible, o un dispositivo USB donde realizar backups.
Ingredientes
Tener instalado el paquete cryptsetup (también llamado cryptsetup-luks en algunas distribuciones). Tener un dispositivo sin usar (su información será reescrita al cifrarlo inicialmente), por ejemplo /dev/sdc, /dev/vgpruebas/cifrado, /dev/sdb2,… En el caso de querer user un fichero (loop) podemos crearlo con este comando:
## En este ejemplo 50 bloques de 1MB ~ 50MB ## Aunque es posible crecer el fichero posteriormente es ## algo enrevesado, mejor afinar con el tamaño que necesitemos ## Podemos usar /dev/zero como origen de datos en la creación ## del fichero también, pero facilitaría un posible criptoanálisis $ dd if=/dev/urandom of=fichero_cifrado bs=1M count=50
Inicialización del cifrado
El primer paso será dar el formato LUKS al dispositivo. Para ello usaremos el comando cryptsetup con argumento luksFormat. Entre las diversas opciones que podemos especificar está el algoritmo de cifrado, el tamaño de la clave o la función hash (resumen) o el fichero clave.
Veamos algunos ejemplos:
## Cifrar el fichero creado en el ejemplo anterior con una ## contraseña, pidiéndola dos veces (para confirmar que ## se tecleó correctamente). Usando valores por defecto ## para el cifrado: $ cryptsetup --verify-passphrase luksFormat fichero_cifrado ## Podemos averiguar el algoritmo, tamaño de clave y función ## hash que se usan por defecto con este comando: $ cryptsetup --help | grep LUKS.*Key LUKS1: aes-xts-plain64, Key: 256 bits, LUKS header hashing: sha1, RNG: /dev/urandom ## Si los valores por defecto no nos convencen, podemos ## especificar otros, por ejemplo un tamaño de clave de ## cifrado mayor (de 256 bits) y una función hash que no ## haya sido declarada insegura (como es el caso de SHA1): cryptsetup --hash=sha256 --key-size=512 --verify-passphrase \ luksFormat fichero_cifrado ## Cifrar un dispositivo (p.e. un disco USB) usando un fichero ## como contraseña. ## Primero creamos el fichero de contraseña (podemos hacerlo ## a mano, o usar /dev/random o /dev/urandom para rellenarlo) $ dd if=/dev/urandom of=mi_secreto bs=1M count=1 ## Un megabyte de contraseña! ## ¿Qué tamaño máximo de contraseña (tecleada o en fichero) ## podemos usar? El siguiente comando lo aclara: $ cryptsetup --help | grep Maximum Maximum keyfile size: 8192kB, Maximum interactive passphrase length 512 (characters) ## Una vez tenemos el fichero de contraseña, podemos dar el ## formato LUKS usando éste, en vez de tecleando una contraseña ## de forma interactiva ## El siguiente comando lo ejecutamos como root, al tratarse ## de un dispositivo (/dev/sdc) y no un fichero de usuario: # cryptsetup luksFormat /dev/sdc /path/al/fichero/mi_secreto ## o # cryptsetup --key-file=/path/al/fichero/mi_secreto luksFormat /dev/sdc ## Por supuesto también podríamos modificar el tamaño de clave ## de cifrado o la función hash.
Clave de cifrado, contraseña y cabecera LUKS
Llegados a este punto debo (intentar) esclarecer algún término. Cuando damos formato LUKS a un dispositivo/fichero hablamos de dos términos similares, pero diferentes:
- Contraseña. Ya sea una tecleada en el momento, o un fichero usado como tal, es la que nos permite acceder al dispositivo LUKS. Esta contraseña no es usada para cifrar los datos. De hecho, una de las ventajas de usar LUKS es que podemos usar varias contraseñas/ficheros contraseña para un mismo dispositivo. Más adelante daré un ejemplo.
- Clave de cifrado. Esta clave se genera automáticamente cuando damos el formato LUKS al dispositivo. Se genera de forma aleatoria (por defecto usando /dev/urandom) y con el tamaño por defecto (256 bits) o el que se especifique con la opción –key-size. Si bien es posible especificar una clave de cifrado de forma manual (–master-key-file) no es recomendable hacerlo salvo que sepas exactamente que estás haciendo.
El formato LUKS se caracteriza por una cabecera que incluye la información del algoritmo de cifrado, tamaño de clave, contraseñas en uso, etc. Podemos consultar dicha cabecera (que no las contraseñas!) con el siguiente comando:
$ cryptsetup luksDump fichero_cifrado LUKS header information for fichero_cifrado Version: 1 Cipher name: aes Cipher mode: xts-plain64 Hash spec: sha256 Payload offset: 4096 MK bits: 512 MK digest: ca cb 7e f6 20 fa 42 8a 7e 0a 92 66 86 bd 20 46 77 28 a5 25 MK salt: a1 e5 54 8d c2 4d db af 3d 72 73 4d f6 0b 9f b7 41 d7 46 31 ce dc f8 11 21 86 8c 22 d9 cf e5 ca MK iterations: 95125 UUID: d58132bd-ea6b-4879-aa73-c8d444300545 Key Slot 0: ENABLED Iterations: 378697 Salt: 1a 54 1a cd 92 74 13 78 5d 72 f4 00 60 b4 69 85 c7 8a a9 fa 50 65 6b 32 f4 13 a4 e2 b6 3b c3 69 Key material offset: 8 AF stripes: 4000 Key Slot 1: DISABLED Key Slot 2: DISABLED Key Slot 3: DISABLED Key Slot 4: DISABLED Key Slot 5: DISABLED Key Slot 6: DISABLED Key Slot 7: DISABLED
En este ejemplo vemos que se está usando AES como algoritmo de cifrado, que el tamaño de clave es de 512 bits, la función hash es SHA256 y que hay una contraseña (de las 8 posibles) en uno (Key Slot 0: ENABLED). El resto de posibles contraseñas no están establecidas (DISABLED).
Usando el dispositivo cifrado
Una vez tenemos el formato LUKS en el dispositivo, para acceder a los datos en claro habrá que «abrir el contenedor cifrado». Es decir, antes de poder usarlo (montarlo o formatearlo) hay que descifrarlo:
## Si vamos a abrir un fichero protegido con ## contraseña "tecleada": # cryptsetup luksOpen fichero_cifrado dispositivo_descifrado ## Si el fichero o dispositivo está protegido con un fichero ## contraseña, se lo indicamos como opción: # cryptsetup --key-file=mi_secreto luksOpen /dev/sdc dispositivo_descifrado ## Unos comentarios sobre estos ejemplos: ## 1.- "dispositivo_descifrado" es el nombre que queremos para el ## dispositivo que se creará trás ejecutar este comando ## y que será el que ya usemos para formatear o montar de forma ## normal ## 2.- Si la contraseña es válida, desde este momento podremos acceder ## al dispositivo "en claro" como /dev/mapper/dispositivo_descifrado ## 3.- Estos comandos se ejecutan con el usuario "root" ya que crean ## un dispositivo nuevo. (Sí, seguro que hay interfaces de usuario ## que permiten estas tareas a un usuario corriente)
Por supuesto, la primera vez que lo abrimos lo más frecuente será formatearlo, como con cualquier dispositivo nuevo:
mkfs.ext4 /dev/mapper/dispositivo_descifrado
Desde ese momento, y después de abrirlo con «luksOpen«, simplemente lo montaremos como el resto de dispositivos de almacenamiento:
mount /dev/mapper/dispositivo_descifrado /media/mis_datos_secretos
Cerrar después de usar
Cuando ya no necesitemos acceso a los datos, y después de desmontar como con cualquier otro dispositivo de almacenamiento, debemos «cerrar» el acceso al dispositivo sin cifrar. O como decía Mamá – deja las cosas como las encontraste:
## Desmontar antes de cerrar el acceso # umount /media/mis_datos_secretos ## Eliminar el acceso al dispositivo descifrado # cryptsetup luksClose dispositivo_descifrado
¿Varias contraseñas?
Para terminar comentaré una de las características que más me gustan de LUKS y que en un principio puede sonar extraña: la capacidad de tener varias contraseñas/ficheros contraseña.
Insistir en que las contraseñas no son las que se usan para cifrar, eso se hace con la clave de cifrado, sino para dar acceso a ésta y poder abrir el dispositivo. Poder usar más de una (y ser capaces de revocarlas, cambiarlas o añadirlas) permite:
- Tener una contraseña por usuario para un mismo dispositivo. De forma que cada uno sabe la suya y si un día se decide evitar el acceso de uno de ellos basta con eliminar su contraseña, lo que no afecta al resto de usuarios.
- Tener una contraseña para nosotros (que tecleamos cuando queremos acceso al dispositivo) y tener un fichero contraseña para tareas automatizadas (por ejemplo backups). Yo tengo un disco USB donde se hacen backups de mi portátil de forma automática. Para no tener que teclear la contraseña (y estar delante del ordenador) cuando se realiza el backup, en el disco del portátil tengo un fichero contraseña que le permite descifrar el USB y realizar el backup. Por supuesto este fichero contraseña sólo puede leerlo el usuario root y el disco de mi portátil está cifrado. Pero ¿qué pasaría si el disco de mi portátil muere? Ya no tendría el fichero contraseña para descifrar el disco USB de backup. Estúpida situación ésta… Salvo que el USB con LUKS tiene una segunda contraseña (que puedo teclear) y que me permitiría acceder a él aunque el disco de mi portátil muriera.
Sobra decir que hay que tener sumo cuidado con los ficheros usados como contraseña, su acceso debería estar extremadamente limitado (permisos, acceso al dispositivo donde se almacenan, etc..).
¿Cómo gestionamos las contraseñas de un dispositivo con LUKS?
Como vimos anteriormente, podemos ver la información de la cabecera LUKS con el comando «cryptsetup luksDump DISPOSITIVO«. En el veremos los «slots» («contraseñas») que están en uso. Normalmente el slot-0 siempre lo está, porque es donde se guarda la contraseña inicial. Podemos añadir una nueva contraseña con el siguiente comando:
## Si queremos una contraseña "tecleada" # cryptsetup luksAddKey /dev/sdc ## Primero nos pedirá una contraseña ya existente ## en alguno de los slots, si no hay contraseña ## sino "fichero contraseña" deberemos usar la ## opción --key-file: # cryptsetup --key-file=/etc/mi_secreto luksAddKey /dev/sdc ## Si queremos añadir un "fichero contraseña" # cryptsetup luksAddKey /dev/sdc /etc/mi_nuevo_secreto ## Al igual que antes, si no hay una contraseña en ## algún slot, sino que son todo ficheros contraseña ## le indicamos alguno de los actuales con --key-file # cryptsetup --key-file=/etc/mi_secreto \ luksAddKey /dev/sdc /etc/mi_nuevo_secreto
También podemos borrar o cambiar contraseñas (o ficheros contraseña) existentes:
## Para borrar un "slot" tenemos dos opciones: ## Teclear (o especificar el fichero contraseña ## después del nombre de dispositivo) $ cryptsetup luksRemoveKey /dev/sdc $ cryptsetup luksRemoveKey /dev/sdc /etc/secreto_a_eliminar ## O si no tenemos/recordamos la contraseña, especificar ## el slot que queremos eliminar (nos pedirá alguna de las ## contraseñas (o ficheros contraseña con --key-file) ## restantes en otro slot $ cryptsetup luksKillSlot /dev/sdc 2 $ cryptsetup --key-file /etc/secreto_valido /dev/sdc 2
Copia de seguridad
El punto más débil de nuestro dispositivo cifrado es la cabecera LUKS. Sin ésta sería imposible acceder a los datos que contiene. Es recomendable por tanto, realizar una copia de seguridad de ella que nos permita acceder a los datos cifrados en el caso de que la cabecera LUKS quede dañada (por error en su gestión, o daño físico en el dispositivo de almacenamiento).
## Podemos sacar una copia de la cabecera con el siguiente comando $ cryptsetup luksHeaderBackup /dev/sdc --header-backup-file cabecera_backup ## O restaurar una cabecera dañada desde un backup $ cryptsetup luksHeaderRestore /dev/sdc --header-backup-file cabecera_backup
No metas cosas en criptas, cogen humedad
Cada vez que alguien dice «encriptar» o «encriptado» Dios mata un gatito. FYI.
$ exit