Si desea seguridad, la transmisión de datos de autenticación a través de TLS es un gran comienzo.
Pero supongamos que el websocket ya está configurado sobre TLS.
Una solución que recomendaría se basaría en un mecanismo de desafío-respuesta:
- El servidor establecería un conjunto aleatorio de bytes (el desafío), establecería un tiempo de espera al final del cual no se autorizaría el desafío.
- El cliente enviaría en "texto simple" el nombre de usuario y el desafío y un hash (con un algoritmo SHA2 o SHA3) de
challenge || username || f(password)
,
f(password)
es cómo se almacena la contraseña en la base de datos (un SHA256 de password
por ejemplo).
- El servidor verifica con los datos del nombre de usuario y el desafío de la respuesta y, de ser así, autentica al usuario. También invalida el desafío para invalidar cualquier intento futuro de usar este desafío.
Este método proporciona una funcionalidad anti-reproducción: un atacante no pudo capturar los datos y reproducirlos más tarde para autenticarse.
Sin embargo, tengo un gran problema con este método: f(password)
tiene aquí aproximadamente la misma seguridad que password
: si la base de datos se filtra, cualquier atacante podría autenticarse como cualquier otra persona. Digo "aproximadamente" porque, si se almacena en texto sin cifrar, ya que muchos usuarios utilizan la misma contraseña en varios sitios web (lo que es malo), sus cuentas podrían verse comprometidas en otro lugar.
No puedo pensar en un método que proteja contra una fuga de base de datos y un mecanismo anti-reproducción, sino un mecanismo de fuga anti-base de datos (enviar la contraseña del usuario en texto claro sobre TLS, la contraseña hash con sal de base de datos y comparar con la base de datos) sobre TLS debería ser suficiente contra la mayoría de los riesgos a los que se enfrentará