Information Security Stack Exchange is a question and answer site for information security professionals. It's 100% free, no registration required.

Sign up
Here's how it works:
  1. Anybody can ask a question
  2. Anybody can answer
  3. The best answers are voted up and rise to the top

I want to authenticate a client of a webpage and make sure it is a particular user.

Let say that we already agreed on a particular hash (SHA256, actually, which I think is secure enough for this) and we both already exchanged a key in a file through a trusted medium.

This key is pretty much a text file with 2K of random letters. I have it stored in my server, they have it stored somewhere in their computer.

These are the steps devised to authenticate the client:

  1. The server generates a challenge, a random string of 128 letters.
  2. The server sends the challenge to the client's browser.
  3. The client has access to an input field where they load their key file.
  4. The client computes an answer by appending key to their challenge contents and computing its hash.
  5. The client sends back only the answer for this particular challenge.
  6. The server compares the received answer with one generated locally and if they match then I can assume that the client is the person who I think it is.

Final assumption, the exchange of both the challenge and the answer through the web may happen through an insecure channel. Actually, assume that it WILL happen through an insecure channel, i.e. HTTP.

Is this scheme secure?

If not, what are its pitfalls?

In step 4, does it make a difference if I compute the hash as challenge + key instead of key + challenge. I guess this has to do with the hash algorithm I chose, and I think SHA256 handles those things well, but I'm no expert.

Does it matters much if I increase/decrease the length of the key and challenge ?

share|improve this question
17  
Just a small question: why reinventing wheel if HTTPS (TLS) client auth. with certificate already exists? – user996142 Mar 1 at 14:49
    
We can't just deploy HTTPS there, we need to make it work through HTTP. – almosnow Mar 1 at 14:52
3  
Unless you're planning on repeating this for every http request, the session will be vulnerable to hijacking. How do you propose to deploy the key to the client? – symcbean Mar 1 at 15:03
19  
No cryptography using client-side JavaScript can be secure without HTTPS. Any MITM attacker can send JavaScript that can do anything with the secrets the browser has access to, then there will be no secret. – billc.cn Mar 1 at 15:04
    
Comments are not for extended discussion; this conversation has been moved to chat. – Rory Alsop Mar 2 at 16:15
up vote -10 down vote accepted

TL;DR

While technically there are threat models where your method provides an advantage over straight http, I'd still suggest using https. See conclusion for more details.

Security Problems Related to MitM Attacks

As everyone else has already stated, this cannot protect against MitM. This is the standard thread model used and nothing done in the browser can solve it assuming you're using the internet to receive your code. If, as you say you're going to use a USB drive to distribute the key and JavaScript (and presumably some HTML) then yes, you can bypass the MitM problem for the authentication sequence (we'll assume you can manage to safely distribute these USB sticks).

It is worth mentioning that beyond the problems of MitM for the authentication bit, there are several other problems with JavaScript. Just a quick for instance, say you used AJAX to load the pages. A MitM would be able to change what you get back and unless you're very careful you could very well fall victim to a cross-site scripting attack where the attacker can then capture your key data. Basically the problem with JavaScript is that it is very hard to "seal" the execution context. With most programs you write them and it's reasonably easy to say this is the program and nothing else can be added after the fact (don't call methods like eval, don't load arbitrary libraries and as far as the language is concerned you're safe). I'm ignoring issues not stemming directly from language features, like buffer overflows, because the point I'm making is that JavaScript is so automated that you can very easily run code from an arbitrary outside source without meaning to, all with the support of the language.

Slight Redemption in Lieu of Reduced Thread Model

What I would like to add is that this does protect against eavesdropping (which is a bit more realistic attack then full MitM). You still won't be able to protect the information that goes back and forth, but at least they won't have access to key. All I'm trying to say here is that there is a significant superset of MitM attacks that include packet capture but not necessarily control of the network stream. Say for instance I take a packet capture for some network diagnostic reason and then save this somewhere insecure. Your method does technically protect the keys from being stolen in such a case.

Clarification of How to Provide Any Sort of Useful Authentication

A further note, you didn't explicitly state it so I thought I'd clarify it. In order to prevent the eavesdropper from being able to use answer to access anything other than what the original client is asking for you need the following process steps:

  • Client makes request
  • Server makes challenge (as you stated)
  • Server associates challenge with whatever it was the client was asking for

That means that the answer will only be valid for the original request (can't be reused). If you're going to use this as a general login scheme where you then provide a session cookie to the client you should know that even an eavesdropper (without MitM capabilities) would be able to impersonate the computer that's logged in until such time as the user either decide to log out or the session expires.

Answer to Question Relating to Key Size

As for how long your challenge/key should be. The chunk size of the SHA-2 hashes (of which SHA-256 is a member) is 512-bits. So if you made the challenge and key 512 random bits each (so 64 completely random ASCII characters) you would be fine.

Final Conclusion

Just to clarify, I agree with what everyone else has said. I don't see the value in doing things this way. You would have to explain more why you have a problem with using standard https (TLS/SSL) to handle your problem. Every modern browser can handle it and it handles everything from what you want (authentication) to many other things that people generally want (like confidentiality). There seems to be some feeling that setting up https is complicated (which I don't agree with). However, it is most certainly easier then trying to write your own JavaScript version complete with USB distributed keys and the like.

share|improve this answer
18  
Hold on - you're going to give people a webpage + key on a USB stick, but you still want to use JavaScript encryption? At that point, why not just bundle some actual encryption tool (e.g. a wrapper around OpenPGP?) I don't see what the browser adds in terms of security here. (And if your requests from the client to the server are still going over plain HTTP, you still have problems.) – nneonneo Mar 2 at 7:15
10  
@almosnow You know what would be far, far more simple and secure? Having your webpage run on HTTPS and not trying to make a custom security implementation. The web browser and server would authenticate and secure the connection automatically, meaning no extra hassle to the users. It's arguably more hassle for them to run the webpage from a local file instead of an address. – JamEngulfer Mar 2 at 13:15
16  
Also, take note that you looked through all of the answers telling you the best solution and picked the single one that agrees with you (and is downvoted for some reason). – JamEngulfer Mar 2 at 13:18
2  
Then why even bother? The thing is, with HTTPS, you don't need to worry about the secure bit, it just covers the bit you need that verifies that it's the correct client. It also has the advantage of being developed and tested over years instead of being put together by someone in a few weeks. – JamEngulfer Mar 2 at 16:31
4  
Why this downvoting? I mean he is adressing the question. His advise isn't the best, but it is at least not claiming that this is the way to go. so why this amount of downvotes? – Zaibis Mar 3 at 9:32

No cryptography using client-side JavaScript can be secure without HTTPS. Any MITM attacker can send JavaScript that can do anything with the secrets the browser has access to, then there will be no secret.

If you absolutely cannot use HTTPS, the user must have to a tool to compute the response outside the browser and paste the result into the browser. Even so, any data transmitted after the authentication is still subject to interception and modification, which makes the authentication pretty useless from a user-protection perspective.

Please read:

What's wrong with in-browser cryptography? - Tony Arcieri or Javascript Cryptography Considered Harmful - NCC Group or the 1,030,000 results returned for searching "wrong with javascript cryptography" on Google

Edit:

BTW, even if you use an external program to handle the authentication and maybe even encryption of data that goes over HTTP, you may still have a lowered security compared to just using HTTPS. Best example is the Korean SEED cipher which exposes users by locking users down to and training them to trust ActiveX controls and IE. See this blog article).

share|improve this answer
    
Yep, haven't considered the MITM actually changing the page's code. That pretty much brings down everything, thanks. Btw, just for the sake of the scheme, if the user computes the response outside the browser, as you say, would this be then, secure? – almosnow Mar 1 at 15:31
1  
You'd still suffer from a MitM intercepting the initial request, which results in the end user being prevented from accessing the content, while the MitM can use the generated token. In this case, the answer becomes a password equivalent - only for a single request, true, but if the data is valuable enough, that might be enough. – Matthew Mar 1 at 15:36
    
That's good enough, I just want to know that they exist on the other side, not more. – almosnow Mar 1 at 15:59

If you have a MitM style attack, it's useless - an attacker sitting between your client and your server can forward on the challenge to the client, wait for them to complete it, then send that back to the server, whilst sending a rejection message to the client (and, since you're getting them to paste in the key, they can steal that too - if the "key input" area is part of the submission form, it'll be included in the POST request made from the client to the server. This depends on the specific implementation, but is a common enough mistake to make).

Similarly, if your client side code can be modified in any way (it can: you're using HTTP), it would be trivial to add code to send the key on to a third party, for later use. This wouldn't be obvious to the end user.

If your server gets compromised at any point, all your key files become worthless - you can't tell whether the original owner or the attacker is trying to connect.

HTTPS client authentication tries to avoid these issues. Firstly, it's hard to MitM the connection, since it's encrypted with a server key - you'd need to compromise the server to be able to MitM (without an implementation flaw). Secondly, the server stores public keys for the clients. Even if they are stolen, you can still use them to verify clients, since you can't regenerate the client's private keys from them. They're public. It doesn't matter who has them. Finally, it's well tested, and has been found pretty secure over time. This is a key point when using any form of security - it's the way methods are found to be secure or not.

Just use HTTPS - yes, it might be painful to implement, but it is designed for this!

share|improve this answer
    
Technically HTTPS does not necessarily protect against a server compromise if PFS is not implemented. Probably fine if the connection is only used for authenticaiton and transmits no data afterwards though... – billc.cn Mar 1 at 15:22
1  
@Matthew One small critique: if your server has been compromised to the point that the client public keys have been stolen it's probably not a good idea to keep using them for authenticating clients. If the basic file access controls wouldn't have been enough to protect the symmetric keys (what you're comparing to I presume) then they likely wouldn't be enough to prevent changing the client certs. Technically there are certain access points like the backup server that might have true read-only access, but if you've been hacked you probably don't know how bad. – CrazyCasta Mar 1 at 22:32
1  
@crazycasta Actually, in this case it doesn't matter - if the public keys are changed, when using peer certificate auth, the private keys held by clients won't authenticate against them - they're pki, rather than symmetric, unlike in the OP's scheme – Matthew Mar 1 at 22:36
2  
@almosnow in the original description "The client has access to an input field where they load their key file. The client computes an answer by appending key to their challenge contents and computing its hash.", when run in browser over http, means that the server sends to the client some javascript that should do that. Instead, the attacker can send javascript code that will not only compute this hash, but also sends the whole key to the attacker. – Peteris Mar 2 at 9:10
2  
"If the key input area is part of the submission form" - even if it isn't in the form the OP serves to the attacker, it will be in the form the attacker serves to the client! – Martin Bonner Mar 2 at 16:04

Insecure is insecure. If the exchange goes over some unprotected HTTP or something similar, then an active attacker can simply observe the initial exchange, let the client perform the authentication step, and hijack the connection from that point, replacing the client requests with his own. The Man-in-the-Middle, by definition, acts right away, not by recording elements and reusing them later.

In conceptual words, with your authentication method, you can somehow make sure that the expected client was involved at some point, but this does not extend to the application data you then receive. The data is not authenticated.

If you want to fix that, then you need to cover each data element sent by the client with an authentication method that the server can verify, e.g. a MAC. This will complicate your protocol, and at that point you'd better yield to the inevitable and use SSL/TLS, because that's really what you need.

(An open question is how to use a stream-oriented protocol like SSL/TLS over a transport medium which is not a stream; this is typical of situations where only plain HTTP is usable, and the HTTP proxy is unwilling to support the CONNECT method. The best that can be offered right now is DTLS, where datagrams can be conceptually conveyed as HTTP requests and responses. But it would require a strict discipline for the client and server to always know whose turn it is to talk, and it would feel definitely clunky.)


Apart from that fundamental issue with the scope of authentication, a few extra remarks:

  • The user key files, as stored on the server, are a juicy target for attackers. A lost backup tape or a SQL injection attack may allow the attacker to read these files, at which point he can impersonate users at will. This is considered bad when the "key files" are in fact user passwords; we talk of "plaintext passwords".

  • Key files are not necessarily much secret. If the user produces his key file himself, then a depressing number of users will simply use a picture they downloaded off the Internet. The attacker can download the same file...

  • If the client code is in Javascript, freshly obtained from the Web server, then you need HTTPS anyway, because otherwise the attacker could simply modify it when the user's browser downloads it.

share|improve this answer

Why ask if it's secure in the first place if you're just going to shoot down anyone telling you how to secure it?

HTTPS doesn't require the client to do anything. You're acting as if they need to install some special software or something, but the browser handles all of that for you. If you just implemented HTTPS server side, your entire operation becomes secure and you don't need your custom authentication system (which could have security holes in the actual implementation/code).

The reality is, you're not going to get any more secure than using HTTPS. Any other solution is just more complex to do and likely not as secure.

share|improve this answer

What you are doing is some sort of half baked message authentication but a bit in reverse.

Normally with message authentication a client will create a message and will sign the message with a key. The message and signature are sent to the server. The server also has the key so it can recreate the signature itself and compare it to the one sent by the client. If they match then the message must be genuine and cannot have been tampered with. This can be used to verify identity but it offers no privacy (anyone can read the message).

Identity is proved because MITM attacks cannot generate/forge the correct signature without the key. It might be possible for a MITM to use a Replay Attack, but this can be prevented by using a cryptographic Nonce in the message.

In relation to your problem, you can get the client to sign any request made to the server (ie generate a MAC and send it to the server as part of the request) in order to prove identity. The server does not need to send any sort of challenge.

share|improve this answer

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.