¿Deben restablecerse las contraseñas automáticamente cuando cambie el método subyacente?

95

Actualmente soy ingeniero en un proyecto en fase de desarrollo. Un 'módulo' en este proyecto proporciona la capacidad de autenticación / autorización del usuario. Sin embargo, hemos llegado a nuestra preocupación de que el algoritmo de hash de contraseñas puede no estar a la altura de la copia (alias no BCrypt). (¡Lo terrible no está muy seguro de qué es y de dónde vino!).

Esto obviamente tiene que cambiar y el parche está siendo programado. Naturalmente, tenemos que actualizar a todos nuestros usuarios de prueba porque sus contraseñas utilizarán el antiguo método de hash, no es un problema, todos los usuarios de demostración están automatizados en la compilación, por lo que está actualizando el script. Pero la siguiente pregunta es qué pasa si se trata de un sistema de producción con usuarios activos y obsoletos, de todas las cantidades. Lo que sería la mejor práctica.

  1. ¿Forzar automáticamente un restablecimiento de contraseña en cada usuario? Esto notificará a cada usuario que su contraseña ha sido cambiada y puede causar dudas / confusión y puede sospechar que ha habido una violación de seguridad. Es posible que se formulen más preguntas que no necesariamente puedan ser respondidas por las partes interesadas del sitio web.
  2. Actualice la base de datos para marcar si es el método nuevo o antiguo, luego, una vez que el usuario haya sido autenticado, actualice su contraseña en la base de datos usando. Requiere un poco de lógica en el servicio y la transición será impecable para cualquier usuario existente. El problema es que si hubo una violación, puede ser evidente que hay dos métodos aquí y si se encuentra que el menos seguro es tan inseguro, obviamente podría romperse.
  3. Restablece todas las contraseñas, usando una versión BCrypted del hash existente. Marcarlo como el estilo antiguo, por lo que para una autenticación exitosa solo se mantiene un hash de la contraseña en lugar de un hash de un hash.
pregunta Crazy Dino 21.03.2016 - 15:03
fuente

6 respuestas

90

Su opción 1. es una mala idea: además de las razones de Experiencia de usuario / Relaciones públicas que usted indica, también le está dando a los atacantes una ventana para interceptar los tokens de restablecimiento de contraseña y comprometer cada cuenta en su servidor. Tampoco resuelve su problema si tiene incluso un usuario que es demasiado perezoso para iniciar sesión / actualizar su contraseña.

A primera vista, tanto el 2. como el 3. me parecen bien. Su número 2 no es menos seguro que lo que está haciendo ahora, pero 2. significaría que tiene que continuar dando soporte al inicio de sesión débil actual para siempre (o hacer algo como "Después de X meses, estamos borrando su contraseña y obligándole a hacerlo". una "recuperación" que rompe la agradable transparencia de usuario que desea, así que ignoremos eso).

Consideremos el caso en el que hay usuarios en la base de datos que nunca volverán a iniciar sesión. Con ambos, 2. y 3. tienes que seguir admitiendo el algoritmo de hash actual en tu base de código para siempre en caso de que inicien sesión, pero al menos 3. tiene la ventaja de que ellos (o más bien, tú) están protegidos contra Ataques de fuerza bruta sin conexión si alguna vez te roban tu base de datos.

Ya que tendrás que mantener la columna de "bandera de estilo antiguo para siempre, hazte un favor y haz que sea un int no un bool para que si alguna vez tienes que actualizar el hashing de contraseña nuevamente. puede grabar en qué estilo están.

ACTUALIZACIÓN: se hizo una pregunta muy similar aquí y se desarrolló sobre la discusión de este hilo.

    
respondido por el Mike Ounsworth 21.03.2016 - 15:14
fuente
14

Si puede hacer la opción 3, no veo por qué consideraría los demás. Es, con mucho, la mejor opción. Con esta opción, mi instinto sería considerar el uso de dos sales diferentes, una para el algoritmo anterior y otra para el nuevo con bcrypt. Estoy imaginando una configuración como esta:

  1. Configura tu nuevo sistema de contraseñas como lo harías si estuvieras empezando hoy.
  2. Cree una nueva tabla separada que tenga campos para el nombre de usuario (o id), el algoritmo de hashing (nombre o id), sal.
  3. Al iniciar sesión, verifique si el usuario tiene un registro en la tabla por separado, y si es así, marque la contraseña de la forma anterior, luego marque el resultado de la nueva forma y compare con el hash bcrypt. Si coincide, vuelva a introducir / eliminar la contraseña de la nueva forma y elimine el registro de la tabla separada.

El inconveniente es que tendrá que mantener la contraseña en la memoria unos milisegundos más (a quién le importa) y tendrá una búsqueda de tabla adicional en cada inicio de sesión, casi para siempre, hasta que la tabla separada esté vacía o hasta las cuentas antiguas se vuelven tan obsoletas que está dispuesto a pedirles que reinicien su contraseña ellos mismos.

    
respondido por el TTT 21.03.2016 - 16:10
fuente
3

Tenga en cuenta que, si su esquema OLD está saturado con sal, no podrá utilizar el esquema # 3, a menos que almacene la sal POR SEPARADO.

Normalmente, la sal se almacena junto con el hash, y usas la sal como entrada para la función hash; si no usas exactamente la misma sal, obtendrás una salida completamente diferente.

Si haces newhash (oldsalt + oldhash, newsalt), entonces, incluso con la contraseña correcta, no podrás volver a crear el hash antiguo (ya que no tienes oldsalt), y no puedes generar el hash final. Lo mismo se aplica a cualquier cosa que tenga parámetros (por ejemplo, bcrypt tiene un parámetro de "costo": esto debe configurarse al cifrar y está incrustado en la salida, para usarlo al validar la contraseña).

TAMBIÉN : como lo mencionaron otros, si está almacenando que el hash es de estilo "antiguo" o "nuevo", considere en su lugar almacenar el "esquema", donde, por ejemplo, 0 es el "antiguo" y 1 es bcrypt (tenga en cuenta que no uso "nuevo"; es "nuevo" ahora, ¡no será "nuevo" para siempre!). Una forma común de hacer esto es tener un marcador al comienzo del hash (¡esto ya puede ser el caso!). bcrypt utiliza uno de los siguientes prefijos estándar: "$ 2a $", "$ 2b $", "$ 2x" o "$ 2y $". Dependiendo de los posibles resultados de su algoritmo "antiguo", es posible que deba crear su propio prefijo para marcarlos, o puede salirse con la suya con "cualquier cosa que no comience con '$' es el algoritmo antiguo.

Y finalmente, como obviamente estás preocupado (¡con razón!) por la seguridad de las contraseñas antiguas, sugeriría obligar a todos a cambiar su contraseña, enviándoles por correo electrónico las instrucciones con un token (¡NO LO HAGAS! No desea que sus usuarios hagan clic en un enlace. Solo dígales que inicien sesión en el lugar habitual). Luego, solicite el token y su contraseña. De lo contrario, alguien que haya robado una contraseña en el pasado puede cambiarla y obtener una "nueva" contraseña válida.

FINALMENTE: tiene una fecha de caducidad: si las contraseñas no se cambian en esta fecha, entonces se deben invalidar. Esta fecha debe estar en el correo electrónico, y no demasiado lejos en el futuro (¿una semana? Depende del tiempo que demande sus clientes en responder). Después de eso, tendrán que pasar por los procedimientos de "restablecimiento de contraseña".

    
respondido por el AMADANON Inc. 22.03.2016 - 02:01
fuente
2

No sé cuál es su esquema de codificación de contraseña, pero si no está tan mal, es probable que la estructura de la contraseña en el formato antiguo y nuevo sea diferente.

Ya he visto algo así en un antiguo sistema BSD cuando el sistema cambió de una codificación de contraseña tradicional a una más segura. El nuevo comenzó con una secuencia de caracteres que no podía existir en el esquema anterior, por lo que cada vez que un usuario con una contraseña anterior ingresó, su contraseña de texto sin cifrar se validó con el método anterior y se volvió a aplicar y almacenar en la base de datos de contraseñas en silencio. El nuevo método. Después de un mes, no había ninguna contraseña antigua en la base de datos, sin que el usuario final se diera cuenta.

Eso estaría en algún lugar entre tu segundo y tercer método.

Sé que en un sistema web de producción real, las cosas ahora pueden ser mucho peores, porque los usuarios pueden esperar semanas o incluso meses antes de volver a conectarse. Pero (dependiendo de la actividad real) puede ser mitigado por el hecho de que un usuario que no se ha conectado durante varios meses puede haber olvidado su contraseña, o puede decirle que lo hizo ... Eso significa que esperaría un poco por más tiempo aquí, probablemente 3 o 6 meses y después de ese tiempo restablecería todas las contraseñas de estilo antiguo a un valor prohibido, lo que obligaría al usuario a restablecer su contraseña en la próxima conexión ... a través de la pantalla contraseña olvidada .

Las cosas buenas aquí son:

  • transparente para usuarios regulares
  • no hay cambios en el esquema de la base de datos, siempre que el campo de la contraseña pueda aceptar ambos estilos
  • los usuarios ocasionales simplemente serán manejados como si hubieran olvidado su contraseña después de meses sin usarla

El inconveniente es que lo obliga a implementar simultáneamente el método de autenticación + una actualización automática de todas las contraseñas de estilo.

    
respondido por el Serge Ballesta 22.03.2016 - 17:30
fuente
0

No mencionaste el idioma que estás usando. Php tiene sus problemas con varias funciones que deberían devolver falso cuando debería ser verdadero en algunos casos, u otras funciones que suenan como si fueran el trabajo pero carecen de la lógica para manejar realmente todas las posibles entradas válidas posibles.

Pero esta es la forma correcta de hacer lo que estás hablando en php, incluso si no estás usando php. El código de alto nivel puede dejarlo con un punto de partida en la codificación para sus propósitos.

enlace

$password = 'rasmuslerdorf';
$hash = '$2y$10$YCFsG6elYca568hBi2pZ0.3LDL5wjgxct1N8w/oLR/jfHsiQwCqTS';

// The cost parameter can change over time as hardware improves
$options = array('cost' => 11);

// Verify stored hash against plain-text password
if (password_verify($password, $hash)) {
    // Check if a newer hashing algorithm is available
    // or the cost has changed
    if (password_needs_rehash($hash, PASSWORD_DEFAULT, $options)) {
        // If so, create a new hash, and replace the old one
        $newHash = password_hash($password, PASSWORD_DEFAULT, $options);
    }

    // Log user in
}

Lo que yo haría sería tu # 2 con lo que se ha mencionado con el uso de un int. Al leer la documentación en PASSWORD_DEFAULT, podría cambiar cuando se encuentren mejores algoritmos y deban eliminar el actual por motivos de inseguridad a medida que se actualice php.

    
respondido por el Eric 22.03.2016 - 00:07
fuente
-1

Avise a los usuarios sobre las fallas de seguridad en el antiguo sistema de autenticación y sugiérales que cambien / restablezcan su contraseña para usar el nuevo sistema de autenticación. Si después de un período de tiempo predeterminado (de lo que se puede informar a los usuarios en la "letra pequeña" en algún lugar) algunos usuarios todavía no han cambiado / restablecido su contraseña, desactive su cuenta y envíe un correo electrónico que les indique cómo hacerlo. activalo de nuevo. De esta manera, sus usuarios habituales sentirán que se les "pide" que cambien su contraseña, y que no se les "diga" que cambien su contraseña, por lo que es poco probable que reaccionen negativamente a la solicitud, incluso si cambian su contraseña (que esperamos que lo hagan) , si su correo electrónico de aviso es lo suficientemente persuasivo) y no tendrá que soportar el antiguo sistema de autenticación para los pocos usuarios que nunca pudieron cambiar su contraseña.

    
respondido por el Micheal Johnson 21.03.2016 - 17:01
fuente

Lea otras preguntas en las etiquetas