Long random string generator in PHP - php

Does anyone know how to generate a long (e.g. 280 characters) random string in PHP without having to use a for loop that will loop through characters 280 times? I need it in order to create a custom session ID.
The PHPSESSID is not secure enough in my opinion being too short and not too random. I know Facebook and Twitter, use long session IDs (150, 550 chars respectively).
There could be an option to use MD5 strings or Bcrypt encryption of different string such as PHPSESSID, host, User-Agent etc. but I'm not sure this is the right way of doing it.

If you're asking a question like that, it probably means you don't know anything about cryptography or security. Trying to generate a "long random string" because, as you say, "The PHPSESSID is not secure enough" will probably lead you to a custom and insecure implementation.
Generating a random string is IMPOSSIBLE, at least not with your current hardware: you may approximate a fair pseudorandom generator but that is only useful for educational purposes.
PHP's Session ID generation algorithm is fairly efficient; if you think it is not secure enough, then you'll likely waste time making it better. You may probably want to use a different authentication mechanism if you are looking at maximum security (using a client certificate for example).
If websites such as Twitter, Facebook, or another site with similar traffic use longer session IDs, it may be not because it is more secure (well in a way), but rather because it avoids conflicts.
Finally, if you want a longer session ID without trying to write your own algorithm, you should use the following PHP configuration directive:
session.hash_function which can take any hash algorithm known by PHP.
You may also want to use session.bits_per_characters to shorten or lengthen the string. Note that if you do this, the string may be longer or shorter, but the data remains the same -- only represented differently (base 16, base 32, etc.)
Additional info:
You may also increase the entropy by using a custom source (file) and setting the length of the seed:
ini_set("session.hash_function", "sha512");
ini_set("session.bits_per_charater", 4); // 4 means hex
ini_set("session.entropy_file", "/dev/urandom");
ini_set("session.entropy_length", "512");

Can it be binary hexadecimal?
bin2hex(mcrypt_create_iv(140, MCRYPT_DEV_URANDOM));
Or if you have access to hash_pbkdf2():
hash_pbkdf2('sha256', PHPSESSID, mt_rand(), 1, 280, false);

You'll probably find that not all the bits in a "long" session id are purely random - they might provide additional information to the application to enable rapid extraction of key information.
PHP session ids by default aren't too bad, and can be improved by providing some extra entropy (e.g. from /dev/urandom)
Look at php_session_create_id in ext/session/session.c in the php source
It goes like this:
get time of day
get remote ip address
build a string with the seconds and microseconds from the current time, along with the IP address
feed that into configured session hash function (either MD5 or SHA1)
if configured, feed some additional randomness from an entropy file
generate final hash value
So getting a duplicate or predicting a session id is pretty difficult.

Make yourself a long string and shuffle it.
$string = 'asdfhdfiadfshsdfDSGFADSFDSFDSFER524353452345';
$new_string = shuffle($string);

Related

Can I provide an encrypted string to users and be sure it is safe for 1-2 hours

We were making kind of a simple game,
in which:
Users receive the next number of play as an encrypted string Before they play
After they play, the encryption password is provided to them to check the play number was correct.
Each encrypted string is only valid for 1-2 hours and number of play , verificating string and encrypted string is regenerated again after that time
The encrypted string includes a verification (5 char) code so both users and we can make sure Decryption process was successful
Sample Character to get Encrypted (QQ9LU is random verification code provided to user before the play):
Next Play Number: 8 - Verify String: QQ9LU
Sample Encrypted String (provided to user before play):
NXRykKOv3B6kuu4Ke3svp7HH3enNiqIZrJSXJiF54QkHHjtXgqpUXxyuP7YUNICeFLg==
Sample Password (provided after play):
Please note this is generated randomly for each encryption
FA00RDjA77hlOzcOzH6kuGcc29CyM7Hw
We use CodeIgniter 2.2.2 Encryption Class to encrypt/decrypt strings
Encryption Method Info:
Function Used: $this->encrypt->encode($msg, $pass); with random pass each time
Cipher is CodeIgniter 2 default MCRYPT_RIJNDAEL_256
Mcrypt mode is MCRYPT_MODE_CBC
My Questions are:
Can i trust that users cannot break the encrypted string (and know the number of play before they get the password) in 1-2 hours (aside from getting lucky)
Is placing random verification code Verify String: T3YH4 in there good or bad? does is affect security? (this is to verify decryption result was successful, also we added it because the only variable in each string was a single digit, for example only number 8 changes to 7, so we wanted to add more variable characters to the string to possibly have a better security)
Any other suggestion is appreciated
Short answers:
From a technical POV, what you're doing is unsafe, although it might be enough for just a 2-hour timeframe.
What you're trying to do here is called "message authentication", but that's not how it should be done, which in turn does impact security. You should use a HMAC instead.
My advice would be to upgrade to CodeIgniter 3 (CI2 will stop receiving even security updates in a few months) as soon as possible, and use its new Encryption library instead. That will make it safe for years, not hours.
Long answer:
The encryption library should do both encryption and authentication for you, but unfortunately the CI_Encrypt class itself is badly written and lacking a lot of functionality (such as authentication), which is why it was DEPRECATED and is being replaced by a new (CI_Encryption) library in CodeIgniter 3.
Explaining all the flaws in here would be quite the task, so I'd rather link you to an external article (not self-promoting, don't worry), which does that quite nicely if you're interested in the low-level details.
No matter which library you use however, one thing must be noted - a password is not the same thing as an encryption key.
Passwords have a varying length and are used by humans, which means that they must be readable by humans, and that in turn limits them to a certain set of characters.
Encryption keys on the other hand have a fixed length (each encryption algorithm is designed to work with a specific key length; for Rijndael-256 that's 32 bytes, which you seem to match) and are not limited to human-readable characters (which means more entropy and therefore more security) - they represent raw binary data.
Anything else can be controlled (and therefore automatically done) by a library, but if you pass a password instead of a key - that's what the library will use, so you should take care of that.
The best and simple way to do that is to use the filesystem functions to create a simple text file for each user in non public path with two lines, the first of them is a unique random string (long string varied in length) and the second is the number.
Then using sha1_file get the hash value of the file then store it in the database related to its path and creating time, then send this hash to the user.
After the user has played, check the value by another script that get the value of the hash from the database, then read the file and parse its second line to display the number.
By this way, you have gave the user a hash not for a string, but it for a file and cracking it to get the file back is not simple as to be done in two hours.
You are giving your Encryption/Decryption logic to client side. Hacker will easily identify how your password and encryption strings are being match.
Many framework have their own password creationg and compare logics. Yii using SALT and other features like SHA1 etc...
Keep it simple and keep all things at your end. Generate your encryption things and store at your end. Follow simple steps,
Generate encryption password (using SALT and/or other encryption tools) and store at your end
Ask client (user) to enter their password (key) and get at server side
Convert your password (key) to encryption password and compare
CPasswordHelper will be helpful for you. Try to download Yii source code and put out their logic for you.
Hope that helps !!
Sounds like a fun game!
I am assuming you are creating these strings in files on a filesystem. If you were hosting them on some web application that would assume different techniques to break the string.
Adding a code to the end of the string is called salting the string. While this makes the string harder to guess, if you are adding a hardcoded salt instead of a randomly generated salt it can still be easily broken by brute force methods.
I would try using a one-way hashed string for the password and storing that in a database. The user is unable to decrypt the string and has to just provide a matching password to gain access to your string. It is possible for programs to break one-way hashed strings but I find it unlikely someone will be smart enough to do that if they are in college and only have two hours. It takes alot of domain knowledge and experience to start generating one-way hashed strings to brute force it.
In addition you are probably safe with the method you are doing currently, students will not likely be able to break a string in 2 hours unless they are familiar with advanced encryption hacking scripts that take some work to find. I am guessing they will do trial and error, using different decryption libraries similar to the example you provide and hoping they get lucky with the library of strings they are trying to match against yours.
Also information is important with any type of encryption. Telling someone you are adding a 5 code salt to your string, will give them some insight into how your encryption algorithm works. They can then try methods of breaking it based on the information you give them. Try the same thing with your own algorithm and leave the students in the dark, I doubt anyone will break anything in the time alotted. Alot of hacking techniques involve going through an information gathering process where the hacker scopes out or maps a system before trying to attack it.

PHP Generate Random 128-bit for persistent login

I'm making a persistent login using this setup.
How I understood it is that I need a extra table with username and a hash of the cookie token. Then you can have multiple tokens for one user.
The issue I'm having is that I need a random token. In the article they advise it to be 128 bits, so 16 hexadecimal values I guess. But these need to be truly random, because this is very important. To store them, I'll bcrypt() them, so that if the db is compromised it still takes 'long' before the cookies can be used to login to another account.
TL;DR: Need a true random 128-bit string/number/... . I've found several methods, but I don't know which ones are truly random.
Don't reinvent the wheel (especially with cryptography, you'll make it worse 99% of the time).
Try something like the OpenSSL implementation.

Entropy in CSRF Tokens and Nonces

I am making a classified ads site with Zend Framework (for portfolio purposes, yes I know the world doesn't have room for "yet another Craigslist clone"). I am trying to implement the ability to post/edit/delete without ever needing an account.
To do this, I feel like I need to have a Nonce generated upon post submission and stored in the database. Then email a link to the user which makes a GET request for the delete, like this:
http://www.somesite.com/post/delete/?id=123&nonce=2JDXS93JFKS8204HJTHSLDH230945HSLDF
Only the user has this unique key or nonce, and upon submission I check the database under the post's ID and ensure the nonce matches prior to deleting.
My issue is how secure the nonce actually is. If I use Zend Framework's Zend_Form_Element_Hash, it creates the hash like this:
protected function _generateHash()
{
$this->_hash = md5(
mt_rand(1,1000000)
. $this->getSalt()
. $this->getName()
. mt_rand(1,1000000)
);
$this->setValue($this->_hash);
}
In reading about mt_rand(), one commenter said "This function has limited entrophy. So, if you want to create random string, it will produce only about 2 billion different strings, no matter the length of the string. This can be serous security issue if you are using such strings for session indentifiers, passwords etc."
Due to the lifetime of the nonce/token in the application, which could be days or weeks before user chooses to delete post, I think more than enough time would be given for a potential hack.
I realize mt_rand() is a huge upgrade from rand() as seen in this visual mapping pixels with rand on the left, and mt_rand on the right. But is it enough? What makes "2 billion different strings" a security issue?
And ultimately, how can I increase the entropy of a nonce/token/hash?
For such security it's not only important how long your output is. It counts how much randomness you've used to create it.
For mt_rand() the source of randomness is its seed and state (number of times you've used it since it was seeded). More mt_rand() calls will just give you more rehasing of the same randomness source (no new entropy).
mt_rand()'s seed is only 32-bit (anything less than 128bit makes cryptographers suspicious ;)
Strength of a keys with 32-bits of entropy is 4 billion divided by (roughly) number of keys you'll generate (e.g. after 100K uses there will be ~1:43000 chance to guess any valid key, which approaches practical brute-forcing).
You're adding salt to this, which makes it much stronger, because in addition to guessing the seed attacker would have to know the salt as well, so if the salt is long, then overall the key may be quite strong despite "low" entropy.
To increase entropy you need to add more random stuff (even slightly random is OK too, just gives less bits) from different sources than mt_rand: microtime(), amount of memory used, process ID... or just use /dev/random, which collects all entropy it can get.
(edit: uniqid() has weak entropy, so it won't help here)
The Zend hash generating code above's input for the md5() hashing function has 1,000,000 X 1,000,000 different possibilities. md5() has 32^16 (1208925819614629174706176) possible outcomes no matter what the input is. On average, the hacker would need to send 500,000,000,000 requests to your server in order to guess the right nonce.
At 100 requests per minute, that's about 3472222 days to hack.

Easy Encryption and Decryption with PHP

My PHP Application uses URLs like these:
http://domain.com/userid/120
http://domain.com/userid/121
The keys and the end of the URL are basically the primary key of the MySQL database table.
I don't want this increasing number to be public and I also don't want that someone will be able to crawl the user profiles just by interating the Id.
So I want to encrypt this Id for display in a way I can easily decrypt it again. The string shouldn't get much longer.
What's the best encryption method for this?
Simple Obscuring: Base64 encode them using base64_encode.
Now, your http://domain.com/userid/121 becomes: http://domain.com/userid/MTIx
Want more, do it again, add some letters around it.
Tough Obscuring: Use any encryption method using MCrypt library.
A better approach (from a usability and SEO perspective) would be to use a unique phrase rather than an obscured ID. In this instance the user's user name would seem an ideal solution, and would also be un-guessable.
That said, if you don't want to use this approach you could just use a hash (perhaps md5) of the user's user name which you'd store in the database along with their other details. As such, you can just do a direct lookup on that field. (i.e.: Having encrypt and decrypt part of the URL is probably overkill.)
You have a variety of choices here:
Generate and store an identifier in the database. It's good because you can then have readable keys that are guaranteed to be unique. It's bad because it causes a database schema change, and you have to actually query that table every time you want to generate a link.
Run an actual key-based encryption, for instance based on PHP's MCrypt. You have access to powerful cryptographic algorithms, but most secure algorithms tend to output strings that are much longer than what you expect. XOR does what you want, but it does not prevent accessing sequential values (and the key is pretty simple to determine, given the a priori knowledge about the numbers).
Run a hash-based verification: instead of using 121 as your identifier, use 121-a34df6 where a34df6 are the first six characters of the md5 (or other HMAC) of 121 and a secret key. Instead of decoding, you extract the 121 and recompute the six characters, to see if they match what the user sent. This does not hide the 121 (it's still right there before the hyphen) but without knowing the secret key, the visitor will not be able to generate the six characters to actually view the document numbered 121.
Use XOR with shuffling: shuffle the bits in the 30-bit identifier, then apply the XOR. This makes the XOR harder to identify because the shuffle pattern is also hidden.
Use XOR with on-demand keys: use fb37cde4-37b3 as your key, where the first part is the XOR of 121 and md5('37b3'.SECRET) (or another way of generating an XOR key based on 37b3 and a secret).
Don't use base64, it's easy to reverse engineer: if MTIx is 121, then MTIy is 122 ...
Ultimately, you will have to accept that your solution will not be secure: not only is it possible for users to leak valid urls (through their browser history, HTTP referer, or posting them on Twitter), but your requirement that the identifier fits in a small number of characters means a brute-force attack is possible (and becomes easier as you start having more documents).
Simplest but powerful encryption method: XOR with a secret Key. http://en.wikipedia.org/wiki/XOR_cipher
No practical performance degradation.
Base64 representation is not an encryption! It's another way to say the same.
Hope this helps.
Obscuring the URL will never secure it. It makes it harder to read, but not much harder to manipulate. You could use a hexadecimal number representation or something like that to obscure it. Those who can read hex can change your URL in a few seconds, anyway:
$hexId = dechex($id); // to hex
$id = hexdec($hexId); // from hex
I'd probably say it's better indeed to just create a random string for each user and store that in your database than to get one using hash. If you use a common hash, it's still very easy to iterate over all pages ;-)
I would write this in comments, but don't have the rep for it (yet?).
When user click on a link you should not use primary key, You can use the pkey in a session and get it from that session. Please do not use query string....
generate an unique string for each user and use it in your urls
http://domain.com/user/ofisdoifsdlfkjsdlfkj instead of http://domain.com/userid/121
you can use base64_encode and base64_decode function for encrypt and decrypt your URLS

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