¿Cómo enviar una contraseña de forma segura a través de HTTP?

115

Si en una pantalla de inicio de sesión el usuario envía un formulario con su nombre de usuario y contraseña, la contraseña se envía en texto sin formato (incluso con POST, corríjame si me equivoco).

Entonces, la pregunta es ¿cuál es la forma correcta de proteger al usuario y su contraseña contra terceros que podrían estar escuchando los datos de comunicación?

Soy consciente de que HTTPS es una solución al problema, pero ¿hay alguna forma de garantizar al menos algún nivel de seguridad utilizando el protocolo HTTP estándar (solicitud POST)? (quizás usando javascript de alguna manera)

EDITAR Es posible que haya omitido algunas cosas importantes.

De lo que trataba era de una página, es decir, una página de inicio de sesión generada por PHP, que por supuesto se envía al usuario en una solicitud HTTP GET como un archivo HTML. No hay una conexión (@Jeremy Powel) establecida entre el servidor y el cliente, por lo que no puedo crear dicho protocolo de intercambio de información. Y quiero que el proceso completo sea transparente para el usuario: quiere enviar una contraseña, no tratar con la criptografía.

Gracias.

Kornelije Petak
fuente
1
Probablemente no podrá lograr esto sin que el cliente use criptografía, pero el usuario no tiene que ver tal proceso. Simplemente ingresa su contraseña y el código que genera su PHP (javascript por ejemplo) lo maneja todo por usted.
Jeremy Powell
13
El problema que describe es la razón por la que se inventó HTTPS. Si envía un secreto al cliente para cifrar la contraseña, un fisgón podrá olerlo y descifrar la contraseña en el viaje de regreso.
jnoss
1
Entonces, S en su sugerencia podría ser solo contraseña (o nombre de usuario + contraseña combinados de alguna manera), ya que este es el único "secreto" que tiene el usuario. ¿Estoy en lo correcto? Entonces, la solución sería la siguiente: - El servidor proporciona a la página HTML un campo de formulario oculto R - El usuario ingresa la contraseña, y antes de que se envíe la contraseña, el javascript calcula H (R, S) y la envía al servidor, quizás incluso usando AJAX - El servidor calcula H (R, S) y lo compara con recibido y envía una respuesta a la solicitud ajax si la autenticación pasó - El javascript redirige el navegador a la página web deseada
Kornelije Petak
2
@jeremy powell: si bien lo que describe es una práctica común, también es vulnerable a un intermediario que puede olfatear la cookie desde un encabezado y hacerse pasar por el usuario reutilizando la cookie. Los ataques de hombre en el medio son difíciles de proteger a menos que esté usando HTTPS
jnoss
1
Para quien llegue a esta pregunta en el futuro: DESPUÉS de iniciar sesión, también debe proteger la cookie de sesión. (Entonces: usar HTTPS es realmente mucho más fácil).
Arjan

Respuestas:

66

Usar HTTP con SSL le hará la vida mucho más fácil y podrá estar tranquilo. Las personas muy inteligentes (¡al menos más inteligentes que yo!) Han examinado este método de comunicación confidencial durante años.

Jeremy Powell
fuente
14
... y "¡¡Pero tengo que pagar por un certificado SSL !!" no es una queja válida, ya que puede obtenerlos por $ 30 en estos días. ¿Sus datos realmente no valen 30 dólares para proteger?
caf
3
¿Qué sucede si el alojamiento web al que se suscribió no admite la adición de certificados SSL?
Calmarius
89
@Calmarius - luego te mudas a un
servidor web
3
@BornToCode Esto técnicamente significa que necesita tener una IP dedicada y debe poseer el hardware del servidor (o al menos un VPS) para usar HTTPS. Los servidores web compartidos no pueden utilizar HTTPS, a menos que todo el servidor esté protegido con el certificado del propietario del servidor.
Calmarius
6
los webhosts compartidos ciertamente pueden hacer https, usando en.wikipedia.org/wiki/Server_Name_Indication
Brian Minton
39

La autenticación segura es un tema amplio. En pocas palabras, como mencionó @ jeremy-powell, siempre favorezca el envío de credenciales a través de HTTPS en lugar de HTTP. Eliminará muchos dolores de cabeza relacionados con la seguridad.

Los certificados TSL / SSL son bastante baratos en estos días. De hecho, si no desea gastar dinero en absoluto, existe una autoridad de certificación automatizada de letsencrypt.org gratuita .

Puede ir un paso más allá y usar caddyserver.com que llama a letsencrypt en segundo plano.

Ahora, una vez que sacamos HTTPS del camino ...

No debe enviar el nombre de usuario y la contraseña a través de la carga útil POST o los parámetros GET. En su lugar, utilice un encabezado de autorización (esquema de autenticación de acceso básico), que se construye de la siguiente manera:

  • El nombre de usuario y la contraseña se combinan en una cadena separada por dos puntos, por ejemplo: nombre de usuario: contraseña
  • La cadena resultante se codifica utilizando la variante RFC2045-MIME de Base64, excepto que no se limita a 76 caracteres / línea.
  • El método de autorización y un espacio, es decir, "Básico", se colocan antes de la cadena codificada.

fuente: Wikipedia: encabezado de autorización

Puede parecer un poco complicado, pero no lo es. Hay muchas buenas bibliotecas que le proporcionarán esta funcionalidad de forma inmediata.

Hay algunas buenas razones por las que debería utilizar un encabezado de autorización

  1. Es un estándar
  2. Es simple (después de aprender a usarlos)
  3. Le permitirá iniciar sesión en el nivel de URL, así: https://user:[email protected]/login(Chrome, por ejemplo, lo convertirá automáticamente en Authorizationencabezado)

IMPORTANTE:
como señala @zaph en su comentario a continuación, enviar información confidencial como consulta GET no es una buena idea, ya que probablemente terminará en los registros del servidor.

ingrese la descripción de la imagen aquí

FullStackForger
fuente
11
El problema con el envío de credenciales (contraseña) como parámetros GET es que el par de usuario / contraseña probablemente terminará en los registros del servidor, lo cual no es una buena idea. Es mejor enviar las credenciales en un POST.
zaph
Ahh ... para nada. La captura de pantalla que está viendo se modificó para ilustrar lo que está sucediendo en un navegador. En el momento en que presione entrar, el navegador convertirá su URL creando un Authorizationencabezado. Solo pruébalo. Los registros recordarán que están limpios. Y, por supuesto, si realiza una llamada desde el servidor (si ese es el escenario que le preocupa), debe generar un encabezado mediante programación, por supuesto.
FullStackForger
No puede registrar lo que no puede ver. username:password@urldesde el navegador se traduce en: url+ Authorizationencabezado de solicitud. En cuanto a las consultas GET ... bueno, como dije, use el encabezado Authroziation. Es mejor.
FullStackForger
2
No aborda el punto de la pregunta: proteger los datos confidenciales de las escuchas de intermediarios. El objetivo no es encontrar una forma alternativa y / o más estandarizada de pasar las credenciales, sino protegerlas sobre un canal inseguro.
Lord of the Goo
3
Debe enfatizarse que esta solución solo funciona si ya está encriptando la conexión con TLS / SSL. base64 no es cifrado.
Escocia
14

Puede utilizar un esquema de respuesta al desafío. Digamos que el cliente y el servidor conocen un secreto S. Entonces el servidor puede estar seguro de que el cliente conoce la contraseña (sin revelarla) al:

  1. El servidor envía un número aleatorio, R, al cliente.
  2. El cliente envía H (R, S) de vuelta al servidor (donde H es una función hash criptográfica, como SHA-256)
  3. El servidor calcula H (R, S) y lo compara con la respuesta del cliente. Si coinciden, el servidor sabe que el cliente conoce la contraseña.

Editar:

Aquí hay un problema con la actualización de R y el hecho de que HTTP no tiene estado. Esto se puede manejar haciendo que el servidor cree un secreto, llámelo Q, que solo el servidor conoce . Entonces el protocolo es el siguiente:

  1. El servidor genera un número aleatorio R. Luego envía al cliente H (R, Q) (que el cliente no puede falsificar).
  2. El cliente envía R, H (R, Q) y calcula H (R, S) y lo envía todo de vuelta al servidor (donde H es una función hash criptográfica, como SHA-256)
  3. El servidor calcula H (R, S) y lo compara con la respuesta del cliente. Luego toma R y calcula (nuevamente) H (R, Q). Si la versión del cliente de H (R, Q) y H (R, S) coincide con el recálculo del servidor, el servidor considera que el cliente está autenticado.

Tenga en cuenta que, dado que el cliente no puede falsificar H (R, Q), H (R, Q) actúa como una cookie (y, por lo tanto, podría implementarse realmente como una cookie).

Otra edición:

La edición anterior del protocolo es incorrecta, ya que cualquiera que haya observado H (R, Q) parece ser capaz de reproducirlo con el hash correcto. El servidor debe recordar qué R ya no están actualizadas. Estoy CW'ing esta respuesta para que puedan editar esto y resolver algo bueno.

Jeremy Powell
fuente
+1: deberá calcular la respuesta en el lado del cliente con javascript (o Flash / Silverlight / etc.)
orip
10
No detiene al hombre en el medio ni a los ataques de suplantación. Por ejemplo, a través de wifi. Parece que esto solo dará una falsa sensación de seguridad, en mi opinión.
Tom Hawtin - tackline
2
Eso protege contra ataques pasivos, pero un hombre en el medio aún puede atacar.
Douglas Leeder
7
También requiere que el servidor conozca la contraseña original.
Douglas Leeder
3
¡No reinventes las criptomonedas! (en producción)
bryanph
11

Si su proveedor de alojamiento web lo permite, o tendrá que tratar con datos confidenciales, utilice HTTPS, punto. (A menudo es requerido por la ley afaik).

De lo contrario, si desea hacer algo a través de HTTP. Yo haría algo como esto.

  1. El servidor incrusta su clave pública en la página de inicio de sesión.
  2. El cliente completa el formulario de inicio de sesión y hace clic en enviar.
  3. Una solicitud AJAX obtiene la marca de tiempo actual del servidor.
  4. La secuencia de comandos del lado del cliente concatena las credenciales, la marca de tiempo y una sal (hash de datos analógicos, por ejemplo, movimientos del mouse, eventos de pulsación de tecla), la cifra utilizando la clave pública.
  5. Envía el hash resultante.
  6. El servidor descifra el hash
  7. Comprueba si la marca de tiempo es lo suficientemente reciente (solo permite una ventana corta de 5 a 10 segundos). Rechaza el inicio de sesión si la marca de tiempo es demasiado antigua.
  8. Almacena el hachís durante 20 segundos. Rechaza el mismo hash para iniciar sesión durante este intervalo.
  9. Autentica al usuario.

De esta manera, la contraseña está protegida y no se puede reproducir el mismo hash de autenticación.

Acerca de la seguridad del token de sesión. Eso es un poco más difícil. Pero es posible hacer que la reutilización de un token de sesión robado sea un poco más difícil.

  1. El servidor establece una cookie de sesión adicional que contiene una cadena aleatoria.
  2. El navegador devuelve esta cookie en la siguiente solicitud.
  3. El servidor verifica el valor de la cookie, si es diferente, destruye la sesión; de lo contrario, todo está bien.
  4. El servidor vuelve a configurar la cookie con un texto diferente.

Entonces, si el token de sesión fue robado y otra persona envía una solicitud, en la próxima solicitud del usuario original, la sesión se destruirá. Entonces, si el usuario navega activamente por el sitio, haciendo clic en los enlaces con frecuencia, el ladrón no llegará muy lejos con el token robado. Este esquema se puede fortalecer al requerir otra autenticación para las operaciones sensibles (como la eliminación de la cuenta).

EDITAR: tenga en cuenta que esto no evita los ataques MITM si el atacante configura su propia página con una clave pública diferente y envía solicitudes de proxy al servidor. Para protegerse contra esto, la clave pública debe estar anclada en el almacenamiento local del navegador o dentro de la aplicación para detectar este tipo de trucos.

Acerca de la implementación: RSA es probablemente el algoritmo más conocido, pero es bastante lento para claves largas. No sé qué tan rápida sería una implementación de PHP o Javascript. Pero probablemente haya algoritmos más rápidos.

Calmarius
fuente
En este caso, ¿la contraseña está realmente protegida? ¿No podría alguien olfatear lo que se envía y descifrarlo usando la clave pública y luego simplemente actualizar la marca de tiempo cuando lo use más tarde? ¿Me estoy perdiendo de algo?
michaellindahl
El cifrado asimétrico de @michaellindahl significa que solo la clave privada, que nunca sale del servidor, puede usarse para descifrar cosas. Las claves públicas solo se pueden utilizar para cifrar.
Dan Pantry
2
Una computadora entre el navegador y el servidor podría cambiar la clave pública en la página de inicio de sesión.
Antti
Gracias por compartir sus métodos, encontré todo muy interesante. ¡La marca de tiempo agregada al enviar credenciales es una buena captura! Una pregunta sobre el uso de una cookie de sesión adicional cuyo valor es una cadena aleatoria: ¿es como un token solo para la siguiente solicitud?
Victor
4

Usaría un sistema de intercambio de claves Diffie-Hellman del lado del servidor y del lado del cliente con AJAX o envíos de formularios múltiples (recomiendo el primero), aunque no veo ninguna buena implementación del mismo en Internet. Recuerde que MITM siempre puede dañar o modificar una biblioteca JS. El almacenamiento local se puede utilizar para ayudar a combatir esto, hasta cierto punto.

nanofaradio
fuente
4

Puede utilizar SRP para utilizar contraseñas seguras en un canal inseguro. La ventaja es que incluso si un atacante rastrea el tráfico o compromete el servidor, no puede usar las contraseñas en un servidor diferente. https://github.com/alax/jsrp es una biblioteca de JavaScript que admite contraseñas seguras a través de HTTP en el navegador o en el servidor (a través del nodo).

Brian Minton
fuente
1

HTTPS es tan poderoso porque usa criptografía asimétrica. Este tipo de criptografía no solo te permite crear un túnel encriptado, sino que puedes verificar que estás hablando con la persona adecuada y no con un hacker.

Aquí está el código fuente de Java que usa el cifrado asimétrico RSA (usado por PGP) para comunicarse: http://www.hushmail.com/services/downloads/

torre
fuente
0

puede usar ssl para su host, hay un proyecto gratuito para ssl como letsencrypt https://letsencrypt.org/

mk990
fuente
2
Marque la tercera oración en cuestión. (Además, siempre lea otras respuestas para asegurarse de no agregar un duplicado menos detallado)
Nathan Tuggy
0

Usar https suena como la mejor opción aquí (los certificados no son tan caros hoy en día). Sin embargo, si http es un requisito, puede usar alguna encripción: encriptarlo en el lado del servidor y describirlo en el navegador de los usuarios (envíe la clave por separado).

Lo hemos utilizado al implementar safevia.net : la inscripción se realiza en el lado de los clientes (remitente / receptor), por lo que los datos de los usuarios no están disponibles en la red ni en la capa del servidor.

parvuselephantus
fuente