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

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ígeme si me equivoco).

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

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

EDITAR Puede que haya omitido algunas cosas importantes.

De lo que se trataba era de una página, es decir, de la página de inicio de sesión generada por PHP, que por supuesto se envía al usuario en la 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 protocolo de enlace. Y quiero que el proceso completo sea transparente para el usuario; desea enviar una contraseña, no tratar con la criptografía.

Gracias.

Usar HTTP con SSL hará que su vida sea mucho más fácil y podrá descansar tranquilo. Las personas muy inteligentes (más inteligentes que yo al menos) han analizado este método de comunicación confidencial durante años.

La autenticación segura es un tema amplio. En pocas palabras, como mencionó @ jeremy-powell, siempre favorece 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 quiere gastar dinero, hay una autorización de autorización gratuita de letsencrypt.org .

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

Ahora, una vez que tenemos HTTPS fuera del camino …

No debe enviar el inicio de sesión y la contraseña a través de la carga 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. Existen muchas bibliotecas buenas que le brindarán esta funcionalidad de manera inmediata.

Hay algunas buenas razones por las que debe usar un encabezado de Autorización

  1. Es un estándar
  2. Es simple (después de aprender cómo usarlos)
  3. Te permitirá iniciar sesión en el nivel de URL, así: https://user:password@your.domain.com/login (Chrome, por ejemplo, lo convertirá automáticamente en encabezado de Authorization )

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 es muy probable que termine en los registros del servidor.

enter image description here

Puedes usar un esquema de respuesta al desafío. Supongamos que tanto el cliente como el servidor conocen un secreto S. Luego, el servidor puede estar seguro de que el cliente conoce la contraseña (sin regalarla) mediante:

  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 frescura de R y el hecho de que HTTP es sin estado. Esto se puede manejar haciendo que el servidor cree un secreto, llámalo Q, que solo el servidor conoce . Entonces el protocolo es así:

  1. El servidor genera un número aleatorio R. Luego envía al cliente H (R, Q) (que no puede ser falsificado por el cliente).
  2. El cliente envía R, H (R, Q), y calcula H (R, S) y lo envía 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 se autenticó.

A tener en cuenta, dado que H (R, Q) no puede ser forjado por el cliente, 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 frescas. Estoy buscando esta respuesta para que puedan editar todo esto y encontrar algo bueno.

Si su servidor web lo permite, o tendrá que tratar con datos confidenciales, entonces use HTTPS, punto. (A menudo es requerido por la ley afaik).

De lo contrario, si quieres hacer algo por HTTP. Haría algo así.

  1. El servidor inserta su clave pública en la página de inicio de sesión.
  2. El cliente rellena 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), lo cifra con la clave pública.
  5. Presenta 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-10 segundos). Rechaza el inicio de sesión si la marca de tiempo es demasiado antigua.
  8. Almacena el hash por 20 segundos. Rechaza el mismo hash para iniciar sesión durante este intervalo.
  9. Autentica al usuario

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

Acerca de la seguridad del token de sesión. Eso es un poco mas dificil Pero es posible hacer que reutilizar 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 próxima solicitud.
  3. El servidor verifica el valor en la cookie, si es diferente, destruye la sesión, de lo contrario todo está bien.
  4. El servidor vuelve a establecer la cookie con texto diferente.

Por lo tanto, si el token de sesión fue robado y otra persona lo envía, la sesión se destruirá con la siguiente solicitud del usuario original. Entonces, si el usuario navega activamente por el sitio, haciendo clic en los enlaces a menudo, entonces el ladrón no irá muy lejos con el token robado. Este esquema puede fortalecerse al requerir otra autenticación para las operaciones confidenciales (como la eliminación de la cuenta).

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ápido sería una implementación de PHP o Javascript. Pero probablemente haya algoritmos más rápidos.

Usaría un sistema de intercambio de claves Diffie-Hellman del lado del servidor y del lado del cliente con AJAX o presentaciones de formularios múltiples (recomiendo el primero), aunque no veo ninguna buena implementación de los mismos en Internet. Recuerde que una biblioteca JS siempre puede estar corrupta o modificada por MITM. El almacenamiento local se puede usar para ayudar a combatir esto, hasta cierto punto.

Puede usar SRP para usar contraseñas seguras en un canal inseguro. La ventaja es que incluso si un atacante olfatea 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 lado del servidor (a través del nodo).

HTTPS es tan poderoso porque usa criptografía asimétrica. Este tipo de criptografía no solo le permite crear un túnel encriptado, sino que también puede verificar que está hablando con la persona adecuada y no con un pirata informático.

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

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