Desmitifique la aplicación Flask.secret_key

127

Si app.secret_keyno está configurado, Flask no le permitirá configurar o acceder al diccionario de la sesión.

Esto es todo lo que la guía del usuario del matraz tiene que decir sobre el tema.

Soy muy nuevo en el desarrollo web y no tengo idea de cómo / por qué funcionan las cosas de seguridad. Me gustaría entender qué está haciendo Flask debajo del capó.

  • ¿Por qué Flask nos obliga a establecer esta secret_keypropiedad?
  • ¿Cómo usa Flask la secret_keypropiedad?
MI V
fuente

Respuestas:

102

Cualquier cosa que requiera cifrado (para proteger contra la manipulación de los atacantes) requiere que se configure la clave secreta. Por acaba en sí Frasco, que 'nada' es el Sessionobjeto, pero otras extensiones puede hacer uso del mismo secreto.

secret_keyes simplemente el valor establecido para la SECRET_KEYclave de configuración, o puede configurarlo directamente.

La sección Sesiones en el Inicio rápido tiene buenos consejos sobre qué tipo de secreto del lado del servidor debe establecer.

El cifrado se basa en secretos; si no estableciste un secreto del lado del servidor para que se use el cifrado, todos podrían romperlo; Es como la contraseña de tu computadora. El secreto más los datos para firmar se utilizan para crear una cadena de firma, un valor difícil de recrear utilizando un algoritmo de cifrado criptográfico ; solo si tiene exactamente el mismo secreto y los datos originales puede recrear este valor, permitiendo que Flask detecte si algo ha sido alterado sin permiso. Como el secreto nunca se incluye con los datos que Flask envía al cliente, un cliente no puede alterar los datos de la sesión y espera producir una firma nueva y válida.

Flask usa la itsdangerousbiblioteca para hacer todo el trabajo duro; las sesiones usan la itsdangerous.URLSafeTimedSerializerclase con un serializador JSON personalizado.

Martijn Pieters
fuente
91

La respuesta a continuación se refiere principalmente a las cookies firmadas , una implementación del concepto de sesiones (como se usa en aplicaciones web). Flask ofrece cookies normales (sin firmar) (vía request.cookiesy response.set_cookie()) y cookies firmadas (vía flask.session). La respuesta tiene dos partes, la primera describe cómo se genera una cookie firmada y la segunda se presenta en forma de un control de calidad que aborda diferentes aspectos del esquema. La sintaxis utilizada para los ejemplos es Python3, pero los conceptos se aplican también a versiones anteriores.

¿Qué es SECRET_KEY(o cómo crear una cookie firmada)?

Firmar cookies es una medida preventiva contra la manipulación de cookies. Durante el proceso de firma de una cookie, SECRET_KEYse usa de forma similar a cómo se usaría una "sal" para confundir una contraseña antes de cifrarla. Aquí hay una descripción (ampliamente) simplificada del concepto. El código en los ejemplos está destinado a ser ilustrativo. Muchos de los pasos se han omitido y no todas las funciones existen realmente. El objetivo aquí es proporcionar una comprensión de la idea general, las implementaciones reales serán un poco más complicadas. Además, tenga en cuenta que Flask hace la mayor parte de esto en segundo plano. Por lo tanto, además de establecer valores para su cookie (a través de la API de sesión) y proporcionar una SECRET_KEY, no solo es desaconsejable volver a implementar esto usted mismo, sino que no es necesario hacerlo:

La firma de galletas de un pobre

Antes de enviar una respuesta al navegador:

(1) Primero SECRET_KEYse establece a. Solo debe ser conocido por la aplicación y debe mantenerse relativamente constante durante el ciclo de vida de la aplicación, incluso a través de reinicios de la aplicación.

# choose a salt, a secret string of bytes
>>> SECRET_KEY = 'my super secret key'.encode('utf8')

(2) crear una cookie

>>> cookie = make_cookie(
...     name='_profile', 
...     content='uid=382|membership=regular',
...     ...
...     expires='July 1 2030...'
... )

>>> print(cookie)
name: _profile
content: uid=382|membership=regular...
    ...
    ...
expires: July 1 2030, 1:20:40 AM UTC

(3) para crear una firma, agregue (o anteponga) la SECRET_KEYcadena de bytes de cookie, luego genere un hash a partir de esa combinación.

# encode and salt the cookie, then hash the result
>>> cookie_bytes = str(cookie).encode('utf8')
>>> signature = sha1(cookie_bytes+SECRET_KEY).hexdigest()
>>> print(signature)
7ae0e9e033b5fa53aa....

(4) Ahora coloque la firma en un extremo del contentcampo de la cookie original.

# include signature as part of the cookie
>>> cookie.content = cookie.content + '|' + signature
>>> print(cookie)
name: _profile
content: uid=382|membership=regular|7ae0e9...  <--- signature
domain: .example.com
path: /
send for: Encrypted connections only
expires: July 1 2030, 1:20:40 AM UTC

y eso es lo que se envía al cliente.

# add cookie to response
>>> response.set_cookie(cookie)
# send to browser --> 

Al recibir la cookie del navegador:

(5) Cuando el navegador devuelva esta cookie al servidor, elimine la firma del contentcampo de la cookie para recuperar la cookie original.

# Upon receiving the cookie from browser
>>> cookie = request.get_cookie()
# pop the signature out of the cookie
>>> (cookie.content, popped_signature) = cookie.content.rsplit('|', 1)

(6) Use la cookie original con la aplicación SECRET_KEYpara recalcular la firma usando el mismo método que en el paso 3.

# recalculate signature using SECRET_KEY and original cookie
>>> cookie_bytes = str(cookie).encode('utf8')
>>> calculated_signature = sha1(cookie_bytes+SECRET_KEY).hexdigest()

(7) Compare el resultado calculado con la firma previamente extraída de la cookie recién recibida. Si coinciden, sabemos que la cookie no ha sido alterada. Pero si solo se ha agregado un espacio a la cookie, las firmas no coincidirán.

# if both signatures match, your cookie has not been modified
>>> good_cookie = popped_signature==calculated_signature

(8) Si no coinciden, puede responder con cualquier cantidad de acciones, registrar el evento, descartar la cookie, emitir una nueva, redirigir a una página de inicio de sesión, etc.

>>> if not good_cookie:
...     security_log(cookie)

Código de autenticación de mensajes basado en hash (HMAC)

El tipo de firma generado anteriormente que requiere una clave secreta para garantizar la integridad de algunos contenidos se llama en criptografía un Código de autenticación de mensaje o MAC .

Especifiqué anteriormente que el ejemplo anterior es una simplificación excesiva de ese concepto y que no era una buena idea implementar su propia firma. Esto se debe a que el algoritmo utilizado para firmar cookies en Flask se llama HMAC y es un poco más complicado que el simple paso a paso anterior. La idea general es la misma, pero debido a razones más allá del alcance de esta discusión, la serie de cálculos es un poco más compleja. Si todavía está interesado en crear un bricolaje, como suele ser el caso, Python tiene algunos módulos para ayudarlo a comenzar :) aquí hay un bloque inicial:

import hmac
import hashlib

def create_signature(secret_key, msg, digestmod=None):
    if digestmod is None:
        digestmod = hashlib.sha1
    mac = hmac.new(secret_key, msg=msg, digestmod=digestmod)
    return mac.digest()

El documental para hmac y hashlib .


La "Desmitificación" de SECRET_KEY:)

¿Qué es una "firma" en este contexto?

Es un método para garantizar que parte del contenido no haya sido modificado por nadie más que una persona o una entidad autorizada para hacerlo.

Una de las formas más simples de firma es la " suma de verificación ", que simplemente verifica que dos datos son iguales. Por ejemplo, al instalar software desde la fuente, es importante confirmar primero que su copia del código fuente es idéntica a la del autor. Un enfoque común para hacer esto es ejecutar la fuente a través de una función hash criptográfica y comparar el resultado con la suma de comprobación publicada en la página de inicio del proyecto.

Digamos, por ejemplo, que está a punto de descargar la fuente de un proyecto en un archivo comprimido desde un espejo web. La suma de comprobación SHA1 publicada en la página web del proyecto es 'eb84e8da7ca23e9f83 ...'

# so you get the code from the mirror
download https://mirror.example-codedump.com/source_code.tar.gz
# you calculate the hash as instructed
sha1(source_code.tar.gz)
> eb84e8da7c....

Ambos hashes son iguales, sabes que tienes una copia idéntica.

¿Qué es una galleta?

Una discusión extensa sobre las cookies iría más allá del alcance de esta pregunta. Proporciono una descripción general aquí, ya que una comprensión mínima puede ser útil para tener una mejor comprensión de cómo y por qué SECRET_KEYes útil. Le recomiendo que continúe con algunas lecturas personales de las cookies HTTP.

Una práctica común en las aplicaciones web es usar el cliente (navegador web) como un caché ligero. Las cookies son una implementación de esta práctica. Una cookie es típicamente algunos datos agregados por el servidor a una respuesta HTTP a través de sus encabezados. Es guardado por el navegador que posteriormente lo envía de vuelta al servidor cuando emite solicitudes, también a través de encabezados HTTP. Los datos contenidos en una cookie se pueden usar para emular lo que se llama estado de estado, la ilusión de que el servidor mantiene una conexión continua con el cliente. Solo, en este caso, en lugar de un cable para mantener la conexión "viva", simplemente tiene instantáneas del estado de la aplicación después de que haya manejado la solicitud de un cliente. Estas instantáneas se llevan adelante y atrás entre el cliente y el servidor. Al recibir una solicitud, el servidor primero lee el contenido de la cookie para restablecer el contexto de su conversación con el cliente. Luego maneja la solicitud dentro de ese contexto y antes de devolver la respuesta al cliente, actualiza la cookie. La ilusión de una sesión en curso se mantiene así.

¿Cómo se ve una cookie?

Una cookie típica se vería así:

name: _profile
content: uid=382|status=genie
domain: .example.com
path: /
send for: Encrypted connections only
expires: July 1 2030, 1:20:40 AM UTC

Las cookies son triviales para leer desde cualquier navegador moderno. En Firefox, por ejemplo, vaya a Preferencias> Privacidad> Historial> eliminar cookies individuales .

El contentcampo es el más relevante para la aplicación. Otros campos contienen principalmente metainstrucciones para especificar varios ámbitos de influencia.

¿Por qué usar cookies en absoluto?

La respuesta corta es el rendimiento. El uso de cookies minimiza la necesidad de buscar cosas en varios almacenes de datos (cachés de memoria, archivos, bases de datos, etc.), acelerando así las cosas en el lado de la aplicación del servidor. Tenga en cuenta que cuanto mayor sea la cookie, mayor será la carga útil en la red, por lo que lo que guarda en la búsqueda de la base de datos en el servidor puede perderlo en la red. Considere cuidadosamente qué incluir en sus cookies.

¿Por qué deberían firmarse las cookies?

Las cookies se utilizan para mantener todo tipo de información, algunas de las cuales pueden ser muy sensibles. Tampoco son seguros por naturaleza y requieren que se tomen una serie de precauciones auxiliares para que se consideren seguras de alguna manera para ambas partes, el cliente y el servidor. La firma de cookies aborda específicamente el problema con el que pueden manipularse para intentar engañar a las aplicaciones del servidor. Existen otras medidas para mitigar otros tipos de vulnerabilidades, le animo a que lea más sobre las cookies.

¿Cómo se puede manipular una cookie?

Las cookies residen en el cliente en forma de texto y pueden editarse sin esfuerzo. Una cookie recibida por su aplicación de servidor podría haberse modificado por varias razones, algunas de las cuales pueden no ser inocentes. Imagine una aplicación web que mantiene información de permisos sobre sus usuarios en cookies y otorga privilegios basados ​​en esa información. Si la cookie no es a prueba de modificaciones, cualquiera podría modificar la suya para elevar su estado de "rol = visitante" a "rol = administrador" y la aplicación no sería más sabia.

¿Por qué es SECRET_KEYnecesario firmar las cookies?

Verificar las cookies es un poco diferente de verificar el código fuente de la forma en que se describió anteriormente. En el caso del código fuente, el autor original es el administrador y propietario de la huella digital de referencia (la suma de verificación), que se mantendrá pública. Lo que no confía es el código fuente, pero confía en la firma pública. Entonces, para verificar su copia de la fuente, simplemente desea que su hash calculado coincida con el hash público.

Sin embargo, en el caso de una cookie, la aplicación no realiza un seguimiento de la firma, sino de ella SECRET_KEY. El SECRET_KEYes la huella digital de referencia. Las cookies viajan con una firma que afirman ser legítima. La legitimidad aquí significa que la firma fue emitida por el propietario de la cookie, esa es la aplicación, y en este caso, es ese reclamo en el que no confía y necesita verificar la validez de la firma. Para hacerlo, debe incluir un elemento en la firma que solo usted conoce, ese es el SECRET_KEY. Alguien puede cambiar una cookie, pero como no tienen el ingrediente secreto para calcular adecuadamente una firma válida, no pueden falsificarla. Como se dijo un poco antes, este tipo de huellas dactilares, donde en la parte superior de la suma de verificación también se proporciona una clave secreta,

¿Qué hay de las sesiones?

Las sesiones en su implementación clásica son cookies que llevan solo una ID en el contentcampo, el session_id. El propósito de las sesiones es exactamente el mismo que el de las cookies firmadas, es decir, para evitar la manipulación de cookies. Sin embargo, las sesiones clásicas tienen un enfoque diferente. Al recibir una cookie de sesión, el servidor usa la ID para buscar los datos de la sesión en su propio almacenamiento local, que podría ser una base de datos, un archivo o, a veces, un caché en la memoria. La cookie de sesión generalmente se configura para que caduque cuando se cierra el navegador. Debido al paso de búsqueda de almacenamiento local, esta implementación de sesiones generalmente incurre en un impacto en el rendimiento. Las cookies firmadas se están convirtiendo en una alternativa preferida y así es como se implementan las sesiones de Flask. En otras palabras, las sesiones de Flask soncookies firmadas, y para usar cookies firmadas en Flask solo use su SessionAPI.

¿Por qué no también encriptar las cookies?

A veces, el contenido de las cookies se puede cifrar antes de ser firmado . Esto se hace si se consideran demasiado sensibles para ser visibles desde el navegador (el cifrado oculta el contenido). Sin embargo, el simple hecho de firmar cookies responde a una necesidad diferente, una en la que existe el deseo de mantener un grado de visibilidad y usabilidad de las cookies en el navegador, al tiempo que evita que se entrometan.

¿Qué pasa si cambio el SECRET_KEY?

Al cambiar SECRET_KEY, está invalidando todas las cookies firmadas con la clave anterior. Cuando la aplicación recibe una solicitud con una cookie que fue firmada con una anterior SECRET_KEY, intentará calcular la firma con la nueva SECRET_KEY, y ambas firmas no coincidirán, esta cookie y todos sus datos serán rechazados, será como si El navegador se conecta al servidor por primera vez. Se cerrará la sesión de los usuarios y se olvidará su cookie anterior, junto con todo lo que esté almacenado en su interior. Tenga en cuenta que esto es diferente de la forma en que se maneja una cookie caducada. Una cookie caducada puede tener su contrato de arrendamiento extendido si su firma se retira. Una firma inválida solo implica una cookie inválida simple.

Por lo tanto, a menos que desee invalidar todas las cookies firmadas, intente mantenerlas SECRET_KEYdurante períodos prolongados.

¿Qué es una buena SECRET_KEY?

Una clave secreta debería ser difícil de adivinar. La documentación sobre Sesiones tiene una buena receta para la generación aleatoria de claves:

>>> import os
>>> os.urandom(24)
'\xfd{H\xe5<\x95\xf9\xe3\x96.5\xd1\x01O<!\xd5\xa2\xa0\x9fR"\xa1\xa8'

Copia la clave y pégala en tu archivo de configuración como el valor de SECRET_KEY.

Además de utilizar una clave que se generó aleatoriamente, podría utilizar una variedad compleja de palabras, números y símbolos, tal vez organizados en una oración conocida solo por usted, codificada en forma de bytes.

No , no establecer el SECRET_KEYdirectamente con una función que genera una clave diferente cada vez que se llama. Por ejemplo, no hagas esto:

# this is not good
SECRET_KEY = random_key_generator()

Cada vez que se reinicia su aplicación, se le dará una nueva clave, invalidando así la anterior.

En su lugar, abra un shell de Python interactivo y llame a la función para generar la clave, luego cópielo y péguelo en la configuración.

Michael Ekoka
fuente