[security] How to send password securely over HTTP?

If on a login screen user submits a form with his username and password, the password is sent in plain text (even with POST, correct me if I am wrong).

So the question is what is the right way to protect the user and his password against the third party who might be eavesdropping on the communication data?

I am aware that HTTPS is asolution to the problem, but is there any way to ensure at least some level of security using standard HTTP protocol (POST request)? (perhaps using javascript in some way)

EDIT I may have left out some important things.

What I was about was a page - that is PHP generated login page, which is of course sent to user in HTTP GET request as a HTML file. There is no (@Jeremy Powel) connection established between server and the client so I can't create such handshaking protocol. And I want the complete process to be transparent to the user - he wants to submit a password, not deal with cryptography.

Thanks.

This question is related to security http encryption passwords plaintext

The answer is


I would use a server-side and client-side Diffie-Hellman key exchange system with AJAX or multiple form submits(I recommend the former), although I don't see any good implementations thereof on the internet. Remember that a JS library can always be corrupted or changed by MITM. Local storage can be used to help combat this, to an extent.


you can use ssl for your host there is free project for ssl like letsencrypt https://letsencrypt.org/


If your webhost allows it, or you will need to deal with sensitive data, then use HTTPS, period. (It's often required by the law afaik).

Otherwise if you want to do something over HTTP. I would do something like this.

  1. The server embeds its public key into the login page.
  2. The client populates the login form and clicks submit.
  3. An AJAX request gets the current timestamp from the server.
  4. Client side script concatenates the credentials, the timestamp and a salt (hashed from analog data eg. mouse movements, key press events), encrypts it using the public key.
  5. Submits the resulting hash.
  6. Server decrypts the hash
  7. Checks if the timestamp is recent enough (allows a short 5-10 second window only). Rejects the login if the timestamp is too old.
  8. Stores the hash for 20 seconds. Rejects the same hash for login during this interval.
  9. Authenticates the user.

So this way the password is protected and the same authentication hash cannot be replayed.

About the security of the session token. That's a bit harder. But it's possible to make reusing a stolen session token a bit harder.

  1. The server sets an extra session cookie which contains a random string.
  2. The browser sends back this cookie on the next request.
  3. The server checks the value in the cookie, if it's different then it destroys the session, otherwise all is okay.
  4. The server sets the cookie again with different text.

So if the session token got stolen, and a request is sent up by someone else, then on the original user's next request the session will be destroyed. So if the user actively browsing the site, clicking on links often, then the thief won't go far with the stolen token. This scheme can be fortified by requiring another authentication for the sensitive operations (like account deletion).

EDIT: Please note this doesn't prevent MITM attacks if the attacker sets up their own page with a different public key and proxies requests to the server. To protect against this the public key must be pinned in the browser's local storage or within the app to detect these kind of tricks.

About the implementation: RSA is probably to most known algorithm, but it's quite slow for long keys. I don't know how fast a PHP or Javascript implementation of would be. But probably there are a faster algorithms.


Using https sounds best option here (certificates are not that expensive nowadays). However if http is a requirement, you may use some encription - encript it on server side and decript in users browser (send key separately).

We have used that while implementing safevia.net - encription is done on clients (sender/receiver) sides, so users data are not available on network nor server layer.


Secure authentication is a broad topic. In a nutshell, as @jeremy-powell mentioned, always favour sending credentials over HTTPS instead of HTTP. It will take away a lot of security related headaches.

TSL/SSL certificates are pretty cheap these days. In fact if you don't want to spend money at all there is a free letsencrypt.org - automated Certificate Authority.

You can go one step further and use caddyserver.com which calls letsencrypt in the background.

Now, once we got HTTPS out of the way...

You shouldn't send login and password via POST payload or GET parameters. Use an Authorization header (Basic access authentication scheme) instead, which is constructed as follows:

  • The username and password are combined into a string separated by a colon, e.g.: username:password
  • The resulting string is encoded using the RFC2045-MIME variant of Base64, except not limited to 76 char/line.
  • The authorization method and a space i.e. "Basic " is then put before the encoded string.

source: Wikipedia: Authorization header

It might seem a bit complicated, but it is not. There are plenty good libraries out there that will provide this functionality for you out of the box.

There are a few good reasons you should use an Authorization header

  1. It is a standard
  2. It is simple (after you learn how to use them)
  3. It will allow you to login at the URL level, like this: https://user:[email protected]/login (Chrome, for example will automatically convert it into Authorization header)

IMPORTANT:
As pointed out by @zaph in his comment below, sending sensitive info as GET query is not good idea as it will most likely end up in server logs.

enter image description here


HTTPS is so powerful because it uses asymmetric cryptography. This type of cryptography not only allows you to create an encrypted tunnel but you can verify that you are talking to the right person, and not a hacker.

Here is Java source code which uses the asymmetric cipher RSA (used by PGP) to communicate: http://www.hushmail.com/services/downloads/


You can use a challenge response scheme. Say the client and server both know a secret S. Then the server can be sure that the client knows the password (without giving it away) by:

  1. Server sends a random number, R, to client.
  2. Client sends H(R,S) back to the server (where H is a cryptographic hash function, like SHA-256)
  3. Server computes H(R,S) and compares it to the client's response. If they match, the server knows the client knows the password.

Edit:

There is an issue here with the freshness of R and the fact that HTTP is stateless. This can be handled by having the server create a secret, call it Q, that only the server knows. Then the protocol goes like this:

  1. Server generates random number R. It then sends to the client H(R,Q) (which cannot be forged by the client).
  2. Client sends R, H(R,Q), and computes H(R,S) and sends all of it back to the server (where H is a cryptographic hash function, like SHA-256)
  3. Server computes H(R,S) and compares it to the client's response. Then it takes R and computes (again) H(R,Q). If the client's version of H(R,Q) and H(R,S) match the server's re-computation, the server deems the client authenticated.

To note, since H(R,Q) cannot be forged by the client, H(R,Q) acts as a cookie (and could therefore be implemented actually as a cookie).

Another Edit:

The previous edit to the protocol is incorrect as anyone who has observed H(R,Q) seems to be able to replay it with the correct hash. The server has to remember which R's are no longer fresh. I'm CW'ing this answer so you guys can edit away at this and work out something good.


You can use SRP to use secure passwords over an insecure channel. The advantage is that even if an attacker sniffs the traffic, or compromises the server, they can't use the passwords on a different server. https://github.com/alax/jsrp is a javascript library that supports secure passwords over HTTP in the browser, or server side (via node).


Examples related to security

Monitoring the Full Disclosure mailinglist Two Page Login with Spring Security 3.2.x How to prevent a browser from storing passwords JWT authentication for ASP.NET Web API How to use a client certificate to authenticate and authorize in a Web API Disable-web-security in Chrome 48+ When you use 'badidea' or 'thisisunsafe' to bypass a Chrome certificate/HSTS error, does it only apply for the current site? How does Content Security Policy (CSP) work? How to prevent Screen Capture in Android Default SecurityProtocol in .NET 4.5

Examples related to http

Access blocked by CORS policy: Response to preflight request doesn't pass access control check Axios Delete request with body and headers? Read response headers from API response - Angular 5 + TypeScript Android 8: Cleartext HTTP traffic not permitted Angular 4 HttpClient Query Parameters Load json from local file with http.get() in angular 2 Angular 2: How to access an HTTP response body? What is HTTP "Host" header? Golang read request body Angular 2 - Checking for server errors from subscribe

Examples related to encryption

mcrypt is deprecated, what is the alternative? Remove 'b' character do in front of a string literal in Python 3 How to resolve the "EVP_DecryptFInal_ex: bad decrypt" during file decryption How to decrypt Hash Password in Laravel RSA encryption and decryption in Python How to fix Invalid AES key length? gpg decryption fails with no secret key error 7-Zip command to create and extract a password-protected ZIP file on Windows? How do I encrypt and decrypt a string in python? AES Encrypt and Decrypt

Examples related to passwords

Your password does not satisfy the current policy requirements Laravel Password & Password_Confirmation Validation Default password of mysql in ubuntu server 16.04 mcrypt is deprecated, what is the alternative? What is the default root pasword for MySQL 5.7 MySQL user DB does not have password columns - Installing MySQL on OSX Changing an AIX password via script? Hide password with "•••••••" in a textField How to create a laravel hashed password Enter export password to generate a P12 certificate

Examples related to plaintext

Converting HTML to plain text in PHP for e-mail How to send password securely over HTTP?