Only require password once to decrypt files at different times? - php

User's content is encrypted, but needs to be decrypted. There are multiple files that need decryption to be viewed, and they will definitely not be viewed at the same time.
I am currently encrypting by using the user's plaintext password to encrypt a randomly-generated key, which encrypts the user's data. The password is hashed and verified normally before doing anything. I am using PHP's aes-128-gcm openssl_encrypt() function.
My current system requires a password every time the user wants to read a file.
I have thought about decrypting all of the content at one, but this doesn't scale well. I have also thought about storing the user's key as a cookie, but I'm worried about security.
Is there a standard way to do this?
Thanks!

The first thing to do is separate the users password out of this. You'll have to decrypt and re-encrypt all their files. There may be other ways around this such as allowing only new files to use this system. But that is very use case specific, such as how long do you keep their files, what is the turn over on them etc..
In any case this is a way to do that:
Encrypt the files they submit using a password you generate.
Store this password in another file we'll call it key.txt for now. Encrypt this file using the users password.
When user logs in (if they don't have it stored) take their password, decrypt key.txt and get the generated password.
Now you can save this generated password anywhere you want, without affecting the users account.
What they see (the end user experience) will look like always they go to downlaod a file, put their password in and get the file. They wont ever know you did this, which is nice for them.
So problem one is fixed.
Now where should we store this?
You could simply store it on the server in the DB. This sort of depends on how confidential the data is, and how secure your server is. Your ultimately responsible for the security of someone else's data, at least this way you can control it.
Make a table with these fields
user_id | ip | password | last_access
When a user goes to download a file, check their last access time and IP address to invalidate the password and make them refresh it. This is very easy to setup and totally under your control. If you save the encryption key, it will always have some level of vulnerability at least this way its all under your control.
Even if you don't want to store it in your DB, the biggest disadvantage here is if someone gets a hold of that table, but if they do that and your storing important data you probably have plenty of problems already.
At least use the first part as that solves a big problem with tying this to their actual account password. Even if a hacker gets the file password from the client (stolen cookies etc.) because it's separate, having that alone wont let them login to your site like the account password would. I am assuming here, a user must login to even get to the download part. Using the same password for both gives them them access to both the means of the getting this data and the method to download it.
To be clear, their is an argument to be made about storing it on the client side. Then if your site is compromised there is less chance someone could get a hold of the password as it (depending how you do it) only exist in memory on both the client and server etc. It puts the responsibility on them.
ASYMMETRIC ENCRYPTION
You could also use asymmetric encryption. Currently it looks you are using AES, which is fine, but it's a Symmetric Key block cypher. Basically there are three common forms of "encryption" (in vernacular):
Hashing (which really isn't encryption) - md5, sha1, sha256 - these are one way, can't be decoded. They have fixed lengths, and always encrypt to the same thing. It's common to see this for file checksum (for validating the contents of the file), Block Chain, Passwords or anything else where you need to compare two "encrypted" values.
Symmetric - AES, 3DES, Blowfish, Twofish - anything you need to encrypt and decrypt. The same key can do both. Generally these will encrypt the same thing to different values each time, because of the IV.
Asymmetric - SSL, DSA, RSA, PGP, used in Crypto currency wallets, TLS etc. With these you have 2 keys, a public one and a private one. The keys cannot decrypt their own encrypted data, only the other key can. So with this if you have one key on the server and the client has the other. You can encrypt their files using your key (decryptable by only their key) and you don't have to worry so much about someone getting your key as it won't allow them to decrypt the files. You can give one key to the client, who can use that key to decrypt their data you encrypted (even without your key). These also encrypt to different "Stuff" each time you use them.
So you can see Asymmetric form has a few advantages to use in a two(or more) party system. It also has the benefit that you don't need their key to encrypt a file. All you need is your part of the pair. So for example if you generate data for them and wan't to encrypt and later have them decrypt it with the same system, you can do that with no issues. This probably eliminates a step, as you would need to ask them, or keep track of their Symmetric anytime you wanted to encrypt something. Here you just need your part of the key pair.
It really isn't much harder to implement (on the server), its just harder to understand what it does. That's why I decided to add this, without this knowledge (which you may or may not already know) it's hard to use these terms and have them make sense. The only real disadvantage for you (if you call it that) if you used Asymmetric encryption, is if a client loses their key you would have no way to decrypt the file. So I would make sure they know to back them up in a secure place. It's the same problem that you see in the news when it comes to losing a crypto currency wallet which is encrypted Asymmetrically
As I said most of my knowledge has to do with encrypting and dealing with data on a server. So I am not sure how to tie that in to the "client experience". I do know for example how to use RSA keys for password less login for SSH etc. Which is kind of the same thing but not quite.
Hope it helps!

they will definitely not be viewed at the same time
Wouldn't the most secure answer here be to simply require the password every time? I would assume (although I'm sure this isn't the answer you're looking for) that simply asking for the password each time might be the best solution.
Although it may be tedious for the user, I would also assume it imparts some sense of security - since it's not quite as simple as logging in (as the files are encrypted).
From my perspective, I would argue that encrypted files should not be mass decrypted anyways?
Sorry, I know this isn't the answer you're looking for - but if you have more information about your motivation, maybe then a more reasonable solution can be found?

Don't do decryption on the server-side - do it client side. It is safe to keep the user's password in memory on their own device.

Related

Encrypt a text into the database

I'm building a simple web site in which the user can upload pieces of text (notes). A note will have a flag "private" and, in case of true, I'd like to encrypt it so that not even me (who can access the database) is able to read the private note.
I know encryptions are made through a key, and the best key I can think of now for this purpose is the user password. Obviously the password is hashed into the database but I could save it during the login in $_SESSION.
Is it a good idea?
For example, in cryptobin.org the user is asked to input a password, but in my site the already signed up user inserts the password at login and I don't want to ask again something new everytime he will upload his note and, I repeat, I need all this because I want to protect his notes from me.
Probably it's not very clear to me how encryption works; in 0bin.net it's claimed that the pastes are encrypted, but no key is asked.
Can you please explain this?
Keyed encryption is based on the premise of having a secret.
plaintext + algorithm + secret → cipher text
cipher text + algorithm + secret → plaintext
You provide the algorithm, the plaintext/cipher text is the subject of interest, and the party which holds the secret has the final piece of the puzzle and thereby the ultimate power.
From this perspective the user's password is a great choice, because only the user should know it and nobody else. In practice this depends on you really not having access to this password. If you store the password server-side, then obviously you have the password and could use it to decrypt the cipher text if you so chose.
Now, what you're proposing is a temporary storage of the password in the session. That's fine, but then it becomes about risk assessment. Who could get the password from the session? Hopefully nobody except you, unless your server is compromised. That still leaves you with a temporary opportunity to decrypt the cipher text yourself if you so chose. Also, keeping the plaintext password around in any form raises the chance of it being exfiltrated to somewhere (server logs, memory dumps, etc.).
If you're being trusted with handling a secret, you need to secure everything that ever comes in contact with that secret.
It's not infeasible, but depending on how serious you are about this, how sensitive the information you're protecting is, and how many possible attack scenarios you want to prevent, this can get rather complex and goes all the way to the physical security of the servers your code is running on.
An alternative is to do all encryption client side (in native app, or Javascript in the browser), which removes a lot of responsibility from the server. It opens new problems (you now need to ensure your Javascript cannot be tampered with), but those are easier to manage.
#Thilo summaries well how 0bin works. The randomly generated secret is part of the generated URL, is not stored on the server at all, and all encryption and decryption is happening in the browser.

Encrypting user data for automatic login to third party system

I find myself in a situation where I have a set of users on a site who all have stored usernames and passwords that allow them to automatically access third party services via SOAP. The idea is that each user should only need to log in to the main site to gain access to multiple services, using their respective stored user info for each service. I feel like such data should be encrypted in my database when stored and then automatically decrypted when it's passed to the php/SOAP function when the user needs to access a given service. What would be the best way to accomplish this?
I've had a look at AES_ENCRYPT, but it seems as though the encryption and decryption makes use of a key that would have to be stored in my code, in plain text...which doesn't seem like the brightest thing to do. Anyway, this is my first time out on something like this (if you couldn't tell); if you could give me some pointers on how I should approach this, I'd really appreciate it.
Many thanks in advance
You stumbled over the biggest problem with encrypting data in the database:
➽ Where to store the key?
Encryption cannot solve the problem of securing data, it can only "concentrate" it to a key. Wherever you store the key, your application must be able to decrypt the data, so can do an attacker. There are two possible solutions to this problem i know of:
Place the key in a place as secure as you can. That means, it should surely be placed outside of the www-root directory in an inaccessible directory on the server. Depending on the importance of the data, you can also consider to outsource encryption to another dedicated server.
Don't store a key at all and derive it from the user password. This is the only really safe way, because not even the server can decrypt the data then. The cons are of course, that the user needs to enter the password every time he uses your service. If the user changes the password, you need to re-encrypt all data. If the user forgets the password, the data is lost.
P.S. I would recommend to encrypt the data before storing it to the database, because MySQL AES_ENCRYPT uses the ECB mode without an IV. This allows to search for a certain value, but is less secure (i'm pretty sure that you don't want to search by password).

Secure password store and recovery

So,
I have a mysql table which stores passwords, and these passwords can not be hashed, cause I need to recover it to plain text later.
I have a javascript, which via ajax/php takes these passwords from a mysql database and sends it to another server that will use it to authenticate, that's why i need to have them in plain text when I send.
I know there's base64 and other encryptation alghorythms, but that's unsafe.
The best solution I found is OpenSSL, but I'm not sure if I'm on the right path. Am I?
OpenSSL is a good place to start looking. It supports a very large number of secure encryption algorithms that you can use to encrypt the plain text passwords. AES-256 or Twofish are good algorithms to start looking at. 3DES is also considered sufficient to today's standards.
For good security, you will need to encrypt each user's password with a different key; that is each user has a unique encryption key to them and you do not use 1 key for all passwords. This could be a hash of the user's password that they use on your site, but often user passwords aren't strong, and if they forget the password to your site/service, then they also lose their encryption key.
For the greatest security, you shouldn't store the encryption keys anywhere. When the user logs in with their password, you can generate the encryption key in memory based on their password. Ideally it would not just be a hash of their password, but their password applied through some sort of transformation algorithm.
If that isn't an option, then you should store the encryption keys on a different physical server than the one that stores the encrypted user passwords. The server that stores the encryption keys should have a number of security and access control features in place with very controlled database access so pretty much only your application can access the keys.
And on top of that, you must disclose in your privacy policy that you may store encrypted forms of their passwords for use with the 3rd party service.
Hope that helps. OWASP may have some other helpful information related to what you are going to do.
thanks for all the answers, I'm going to use an php encryptation method described here: https://stackoverflow.com/a/6639179/1415262
and try some openssl.
For all of the other answers, I have a few problems with them and short time to explain why.
PS.: I can't up vote yet, but special thanks to #drew010 and #fabio :)
I would HIGHLY recommend that you don't store passwords in plaintext and maybe generate some kind of one-time usage key that is passed from one server to another.
So server one has a key linked to a specific user that is unique, this key is also on server two and that is the key that's passed.

Two-key encryption/decryption?

I'm looking to store some fairly sensitive data using PHP and MySQL and will be using some form of reversible encryption to do so since I need to get the data back out in plain text for it to be of any use.
I'll be deriving the encryption key from the users' username/password combination but I'm stumped for what to do in the (inevitable) event of a password being forgotten. I realise that the purpose of encryption is that it can only be undone using the correct key but this must have been addressed before..
I'm trying to get my head around whether or not public key cryptography would apply to the problem but all I can think of is that the private key will still need to be correct to decrypt the data..
Any ideas?
It's not clear what you are striving for, so advice on how to implement it is hard.
Standards like PGP and S/MIME encrypt each message with a new symmetric key. Those keys are then encrypted for each recipient of the message. This way, instead of duplicating the message (which could be very large) for each recipient, everyone gets the same ciphertext, and only the key (which is small) is duplicated—but encrypted differently for each recipient.
Maybe you could do something similar here, encrypting the key with the user's password, and encrypting another copy with your public key. If the user forgets their password, you can recover the message for them (after an appropriate backup identity verification) using your private key.
The conventional solution is to have a "recovery agent": one user that holds a second password that can be used to decrypt all data. Strict usage policies would apply to using the recovery password, such as putting it physically into a safe.
Then, either encrypt all data twice: once with the user key and once with the recovery key; alternatively, generate a session key for every set of data, and encrypt the data only once, but the session key twice.
For that to work, at least the key of the recovery agent must be asymmetric, since the private part will live in the safe, and the public key in the software.
As yet another alternative using the same scheme: encrypt the user's passwords with the recovery key on password change. This is simpler to implement, but will allow to recover the passwords and not just the data, which may be undesirable.
I'm looking to store some fairly
sensitive data using PHP and MySQL and
will be using some form of reversible
encryption to do so since I need to
get the data back out in plain text
for it to be of any use.
Protecting sensitive data is good. Now:
Whose data is it? (yours, your user's, or a third party?)
What does it need to be protected from? (disclosure, corruption (accidental or intentional...)
Who does it need to be protected from
Uninvolved parties goes without saying.
Do you need / want to avoid accessing the plaintext data yourself (useful for deniability),
Do you need to protect either your user's data from being visible to a third party,
Or a third party's data from the user,
Or your data from the user or a third party?
What are likely attacks?
Do you need to protect in the case where the server is completely compromised?
Do you need to protect against an application level attack where the user simply gains access to some but not all available data (e.g. access to the SQL database, but not the filesystem)?
Will the amount of data be small enough that the attacker can guess and simply check whether he/she got it right? (short passwords, numbers, simple words, fixed form text are likely candidates)
Will the attacker have known plaintext with which to attack?
Is it better for the data to go away (or to re-retrieve the data) if the user forgets their password, or is it worth an increased risk of exposing the data to avoid that cost?
There are probably other questions, but this is the type of thing you want to think about when using encryption. The answers will help you figure out what you need vs. what you want, and will probably help point in the right direction. You might not want to share all of the answers with us.
I'll be deriving the encryption key
from the users' username/password
combination but I'm stumped for what
to do in the (inevitable) event of a
password being forgotten. I realise
that the purpose of encryption is that
it can only be undone using the
correct key but this must have been
addressed before..
You might have decided on a solution without considering the impact. That doesn't mean the solution is wrong, but this question suggests you should think about what you are willing to risk for security. Sometimes data will be risked.
I'm trying to get my head around
whether or not public key cryptography
would apply to the problem but all I
can think of is that the private key
will still need to be correct to
decrypt the data..
This too sounds like a solution in search of a problem. Public key cryptography is useful when you have two (or more) separate actors with an interest in communicating data between them. Those actors can be real (people) or functional (components of a system), but without two actors, there is no reason to have a separate public and private key.
Basically, if you encrypt something, and lose the encryption key, you're screwed.
When it comes to securing data, you need to consider why you're securing it, and what you're attempting to secure it against. And what tradeoffs are worth making in order to do so - the only truly secure system is one that is completely isolated from the internet, which is a level of security that is self-defeating for most applications.
So here are some questions to ask yourself:
If someone compromises my database, is it acceptable for them to be able to access this data?
What if someone compromises my entire application stack?
If the answers to the above two questions are "no", then the key material must be held by the user. And they will lose access to their data if they lose the key.
You can provide an option for manual key recovery if you also have a "master key" that you don't store anywhere near your application, only you hold it and you use it to manually reset passwords. If that's also not an option (say, only the user should be able to access the data, not the system administrator), then you're going to have to make a compromise somewhere.
This is a question I have thought about myself and as I see it the following options are available (with option #1 being the most secure):
Provide no reset password functionality - if they have forgotten their password then they are locked out.
Generate a new secure master key and encrypt & hash the user's key with this master key and store the cipher text and hash result in the database. The secure key is then made known to the user either by adding it to a file that the user downloads, emailing to the user or displaying the secure master key on screen. To reset the password the user would have to enter this master key which is then hashed and compared and if they match, the user's key in the database is decrypted.
Ask the user to provide 2 security questions and answers when registering; hash the answers and store the questions and answer hash in the database. The second answer is used as the master key to encrypt the user's key. To receive a password reset request email the user has to answer the first question correctly. Once they click the link in the email the web page then asks the second question, if this is correct and the query string parameter values are valid then use the answer to the second question to decrypt the user's key.
Use an application global master key (maybe stored in the web/UI application and use this to encrypt and store the user's key. Once a user is verified through a password reset email process the user's key is decrypted using the application global master key and then reencrypted with their new password.
In summary, the benefits of each option is as follows:
This is the ultimate for security and would possibly be the only option if the data was critical to be kept encrypted. However, in the real world people forget their passwords as sure as the sun rises and not providing a reset password function could be a bad commercial decision.
This is secure as the master key is not stored on the front end or database so if the platform is compromised then the data would require some significant effort to decrypt. However, the downside is the user could still lose their master key anyway.
The weakness here is if the database is compromised the answer to the question could be researched and then used to decrypt the users encrypted key.
This approach leaves the application key in the stack leaving your data vulnerable if your platform is hacked. The only protection you have is that if the database server is hacked then the data would still be safe.
As with most things in the world of software development you need to consider what is best for what you are trying to accomplish and aim for the correct balance.
Why are you using a different key for every user?
If you choose one key, it is much easier to handle.
Store your encryption key outside of the database.
Your application will still have to have access to it, but someone with a db dump will not be able to read the encrypted info.
Generate a random session key.
Use the session key to encrypt the data.
Encrypt the random key with any number of user passwords that you need.
This way you can use any user password to decrypt the data.

What encryption algorithm is best for encrypting cookies?

Since this question is rather popular, I thought it useful to give it an update.
Let me emphasise the correct answer as given by AviD to this question:
You should not store any data that needs encrypting in your cookie. Instead, store a good sized (128 bits/16 bytes) random key in the cookie and store the information you want to keep secure on the server, identified by the cookie's key.
I'm looking for information about 'the best' encryption algorithm for encrypting cookies.
I hava the following requirements:
It must be fast
encrypting and decrypting the data will be done for (nearly) every request
It will operate on small data sets, typically strings of around 100 character or less
It must be secure, but it's not like we're securing banking transactions
We need to be able to decrypt the information so SHA1 and the like are out.
Now I've read that Blowfish is fast and secure, and I've read that AES is fast and secure.
With Blowfish having a smaller block size.
I think that both algorithms provide more than adequate security? so the speed would then become the decisive factor.
But I really have no idea if those algorithm are suited for small character string and if there are maybe better suited algorithm for encrypting cookies.
So my question is:
What encryption algorithm is best for encrypting cookie data?
Update
To be more precise, we want to encrypt 2 cookie: one with session information and the other with 'remeber me' information.
The platform is PHP as apache module on Linux on a VPS.
Update 2
I agree with cletus that storing any information in a cookie is insecure.
However, we have a requirement to implement a 'remeber me' feature. The accepted way to go about this is by setting a cookie. If the client presents this cookie, he or she is allowed access the system with (almost) equal rights as if he/she presented the valid username password combination.
So we at least want to encrypt all data in the cookie so that it:
a) malicious users can't read it's contents,
b) malicious users can't fabricate their own cookie or tamper with it.
(All data from cookies is sanitized and checked for validity before we do anything with it, but that's another story)
The session cookie contains a sessionId/timestamp nothing more. It could probably be used without encryption, but I see no harm in encrypting it? (other than computation time).
So given that we have to store some data on in a cookie, what is the best way to encrypt it?
Update 3
The responses to this question made me reconsider the chosen approach. I can indeed do the same without the need for encryption. Instead of encrypting the data, I should only send out data that is meaningless without it's context and cannot be guessed.
However, I'm also at a loss:
I thought that encryption enabled us send data out in to the BigBadWorld™, and still be (fairly) sure that nobody could read or tamper with the it...
Wasn't that the whole point of encryption?
But the reactions below push toward: Do not trust encryption to accomplish security.
What am I missing??
No real reason not to go with AES with 256 bits. Make sure to use this in CBC mode, and PKCS#7 padding.
As you said, fast and secure.
I have read (not tested) that Blowfish may be marginally faster... However Blowfish has a major drawback of long setup time, which would make it bad for your situation. Also, AES is more "proven".
This assumes that it really is necessary to symmetrically encrypt your cookie data. As others have noted, it really shouldnt be necessary, and there are only a few edge cases where there's no other choice but to do so. Commonly, it would better suit you to change the design, and go back to either random session identifiers, or if necessary one-way hashes (using SHA-256).
In your case, besides the "regular" random session identifier, your issue is the "remember me" feature - this should also be implemented as either:
a long random number, stored in the database and mapped to a user account;
or a keyed hash (e.g. HMAC) containing e.g. the username, timestamp, mebbe a salt, AND a secret server key. This can of course all be verified server-side...
Seems like we've gotten a little off topic of your original, specific question - and changed the basis of your question by changing the design....
So as long as we're doing that, I would also STRONGLY recommend AGAINST this feature of persistent "remember me", for several reasons, the biggest among them:
Makes it much more likely that someone may steal that user's remember key, allowing them to spoof the user's identity (and then probably change his password);
CSRF - Cross Site Request Forgery. Your feature will effectively allow an anonymous attacker to cause unknowing users to submit "authenticated" requests to your application, even without being actually logged in.
This is touching on two separate issues.
Firstly, session hijacking. This is where a third party discovers, say, an authenticated cookie and gains access to someone else's details.
Secondly, there is session data security. By this I mean that you store data in the cookie (such as the username). This is not a good idea. Any such data is fundamentally untrustworthy just like HTML form data is untrustworthy (irrespective of what Javascript validation and/or HTML length restrictions you use, if any) because a client is free to submit what they want.
You'll often find people (rightly) advocating sanitizing HTML form data but cookie data will be blindly accepted on face value. Big mistake. In fact, I never store any information in the cookie. I view it as a session key and that's all.
If you intend to store data in a cookie I strongly advise you to reconsider.
Encryption of this data does not make the information any more trustworth because symmetric encryption is susceptible to brute-force attack. Obviously AES-256 is better than, say, DES (heh) but 256-bits of security doesn't necessarily mean as much as you think it does.
For one thing, SALTs are typically generated according to an algorithm or are otherwise susceptible to attack.
For another, cookie data is a prime candidate for crib attacks. If it is known or suspected that a username is in the encrypted data will hey, there's your crib.
This brings us back to the first point: hijacking.
It should be pointed out that on shared-hosting environments in PHP (as one example) your session data is simply stored on the filesystem and is readable by anyone else on that same host although they don't necessarily know which site it is for. So never store plaintext passwords, credit card numbers, extensive personal details or anything that might otherwise be deemed as sensitive in session data in such environments without some form of encryption or, better yet, just storing a key in the session and storing the actual sensitive data in a database.
Note: the above is not unique to PHP.
But that's server side encryption.
Now you could argue that encrypting a session with some extra data will make it more secure from hijacking. A common example is the user's IP address. Problem is many people use the same PC/laptop at many different locations (eg Wifi hotspots, work, home). Also many environments will use a variety of IP addresses as the source address, particularly in corporate environments.
You might also use the user agent but that's guessable.
So really, as far as I can tell, there's no real reason to use cookie encryption at all. I never did think there was but in light of this question I went looking to be proven either right or wrong. I found a few threads about people suggesting ways to encrypt cookie data, transparently do it with Apache modules, and so on but these all seemed motivated by protecting data stored in a cookie (which imho you shouldn't do).
I've yet to see a security argument for encrypting a cookie that represents nothing more than a session key.
I will happily be proven wrong if someone can point out something to the contrary.
Security Warning: These two functions are not secure. They're using ECB mode and fail to authenticate the ciphertext. See this answer for a better way forward.
For those reading through wanting to use this method in PHP scripts. Here is a working example using 256bit Rijndael (not AES).
function encrypt($text, $salt)
{
return trim(base64_encode(mcrypt_encrypt(MCRYPT_RIJNDAEL_256, $salt, $text, MCRYPT_MODE_ECB, mcrypt_create_iv(mcrypt_get_iv_size(MCRYPT_RIJNDAEL_256, MCRYPT_MODE_ECB), MCRYPT_RAND))));
}
function decrypt($text, $salt)
{
return trim(mcrypt_decrypt(MCRYPT_RIJNDAEL_256, $salt, base64_decode($text), MCRYPT_MODE_ECB, mcrypt_create_iv(mcrypt_get_iv_size(MCRYPT_RIJNDAEL_256, MCRYPT_MODE_ECB), MCRYPT_RAND)));
}
Then to save the cookie
setcookie("PHPSESSION", encrypt('thecookiedata', 'longsecretsalt'));
and to read on the next page:
$data = decrypt($_COOKIE['PHPSESSION'], 'longsecretsalt');
Fast, Encrypted Cookies with Libsodium
If you need fast, secure encrypted cookies in PHP, check out how Halite implements them. Halite relies on the libsodium PECL extension to provide secure cryptography.
<?php
use \ParagonIE\Halite\Cookie;
use \ParagonIE\Halite\Symmetric\Key;
use \ParagonIE\Halite\Symmetric\SecretKey;
// You can also use Key::deriveFromPassword($password, $salt, Key::CRYPTO_SECRETBOX);
$encryption_key = new SecretKey($some_constant_32byte_string_here);
$cookie = new Cookie($encryption_key);
$cookie->store('index', $any_value);
$some_value = $cookie->fetch('other_index');
If you cannot install PECL extensions, ask your sysadmin or hosting provider to do it for you. If they refuse, you still have options.
Secure Encrypted Cookies in PHP, Hold the Salt Please
The other answers instruct you to encrypt your data with openssl or mcrypt, but they're missing a crucial step. If you want to safely encrypt data in PHP, you must authenticate your messages.
Using the OpenSSL extension, the process you would need to follow looks like this:
Preamble
(Before you even think about encryption) Generate a 128-bit, 192-bit, or 256-bit random string. This will be your master key.
Do not use a human-readable password. If you, for some reason, must use a human-readable password, ask Cryptography SE for guidance.
If you need special attention, my employer offers technology consulting services, including development of cryptography features.
Encryption
Generate a random Initialization Vector (IV) or nonce. e.g. random_bytes(openssl_cipher_iv_length('aes-256-cbc'))
Use HKDF or a similar algorithm for splitting your master key into two keys:
An encryption key ($eKey)
An authentication key ($aKey)
Encrypt your string with openssl_encrypt() with your IV and an appropriate modate (e.g. aes-256-ctr) using your encryption key ($eKey) from step 2.
Compute an authentication tag of your ciphertext from step 3, using a keyed hash function such as HMAC-SHA256. e.g. hash_hmac('sha256', $iv.$ciphertext, $aKey). It's very important to authenticate after encryption, and to encapsulate the IV/nonce as well.
Package the authentication tag, IV or nonce, and ciphertext together and optionally encode it with bin2hex() or base64_encode(). (Warning: This approach might leak cache-timing information.)
Decryption
Split your key, as per step 2 in encryption. We need the same two keys during decryption!
(Optionally, decode and) unpack the MAC, IV, and ciphertext from the packed message.
Verify the authentication tag by recalculating the HMAC of the IV/nonce and ciphertext with the user-provided HMAC by using hash_equals().
If and only if step 3 passes, decrypt the ciphertext using $eKey.
If you want to see how this all looks together, see this answer which has sample code.
If this sounds like too much work, use defuse/php-encryption or zend-crypt and call it a day.
Remember Me Cookies
However, we have a requirement to implement a 'remeber me' feature. The accepted way to go about this is by setting a cookie. If the client presents this cookie, he or she is allowed access the system with (almost) equal rights as if he/she presented the valid username password combination.
Encryption is actually not the correct tool for this job. You want to follow this process for secure remember me cookies in PHP:
Generating a Remember Me Token
Generate two random strings:
A selector which will be used for database lookups. (The purpose of a random selector instead of just a sequential ID is to not leak how many active users are on your website. If you're comfortable leaking this information, feel free to just use a sequential ID.)
A validator which will be used to authenticate the user automatically.
Calculate a hash of validator (a simple SHA-256 hash will suffice).
Store the selector and the hash of the validator in a database table reserved for automatic logins.
Store the selector and validator in a cookie on the client.
Redeeming a Remember Me Token
Split the incoming cookie into the selector and validator.
Perform a database lookup (use prepared statements!) based on selector.
If a row is found, calculate a hash of the validator.
Compare the hash calculated in step 3 with the hash stored in the database, once again using hash_equals().
If step 4 returns true, log the user in to the appropriate account.
This is the strategy that Gatekeeper adopted for long-term user authentication and it is the most secure strategy proposed to date for satisfying this requirement.
You can achieve what you want securely by using AES in EAX mode. The ciphertext will be larger than the plaintext; that's normal for secure encryption.
The attacker will of course know the length of your plaintext from the ciphertext, but they shouldn't be able to determine anything else.
Generate AES keys randomly.
Be sure and use a fresh nonce for each encryption, and use the "associated data" field to ensure that a thing you encrypted for one purpose isn't presented as being for another (so things like the user name and cookie name could go in there)
the reactions below push toward: Do
not trust encryption to accomplish
security.
More "if you're not an encryption expert you'll underestimate how easy it is to get wrong". For example, AFAICT no-one else in this thread has discussed chaining modes or message integrity, which covers two common beginner's mistakes.
While both a very strong ones, AES is a standard.
As for security of small chunks of data: the smaller - the better. The less encrypted data is exposed, the longer you can use the key. There is always a theoretical limit of how much data can be encrypted within one key of given algorithm without exposing system to risks.
As pointed out a few times in previous comments, you must apply integrity protection to any ciphertext that you send out to the user and accept back. Otherwise the protected data can be modified, or the encryption key recovered.
Especially the PHP world is full of bad examples that ignore this (see PHP cryptography - proceed with care) but this does apply to any language.
One of few good examples I've seen is PHP-CryptLib which uses combined encryption-authentication mode to do the job. For Python pyOCB offers similar functionality.
Why do you want to encrypt the cookie?
As I see it, there are two cases: either you give the client the key, or you don't.
If you don't give the key to the client, then why are you giving them the data? Unless you're playing some weird game with breaking weak encryption (which you're explicitly not), you might as well store the data on the server.
If you do hand the client the key, then why do you encrypt it in the first place? If you don't encrypt the communication of the key, then encrypting the cookie is moot: a MITM can look at the cookie and send you any cookie he wants. If you use an encrypted channel to the client, why the extra overhead of encrypting the stored data?
If you're worried about other users on the client's machine reading the cookie, give up and assume the browser sets good permission bits :)
If you encrypt the cookie, the server still has to decode it to read it (to check for same key), therefore any encrypted cookie is pointless, because if stolen (and un-edited) it will still lead the hacker right to your account. Its just as unsafe as no encrypted at all.
I believe the real issue of someone stealing your cookie is the connection between the server and client. Use SSL connection provided by your host.
As for your cookie, you need to make a long random id per user in the database, (have it change every log on) and just set that as the cookie or session. The cookie that contains the key can be checked via php and if it is equal to an account or table in your database, dump the data on the web page like normal.
AES (also known as Rijndael) is the most popular. The block size is 128-bits, that's only 16-bytes, and you're talking "around 100 characters".
I think that "giving away" any data even encrypted when it is about username and password is not good ...
There are many JS that can sniff it ...
I suggest you create in users DB table a field cookie_auth or whatever ...
after first login gather : current: browser, IP,ans some own salt key, plus your hostname var ...
create a hash and store in that field ...
set a cookie ...
when cookie "responds" compare all of these with the stored hash and done ...
even if someone "steal" a cookie they won't be able to use it :-)
Hope this helps :-)
feha
vision.to
In addition, I have tried the mcrypt_encrypt and one thing please keep in mind. If you do base64_encode(mcrypt_encrypt(...)).
and then later, you do base64_decode and output the encrypted data (echo). You probably will be screwed and not seeing anything. However, if you do mcrypt_decrypt( ... base64_decode($value) ). You will see the original data.
So many terrifying things been said, which is true though, but let's see the bright side, a little common sense and continuous watch over your site might save you all the time.
Saving cookies is an important part of web development so one can't ignore it. But also we should avoid as much as possible; I see the use of Cookies only if I want to extends the login session even after user close the browser. If ones don't want to extends the user session beyond browser closing, then Session component should be used. Even with Session component usage one should be aware of Session Hijacking.
Anyways, back to Cookie thing; In my opinion if one's follow the following precautionary measurement, I am pretty sure we can be on the safer side.
I divide the precautionary measurement in to two phase
Phase1: Development
Set path attribute
Set expiration_date
set secure, httpOnly attributes
Use latest encryption Algorithms
Use two algorithms: for instance use blowfish and then use base64_encode on top of it.
Phase 2: Operation/Audit
Periodically make site audit, using tools like burp.

Categories