2

I am developing a mobile application wherein I need to implement the 'Remember Me' functionality. There are certain pages within the application which require you to login. What I need to do is, if the 'Remember Me' checkbox is selected, then I need to store the username and password so that it can be populated later if the user logs out and arrives at the login page again.

In my previous implementation, I just captured the data from the login form and stored it in local storage. However, I now need to know how I can secure it.

I have followed the advice given in this answer to implement a sample implementation. I am using SJCL to encrypt and decrypt the data (in javascript).

function encryptLoginData(data) {
    //Generate PBKDF2
    var result = sjcl.misc.cachedPbkdf2(data.password, {count: 1000});
    var key = sjcl.codec.hex.fromBits(result.key);
    var salt = sjcl.codec.hex.fromBits(result.salt);

    //Encrypt data using the key and the salt generated from password
    var finalData = sjcl.json.encrypt(key, JSON.stringify(data), {salt: salt});
    var encryptedData = JSON.stringify([key, finalData]);
    return encryptedData;
}

Later, to populate the data I am using this:

function decryptLoginData(data) {
    //Decryption of login data
    var fetchedData = JSON.parse(data);
    var decryptedData = JSON.parse(sjcl.json.decrypt(fetchedData[0], fetchedData[1]));
    return decryptedData;
}

Now if you notice, I have to store the key and the encrypted data together so that I can decrypt it later. However, anyone who has access to the javascript and the key can easily decrypt the data using a single sjcl.json.decrypt. What I think is, I am just obscuring the data further and not really securing it? Am I missing something? Is there a more secure way of doing this?

PS: I am new to cryptography.

PSS: I have gone through this and this but dont really follow how to use it my context.

Karan Thakkar
  • 121
  • 1
  • 2

3 Answers3

5

When you are new to cryptography, don't use cryptography. Nothing personal here; that's because you cannot test for security. You cannot easily (or at all) know if some system/protocol/algorithm is secure or not. Such tests simply do not exist. This is very unlike functionality, which can be tested: it is easy enough to see if a server boots up and answers requests, but it is impossible to verify if all this happens securely. Theoretical reasons for that have deep fundamental roots like the halting problem.

The only known way to use cryptography correctly is to let it in the hands of people who have devoted their lives to the study of cryptography. If those guys cannot collectively come up with an attack on your system, despite years of silent toil and meditation, then your system is probably secure, or at least more secure than that of your competitors, which is often good enough.

A "remember me" feature is about keeping some state on the user side, so that when the user comes back to the server, the server will automatically recognise him, and won't ask for an irksome password entry. Whatever way you put it, this means that the user device (mobile phone, as I understand it) contains everything which is needed to "log on" the server without any special user interaction. In particular, if the mobile phone is stolen, then the thief can obtain the same data and then log on the server. This is unavoidable, and that's (again) fundamental: if the user needs not enter his password, then there is nothing specific about the human user, and his physical presence is not required. That's more-or-less the point of the "remember me" feature.

No amount of cryptography anywhere will change that. If the app can log on the server without user intervention, then it can.

A "remember me" feature is thus an inescapable risk which you must balance with the improved user experience (users basically prefer not entering their password over entering their password). There are some ways to make the trade-off a bit more favourable for you:

  • Whatever the app stores, don't make it password-dependent. Don't store user passwords or any hash computed from the user password. User passwords are sensitive data whose scope extends beyond your app; user passwords are highways into their brains. Instead, store a "session key" which was generated randomly server side. Make it 16 byte long (at least), and use on the server a cryptographically secure PRNG to produce it (e.g. /dev/urandom or a wrapper around it).

  • If using a random session key, then you can cancel it on the server at any time, which is a good thing compared to the user password, because you cannot make the user "forget" his password, let alone any potential attacker.

  • Make it time-limited; for instance, make the "remember me" feature valid for one week. This will force the user to re-enter his password regularly, but you can choose the schedule; yet again, trade-offs. With a time-limited session key, you can contain the damage from any attacker within a strictly bounded time frame.

So now your problem is "simply" a matter of:

  1. obtaining a random session key from the server;
  2. storing it on the user device;
  3. sending it back to the server when needed.

In an HTTP/Web context, all of this already exists and is called HTTP cookies. Just make sure that you set the "Secure" and "HttpOnly" flag, and that you use SSL (HTTPS) (if you are not using SSL, then you already have a lot of bigger problems, so do that first). The system which is easiest to implement securely is the system which has already been implemented securely by someone else. So just use it.


Now, if your app is about logging on an uncooperative server who is totally unaware of your "remember me" feature and will not send a session key as a cookie, then you have a problem. In that rotten situation, you have to store user credentials on the client side, and that's not good. Password manager software tries to cope with such issues, but the trade-offs are not as good as previously described. Users, generally, won't like it. There will be some local encryption with a user "master password", and that's a hard problem; moreover, Javascript is not at all the right language for that (in particular because of its low performance, which contradicts slow hashing).

It is highly preferable if you do not go there.

Tom Leek
  • 170,038
  • 29
  • 342
  • 480
  • That was excellent (+1). Out of sheer curiosity, may I just ask: "Javascript is not at all the right language for that". Which language is it then, if any at all? – Lex Aug 07 '13 at 09:56
  • 1
    For password hashing, we need a lot of muscle and a direct access to all the CPU potentialities, so this points to assembly, C or C++. Traditionally, C is used. To some extent, languages with good JIT (e.g. Java, C#) can also be used, but with some overhead (for pure computational tasks of that kind, Java code is typically 2 to 4 times slower than corresponding C code). – Tom Leek Aug 07 '13 at 11:04
  • Yes. You correctly identified what my problem is. Right now, I am interacting with an **uncooperative server**, so to speak. So, whatever credentials I am storing, are on the client side so that they can be populated in the form again for the user at a later time. In that case, is there something I can do to make the storing of credentials more secure? – Karan Thakkar Aug 08 '13 at 05:46
0

You're absolutely right, as you say:

anyone who has access to the javascript and the key can easily decrypt the data using a single sjcl.json.decrypt.

However,

Am I missing something?

Yes:

Is there a more secure way of doing this?

Yes, follow these best practices:

  • Simplify your javascript authentication, since complexity causes security weaknesses. The problem you're trying to fix has already been solved. Once the user is logged on securely; have the server store a persistent Secure, HTTP only cookie.

  • For the server side code that currently validates your password, make it validate the cookie before continuing the call.

  • Support the ability to (server side) revoke/expire the remember me functionality. This can be done with a unique GUID per cookie, or a server hashed/signed JSON string that includes expiration date with the GUID

  • You can make it more difficult for an attacker to extract your keys by storing a different SSO key in LocalStorage and validating the pair of keys for each call. (Localstorage + Secure/Persistent/HTTP cookie)

makerofthings7
  • 50,488
  • 54
  • 253
  • 542
  • What I did was, use the password to generate a PBKDF2 key and use that key to encrypt the login credentials. Also, I cannot use the server to store any cookies. Everything has to be on the client side. So if that is the case, would it mean that my technique is as good as being not implmented at all? – Karan Thakkar Aug 06 '13 at 14:01
  • @KaranThakkar What type of app or architecture doesn't allow for server cookies to be persisted? I never thought this was not a possibility. Yes, hashing using PBKDF2 is a million times better than storing a cleartext password. Kudos to you for keeping security in mind and not doing that! – makerofthings7 Aug 06 '13 at 14:04
  • Borrowing @TomLeek's vocabulary, for the moment, the server I am interacting with is an _uncooperative server_. It is unaware of the 'Remember Me' feature. In that case I need to store the credentials on the client side. I needed advice in implementing a secure way of storing that. – Karan Thakkar Aug 08 '13 at 05:40
0

There is no secure way of storing credentials on your mobile phone. A commonly used approach is to require the user to set a password for the app. This password will then be used to encrypt the information which needs to be remembered. This password is not stored on the phone (except in-memory when using the app).

If you are only using one password which needs to be remembered, then there is unfortunately no secure way of storing this data. The only other option is to use obfuscation, which is security by obscurity.

Lucas Kauffman
  • 54,229
  • 17
  • 113
  • 196
  • `A commonly used approach is to require the user to set a password for the app.` Wouldn't this mean that *this* password will have to be stored somewhere? Wouldn't it mean that *it* can be compromised as well? Forgive me, but I am just trying to make some sense. – Karan Thakkar Aug 06 '13 at 14:03
  • You store the password in a hashed form – Lucas Kauffman Aug 06 '13 at 14:07