1

I'm trying to find a secure Solution to a Problem I have concerning Cookies / Sessions and DB Data.

I already read trough different articles like http://www.devshed.com/c/a/PHP/Sessions-and-Cookies/ that explain different Cookie theft and Session fixation methods, to get an idea about the security issues I'm going to face.

The Problem is the following: I have DB tables that store data entrypted with AES_ENTRYPT(), using the user password to encrypt them. This means, to even read the Informations, I'll have to use the plaintext password to decrypt the data.

This probably wouldnt be a Problem, if I only store the password in the $_SESSION variable, but that would deny the ability to stay logged in over a cookie over multiple days.

In other words, I would have to store the plaintext password in the cookie (at least, to enable the feature to stay logged in)

Now, you could use a salted MD5() or SHA-256() hash as identifier, instead of the password. But I cant decrypt the data, with the hash. This means I would have to store the password server side (in a Database? Or are there other secure ways?), connecting it to the identifier - but then the password would be accesible for everyone that has access to the Database, and could directly decrypt the data there.

Is there a secure way, to connect an identifier that I store in a cookie, and connect it to a server-stored user input (the password / accountname) whitout actually giving someone that can access the Database the possibility to read that server-side stored user input?

The requirement is, that even in the worst case scenario, where someone has a dump of the Database and the cookie (but no access to the Server RAM) that person shouldnt be able to access the user password and decrypt the stored data with it.

To avoid confusion, a small recap:

This is not an user identification Issue - the login process happens separately (over the usual way: md5() hash of the password / logindata). My problem is, that userdata (like adress, name, email) is encrypted WITH the user password. So I need the password from the login, to decrypt them. This is no problem if the user just logs in, since I have the password in the $_POST data, and can use it. But after the log in? As soon as $_POST or $_SESSION is gone, I have no way to decrypt the data again.


Possible solution

After some input, I may figured out a way - it's not perfectly secure, but it should work well enough:

(This is separated from the login / user authentification process, I'm only refering to the encryption / key part)

user registers OR changes password:

    generate a new hash out of $email and $password => $auth

    do NOT store $auth in the usertable, just keep it

    generate a new random key for the user => $key (only on registering, not on pw change)

    encrypt $key with $auth, storing it into the usertable

    encrypt all user data with $key

user logs in (or after registering / password change):

    generate $auth ($password + $email)

    set cookie with $auth as variable

user is logged in (cookie / session / after login):

    decrypt $key with $auth

    use $key for data encryption / decryption (serverside)

The only Issue here is that if someone can get $auth, they could decrypt the $key and then the data. I'm thinking about generating a new $auth for every login, but this would raise the question how I decrypt the key if the old $auth is lost. The difference between this and a token is that this adds another layer, where the token isnt the encryption key itself. Anyway, I think the solution is the closest, outside of public / private keys, to what I intended. Thank you very much.

Katai
  • 2,773
  • 3
  • 31
  • 45
  • No, no, no. You do not need to store the user's password in the browser (*especially* in **plaintext**) EVER. Typically, when you store a password, you use a hash method (non-reversible) and not an encryption (reversible) method, either. You're going about it all wrong. – Jared Farrish Jun 08 '12 at 08:55
  • I think I didnt explain myself wrong. I try to avoid storing the password clientside - but I'm trying to avoid storing the password serverside (plaintext) too! But I need a 'user input' to encrypt / decrypt the userdata. – Katai Jun 08 '12 at 09:02
  • Here's what's wrong: You're assuming to check a password in your database against one that has been provided for authentication, you have to decrypt; you don't, you *compare* the stored hash value against the *hashed* provided password. There is no decryption, you just hash the submitted password against the already hashed value using the same method, then compare the two. It's pretty simple, really. And reinitializing a session that has been removed from memory is not necessarily related to a login; use a token, like suggested below, and store your user sessions in a table in the db. – Jared Farrish Jun 08 '12 at 09:08
  • No no - I'm not talking about user idenfitication here - the encrypted data, is for example, the user address. Not the password. The login happens over hash identification with a salted hash, like md5(accountname#password) -> auth – Katai Jun 08 '12 at 09:10
  • 1
    So you have data encrypted in a database, you need a way to maintain the salt for a "session" belonging to a browser (and presumably a user)? (Also, I'd avoid MD5 in favor of `crypt()` with BLOWFISH.) – Jared Farrish Jun 08 '12 at 09:13
  • edited by question to try to clarify my problem - see the last paragraph – Katai Jun 08 '12 at 09:13
  • Why did you use the user password for encryption, instead of a generated value stored only on the server, assigned to the user? Or a private key the user maintains on their own? – Jared Farrish Jun 08 '12 at 09:15
  • because if the value to decrypt the encrypted data is stored inside of the DB too, the encryption wouldnt make much sense - the password is used as encryption key, to have a 'outside-of-programm-and-db' value, that isnt saved serverside - so that no one with full serverside access could use the encrypted data. – Katai Jun 08 '12 at 09:46
  • I don't know, I would consider [public/private key encryption](http://en.wikipedia.org/wiki/Public-key_cryptography) and have the end-user generate and store the private key. The trick is, running encryption/decryption directly in the browser would be tricky. But your server would never have the key. – Jared Farrish Jun 08 '12 at 10:03

4 Answers4

1

Have a field in your users table called token which can be a random hash when you first insert the user...

$token = md5(uniqid(mt_rand(), true));

Then store the user ID and token in the cookie, which you can use to lookup the user.

fire
  • 21,383
  • 17
  • 79
  • 114
  • And where do I lookup the user? What I mean is, where do I store the plaintext password? Database is no option (the password is hashed there) – Katai Jun 08 '12 at 08:55
  • The only time you check the password is when they login, from what I understand your talking about setting a cookie to keep the user logged in once they have been authorized. My solution is the right way to do this AFTER they have logged in. – fire Jun 08 '12 at 09:01
  • Yes, but the problem is, to access the data, I'll have to decrypt it. I cant do that over the user ID or the stored hash, for that, I'll need the password – Katai Jun 08 '12 at 09:04
1

As I understand your requirement you cannot store the password client-side or server-side. Therefore you cannot store it at all. Make the user input the password for every page load.

You can store things in RAM using for example memcached or APC, or by creating your own software you can communicate with on the server. This will not give you much enhanced security as anybody who have access to the server (through a shell) can ask for the password in the exact same way as your software. In my opinion it destroys most of the purpose of the "no file, no database" requirement, but it may be a solution for you.

Emil Vikström
  • 90,431
  • 16
  • 141
  • 175
  • If there would be a way to store the user-input server side, whitout doing it in the DB (or in files) that I dont know, that could be a solution – Katai Jun 08 '12 at 09:02
  • Can it? Wouldn't that destroy the entire purpose of the "no-file, no-database" requirement? But if you are allowed to store it in RAM, you should check out memcached! – Emil Vikström Jun 08 '12 at 09:04
  • Yeah, I probably have to look into memcached then - the problem is, if I would store the key (in this case, the user-password) that I use to encrypt the data, in the database where the data is held, it wouldnt make much sense, right? That's why I'm so dependant of a user-input – Katai Jun 08 '12 at 09:07
1

The key is to find a way to store a piece of information in the cookie and a piece of information in the database that together get the plaintext password, but individually are useless.

The best I can come up with is to create a random token similar to what @fire suggested:

$token = md5(uniqid(mt_rand(), true));`

Then use this token to encrypt the password and store the encrypted password in the cookie with the userid, so you know what this encrypted piece goes with.

When the client returns, the cookie can be used to retrieve the database row by id and you can pull the token from the database to decrypt the cookie information to get the plaintext password. This way if someone hacks the cookie, they get an encrypted password; if they get into the database, they get the key to unlock the password but don't have the encrypted password.

Jared Farrish
  • 48,585
  • 17
  • 95
  • 104
David Fritsch
  • 3,711
  • 2
  • 16
  • 25
  • This could be an idea, I'll look into it and give you feedback – Katai Jun 08 '12 at 09:44
  • I added my solution to my question - I did something similar like you suggested, and it seems like the closest solution for me, avoiding public / private keys. – Katai Jun 08 '12 at 11:02
1

To keep the user logued

Use a simple token system, store the token in the user database AND on a cookie user-side. If you need more information about that, i'd suggest google (it's pretty simple)

For the plain-text password

You can use several methods to store it in RAM, this is an exemple with memcache

$memcache->set($token, $password); 

and then, retrieve the information from the RAM with:

$user_password = $memcache->get($_COOKIE['token']);

there is several solution to store data in ram with PHP: Memcache and APC (the user cache part)

Jey
  • 36
  • 2