views:

162

answers:

6

INITIAL NOTE: This is just for a personal tinkering project; I'm not writing enterprise security here, and if I were, I'd know better than to try to write my own scheme. :-D

EDIT: To stress the above point, I tried to tag this under "iKnowThisWouldBeABadIdeaInRealLife", but SO wouldn't accept it because it was >25 chars. Just be aware that I KNOW it's not commercial grade!

I need a way to authenticate a user over HTTP (can't use HTTPS in this case). I need to know that the person on the other end really is who they say they are (to some reasonably high degree of confidence). Once I'm sure the user is legit, I do not care if the content between the client and the server are sent as plaintext.

The trouble I'm looking at is in trying to send a password from the client to the server without sending it as plaintext. I've thought about trying some public-key crypto in javascript, since some Google searching has turned up some fun-looking libraries.

Here's the scheme I'm thinking about:

(suppose A and A' represent the private and public keys, respectively; also, enc(text, key) and dec(cyphertext, key) represent the encryption/decryption functions)

    +------------------------+------------------------------+
    |         SERVER         |            CLIENT            |
    +------------------------+------------------------------+
(1) | t = randomToken()      |                              |
(2) | enc(t, A)           -------->  c                      |
(3) |                        |       A' = getKeyFromUser()  |
(4) | p                 <--------    p=dec(c, A')           |
(5) | if (t==p)              |                              |
    |     allowAccess()      |                              |
    | else                   |                              |
    |     denyAccess()       |                              |
    +------------------------+------------------------------+

One weakness I see in this is that the BAD GUY who was listening to the exchange, while he doesn't have A, now has a known ciphertext/plaintext combo, which I remember from crypto class is a BAD IDEA. I figure some salting could alleviate this somehow?

So here are my [two] questions:

  1. Is this a 'good' scheme? (remember that I don't CARE if anything following this initial exchange is plaintext - I'm ONLY trying to verify initial identity here)
  2. What would be the best way to get around the "known plaintext/cyphertext pair" weakness mentioned above? Salting?

Thanks!


EDIT: Thanks for all the discussion! Just to clarify:

  • I'm NOT worried about assuring the CLIENT that the SERVER is who they say they are. Only the opposite (assuring the SERVER the CLIENT is who they say they are)
  • I know about MITM attacks. This scheme is NOT intended to protect against them.
  • I know there exist plenty of solutions for this already. This is purely a learning exercise for me! I'm not trying to re-invent the wheel or build a better mousetrap. This is just for fun!
  • Here was my thought-process for the scheme: (I know I'm not quite using public vs. private keys properly, but bear with me for a sec)

    • Bob walks up to Alice and says, "Hey, I'm Bob."

    • Alice says, "Okay. I know Bob's 'private key'. If you're really Bob, take this secret message I just encrypted (with Bob's private key), and decrypt it for me."

    • Bob replies with the correct message, and Alice is happy.

+3  A: 

You could use HTTP Digest Authentication. That's already supported by most browsers, servers and various libraries.

EDIT:

If you really want to use some public key cryptography without SSL/TLS, you could have a look at HTTPsec

If you really want to invent your own authentication scheme, you should note that you got the encryption/decryption wrong with your labels (not sure if it's a typo, or maybe I'm not reading the diagram properly, it looks like you're doing enc(t,A) on the server side):

  • you encrypt using the public key
  • you decrypt using the private key
  • you sign using the private key
  • you verify the signature using the public key

In your scheme, you'd need the server to publish its public key and have the client encrypt its answer using that, to send it to the server, which would then be able to decrypt it with its private key. The difficulty then is to make sure that the public key is indeed that of the legitimate server and that it hasn't been intercepted and replaced by an attacker in the middle: that's where PKIs and certificates come into action with TLS (and HTTPS).

Bruno
HTTP Digest Authentication is very insecure without HTTPS.
Darron
Thanks, Bruno! I'm actually not interested in existing methods, I'm trying to tinker with public-key-enc in javascript (this isn't for any production code - just a learning exercise).Thanks again!
loneboat
@Darron : From Wikipedia: "Digest access authentication is intended as a security trade-off. It is intended to replace unencrypted HTTP basic access authentication, which is extremely weak. It is not, however, intended to replace strong authentication protocols, such as public-key or Kerberos authentication."Also, this isn't meant for production code. Thanks for chiming in!
loneboat
@Darron, HTTP Basic Authentication is very insecure without HTTPS. HTTP Digest Authentication isn't (it has limitations, but it's reasonably strong). The problem is that it doesn't protect you against an attacker changing the entity (or most headers) in the request, but @loneboat was specifically not worried about that.
Bruno
@Bruno: The reason I'm encrypting with A is that I'm asking the user for A' in step (4). IOW, the server is saying, "Hey, I encrypted a secret message that only I (the server) know. Can you (the client/user) tell me the original unencrypted message (by using the key symmetric to A [which is A'])?" I guess they're really both "private" keys, since bad-guys shouldn't ideally ever see either.
loneboat
You can't encrypt with a private key, you can only sign or decipher. You'd need the client to send its public key first, have the server encrypt using the client's private key and make the client send the deciphered result back. Even then, that doesn't solve the problem of knowing who really owns the key, unless the server already knows the public keys of the clients. This is possible indeed, and similar to knowing the password hash to some extent; that's also more or less what sites like GitHub do when you register your SSH public key to get write access to a repository.
Bruno
@Bruno Technically you can encrypt with the private key. The private and public keys are just a pair, they complement each other. If you encrypt with the one key, you decrypt it with the other.
Aillyn
@Bruno: I thought the labels "public" and "private" were somewhat superficial, and that there was no functional difference between the keys, other than that they are symmetric? From what I understood (and you have to understand, I have NEVER BEEN WRONG IN MY LIFE! Just kidding ;-), Either key can encrypt, and only the OTHER key can decrypt.
loneboat
@Bruno: In the area I operate in, HTTP Digest is considered insecure. I'll admit that not everyone has the same security requirements.
Darron
@Aillyn, in RSA, there's a quite strong asymmetry between public and private key content: the public exponent is usually much shorter than the private exponent (which is the number hard to get). 65537 is a typical public key exponent, the modulus and the private exponent are much longer. You can't just swap the role of the key just like that (otherwise the search space would be too easy to brute force).
Bruno
@Bruno I am not saying they can be used interchangeably, I am just saying they could be swapped ;)
Aillyn
Digest authentication *is* insecure. It protects the user password, but it doesn't protect the traffic itself, and provides no server authentication. It's horribly susceptible to man-in-the-middle attacks.
erickson
loneboat
Maybe, but I'm missing the point of swapping their roles. If you create a realistic RSA key pair, publish the private key (A) and keep the public key (A') private, you take the risk that the whoever knows the private key (A) will be able to guess the public exponent (and thus the key) within seconds, not even mentioning that some RSA private key storage formats (e.g. PEM) actually include both exponents anyway.
Bruno
@erickson and @Darron, you are right, I wouldn't use HTTP Digest because of the downgrade attacks and the lack of entity protection anyway, but considering the question, that sounds better than a custom solution involving swapping the roles of public and private keys ;-)
Bruno
@Bruno - I can agree with that!
erickson
+1  A: 

You basically want to implement SSL on HTTP; which is somewhat feasible, but will never be as good as the real thing.

Being able to send encrypted data back and forth is only half the problem. Another part of the problem is ensuring you are actually talking to the server (think of man in the middle attacks).

I am not really sure how the SSL works, but that is the way to go. You should read up on that. From the looks of your scheme, it seems totally pointless however. Why are you sending plaintext back to the server? That defeats the purpose of having this set up.

Here's how I would do this:

  1. Server sends a random token to the client. Server stores the token in a "pending" list. These can be purged in some interval (eg: every 15 minutes)
  2. Client sends enc(comb(username, password, token), pubKey) back to the server, where comb() is some function that combines the username, password and the random token.
  3. Server gets the username and password back using decomb(dec(message, privKey)) where decomb() is the inverse of comb().
  4. Server checks if the token is present in the pending list. If it's not, reject the login attempt. If it is, it server can proceed to perform authentication as usual.

If the generated tokens are never repeated, an attacker can't just re-send the same encrypted message, nor can they decrypt the message to find out what everything was.

Aillyn
Thanks, Aillyn! As mentioned in the question, this isn't for encrypting two-way content. I JUST want to verify identity of the client.Thanks!
loneboat
@loneboat You want the client to be able to authenticate without sending keys in plaintext, is that what it is?
Aillyn
@Aillyn: Exactly!
loneboat
@lone I update my answer
Aillyn
Thanks for the answer, looks like what I'm after!
loneboat
@Aillyn to answer your question this is the purpose of a PKI and why browsers come with a list of keys from trusted Certificate Authorities. I don't think you can implement a PKI in javascript...
Rook
This solution suffers from more or less the same problems as HTTP Digest, more specifically, attackers could replace the public key with their own, so similar MITM attack problems. As @Rook said, you'd need some form of PKI to verify the identity associated with the public key.
Bruno
+1  A: 

Assuming that your client and server exchange a public key before this exchange begins, you could do something like:

  1. Client sends HTTP request with a header containing both the user name and encrypt(private-key, user+URL)
  2. The server tries to decrypt the contents of the header using the public key for the specified user.
  3. If decryption fails, or the contained user or URL dont match, the request is rejected.

The URL is included to prevent replay attacks against a different resource. More security against replays can be obtained by including a timestamp in the encrypted data.

If you use a quality encryption scheme, known plaintext attacks are not a large concern.

EDIT: removed mention of AES128.

Darron
How are you going to use symmetric encryption in this scenario ? -1
Aillyn
Right. I mention public/private keys then talk about AES128. My mistake.
Darron
But the same scheme works if you securely pre-exchange a symmetric encryption key.
Darron
@Darron Yeah. Which is what SSL does.
Aillyn
A: 

Like Bruno said what you describe is along the lines of HTTP digest, which is pretty secure. But if you read up on it there are loop holes. Not big. Someone would really have to be listening to wait to pick up the keys in the handshake to be able to break in. If security is an issue you have to use https and make way for it. That is why its there. https has certificates to make the handshake resistant to eavesdroppers in the start.

Vass
A: 

First of all HTTPS does more than just encryption. It also prevents active MTIM attacksand protects your session id. The password is meaningless if you just spill the session id. This is part of OWASP A9.

However, someone has implemented a JavaScript solution. Which encrypts both the session id and password. It uses a Diffie-Hellman Key exchange, similar SSL. Even the authors say its not a good replacement for https, but better than nothing. I wouldn't even consider using anything other than https.

Rook
+2  A: 

If you use RSA with a proper padding mechanism, like OAEP, it's not vulnerable to known-plaintext attacks. So there's no need to get around that.

A stranger walks up and claims to be Bob. To prove it, they prove that they know a secret only the real Bob knows. In public key cryptography, the secret is the private key.

In your proposal, you first have to get Bob's public key, and make sure it's really Bob's, so that later you can use it to encrypt challenges. How do you bootstrap this process?

Now "Bob" responds to your challenge correctly, and tacks some instructions to his request. How do you know that the woman-in-the-middle, Eve, didn't chop off Bob's real instructions and substitute her own?

This is a bad scheme, and doesn't come close to providing security.

If you are going to pursue this at all, you should be looking at ways to digitally sign an request. For example, POST a digitally signed chunk of data. All of the HTTP-level information is discarded as untrustworthy, and only the content of the digitally-signed message is acted on.

erickson
+1 HTTPsec combined with a PKI could be a solution to achieve that.
Bruno
Thanks for the post, erickson! Note that in the question, I explicitly stated (more than once) that I'm not worried about MITM attacks or even sending everything in plaintext AFTER the auth. Here was my thought-process for the scheme: Bob walks up to Alice and says, "Hey, I'm Bob." Alice says, "Okay. I know Bob's 'private key' (I know I'm not quite using public vs. private keys properly, but bear with me for a sec). If you're really Bob, take this secret message I just encrypted (with Bob's private key), and decrypt it for me." Bob replies with the correct message, and Alice is happy.
loneboat
@loneboat - in that scenario, it would make sense for Alice to be satisfied. But suppose this exchange happens by mail, and Alice gets a response to her challenge in an envelope. One page has the decrypted token, and the other has some instructions. Is she happy now? This analogy fits a HTTP server-client scenario much more accurately.
erickson
@loneboat, you're still missing the point that if you swap the roles of public/private keys, a typical RSA 2048-bit pub key will have a 16-bit pub exp, which can be brute forced in virtually no time when the normally-private exp has been published, thus giving away the key-pair to the attacker and thus this pointless. What Alice knows (or should know) of Bob is his public key, and that's what she should use to encrypt.The main point is that you need to bind that key to the identity somehow, possibly via a PKI, web of trust or some pre-registration, out of bands. (@erickson is right.)
Bruno
@Bruno: I don't know why you keep bringing up RSA (or any specific implementation). The weakness you're noting is specific to the implementations you're talking about, while I've mentioned specifically before that I'm just talking about generalized PKE, wherein EITHER key could be any arbitrary size. I think you're misunderstanding my central question. Read the accepted answer by Aillyn, I think she summarized well what I was talking about (she even articulated it better than me, I think!) Thanks for your comments, though! :-)
loneboat
@loneboat, it's your question/your choice and I don't care about the points. Only taking RSA as an example. What I'm saying is, if you consider a scenario where the server knows the client's priv key, you've completely misunderstood pub key crypto. If you want to verify that a client has the private key, you make it sign something with its priv key, and you verify it against a pub key you trust to be bound to that ID. That's exactly what the CertificateVerify is about in the client-cert TLS handshake, but you also need to check that the pub key matches the ID (hence PKI or key known a priori).
Bruno