Mantiene una aplicación existente con una base de usuarios establecida. Con el tiempo se decide que la técnica actual de hashing de contraseñas está desactualizada y debe actualizarse. Además, por razones de UX, no desea que los usuarios existentes se vean obligados a actualizar su contraseña. Toda la actualización de hashing de contraseñas debe realizarse detrás de la pantalla.
Suponga un modelo de base de datos 'simplista' para usuarios que contiene:
- CARNÉ DE IDENTIDAD
- Contraseña
¿Cómo se llega a resolver tal requisito?
Mis pensamientos actuales son:
- crear un nuevo método de hash en la clase apropiada
- actualizar la tabla de usuario en la base de datos para contener un campo de contraseña adicional
- Una vez que un usuario inicia sesión con éxito utilizando el hash de contraseña obsoleto, complete el segundo campo de contraseña con el hash actualizado
Esto me deja con el problema de que no puedo diferenciar razonablemente entre los usuarios que tienen y los que no han actualizado su hash de contraseña y, por lo tanto, me veré obligado a verificar ambos. Esto parece terriblemente defectuoso.
Además, esto básicamente significa que la antigua técnica de hashing podría verse obligada a permanecer indefinidamente hasta que cada usuario haya actualizado su contraseña. Solo en ese momento podría comenzar a eliminar la antigua comprobación de hashing y eliminar el campo de base de datos superfluo.
Estoy buscando principalmente algunos consejos de diseño aquí, ya que mi 'solución' actual está sucia, incompleta y lo que no, pero si se requiere un código real para describir una posible solución, siéntase libre de usar cualquier lenguaje.
fuente
Respuestas:
Sugeriría agregar un nuevo campo, "hash_method", con quizás un 1 para indicar el método anterior y un 2 para indicar el método nuevo.
Hablando razonablemente, si te importa este tipo de cosas y tu aplicación tiene una vida relativamente larga (que aparentemente ya lo es), esto probablemente volverá a suceder ya que la criptografía y la seguridad de la información es un campo tan evolutivo e impredecible. Hubo un tiempo en que una ejecución simple a través de MD5 era estándar, ¡si se usaba hashing! Entonces uno podría pensar que deberían usar SHA1, y ahora hay sal, sal global + sal aleatoria individual, SHA3, diferentes métodos de generación de números aleatorios listos para criptografía ... esto no va a simplemente 'detenerse', por lo que podría así arregla esto de una manera extensible y repetible.
Entonces, digamos que ahora tiene algo como (en pseudo-javascript para simplificar, espero):
Ahora que se da cuenta de que hay un método mejor, solo necesita dar una refactorización menor:
Ningún agente secreto o programador resultó dañado en la producción de esta respuesta.
fuente
newHash(oldHash, salt)
onewHash(password, salt)
Si se preocupa lo suficiente por implementar el nuevo esquema de hash para todos los usuarios lo más rápido posible (por ejemplo, porque el anterior es realmente inseguro), en realidad hay una forma de "migración" instantánea de cada contraseña.
La idea es básicamente cortar el hash . En lugar de esperar a que los usuarios proporcionen su contraseña existente (
p
) en el próximo inicio de sesión, inmediatamente usa el nuevo algoritmo de hash (H2
) en el hash existente ya producido por el algoritmo anterior (H1
):Después de hacer esa conversión, aún puede realizar perfectamente la verificación de contraseña; solo necesita calcular en
H2(H1(p'))
lugar del anteriorH1(p')
cuando el usuario intenta iniciar sesión con contraseñap'
.En teoría, esta técnica se podría aplicar a múltiples migraciones (
H3
,H4
, etc.). En la práctica, querrá deshacerse de la antigua función hash, tanto por razones de rendimiento como de legibilidad. Afortunadamente, esto es bastante fácil: en el próximo inicio de sesión exitoso, simplemente calcule el nuevo hash de la contraseña del usuario y reemplace el hash de un hash existente con él:También necesitará una columna adicional para recordar qué hash está almacenando: el de la contraseña o el hash anterior. En el desafortunado caso de fuga de la base de datos, esta columna no debería facilitar el trabajo del cracker; independientemente de su valor, el atacante aún tendría que invertir el
H2
algoritmo seguro en lugar del antiguoH1
.fuente
Su solución (columna adicional en la base de datos) es perfectamente aceptable. El único problema con él es el que ya mencionó: que el hashing anterior todavía se usa para usuarios que no se han autenticado desde el cambio.
Para evitar esta situación, puede esperar a que sus usuarios más activos cambien al nuevo algoritmo de hash y luego:
Elimine las cuentas de usuarios que no se han autenticado durante demasiado tiempo No hay nada de malo en eliminar una cuenta de un usuario que nunca visitó el sitio web durante tres años.
Envíe un correo electrónico a los usuarios restantes entre aquellos que no se hayan autenticado por un tiempo, diciéndoles que pueden estar interesados en algo nuevo. Ayudará a reducir aún más la cantidad de cuentas que usan el hashing anterior.
Tenga cuidado: si tiene una opción de no spam, los usuarios pueden revisar su sitio web, no envíe el correo electrónico a los usuarios que lo revisaron.
Finalmente, unas semanas después del paso 2, destruya la contraseña para los usuarios que todavía usan la técnica anterior. Cuando y si intentan autenticarse, verán que su contraseña no es válida; Si todavía están interesados en su servicio, podrían restablecerlo.
fuente
Probablemente nunca tendrá más de un par de tipos de hash, por lo que puede resolver esto sin ampliar su base de datos.
Si los métodos tienen diferentes longitudes de hash, y la longitud de hash de cada método es constante (por ejemplo, la transición de md5 a hmac-sha1), puede distinguir el método de la longitud de hash. Si tienen la misma longitud, puede calcular el hash usando primero el nuevo método, luego el método anterior si la primera prueba falla. Si tiene un campo (no mostrado) que indica cuándo se actualizó / creó el usuario por última vez, puede usarlo como un indicador de qué método usar.
fuente
Hice algo como esto y hay una manera de saber si es el nuevo hash Y hacerlo aún más seguro. Supongo que más seguro es algo bueno para ti si te estás tomando el tiempo para actualizar tu hash ;-)
Agregue una nueva columna a su tabla de base de datos llamada "sal" y, bueno, úsela como sal generada al azar por usuario. (prácticamente evita los ataques de la mesa del arco iris)
De esa manera, si mi contraseña es "pass123" y la sal aleatoria es 3333, mi nueva contraseña sería el hash de "pass1233333".
Si el usuario tiene sal, sabes que es el nuevo hash. Si la sal del usuario es nula, sabes que es el viejo hash.
fuente
No importa cuán buena sea su estrategia de migración, si la base de datos ya ha sido robada (y no puede probar negativamente que esto nunca ha sucedido), las contraseñas almacenadas con funciones hash inseguras ya pueden verse comprometidas. La única mitigación para esto es requerir que los usuarios cambien sus contraseñas.
Este no es un problema que se pueda resolver (solo) escribiendo código. La solución es informar a los usuarios y clientes sobre los riesgos a los que han estado expuestos. Una vez que esté dispuesto a ser abierto al respecto, puede realizar la migración simplemente restableciendo la contraseña de cada usuario, ya que es la solución más fácil y segura. Todas las alternativas realmente tratan de ocultar el hecho de que la cagaste.
fuente