Fernet, ¿qué es y cómo funciona?

Fernet es una herramienta útil en el arsenal de un desarrollador de Python. Su objetivo es ayudarlos a proteger los datos sin correr todos los riesgos que conlleva la implementación de primitivas criptográficas tu mismo.

Si estás interesado en implementar fernet, es posible que te preguntes si es una opción adecuada para cifrar y autenticar mensajes. En este artículo, examinaremos cómo fernet cifra y descifra los datos, destacando sus fortalezas y limitaciones.

¿Qué es Fernet?

Fernet es una receta que proporciona cifrado simétrico y autenticación de datos . Es parte de la biblioteca de criptografía para Python, desarrollada por la Autoridad Criptográfica de Python (PYCA).

Hay una variedad de casos de uso diferentes para Fernet. Los ejemplos del mundo real incluyen:

  • Apache Airflow: esta plataforma de supervisión y programación de flujos de trabajo implementa fernet para cifrar contraseñas tanto para la configuración de variables como para la configuración de conexiones. Esto ayuda a mantener las contraseñas a salvo de los atacantes.
  • Overcloud de Red Hat: las claves de Fernet se pueden utilizar para proporcionar cifrado en Overcloud de Red Hat, que es el entorno de la plataforma OpenStack de la empresa para crear y administrar recursos de red en nubes públicas y privadas.
  • Databricks: Fernet puede desempeñar un papel en la protección de la información de identificación personal junto con otras herramientas como Databricks. Esta información es apreciada por los piratas informáticos, por lo que es importante contar con mecanismos seguros de encriptación y autenticación como fernet para protegerla.

¿Qué es una receta?

Las recetas son pequeños scripts de Python que se utilizan para resolver problemas comunes.

¿Qué es la Autoridad Criptográfica de Python (PYCA)?

La Autoridad Criptográfica de Python (PyCA para abreviar) no es tan oficial como su nombre lo hace parecer. Aunque no existe un organismo oficial de licencias que lo supervise, PyCA es un equipo de desarrolladores poco integrado que trabaja en conjunto para resolver problemas criptográficos comunes en el lenguaje. Su trabajo tiene como objetivo facilitar que los codificadores protejan sus aplicaciones.

¿Qué es Cryptography?

Cryptography es un paquete de Python Cryptographic Authority que hace que sea más fácil y seguro para los desarrolladores de Python implementar los mecanismos criptográficos necesarios dentro de su software. Los componentes de la criptografía se pueden dividir en dos niveles.

Una son las recetas de alto nivel, que requieren que los desarrolladores tomen muy pocas decisiones en el proceso de implementación. Debido a que hay tan pocas decisiones de implementación, hay menos lugares para que un desarrollador se equivoque, lo que en última instancia hace que estas recetas de alto nivel sean mucho más seguras que si un programador intentara improvisar todos los algoritmos por su cuenta.

Dado lo compleja que es la criptografía y lo fácil que es para alguien abrir su aplicación a ataques de canal lateral, estas recetas ayudan a eliminar las conjeturas y conducen a un ecosistema digital mucho más seguro.

El otro componente principal de la criptografía es su colección de primitivas criptográficas. En contraste con las recetas de alto nivel que están básicamente listas para usar, estas primitivas son más como bloques de construcción criptográficos, que deben ensamblarse de la manera correcta para satisfacer completamente las necesidades de seguridad de una aplicación determinada.

Estas primitivas criptográficas solo deben implementarse cuando las recetas de alto nivel no son adecuadas para una tarea determinada, y deben abordarse con extremo cuidado. Esto se debe a que hay muchas más opciones que puede tomar un desarrollador al implementar estas primitivas criptográficas, lo que significa que hay muchos más lugares donde las cosas pueden salir mal. Es mejor evitarlos a menos que tengas mucha experiencia en el campo de la criptografía. Los desarrolladores de PyCA incluso se refieren a ellos como «materiales peligrosos» , por lo que debes tener cuidado siempre que los manipules.

Criptografía frente a otras bibliotecas criptográficas de Python

Si has estado buscando recursos que te ayuden con las implementaciones criptográficas en Python, es posible que hayas visto bibliotecas como PyOpenSSL, M2Crypto y PyCrypto.

Los desarrolladores de PyCA afirman que crearon el paquete de criptografía para abordar algunos de los problemas que encontraron en estas bibliotecas heredadas. Entre los problemas en las bibliotecas de la competencia, encontraron:

  • Falta de algoritmos útiles como HKDF y AES-GCM.
  • Una falta de mantenimiento.
  • API que eran propensas a errores y tenían opciones predeterminadas inseguras.
  • Compatibilidad limitada con Python 3 y PyPy.
  • Falta de API de alto nivel
  • Algunos algoritmos se implementaron mal. En algunos casos, esto abrió potencialmente la puerta a ataques de canal lateral.

Otra opción que puedes haber encontrado es NaCl, que está construido por un equipo que incluye algunas de las figuras más importantes del mundo de la criptografía.

Al igual que el paquete de criptografía, el objetivo de NaCl es simplificar la criptografía y facilitar a los desarrolladores la implementación segura de estos algoritmos . Si estás más inclinado hacia el diseño de NaCl, el equipo de PyCA también mantiene PyNaCl, por lo que puedes confiar tanto en él como en la criptografía.

¿Cómo encripta y autentica los datos Fernet?

Como dijimos, fernet es una receta del paquete de criptografía que puedes usar para encriptar y autenticar datos. Como receta, es relativamente fácil de implementar y elimina gran parte de las conjeturas involucradas en la criptografía.

Cuando fernet se implementa correctamente, un atacante no puede leer ni entrometerse en un mensaje que se ha cifrado y autenticado con él.

Hay tres entradas importantes para fernet, junto con un vector de inicialización elegido al azar:

  • Un mensaje de texto sin formato proporcionado por el usuario. Estos son los datos que el usuario quiere cifrar y toman la forma de una secuencia arbitraria de bytes.
  • Una clave proporcionada por el usuario, que tiene una longitud de 256 bits.
  • La hora actual.

Cuando estas entradas se ejecutan a través de la receta de fernet, se produce un token. Este contiene el mensaje en forma cifrada, así como un HMAC (explicaremos qué es esto más adelante en la página) que autentica el mensaje. Juntos, evitan que el mensaje se lea o modifique sin la clave.

Primitivas criptográficas de Fernet

Fernet se construye a partir de varias primitivas criptográficas. Estos son los bloques de construcción de «materiales peligrosos» que mencionamos antes. Sin embargo, en la receta de fernet, han sido ensamblados por especialistas. El uso de fernet en lugar de vincular las primitivas criptográficas tu mismo puede ayudarte a evitar las muchas trampas que surgen al implementar tu propia criptografía.

Por muy seguro que estés de tus habilidades, la criptografía es un campo minado, y siempre es mejor dejarlo en manos de los expertos siempre que puedas. No hay necesidad de reinventar la rueda, especialmente cuando reinventar la rueda podría conducir a un gran desastre de seguridad.

Las primitivas criptográficas de Fernet incluyen:

  • AES de 128 bits en modo CBC.
  • Un HMAC con SHA-256.

No te preocupes, te explicaremos qué significa cada uno de estos términos en los próximos párrafos.

¿Qué es AES?

Una de las primitivas criptográficas es AES, Advanced Encryption Standard. Fue desarrollado y estandarizado por el Instituto Nacional de Estándares y Tecnología en colaboración con la comunidad criptográfica más amplia.

Es un algoritmo de cifrado de clave simétrica rápido que usamos todo el tiempo para cifrar datos almacenados y en tránsito. Es posible que nunca lo notes en tu navegación diaria, pero si te conectas a este sitio web a través de HTTPS (busca el icono de candado a la izquierda de la barra de direcciones), lo más probable es que la información que viaja entre nuestro servidor y tu navegador web esté garantizada por AES.

AES tiene tres tamaños de clave diferentes, 128 bits, 192 bits y 256 bits. Sin embargo, Fernet solo usa claves AES de 128 bits. AES de 128 bits se considera lo suficientemente seguro para la gran mayoría de las aplicaciones.

¿Qué es el modo CBC?

Antes de que podamos explicar qué es el modo CBC, debemos retroceder un poco y explicar por qué lo necesitamos en primer lugar. AES es un cifrado de bloque. Básicamente, esto significa que el algoritmo procesa los datos en fragmentos de longitud fija, que se conocen como bloques. Si tienes una gran cantidad de datos, no se pueden cifrar todos a la vez. Primero, deben dividirse en bloques que tengan una longitud fija, y cada bloque se procesa por separado.

Un cifrado de bloque como AES solo puede cifrar de forma segura un bloque de datos por sí solo. AES tiene un tamaño de bloque de 128 bits, lo que significa que solo puede procesar un máximo de 128 bits. Esta es una cantidad de datos relativamente pequeña, por lo que para muchas aplicaciones prácticas, necesitamos una forma de cifrar de forma segura varios bloques de datos juntos.

Hay varias formas en que podemos hacer esto, cada una de las cuales se conoce como modos de operación. Estos modos de operación especifican cómo un cifrado como AES cifrará de forma segura múltiples bloques de datos, lo que permitirá que AES se utilice para cifrar y descifrar volúmenes de datos mucho más grandes.

CBC se refiere al encadenamiento de bloques de cifrado, que es un método más antiguo para cifrar varios bloques de datos juntos. Comienza con tres entradas:

  • El texto sin formato que un usuario desea cifrar.
  • Un vector de inicialización (IV), que es una entrada que solo se usa para el primer bloque. Los bloques subsiguientes utilizan la salida del bloque anterior como entrada. Como no hay bloques antes del primero, necesitamos otra entrada que pueda proporcionar el estado inicial. Aquí es donde intervienen los vectores de inicialización. En el caso de fernet, se debe usar un nuevo vector de inicialización elegido al azar para cada token.
  • Una clave que ayuda a proporcionar confidencialidad a los datos.

El texto sin formato se agrega al vector de inicialización a través de una operación XOR, que es una operación lógica que básicamente realiza un tipo especial de adición a nivel binario.

El resultado de la operación XOR luego se ejecuta a través de los muchos pasos del cifrado de bloque AES, junto con la clave. Esto produce un bloque de texto cifrado, que luego se somete a XOR con el siguiente bloque de texto sin formato que debe cifrarse. Esto significa que en el segundo bloque, así como en todos los bloques subsiguientes, la salida del bloque anterior actúa de la misma manera que lo hizo el vector de inicialización para el primer bloque.

El descifrado en modo CBC sigue un proceso similar, pero algo invertido. El primer bloque de texto cifrado se ejecuta a través del cifrado de descifrado junto con la clave. El mismo vector de inicialización se somete a XOR con el resultado, revelando el primer bloque de texto sin formato. El segundo y todos los bloques subsiguientes siguen un proceso de descifrado similar, excepto que el texto cifrado del bloque anterior toma el lugar del vector de inicialización.

Debilidades del modo CBC

CBC es un modo antiguo de operación, y no está exento de inconvenientes. Por un lado, CBC es relativamente lento, porque es un algoritmo secuencial. Se requiere la salida del bloque anterior antes de que pueda comenzar a calcular el bloque siguiente, lo que significa que no puede cifrar varios bloques en paralelo. En última instancia, esto lo hace más lento que algunos de los modos de operación rivales que permiten la paralelización.

CBC también puede ser vulnerable a una variedad de ataques si no se implementa correctamente, como los ataques de oráculo de relleno. Por eso es importante que AES-CBC se implemente junto con los esquemas de relleno adecuados, los vectores de inicialización correctos y los mecanismos de autenticación seguros.

Otro problema es que AES-CBC carece de autenticación integrada. Esto significa que si deseas poder saber si se han entrometido los datos, debe implementarse junto con mecanismos de autenticación adicionales. Fernet resuelve este problema utilizando un HMAC, que describiremos más adelante.

¿Qué es el relleno?

El cifrado de bloque AES procesa datos en bloques de 128 bits. Pero, ¿qué sucede si tenemos 50 bits de datos, 135 bits de datos o 587 bits de datos?

Rellenamos el espacio extra con relleno.

En esencia, el relleno implica agregar datos adicionales para garantizar que la entrada tenga el tamaño correcto para que un algoritmo como AES pueda procesarla. En el caso de 50 bits de datos, necesitaríamos rellenarlo con 78 bits de datos adicionales para convertirlo en un bloque completo de 128 bits. Con 135 bits de datos, nos sobrarían 7 bits del primer bloque de 128 bits, lo que significa que necesitaríamos 121 bits adicionales para rellenar nuestro segundo bloque.

Si tuviéramos 587 bits de datos que necesitábamos cifrar, los primeros 512 bits se dividirían en cuatro bloques de 128 bits. Los 75 bits restantes quedarían para un quinto bloque, que necesitaría 53 bits adicionales de relleno para llenarlo hasta un tamaño de bloque de 128 bits. Esto quiere decir que tendríamos cinco bloques de 128 bits, con un total de 640 bits.

El relleno en realidad logra más que simplemente llenar el espacio vacío. Los esquemas de relleno pueden ayudar a prevenir ataques al ocultar la longitud del mensaje que está encriptado. Fernet rellena los datos con un esquema conocido como PKCS#7.

Ten en cuenta que el modo CBC no siempre requiere relleno. En otras situaciones, el robo de texto cifrado puede ser apropiado. Sin embargo, Fernet usa relleno PKCS7.

¿Qué es un HMAC?

Ahora que sabemos cómo fernet usa AES para encriptar datos, es hora de hablar sobre cómo se autentican estos datos. Anteriormente, mencionamos que AES-CBC no tiene autenticación integrada. Si bien esto es un poco complicado, no es un problema insuperable. Simplemente significa que debemos agregar medidas de autenticación adicionales para que AES-CBC sea seguro.

Fernet realiza su autenticación a través de un HMAC que utiliza la función hash SHA-256. HMAC significa código de autenticación de mensajes basado en hash, que es un tipo específico de código de autenticación de mensajes (MAC). Los HMAC usan claves secretas y funciones hash de manera especial para que un destinatario pueda verificar la autenticidad e integridad de los datos que ha recibido.

Si bien la configuración de fernet de AES-CBC junto con un HMAC SHA-256 puede proporcionar la confidencialidad, integridad y autenticidad, es una forma anticuada de hacerlo.

El modo CBC se desarrolló inicialmente en los años setenta y, si bien nos ha servido bien, existen otras opciones que también pueden cumplir el rol. Un ejemplo es AES-GCM, que puede cifrar y autenticar datos. Esto significa que si se implementa AES-GCM, no hay necesidad de un mecanismo de autenticación adicional como un HMAC.

La biblioteca de criptografía incluso incluye primitivas criptográficas para AES-GCM. Sin embargo, son parte de la capa de materiales peligrosos, por lo que solo aquellos con experiencia deben implementarla.

A pesar del hecho de que AES-GCM incluye la autenticación, aún existen algunas trampas en las que un desarrollador sin experiencia podría caer si tuviera que implementar las primitivas criptográficas por sí mismo. Un ejemplo es reutilizar un nonce con una clave determinada. Esto puede socavar la seguridad de cualquier mensaje que esté protegido por ese nonce y par de claves.

¿Qué es un token de fernet?

Cuando un usuario quiere encriptar un mensaje con fernet, la receta toma el mensaje de texto sin formato, una clave y una marca de tiempo como entradas y luego usa esta información para producir un token. El token incluye una versión cifrada del mensaje de texto sin formato, así como la información necesaria para verificar la integridad del mensaje. Esto significa que el token proporciona confidencialidad, integridad y autenticación al mensaje de texto sin formato.

Los tokens de Fernet son archivos de notación de objetos de JavaScript (JSON), que es un formato de archivo independiente del idioma que se utiliza para el intercambio de datos. Los tokens se codifican de acuerdo con la especificación base64url, que es una forma de codificar caracteres que es segura para los nombres de archivo.

La codificación base64url de un token de fernet es una concatenación de los siguientes campos:

  • Versión: el campo de versión es un número de 8 bits que especifica qué versión de fernet se utilizará. Actualmente solo hay una versión definida, y su valor es 128 en decimal o 0x80 en hexadecimal.
  • Marca de tiempo: la marca de tiempo es un número entero de 64 bits. Es a la vez sin firmar y big-endian. El entero de 64 bits es una marca de tiempo de la cantidad de segundos que han pasado desde el 1 de enero de 1970 UTC y la fecha de creación del token fernet.
  • Vector de inicialización: para que fernet sea seguro, se debe usar un vector de inicialización único para el cifrado AES. Deben ser números de 128 bits. Fernet genera números aleatorios con os.urandom en Python. Esto utiliza fuentes de entropía del sistema operativo para garantizar que el número único sea lo suficientemente aleatorio. Esto evita que un atacante pueda predecir la salida del número aleatorio.
  • Texto cifrado: el campo de texto cifrado variará en longitud, según el tamaño del mensaje de texto sin formato inicial que se haya cifrado. Siempre será un múltiplo del tamaño del bloque AES de 128 bits, con relleno utilizado para llenar el último bloque.
  • HMAC: el HMAC cubre cada uno de los cuatro campos anteriores. Esto significa que la versión, la marca de tiempo, el vector de inicialización y el texto cifrado han sido autenticados por el HMAC, lo que permite que un destinatario verifique la integridad y autenticidad de estos datos. Los cuatro campos anteriores se concatenan y luego se ejecutan a través de un HMAC SHA-256.

Proceso de encriptación y autenticación de fernet

El proceso de encriptación y autenticación de fernet se desarrolla a lo largo de los siguientes pasos:

  • Se registra la marca de tiempo.
  • os.urandom() se usa para generar un vector de inicialización único y suficientemente aleatorio.
  • El texto cifrado se construye :
    • El texto sin formato se rellena de acuerdo con PKCS #7 para que cada bloque sea de 128 bits.
    • El mensaje rellenado se cifra con AES de 128 bits en modo CBC, utilizando una clave de cifrado proporcionada por el usuario, así como el vector de inicialización generado por os.urandom().
  • Se calcula un HMAC para los campos de versión, marca de tiempo, vector de inicialización y texto cifrado.
  • Todos los campos anteriores, más el HMAC, se concatenan juntos.
  • El token está codificado de acuerdo con la especificación base64url.

Verificando el token de fernet

Un usuario puede verificar el token y descifrarlo si también tiene la clave secreta. Esto les permite acceder al mensaje, mientras asegura que mantiene su autenticidad e integridad. El proceso incluye los siguientes pasos:

  • Invierte la codificación base64url del token.
  • Revisa que el primer byte del token sea 0x80 (esto es 128 en decimal. Te dice la versión de fernet que se está usando).
  • Si el token tiene una antigüedad máxima, verifica que el token no sea demasiado antiguo.
  • Calcula el HMAC a partir de los campos de versión, marca de tiempo, vector de inicialización y texto cifrado . Esto requiere la clave proporcionada por el usuario.
  • Comprueba que esta marca de tiempo calculada coincida con la marca de tiempo incluida en el token.
  • Utiliza la clave de cifrado y el vector de inicialización para descifrar el texto cifrado AES-CBC.
  • Retira el relleno del mensaje descifrado. Esto le da el texto sin formato original.
    Si el proceso de verificación falla, fernet le dará al usuario un mensaje de token no válido, junto con una descripción que explica por qué falló el proceso.

Usando contraseñas con fernet

Los usuarios pueden optar por utilizar contraseñas en fernet para la protección de datos. Sin embargo, primero, la contraseña debe ejecutarse a través de una función de derivación de clave como bcrypt, Scrypt o PBKDF2HMAC. La clave debe almacenarse en un lugar donde fernet pueda recuperarla fácilmente; de ​​lo contrario, no podrá derivar la clave de la contraseña en futuros intentos.

Limitaciones del fernet

Fernet está diseñado para que no exponga bytes no autenticados. Debido a esto, todo el contenido del mensaje debe poder caber en la memoria disponible. Esto hace que fernet no sea adecuado para cifrar archivos muy grandes.

En general, fernet es una herramienta útil para ayudar a los desarrolladores de Python a proteger cantidades más pequeñas de datos. Elimina muchas de las conjeturas peligrosas de la implementación de la criptografía, lo que hace que fernet sea ideal para aquellos que carecen de experiencia en el campo.

Si bien el fernet tiene un propósito útil, también existen alternativas que pueden desempeñar funciones similares. Uno de ellos es AES-GCM, que tiene medidas de encriptación y autenticación incorporadas. Esto contrasta con el modo de operación AES-CBC de fernet, que requiere un HMAC adicional para la autenticación.