62

The very basic issue all developers face: Whenever user submits the form, the password is sent via network and it must be protected. The site I develop for doesn't have HTTPS. Neither does the owner want to buy a SSL certificate, nor is he interested in a self-signed one. So I want to protect the password sent via HTTP using Javascript when submitting form.

This other question DOES NOT give any sensible solution and I am in another situation.

If I use MD5, one can reverse that password string. What about nonce/HMAC? Any available Javascript library for that? Or do you have any suggestion/hint to tackle?

E_net4
  • 27,810
  • 13
  • 101
  • 139
Viet
  • 17,944
  • 33
  • 103
  • 135

11 Answers11

84

There is no way to send a password securely that the user can verify without SSL.

Sure, you can write some JavaScript that will make a password secure for over-the-wire transmission through hashing or public-key-encryption. But how can the user be sure that the JavaScript itself has not been tampered with by a man-in-the-middle before it reached them, to send the password to an attacker instead of the site, or even just compromise the security of the algorithm? The only way would be for them to be expert programmers and have them inspect every line of your page and script to ensure it was kosher before typing the password. That is not a realistic scenario.

If you want passwords to be safe from man-in-the-middle attacks, you must buy an SSL cert. There is no other way. Get used to it.

If I use MD5, one can reverse that password string.

No... not trivially at least. Whilst MD5 has attacks against it, it's a hashing algorithm and thus unreversable. You would have to brute-force it.

But again, a man-in-the-middle attacker doesn't need to look at your MD5s. He can simply sabotage the JavaScript you send the user to make the MD5s.

bobince
  • 528,062
  • 107
  • 651
  • 834
  • 1
    Excellent post. I tried to make a secure password input with JavaScript, and realized it wasn't possible for these exact reasons. I wish I could have read this first. – ojrac Jan 05 '10 at 00:30
  • 7
    <> You're forgetting that there are pre-calculated tables of MD5'd password hashes, which means that "Brute forcing" could well be fast and trivial. – EricLaw Jan 05 '10 at 03:24
  • 1
    EricLaw: You're referring to Rainbow tables? They only contain hashes of text up to a certain length. – Noon Silk Jan 05 '10 at 12:46
  • Anyway, before choosing a hashing function, everyone should check the latest details of the one they decide on: http://valerieaurora.org/hash.html, specifically, MD5, SHA0, SHA1, are all considered dead. Use only SHA-2 and up. – Noon Silk Jan 05 '10 at 13:06
  • "If you want passwords to be safe from man-in-the-middle attacks, you must buy an SSL cert. There is no other way." Nonsense. You just add a challenge for a second round of hashing. See http://groups.google.com/group/comp.lang.php/browse_thread/thread/c5960aa0afac2621/ffd98ae87112ccc5 C. – symcbean Jan 07 '10 at 14:37
  • 1
    As Jerry Stuckle says in that thread, it's still of no use. Adding more security on top makes no difference; the user would still have to read and verify that the client-side script sent to them was kosher before starting to type their credentials. That isn't going to happen. You need a secret of some sort to bootstrap a secure connection; SSL provides that in the form of shared CAs. Without it, you could be talking to any random active-MitM hacker pretending to be the target website. Password-hashing login schemes, although potentially useful for other reasons, can only defeat passive MitM. – bobince Jan 07 '10 at 16:07
24

The solution here is to not send the password at all. Use challenge/response.

In the original form include a large block of random text along with a key. Store the original random text in the session based on key on the server. When the client submits the form, use JS to hash the random text and password together. Then send the username, key, and hashed random text to the server. DO NOT send the password. On the server, use the key to lookup the original random text, perform the same hashing operation with the stored password. If the server-hashed value matches the client hashed value, then you know the client entered the right password without ever sending the password to the server.

Whether the password is right or not, expire the key and random text so each are one-time-use.

Samuel Neff
  • 73,278
  • 17
  • 138
  • 182
  • +1 Hi Sam, thanks. The concept you described is mentioned by "nonce" in my question. I'm not sure how to prove its reliability & security yet. – Viet Jan 05 '10 at 04:54
  • 1
    You will still have to send the password at least once to the server for storing in database. What will you do in that case? – Syed Ali Jun 13 '13 at 19:30
  • @sttaq, yes, if you're managing your own passwords then use you need to pass it when setting the password. We authenticate against LDAP so we don't actually have the original password stored. Good point though. – Samuel Neff Jun 14 '13 at 15:26
  • @sttaq What I'd do is have the server create the password and email it to the owner – lesolorzanov Jun 24 '14 at 16:00
  • 3
    @ZloySmiertniy, when you need to provide password reset via e-mail, it's better to send a one-time link that resets the password and not send the actual password itself. This prevents security issues of the password being transmitted via e-mail and being stored in e-mail later. – Samuel Neff Jun 24 '14 at 16:02
  • 2
    And if I'm the "man in the middle" I would just catch that random hashed string, and use that to authenticate, so it wouldn't matter what you did in the browser. And by doing the same hashing on the client as on the server, you've just shown me how you authenticate on the serverside, what algorithm you're using, how many passes etc. – adeneo Oct 14 '16 at 01:00
  • 1
    This is not sufficient to secure against a MITM that would modify the JS code returned by the server to capture the password field content (or via a cross site attack that would register a listener on the password input). To protect against this you need Subresource integrity to assert your javascript and CSP. – xryl669 Feb 13 '19 at 22:14
  • 4
    @xryl669 yes, very good points. This question and answer are 8 years old and today I would not recommend anyone run a site without https as now it's far more common and cheaper than it was 8 years ago. – Samuel Neff Feb 14 '19 at 20:23
12

If you REALLY want to deep-dive into this, look at the Diffie-Hellman key exchange which was created to "allow two parties that have no prior knowledge of each other to jointly establish a shared secret key over an insecure communications channel"

I'm not a cryptography expert though, so I don't fully know if it's really secure if an attacker has both the Client (JavaScript source code) and the transport mechanism (Packet sniffer)

Michael Stum
  • 177,530
  • 117
  • 400
  • 535
5

You can use a javascript RSA implementation to encrypt the password before sending. (Here is an example of RSA In Javascript.)

But I believe both this one and using a hash function will be vulnerable to replay attacks. So, be careful.

Szere Dyeri
  • 14,916
  • 11
  • 39
  • 42
4

Unfortunately there will be no way to ensure security of a non-encrypted request. Anyone with access to your javascript will simply be able to reverse engineer it/tamper with it and anyone with a packet sniffer will be able to watch the unencrypted traffic. These two facts together mean:

No SSL? No security.

rfunduk
  • 30,053
  • 5
  • 59
  • 54
  • I would also argue that SSL is as bad as no security as you can purchase a CA signed certificate for a domain that you don't own. Albeit it is becoming more rare, but I'm sure I can find a CA that will take my money and sign a certificate. With that - how do you know the chase.com you go to is actually chase's site and not an attackers? They both can have valid signed certificates. – Natalie Adams Aug 25 '10 at 15:45
  • The big ones wont do this and lots of the others are poorly supported in the browsers. The situation isn't perfect but I wouldn't call it 'as bad' as no security. – rfunduk Aug 26 '10 at 11:48
  • That's wrong. SSL is just a protocol, there is no security by SSL per se. The security comes from Diffie Helman/Key derivation and you can implement it in Javascript as well. You need at least 3 step (2 exchanges) between client and server to ensure that Javascript code is correct (just like SSL). Everything is checked server side. – xryl669 Feb 13 '19 at 21:52
1

Any transmission that you have will be in the clear; that is, without SSL your critical information will be exposed. It is worth discussing that point with the site Owner. In other words, it's best to take necessary measure to fortify your data transmission, and SSL is one the basic, cheap steps you can take.

David Robbins
  • 9,996
  • 7
  • 51
  • 82
1

i don't think the issue here is the technology, but how you explain the importance of SSL. Provide them with reliable reading materials, i'm sure there are plenty over the web.

Martin Ongtangco
  • 22,657
  • 16
  • 58
  • 84
1

The solution requires the client to be able to encrypt the password using a secret encryption key known only to the client and the server.

SSL accomplishes this by requiring both the server and the client web browser to have their own asymmetric public/private keypair, which they use to encrypt and transmit a random session key between them. The rest of the conversation then uses that secure session key.

So you're asking how to solve the same problem as SSL without the benefit of having a secret key that is known only to the client and server. I'm no expert, but it looks like this can't be done, or at least not easily.

David R Tribble
  • 11,918
  • 5
  • 42
  • 52
  • No it doesn't. Only one only end needs a certificate. The client does not generate the session key; does not encrypt it; and does not send it. It is negotiated via a key agreement protocol. – user207421 Mar 19 '17 at 23:26
  • @EJP - Yes, the server creates and encrypts the session key. But how is the key agreement protocol performed without the client having its own (asymmetric) encryption key? – David R Tribble Mar 20 '17 at 17:58
  • The principle is that: the server sends its identity and a pseudo random value (premaster key) that's derived from a secret he knows. Once the client validate the server's identity (in the certificate), it generate a random keypair. With the private part, it uses the premasterkey and transform it and send it back. Then they both derive a common secret key without none of them revealing their private key, thus a MITM could not find out the session key. – xryl669 Feb 13 '19 at 21:45
  • @xryl669 - Right, so both the server and the client both have their own public/private keypairs, which they use to derive a session key known only to them. OP was asking how to do this without keypairs, which as far as I can tell, cannot be done securely. – David R Tribble Feb 20 '19 at 17:41
  • What's important is that the client generate a new keypair for each session (so the client does not need one beforehand). The server however is always using the same keypair. SSL just adds a chain of signature to ensure the server is the right one from some known "gold" roots. – xryl669 Feb 21 '19 at 09:34
  • So, to answer the question, without the chain of trust, one solution is to use SSH like system, that is, once the client has validated the server, it stores in a permanent cache (for example localStorage) the server's public key and its IP address (for example). Upon further connection, any mismatch should stop the client from processing further. – xryl669 Feb 21 '19 at 09:36
1

If you don't have access to SSL, MD5 should be adequate to prevent accidental discovery of passwords (such as in a network log file or something). Anything else would be a waste of time. Just make sure the app doesn't give access to sensitive information (ie, credit card numbers, medical history, etc).

Like others commenters have suggested, a serious attacker will be able to break any type of security on the page. Even SSL is a small barrier since most users use easy-to-guess passwords, re-use the same passwords everywhere, will give their password to anybody that asks, or can be tricked into giving up their password by a copied page or "tech support" phone call.

Brian
  • 37,399
  • 24
  • 94
  • 109
0

-- English -- i think in something, but i don't know if it could be really secure. If you can put your form in a php file, then you can create an algoritm for create a string based in time or in something else, and then put this string in your html.

When the user type a password in a password input field, when you debug it you canot see the value typed by user, so before send the information via post or get, you can use the password user as a hint to encrypt the encrypted string previosly generated, and then, just sent it insted of the password typed by user.

In this way, the attackers dont have all inside the js code, so they will need discover the algoritm that you create to decrypt it.

This is just an idea, so if you can tell me how this can not be safe, I would appreciate it.

-- Spanish -- Se me acaba de ocurrir algo que puede servir, pero no se si realmente sea algo seguro. Por medio de php puedes generar un algoritmo que cree un string en base al timestamp o algo más, y después colocar esta cadena en el html.

Note que cuando alguien escribe una contraseña en un campo input tipo password, con un debug no se puede ver el valor que tecleo el usuario (no se si exista manera pero no quise investigar más), asi que podemos utilizar la contraseña que el usuario escribió como palabra clave para encriptar la cadena de texto que previamente habiamos generado con php, por medio de un algoritmo en JS. Sería algo así como encriptar lo encriptado. Posteriormente lo que estariamos enviado no sería la contraseña tecleada, si no esta última cadena resultante.

Buscando un contra, lo único que se me ocurra es que el atacante tendrá que dedicarle mucho tiempo para tratar de encontrar el agoritmo que creamos por medio de php y poder decriptar la cadena final, o tendrá que hackear el servidor para acceder al php y obtener el algoritmo.

Esto es solo una idea, por lo que si pueden decirme como esto puede no ser seguro, se los agradecería.

0

As mentioned, none of this is secure against server spoofing, as that requires an ability to trust the client-side Javascript. But if we're sure that the server can't be spoofed (signed cert, hash signing immune to length-extension, etc.) but not that the connection is immune to eavesdroppers, here's how I'd implement it.

I think the most secure way is, instead of storing H(password), where H is your hash function of choice, store g^H(password) i.e. use the password as the private key for Diffie-Hellman key exchange. (You should also probably use a random g for different users, too--it becomes your salt.) Then to verify, you generate a nonce b, send the user g^b, and compute (g^H(password))^b. The user does not need to know g--they need only compute (g^b)^H(password) = (g^H(password))^b. Now you have a number that both parties know iff the user entered the right password, and constructing a challenge-response zero-knowledge proof based on knowing the correct number is trivial, while the random number used as the server's "private key" makes the approach immune to replay attacks.

NXTangl
  • 89
  • 3
  • Whatever the client can do, a MITM can do as well. You speak of a "signed cert", what's the difference to an installed SSL certificate? And what do you mean with "server can't be spoofed", does it mean the communication cannot be altered, only listened to? – martinstoeckli Dec 09 '15 at 10:34
  • What I meant was, basically, this solution is useless unless the server's identity is already validated to the client, which usually implies SSL certificates and end-to-end encryption in today's world. Yes, I meant a situation where eavesdropping is possible but impersonating the server or altering its output undetectably is not, e.g. the server's public key is known (validated by trusted sources) and it signs all its messages with a SHA3 hash encrypted with its private key. – NXTangl Dec 09 '15 at 21:47
  • Am I understanding right that you then mean that as long as the content is delivered securely this mechanism would be *somewhat* secure for a non-ssl api layer? Not that I'm advocating, just curious. I'm actually wanting to implement something in our internal stack like this. We have a single authentication server that we want all of our apps to share. Our individual apps don't need to know about the password, so this challenge response concept might answer that need, though we'd still serve everything SSL. – lassombra Feb 11 '17 at 16:12
  • For the record, I mean that the a malicious server could just serve deliberately unsecure Javascript that transmits the message in plain text. – NXTangl May 01 '18 at 04:04