La guía definitiva para la autenticación de sitios web basada en formularios
Autenticación basada en formularios para sitios web
Creemos que Stack Overflow no debería ser sólo un recurso para preguntas técnicas muy específicas, sino también para directrices generales sobre cómo resolver variaciones de problemas comunes. "La autenticación basada en formularios para sitios web" debería ser un buen tema para tal experimento.
Debería incluir temas como:
- Cómo iniciar la sesión
- Cómo cerrar la sesión
- Cómo permanecer conectado
- Gestión de las cookies (incluida la configuración recomendada)
- Cifrado SSL/HTTPS
- Cómo almacenar las contraseñas
- Uso de preguntas secretas
- Funcionalidad de nombre de usuario/contraseña olvidada
- Uso de nonces para evitar falsificaciones de solicitudes en sitios cruzados (CSRF)
- OpenID
- Casilla de verificación "Recuérdame".
- Autocompletado de nombres de usuario y contraseñas en el navegador
- URLs secretas (públicas URL protegidas por digest)
- Comprobación de la solidez de la contraseña
- Validación del correo electrónico
- y mucho más sobre autenticación basada en formularios...
No debería incluir cosas como:
- Roles y autorización
- Autenticación básica HTTP
Por favor, ayúdenos con:
- Sugiriendo subtemas
- Enviando buenos artículos sobre este tema
- Editar la respuesta oficial
5304
3
PARTE I: Cómo iniciar sesión
Asumiremos que ya sabes cómo construir un formulario HTML de login+contraseña que envía los valores a un script en el lado del servidor para la autenticación. Las secciones siguientes tratarán sobre los patrones para una autenticación práctica y sólida, y sobre cómo evitar las trampas de seguridad más comunes. ¿HTTPS o no HTTPS? A menos que la conexión sea ya segura (es decir, que se haya hecho un túnel a través de HTTPS usando SSL/TLS), los valores de tu formulario de inicio de sesión se enviarán en texto claro, lo que permite que cualquiera que espíe la línea entre el navegador y el servidor web sea capaz de leer los inicios de sesión a medida que pasan. Este tipo de espionaje es realizado rutinariamente por los gobiernos, pero en general, no abordaremos los cables "propios" más que para decir esto: Simplemente usa HTTPS. En esencia, la única forma práctica de protegerse contra el espionaje telefónico/espionaje de paquetes durante el inicio de sesión es utilizando HTTPS u otro esquema de encriptación basado en certificados (por ejemplo, [TLS][1]) o un esquema de respuesta a desafíos probado y comprobado (por ejemplo, el SRP basado en [Diffie-Hellman][2]). Cualquier otro método puede ser fácilmente burlado por un atacante que espíe. Por supuesto, si estás dispuesto a ser un poco impráctico, también podrías emplear algún tipo de esquema de autenticación de dos factores (por ejemplo, la aplicación Google Authenticator, un libro de códigos físico al estilo de la "guerra fría", o un dongle generador de claves RSA). Si se aplica correctamente, esto podría funcionar incluso con una conexión no segura, pero es difícil imaginar que un desarrollador esté dispuesto a implementar la autenticación de dos factores pero no el SSL. (No) Enrolle su propio cifrado/cashing de JavaScript. Dado el coste percibido (aunque ahora [evitable][27]) y la dificultad técnica de configurar un certificado SSL en tu sitio web, algunos desarrolladores se ven tentados a implementar sus propios esquemas de cifrado o hashing en el navegador para evitar pasar los inicios de sesión en texto claro por un cable no seguro. Aunque se trata de una idea noble, es esencialmente inútil (y puede ser un [fallo de seguridad][3]) a menos que se combine con una de las medidas anteriores, es decir, asegurando la línea con un cifrado fuerte o utilizando un mecanismo de desafío-respuesta probado (si no sabes lo que es, sólo tienes que saber que es uno de los conceptos más difíciles de probar, más difíciles de diseñar y más difíciles de implementar en la seguridad digital). Si bien es cierto que el hashing de la contraseña puede ser efectivo contra la revelación de la contraseña, es vulnerable a los ataques de repetición, a los ataques Man-In-The-Middle / hijackings (si un atacante puede inyectar unos pocos bytes en su página HTML no segura antes de que llegue a su navegador, puede simplemente comentar el hashing en el JavaScript), o a los ataques de fuerza bruta (ya que está entregando al atacante tanto el nombre de usuario, como la sal y la contraseña hashed). CAPTCHAS contra la humanidad Los [CAPTCHA][4] están pensados para frustrar una categoría específica de ataque: diccionario automatizado/fuerza bruta de ensayo y error sin operador humano. No hay duda de que esta es una amenaza real, sin embargo, hay maneras de lidiar con ella sin problemas que no requieren un CAPTCHA, específicamente esquemas de estrangulamiento de inicio de sesión del lado del servidor diseñados adecuadamente - hablaremos de ellos más adelante. Sepa que las implementaciones de CAPTCHA no son creadas de la misma manera; a menudo no son solucionables por humanos, la mayoría de ellas son realmente ineficaces contra los bots, todas ellas son ineficaces contra la mano de obra barata del tercer mundo (según [OWASP][5], la tasa actual de explotación es de 12 dólares por 500 pruebas), y algunas implementaciones pueden ser técnicamente ilegales en algunos países (ver [OWASP Authentication Cheat Sheet][6]). Si tienes que usar un CAPTCHA, usa el de Google [reCAPTCHA][7], ya que es OCR-difícil por definición (ya que usa escaneos de libros mal clasificados por OCR) y se esfuerza por ser amigable con el usuario. Personalmente, tiendo a encontrar los CAPTCHAS molestos, y los utilizo sólo como último recurso cuando un usuario ha fallado en el inicio de sesión un número de veces y los retrasos de estrangulamiento están al máximo. Esto ocurrirá en contadas ocasiones para que sea aceptable, y refuerza el sistema en su conjunto. Almacenamiento de contraseñas/verificación de inicios de sesión Puede que esto sea finalmente de dominio público después de todos los hackeos y filtraciones de datos de usuarios que hemos visto en los últimos años, pero hay que decirlo: No almacene las contraseñas en texto claro en su base de datos. Las bases de datos de usuarios se piratean, se filtran o se obtienen a través de la inyección de SQL, y si almacena contraseñas en texto plano, la seguridad de su inicio de sesión se acaba al instante. Entonces, si no puedes almacenar la contraseña, ¿cómo compruebas que la combinación de nombre de usuario y contraseña enviada desde el formulario de acceso es correcta? La respuesta es un hash usando una [función de derivación de clave][24]. Cada vez que se crea un nuevo usuario o se cambia una contraseña, se toma la contraseña y se ejecuta a través de una KDF, como Argon2, bcrypt, scrypt o PBKDF2, convirtiendo la contraseña en texto claro ("correcthorsebatterystaple") en una cadena larga y de aspecto aleatorio, que es mucho más segura para almacenar en su base de datos. Para verificar un inicio de sesión, se ejecuta la misma función hash en la contraseña introducida, esta vez pasando la sal y comparando la cadena hash resultante con el valor almacenado en su base de datos. Argon2, bcrypt y scrypt ya almacenan la sal con el hash. Consulta este [artículo][23] en sec.stackexchange para obtener información más detallada. La razón por la que se utiliza una sal es que el hash en sí mismo no es suficiente - querrás añadir una "sal" para proteger el hash contra [tablas arco iris][8]. Una sal previene efectivamente que dos contraseñas que coincidan exactamente sean almacenadas como el mismo valor hash, previniendo que toda la base de datos sea escaneada en una sola ejecución si un atacante está ejecutando un ataque de adivinación de contraseñas. Un hash criptográfico no debería usarse para el almacenamiento de contraseñas porque las contraseñas seleccionadas por el usuario no son lo suficientemente fuertes (es decir, no suelen contener suficiente entropía) y un ataque de adivinación de contraseñas podría ser completado en un tiempo relativamente corto por un atacante con acceso a los hashes. Por eso se utilizan los KDF, que efectivamente ["estiran la clave"][25], lo que significa que cada vez que un atacante adivina la contraseña provoca múltiples repeticiones del algoritmo hash, por ejemplo 10.000 veces, lo que hace que el atacante adivine la contraseña 10.000 veces más lentamente. **Datos de la sesión: "Ha iniciado sesión como Spiderman69". Una vez que el servidor ha verificado el nombre de usuario y la contraseña con su base de datos de usuarios y ha encontrado una coincidencia, el sistema necesita una forma de recordar que el navegador ha sido autenticado. Este hecho sólo debe ser almacenado del lado del servidor en los datos de la sesión.
Artículo definitivo
Envío de credenciales
La única forma práctica de enviar credenciales de forma 100% segura es utilizando [SSL][1]. Usar JavaScript para hacer hash de la contraseña no es seguro. Escollos comunes para el hash de contraseñas del lado del cliente:
Almacenamiento de contraseñas
Nunca almacene las contraseñas como texto plano en la base de datos. Ni siquiera si no te importa la seguridad de tu propio sitio. Asume que algunos de tus usuarios reutilizarán la contraseña de su cuenta bancaria online. Entonces, almacene la contraseña con hash, y deseche la original. Y asegúrese de que la contraseña no aparezca en los registros de acceso o de la aplicación. OWASP recomienda el uso de Argon2 como primera opción para las nuevas aplicaciones. Si no está disponible, se debe utilizar PBKDF2 o scrypt en su lugar. Y finalmente, si ninguno de los anteriores está disponible, utilice bcrypt. Los hash por sí mismos también son inseguros. Por ejemplo, contraseñas idénticas significan hashes idénticos - esto hace que las tablas de búsqueda de hash sean una forma efectiva de descifrar muchas contraseñas a la vez. En su lugar, almacena el hash salted. Una sal es una cadena que se añade a la contraseña antes del hash - utiliza una sal diferente (aleatoria) por usuario. La sal es un valor público, así que puedes almacenarla con el hash en la base de datos. Ver aquí para más información sobre esto. Esto significa que no puedes enviar al usuario sus contraseñas olvidadas (porque sólo tienes el hash). No restablezca la contraseña del usuario a menos que haya autenticado al usuario (los usuarios deben demostrar que son capaces de leer los correos electrónicos enviados a la dirección de correo electrónico almacenada (y validada)).
Preguntas de seguridad
Las preguntas de seguridad son inseguras - evita usarlas. ¿Por qué? Todo lo que hace una pregunta de seguridad, lo hace mejor una contraseña. Lee Parte III: Uso de preguntas secretas en [@Jens Roland answer][6] aquí en este wiki.
Cookies de sesión
Después de que el usuario inicie la sesión, el servidor envía al usuario una cookie de sesión. El servidor puede recuperar el nombre de usuario o el id de la cookie, pero nadie más puede generar dicha cookie (TODO explicar los mecanismos). [Las cookies pueden ser secuestradas][7]: son tan seguras como el resto de la máquina del cliente y otras comunicaciones. Pueden ser leídas desde el disco, olfateadas en el tráfico de red, levantadas por un ataque de cross-site scripting, suplantadas desde un DNS envenenado para que el cliente envíe sus cookies a los servidores equivocados. No envíe cookies persistentes. Las cookies deben expirar al final de la sesión del cliente (cierre del navegador o salida de su dominio). Si quieres autologizar a tus usuarios, puedes establecer una cookie persistente, pero debe ser distinta de una cookie de sesión completa. Puede establecer una bandera adicional que indique que el usuario se ha autolocalizado, y que necesita entrar de verdad para las operaciones sensibles. Esto es muy popular en los sitios de compras que quieren ofrecerle una experiencia de compra personalizada y sin problemas, pero protegiendo sus datos financieros. Por ejemplo, cuando vuelves a visitar Amazon, te muestran una página que parece que has iniciado la sesión, pero cuando vas a hacer un pedido (o a cambiar tu dirección de envío, tarjeta de crédito, etc.), te piden que confirmes tu contraseña. Los sitios web financieros, como los bancos y las tarjetas de crédito, por otro lado, sólo tienen datos sensibles y no deberían permitir el inicio de sesión automático o un modo de baja seguridad.
Lista de recursos externos
En primer lugar, una fuerte advertencia de que esta respuesta no es la más adecuada para esta pregunta exacta. Definitivamente no debería ser la respuesta principal.
Me adelantaré y mencionaré la propuesta de Mozilla BrowserID (o quizás más precisamente, el Protocolo de Correo Electrónico Verificado) en el espíritu de encontrar un camino de actualización hacia mejores enfoques de autenticación en el futuro.
Lo resumiré así:
@
dominio" es concisa y soportada por una amplia gama de protocolos y esquemas URI. Este identificador es, por supuesto, el más universalmente reconocido como una dirección de correo electrónico.No se trata estrictamente de una "autenticación basada en formularios para sitios web". Pero sí es un esfuerzo para pasar de la norma actual de autenticación basada en formularios a algo más seguro: la autenticación soportada por el navegador.