¿Almacenamiento encriptado seguro de valores que permita búsquedas de base de datos?

4

Estoy desarrollando un sistema que tiene que almacenar los números de identificación nacionales de Finlandia de la forma más segura posible y al mismo tiempo permitir "¿existe este número de identificación en el sistema?": escriba consultas en una API web.

A primera vista, la respuesta puede parecer obvia: almacene un hash con sal del número, de modo que cuando se le dé una consulta API que contenga un número de identificación sin cifrar, vea si su versión con sal y hash existe en la base de datos, como lo haces con contraseñas.

Sin embargo, aunque este enfoque podría funcionar lo suficientemente bien con las contraseñas, no es tan simple con los números de identificación, ya que hay muy pocos posibles. Su formato es DDMMYY [- + A] XXXZ. Para cualquier año YY usted tiene 365 o 366 combinaciones posibles de DDMM. Para cada combinación DDMMYY tiene un número secuencial XXX, de 002 a 899. Z es un carácter de suma de comprobación calculado a partir de los nueve dígitos anteriores. El separador - / + / A entre la fecha y la secuencia numérica denota el siglo de nacimiento.

Si un atacante obtiene acceso al servidor, tiene acceso al código fuente de Python y a la base de datos y, por lo tanto, puede ver instantáneamente cómo se realiza el uso de sal y el hashing de los números de identificación. Dado el pequeño número de números de ID posibles (que pueden reducirse aún más con el conocimiento demográfico sobre las personas almacenadas en la base de datos), es trivial generar todos los hashes con sal posibles para cada número de identificación posible, para cada persona en la base de datos.

Consideré criptografía de clave pública, pero eso falla porque el cifrado de un valor con una clave pública en dos ocasiones diferentes produce dos textos cifrados diferentes, por lo que no se pueden usar en las búsquedas, como pueden hacer los hashes.

¿Me falta algo obvio, o realmente no hay una forma razonablemente segura de almacenar números de ID que puedan usarse para búsquedas Y que puedan soportar una violación del servidor, donde terminan los hashes y el código fuente con el que fueron creados? en las manos equivocadas?

    
pregunta JK Laiho 22.04.2015 - 13:56
fuente

2 respuestas

1

He enfrentado un problema similar, para el cual implementé la siguiente mitigación. Apreciaría mucho el aporte, ya que no afirmo que esta sea necesariamente una solución excelente.

TL; DR

Almacene los ID como HMAC(ID, master-key) y la contraseña de cada usuario se usa para derivar una clave por usuario para cifrar master-key .

Capacidades / limitaciones del adversario

Protege contra un adversario capaz de leer todos los datos en reposo, pero no uno que pueda leer memoria (por ejemplo, interceptar la comunicación entre procesos entre el servidor web y el CGI).

Las sales uno a uno dificultan la búsqueda

La búsqueda eficiente en su caso requiere indexación y búsqueda de un valor único . Al incluir cada número de ID, debe calcular HASH(search query, salt) para todas las sales que es (a) ineficiente cuando se realiza para cada búsqueda, y (b) ineficaz desde una perspectiva de seguridad como se describe a continuación.

Pequeño espacio de búsqueda

El problema principal radica en el hecho de que el espacio de búsqueda de los números de identificación es muy pequeño. Los ataques de fuerza bruta en hashes son, además de otros medios, mitigados por el aumento de los factores de trabajo (también conocido como estiramiento) con PBKDF2, bcrypt, scrypt, etc. Sin embargo, el espacio de búsqueda es de hecho tan pequeño que cualquier otro significativo -un aumento suficiente en el factor de trabajo para los adversarios sería demasiado incómodo para los usuarios (por ejemplo, 30 s + por búsqueda).

Cambiar el problema

Mi enfoque fue modificar el problema para proteger una clave secreta. HMAC permite tanto los datos (el número de identificación) como una clave. Las ID se almacenan como HMAC(ID, key) mientras que las búsquedas se realizan con HMAC(search, key) . Esto requiere coincidencias exactas, pero se puede hacer que no distinga mayúsculas de minúsculas con uppercase(ID) y uppercase(search) . Un ataque de fuerza bruta con una clave de 256 bits es inviable incluso sin un factor de trabajo incrementado.

¿Cómo protegemos la clave? Los usuarios de Query-API están autenticados, por lo que su contraseña se puede utilizar (después de usar sal y estirar) como el "material de ingreso de claves" (IKM) para HKDF . HKDF permite que se generen claves independientes desde una única fuente de entropía (el IKM) mediante la inclusión de 'información contextual' y una sal. Para la función HKDF(IKM, context, salt) donde IKM = PBKDF2(password, rounds) luego calculamos HKDF(IKM, 'Authentication', 'user-specific non-secret auth salt') y HKDF(IKM, 'KeyWrapping', 'user-specific non-secret wrap salt') . El primero se almacena en la base de datos de manera muy similar a un hash de autenticación con contraseña y el último se usa para envolver (es decir, cifrar) la clave maestra utilizada en los HMAC anteriores (tenga en cuenta que cada usuario tiene su propia clave maestra envuelta).

Cada vez que se autentica una sesión, la clave maestra no envuelta se ajusta dentro de una sesión cifrada. Puede utilizar HKDF con el ID de sesión (ya secreto) para derivar las claves de cifrado / almacenamiento para la sesión. A diferencia de las contraseñas que deben extenderse (incurrir en una demora de uno o dos segundos en la autenticación), los ID de sesión se pueden generar desde un CSPRNG (asegúrese de usar HttpOnly y secure flags).

Esto plantea otro problema, ya que necesitamos envolver inicialmente la clave maestra para cada usuario sin saber realmente su contraseña. Al crear el usuario en la base de datos, deben tener un par de clave privada / pública creado. La clave privada está encriptada con HKDF(IKM, 'PrivateKeyEncryption', 'user-specific non-secret PKI salt') . Cada usuario puede tener la clave maestra que le ha dado un usuario administrativo. Al iniciar sesión, el sistema verifica su bandeja de entrada de la clave de acceso en busca de cualquier nueva clave maestra y luego la envuelve para la próxima vez. En realidad, podría utilizar el enfoque asimétrico, pero es computacionalmente más costoso.

Caveats

  • Es imposible restablecer las contraseñas perdidas sin requerir que un usuario administrativo le dé la clave maestra al usuario.
  • NO soy un criptógrafo : puede haber vulnerabilidades derivadas de tener varias instancias de texto cifrado del mismo texto simple (EDIT: vea el primer comentario). Si es así, ¿puede mitigarse esto usando prefijos y sufijos entrópicos de longitud aleatoria? ¿O es una IV suficiente? Puede haber otros problemas que he pasado por alto completamente.
  • Esto se basa en claves secretas efímeras (contraseña al iniciar sesión, ID de sesión en cada solicitud) almacenadas en la memoria. No estoy seguro de cómo funcionará tu trabajo cron; una posible solución es hacer que use un cifrado asimétrico con el conocimiento de una clave pública cuya clave privada esté encriptada con la clave maestra a la que todos los usuarios pueden acceder (cualquier sesión autenticada puede mover claves a la base de datos).
  • ¿Su modelo de amenaza requiere una ingeniería tan extensa? La complejidad resulta en más lugares para que las cosas salgan mal.
respondido por el Arran Schlosberg 29.04.2015 - 05:30
fuente
0

Si un atacante obtiene su código Python, ambas opciones (usar salt + hash o criptografía de clave pública) no son lo suficientemente seguras ...

Pero cuando dices:

  

Consideré criptografía de clave pública, pero eso falla porque el cifrado de un   valor con una clave pública en dos ocasiones diferentes produce dos   diferentes textos cifrados, por lo que no se pueden utilizar en búsquedas como hashes   puede.

Solo es cierto si firmas contenidos diferentes (debido a tu sal), pero ¿por qué agregar sal entonces? ¿Por qué no firmar con clave pública su hash (sin sal) y delegar la seguridad a la clave pública cifrada? El contenido en chip generado siempre será el mismo, pero se asegurará con un método de chiper fuerte (tan fuerte como usted elija). Pero una vez más, si un atacante obtiene su clave privada, también será su dueño ...

Si no me falta algún mecanismo, creo que si no se guarda ningún secreto privado (tu código de Python, tu clave privada ...) cualquiera puede generar lo mismo que tú.

    
respondido por el exoddus 22.04.2015 - 14:21
fuente

Lea otras preguntas en las etiquetas