You are here : Home » Learning security » Applications » Web applications » One time passwords

One time passwords

D 23 October 2010     H 18:38     A Emeric Nasi     C 0 messages


agrandir


Prerequisites : This article suppose the reader has programming basis and already knows about hash functions (if not read this article ).
License : Copyright Emeric Nasi, some rights reserved
This work is licensed under a Creative Commons Attribution 4.0 International License.
Creative Commons License

We regularly hear that a password is an authentication from the past and will be replaced by new strong auth. techs. However in real life, everybody uses a lot of passwords every day (even if SSO or openID technologies reduces the number of passwords you have to remember).
Protecting our passwords is vital for our security. I already wrote an article explaining why and how passwords should be at the same time strong and easy to remember, in this article my concern is how using one-time passwords can protect your password from eavesdropping on a network. I will also show a simple algorithm to build your own OTP factory for websites login page.

I. Why should we use one-time passwords?

I.1 What are "one-time passwords"

One-time password or OTPs refers to various techniques aiming to "never use the same password or key twice". These passwords are also called throwable passwords or session passwords.
There are various ways to do that :

  • Using time synchronization (with the aid of a device shared by both party).
  • Using a symmetric encryption (public and private key) and certificates to securely generate and share a session key (ex. SSL protocol) .
  • Using per client "tickets" or tokens to generate temp session key (ex. Kerberos protocol).
  • Using a challenge to generate a different authentication key at each client identification (ex. CHAP protocol)

I.2 Various benefits of one-time passwords

The main benefit of OTPs is to prevent eavesdropping. Even if an attacker gets the temporary password he will only be able to use it during the time your session is opened.
Remember that it is easy to sniff data on a network and that with simple tools like ettercap and sslstrip an attacker can bypass SSL tunneling security. And even if the attacker is not able to break the encryption tunnel, the end machines may not be secured and your password may be stolen before or after it enters the tunnel.
Another asset is that depending on the way you implemented one-time passwords you can protect your users from themselves. For example, people who have short passwords or who uses the same password all the time. By ensuring their password is never transfered to your webapp but instead a strong and random session key, you protect these users from bruteforce and dictionary attacks.

II. Simple one-time passwords technique.

II.1 The objectives

When surfing on the Internet most HTTPS sites (gmail, yahoo, facebook, online merchant and banks) provides a server certificate but do not ask for a client one (it would be too fastidious). So the client authentication part is a simple good old password.
Thats why here our goals are :

  • The user has only one password to remember (Some OTPs implementation using shared device, or OTP transmitted by mobile or on parer implies that the user has to type in his OTP himself, thus being a potential victim for social engineering).
  • Never transfer the main password in clear text, even when using encrypted tunneling(VPN, SSL, IPSec). Instead we will transfer a per session authentication key.
  • Always use a different authentication key (the OTP).
  • Be sure that it is not possible to guess the original password from OTP.
  • And still be identified and authenticated !

II.2 The elements we need

- The user login
- The user password
- A client (for example a navigator)
- A webserver hosting a website
- A storage on the webserver ( for example a PostgreSQL databae)
- A strong hash function we will call ’Hash(x)’
- An encryption function (can be simple) we call ’Encrypt(x,k)’
- The corresponding decryption function ’Decrypt(x,k)’
- A function used to create a random key called ’CreateKey()’
- A generic function used to send data we call ’Send(x)’
- Generic functions used to manage store datas
- Generic functions used to verify datas

II.3 The algorithm

The core of this technique is the hash function.
y = Hash(x)
If the hash is strong it is impossible to reverse from y to x.
If to entities (here the server and the client), share a common secret (the password) and if the server send a random temp key (k) to the client. The client can generate a session password doing
y1 = Hash(Encrypt(x,k1))
The next time the client need to authenticate, the server sends a new key k2 to the client and the client creates the new one-time password doing :
y2 = Hash(Encrypt(x,k2))
If we are sure that even if an attacker has y and k he cannot reverse to x, then y is a good temporary password, and only y needs to be send for authentication.

Note : x (the real password) is never send from one entity to another!!!

Have a look at the detailed algorithm :

0 - Initial step : The user arrive on the website login page and type in his credentials
(login and password).
1 - Identification step : The login is sent to the webserver for identification.
(remember it is important to differentiate identification and authentication).
The server verifies that the login exists.
Client side : Send(login)
Server side : if IsLoginInStorage(login)
                    then
                         Step2()
                    end
2 - Challenge creation step : The server creates a random key and associates it to the login. Then it sends this key to the client.
Server side : key=CreateKey()
                    StoreKeyByLogin(key,login)
                    Send(key)
3 - Resolve challenge step : The client encrypts his password with the random key,
then hash the result to create our temporary password.
Then it sends its credentials to the server.
Client side : encryptedPwd = Encrypt(password, key)
                   tmpPwd = Hash(encryptedPwd)
                   Send(login, tmpPwd)
4 - Authentication step : After some verification the server uses the same method as the client in step3 and then compares the result with the temporary password.
If they are the same you are authenticated.
Server side : if IsLoginInStorage(login)
                    then
                         if IsChallengeCreated(login)
                         then
                               key = getChallengeFromStorage(login)
                               password = getPasswordFromStorage(login)
                               encryptedPwd = Encrypt(password, key)
                               testPwd = Hash(encryptedPwd)
                               if testPwd == tmpPwd
                               then
                                     Authenticated()
                               else
                                     Fail()                    
                               end
                          end
                    else
                        Fail()
                    end

Ok We did it!
However this first version has a lot of lacks. First, the challenge is sent back only if the login exists and that is bad because it allows "login existence guessing".
Secondly, we do not want our password to be stored in clear text in the database. We want to make sure our password is never stored or transfered in clear text.

II.3 Improved algorithm

In this case, during account creation, the password was hashed before it was stored so it is not in plain text in the storage.

0 - Initial step : Idem + Use special input component so that password cannot be guessed by keylogger.<br />
1 - Identification step :
Client side :  hashedPwd=Hash(password)
                    Send(login)
Server side : if IsLoginInStorage(login)
                    then
                         Step2()
                    else
                         Step2Bis()
                    end
2 - Challenge creation step :
Server side : key=CreateKey()
                    StoreKeyByLogin(key,login)
                    Send(key)
2Bis - False Challenge creation step :
Server side : key=CreateKey()
                    Send(key)
3 - Resolve challenge step :
Client side : encryptedPwd = Encrypt(hashedPwd, key)
                   tmpPwd = Hash(encryptedPwd)
                   Send(login, tmpPwd)
4 - Authentication step :
Server side : if IsLoginInStorage(login)
                    then
                         if IsChallengeCreated(login)
                         then
                               key = getChallengeFromStorage(login)
                               hashedPassword = getPasswordFromStorage(login)
                               encryptedPwd = Encrypt(hashedPassword, key)
                               testPwd = Hash(encryptedPwd)
                               if testPwd == tmpPwd
                               then
                                     Authenticated()
                               else
                                     Fail()                    
                               end
                          end
                    else
                        Fail()
                    end

II.4 Implementation tips

Anyone who did a little programming is able to implement this technique using his favorite language. Remember that this method’s security relies completely on the strength of the hash function. My personal advice would be to use a SHA2 variant (SHA256 or SHA512). Do NOT use MD5!

This technique implies a series of exchanges between client and server, I recommend to use AJAX for the client side to make it more comfy for the end user.
The main method could be something like (javascript):

  1. <script language="js" type="text/javascript">
  2.  
  3. var xmlHttp;
  4.  
  5. function secureClientAccess()
  6. {
  7. // Hash the password
  8. var password;
  9. password = (document.getElementById("textBoxPassword")).value;
  10. var sha256Password = sha256Hash(password);
  11. (document.getElementById("textBoxPassword")).value = sha256Password;
  12.  
  13. // Create AJAX Object
  14. xmlHttp =createXMLHttpRequestObject();
  15. // Send login to server
  16. var login = (document.getElementById("textBoxLogin")).value;
  17. xmlHttp.open("POST", "http://server_url", false);
  18. xmlHttp.setRequestHeader("Content-type", "application/x-www-form-urlencoded");
  19. xmlHttp.send("login="+login);
  20.  
  21. //get challenge key
  22. var key = xmlHttp.responseText;
  23.  
  24. // Resolve challenge :
  25. // Encrypt hashed password
  26. var encryptedPwd = encrypt(sha256Password, key);
  27. // Hash encrypted password
  28. var tmpPwd = sha256Hash(encryptedPwd);
  29. (document.getElementById("textBoxPassword")).value = passw;
  30.  
  31. // Send login+ resolved challenge for authentication
  32. xmlHttp.send("login="+login+"&tmpPwd="+tmpPwd); //Other ways possible
  33. // etc...
  34.  
  35. }
  36.  
  37. </script>

Download

Also in this section

2 January – XSS : Get string without quote

7 April 2011 – Javascript -Java compatible encryption

Any message or comments?
pre-moderation

This forum is moderated before publication: your contribution will only appear after being validated by an administrator.

Who are you?
Your post