1

I have been interested in implementing authentication method for a web app that is run in modern browsers, without having to send the password over the wire.

Can anyone suggest improvements or even tell me whether or not I am going wrong.

The authentication is done over an unsecured HTTP connection using AJAX.

My current understanding of an authentication protocol implementation

Server has a users password that was hashed using bcrypt and stored during registration.

User enters id and password. A XMLHttpRequest is sent to the server to begin authentication, X-R1 is a random integer created on client.

GET /auth/{id} HTTP/1.1
X-R1: 123412341234
  1. Server gets values.

    • ServerPassword - users bycrpt hashed password from password database

    • X-Salt - extracts the salt from the ServerPassword

    • X-R1 - from client in the request header

    • X-R2 - randomly generated integer on server.

  2. Server creates values

    • X-Session - session key, concatenating X-R1 + X-R2 + id it then one way hashes this string

    • Verifier - shared secret, concatenating X-R1 + X-R2 + ServerPassword and then one way hashing this string.

    • Saves these values to a session store.

Server response.

HTTP/1.1 202 ACCEPTED
X-R2: 432143214321
X-Salt: $2a$10$rBV2JDeWW3.vKyeQcM8fFO
X-Session: 92429d82a41e930486c6de5ebda9602d55c39986
  1. Client gets values

    • X-R1 - same value from before sending the request.

    • ClientPassword - plain text, what user entered.

    • X-R2 - from server sent header

    • X-Salt - from server sent header.

  2. Client creates values

    • HashedPassword - created using a JavaScript implementation of bcrypt by using the ClientPassword and X-Salt

    • Verifier - Concatenating X-R1 + X-R2 + HashedPassword and then one way hashing this value. Client uses a JavaScript implementation of bcrypt to hash the password that the user entered with X-Salt's string.

Now the client and the server both think they have the same verifier so to confirm this client sends a further request but this time hashing the request headers and body with the verifier and then adding 'X-Auth' to the headers with the resulting hash.

POST /auth/{id} HTTP/1.1
X-Session: 92429d82a41e930486c6de5ebda9602d55c39986
X-Auth: ad801198a9fab4e4ef79eb97624a4bf9c78b450a

Server now looks up the X-Sessions's in it's session store. Does the same hashing of the request headers and body and then checks to see if the hash matches X-Auth's value, if so the server assumes the client is authorised and responds accordingly.

HTTP/1.1 200 OK

Client knows server has the same Verifier and stores it in localStorage using X-Session's value for the key, for further requests.

Concerns

I am an average developer and no crypto expert and from my understanding I can not see any issues that might occur with this method.

I can see the great advantages, such as:

  1. The password is never sent over the wire. Prevent MITM from getting your password.

  2. MITM can't modify request as it is hashed with the verifier and then sent.

  3. The server doesn't need to use bcrypt every time a user wants to authenticate.

1
  • This question seems more appropriate for IT Security.Stack Exchange. P.S. The standard answer is to just use SSL and send username + password, and then use session cookies. That typically meets most reasonable requirements in practice. To do better than that, you'll probably want something like SRP or password-authenticated key exchange. Make sure to read up on those topics; it'll help you ask a more informed question.
    – D.W.
    Oct 2, 2013 at 19:29

3 Answers 3

6

The authentication is done over an unsecured http connection using AJAX.

I stop you right here. If the Javascript code running on the client side went over unsecured HTTP, then that code could have been altered in transit by any half-competent Man-in-the-Middle attacker. It can then do a lot of evil things, like, for instance, sending a copy of the user's password to some attacker-controlled server.

Client-side cryptography in Javascript works only if the said Javascript came over a secure link, i.e. HTTPS. And if you have that link, then why would you bother any further ?


Apart from that, what you describe has nothing "SRPish" whatsoever. SRP is a Password Authenticated Key Exchange protocol, which means that it grants immunity against offline dictionary attack, something which cannot be achieved with symmetric cryptography tools like hash functions and bcrypt. At best, bcrypt will grant resistance against offline dictionary attacks, meaning that an eavesdropper, spying on a connection, may learn enough to "test passwords at home" but will suffer for each test.

SRP does "better" through some extra mathematics. See the RFC. There are modular exponentiations in there.

SRP-with-TLS makes sense only because it works at the SSL level directly, is done by the browser, and uses an implementation which is already on the client side. When the browser does not implement SRP at the SSL level, then you are back to the "usual" setup with normal SSL (with a server certificate), and sending the user password "as is" through the SSL.

3
  • What if the ajax requests were unsecured but the delivery of the JavaScript files on initial page load was HTTPS?
    – jamcoope
    Oct 2, 2013 at 15:11
  • Then this may work. But what you describe is still not at all SRP-ish; and you will suffer because Javascript is very feeble when it comes to do cryptography. Oct 2, 2013 at 15:19
  • @ThomasPornin - I have removed references to SRP (after looking through RFC). Basically I am trying to create a secure way to authenticate users to an API that is accessed by browsers and potentially mobile apps.
    – jamcoope
    Oct 2, 2013 at 18:55
2

You are sending password-hash on the wire to the server. If your database, which stores the hash and the salt, gets hacked, hacker will submit just the required value using his/her own JavaScript and will fool the server.


So if you are doing this on https, sending password to the server and doing hashing there is better approach. Since SSL/TLS is secure to guarantee that only server will be able to see the password and everyone must trust the server for doing its bit properly (i.e. generate salted hash from the password and compare it with db value). And if this is your approach, I am sure you don't need to reinvent the wheel. Whatever library you are using, this implementation will be there.

1

If you send a hashed version of the password to authenticate then the hash becomes the password. I've seen this before, and it doesn't work.

  • Hashing isn't meant to prevent a Man-in-the-middle attack, it's used to prevent a stolen database from becoming a list of passwords.
  • It seems like you are leaking the server-side salt, which means that passing the hashes in your database is easier
  • A man-in-the-middle could modify the server's response, setting both X-Salt and X-R2 to the empty string. If they previously intercepted X-R1, they only need to bruteforce the user's password to pass the hash (the hashcode the client sends to the server after the forged server response). Then you're back to square one.

You are trying to implement your own encryption scheme, and that's great for learning how this stuff works. Don't do it in production. Don't. Hundreds of people spent years making SSL/TLS. Use it.

You want something like this instead of X-R1 and X-R2:

http://en.wikipedia.org/wiki/Diffie%E2%80%93Hellman_key_exchange

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .