Passworded cookie

Discussions of secure PHP coding. Security in software is important, so don't be afraid to ask. And when answering: be anal. Nitpick. No security vulnerability is too small.

Moderator: General Moderators

User avatar
kaisellgren
DevNet Resident
Posts: 1675
Joined: Sat Jan 07, 2006 5:52 am
Location: Lahti, Finland.

Re: Passworded cookie

Post by kaisellgren »

dbsights wrote:Also you said that the problem was the weakness of the password not the encryption.
The password is the identifier in your case so,
dbsights wrote:How would you exploit this fact?
Session Forging attacks play a large role here.

It's more theoretical than practical.
dbsights wrote:I have heard that APC can have trouble storing large numbers of objects.
You could test drive APC to see if there are problems. I've not encountered anything bad myself.
dbsights
Forum Newbie
Posts: 13
Joined: Thu Jun 11, 2009 6:44 pm

Re: Passworded cookie

Post by dbsights »

So I've run into two problems with my new sessions. One is pretty specific just to my situation/windows, while the other is just a general problem. Any advice for concurrency over sessions?
User avatar
kaisellgren
DevNet Resident
Posts: 1675
Joined: Sat Jan 07, 2006 5:52 am
Location: Lahti, Finland.

Re: Passworded cookie

Post by kaisellgren »

What are the problems you are exactly facing?
dbsights
Forum Newbie
Posts: 13
Joined: Thu Jun 11, 2009 6:44 pm

Re: Passworded cookie

Post by dbsights »

Basically, random number generation without linux sucks. I was generating some home-cooked solutions, but none of them were nearly as good as simply using /dev/. I am currently in the midst of fixing this by installing linux for the first time on my laptop.

Code: Select all

 
            //Create new session ID
            $key = '456jgh9jh4h69fgUII$h835G784h89f4s';
            //Length of the ID
            $identifier = '';
            
            for($i = 0; $i < 64; $i++)
            {
                $rand = '';
                
                //Multiple inputs per character
                for($j = 0; $j < 8; $j++)
                {
                    $rand .= chr(mt_rand(0, 255));
                }
                
                //Hash chars
                $identifier .= hash('sha256', $rand);
            }
            
            //Hash Ident.  HMAC, Ident
            $identifier = uniqid(hash_hmac('sha256', $identifier, $key), true);
 
 


There was a 0.58% chance of collisions when the random number generator was repeatedly autoseeded (as it would be by multiple users).

The second problem I was having had to do with concurrency over multiple sessions. I am still trying to reason this out, which is why I asked for your help. I figure that this must be a very common problem and so there is probably already resources I can use to learn about it. However, I have had a hard time finding them. Basically, I want to know how to insulate data cached around the server from conflicting changes. For maximum usability, I am considering a case of multiple processes reading from distributed memory, such as would occur on a large scale server. Once this is worked out, then I can proceed with my session class by combining it w/ a more general framework for caching.

To solve the caching I have two general ideas:
1) A flag is set to indicate when something is going to be written soon.
2) Times are associated with properties and the newest is always kept.

What do you think?
User avatar
kaisellgren
DevNet Resident
Posts: 1675
Joined: Sat Jan 07, 2006 5:52 am
Location: Lahti, Finland.

Re: Passworded cookie

Post by kaisellgren »

dbsights wrote:Basically, random number generation without linux sucks.
In my opinion, Linux > Windows when it comes to web servers. Microsoft is gaining popularity more and more with its free IIS, but it still lacks in certain aspects. You can, however, use Windows' built-in RNG, which is installed on each Windows based machine. Press Start -> Run and type services.msc and look for Cryptographic Services. If you can find it, your machine supports CSP. (http://msdn.microsoft.com/en-us/library ... S.85).aspx). On 32-bit systems, you can simply use PHP's COM object to initialize the appripriate CSP object and create strong random data, which I have analyzed to be even stronger than /dev/urandom in many tests. However, on 64-bit systems you are out of luck. As far as I can tell, on 64-bit Windows systems there is no way to generate strong random data without using third-party applications (outsourcing). MSDN has a .NET component, which seems to deliver strong bytes from the CSP, but thanks to PHP - PHP is unable to use 64-bit NET components that has to work with VARIANTs.

Keep in mind one thing: hashes & ciphers != RNGs. If you want good entropy, good randomness and unpredictability, don't use hashes and don't encrypt anything.

The use of HMACs, hashes and uniqid's actually make you lose entropy. You should simply For loop and generate a char from chr(mt_rand()+...) where ... is a list of "random" sources. Never concat anything into the set, always sum up. Don't do chr(mt_rand().another_random_source()) it will make you lose entropy a lot. You need to plus it (sum up) and then use a modulos % 255 to make it within the acceptable character range.

There are several things you could use to generate "random" data.

microtime() - probably most known way. However, I would never use more than the last 4 digits of it as I have noticed how non-random results you get by using the entire value. Also, in tight loops this becomes not so random.
getmypid() - gets the process ID, which is rather random, but it doesn't change until you reload the page (aka execute the script again).
memory_get_usage() - has weak avalance -type effect and is too predictable due to nondynamicness of the value. If you use it, take only last 2 bytes of it.

Since you can use COM on Windows 32-bit, and /dev/urandom on Unix -like systems, the only situation where you basically have to use your own RNG is on a Windows machine. So, you could read random (mt_rand()) files on the system root and read their data statistics. Then do something like:

Code: Select all

$stat = stat('a_randomly_chosen_windows_root_file');
$more = $stat[7]+substr($stat[8],-3,3)+substr($stat[9],-3,3)+substr($stat[10],-3,3))
And again, you would sum the $more into the mt_rand() and others. I used substr() to take only the last bytes as the whole value, again, is not very random. To do even better, you could read server log file info or the system temp or anywhere you have lots of files which are frequently accessed or changed.

Or you could read http://www.random.org for random data, but the random data is transferred over the network in plain-text format and you are not the only one who knows the random data..

Basically, on a 64-bit Windows server you are more or less out of luck for now. In the future, PHP has openssl_get_pseudo_bytes() or similar function that will rescue us and hopefully PHP fixes the NET component bugs, too, which have existed for years. :evil: (http://bugs.php.net/bug.php?id=41286)

OpenSSL has a quite cool way to generate random data - by hashing the screen content among with several other wicked details (mostly hardware related).

I'm not entirely sure what is your problem with sessions or caching, but does it have something to do with locking? You can lock files with flock() and (My)SQL offer their equivalents depending on the RDBMS and the used storage engine. (http://search.mysql.com/search?site=ref ... lr=lang_en)
dbsights
Forum Newbie
Posts: 13
Joined: Thu Jun 11, 2009 6:44 pm

Re: Passworded cookie

Post by dbsights »

Interesting facts about entropy. Just read your blog article :D. My intention with the hashes was to obscure the mt_rand as it is linear and just exposing its output would leave it very vulnerable, the HMAC was to prevent hash guessing attacks. Did not know about those other functions to collect data, they are really useful. I'm pretty sure with a bit more random data added into the mix, this simple session would be more than good enough as it is both hard to guess, fast, and very hard to collide. However, now that my linux laptop is finally running (although it seems sluggish compared to windows) this problem has become trivial.

As for concurrency, I am working on a situation independent of mysql, most likely implemented through memcached, so the database methods will not work. Also, locking seems unattractive because it will slow down the application while locks are in place.
User avatar
kaisellgren
DevNet Resident
Posts: 1675
Joined: Sat Jan 07, 2006 5:52 am
Location: Lahti, Finland.

Re: Passworded cookie

Post by kaisellgren »

dbsights wrote:My intention with the hashes was to obscure the mt_rand as it is linear and just exposing its output would leave it very vulnerable
Exposing the state of a linear congruential generator is indeed very dangerous. By summing a good set of other pseudo "random" sources eliminates this problem, but is not of course an ideal solution and for security demanding software you may want to disable the service at this point and alarm the webmaster of not being able to serve good random data.
dbsights wrote:Did not know about those other functions to collect data, they are really useful.
What functions and what data? Random data?
dbsights wrote:However, now that my linux laptop is finally running this problem has become trivial.
Now I'm not following, what kind of problem?
dbsights wrote:most likely implemented through memcached
Ah, okay. I'm pretty sure there's something similar in case of memcached. I haven't used it besides some testing in the past.
dbsights wrote:Also, locking seems unattractive because it will slow down the application while locks are in place.
Locks are used pretty much everywhere. Even your operating system does so often and so do web applications. For example, the PHP's built-in session system locks the files. Caring so much about lock's performance seems a bit ridiculous at this point, because even your earlier encryption & hash systems for cookies were slower. :P
dbsights
Forum Newbie
Posts: 13
Joined: Thu Jun 11, 2009 6:44 pm

Re: Passworded cookie

Post by dbsights »

Hah it was really not all that slow, I was running tests w. 1000's of iterations and it didn't really tax my computer.

So as for concurrency, here are my ideas compared.

1. Object locking. All objects in memcached contain a simple flag that when set, prevents scripts from reading them. This flag is set after a read is made that will be acted upon (will result in a write). Once the write is complete, the lock is removed. (Variant: the flag only blocks other writing processes, not read-only processes).

PROS
Low overhead
Simple to implement

CONS
What to do with blocked reads?

2. Property locking. Identical to above but with a separate flag for each property

PROS
Reduces instances of blocked reads when many properties are in demand

CONS
Larger overhead

3. Object versioning. For each object in memcached, there is also a 'super object' containing meta data about the object it manages. Whenever a process needs to read an object, it first reads the super object associated with that object to find the most up to date version of the object and reads it unconditionally. If this process was reading with intent to write, it also writes the time that it read the source into the super object. When the process needs to write to the object it instead creates a new object with the modified properties. It then updates the super object entry (containing the read time) with a reference to the new object and the time it wrote. Finally, it parses the super object, destroying any out of date objects and setting the new source as the most recent version; combining versions if necessary by taking only the differences. If identical properties have been altered, the property that was written latest is taken.

PROS
Solution to write blocking on object

CONS
Double access time for reads and writes
Additional overhead to parse super object
What if multiple processes overwrite the super object?

4. Property versioning. A mix of all the above methods. Every property on an object has an associated timestamp. When a process wants to read a property from an object, it first records the time, then it reads the property. When writing, the process checks that the timestamp is not greater than the read time. If it is, it rereads the property and recalculated. If the timestamp is less than the read time, it writes the property and the time.

PROS
Simple, holistic solution

CONS
High overhead
Code may run longer (possibly a lot, depends on the demand for that object. This is only for read/write, reads will progress at normal speed)

So what do you think, or do you have a better solution of your own. Right now I am leaning towards either #1 or #4. Maybe multiple systems could be used in a framework of some kind so that objects only got as much write-protection as they needed...
User avatar
kaisellgren
DevNet Resident
Posts: 1675
Joined: Sat Jan 07, 2006 5:52 am
Location: Lahti, Finland.

Re: Passworded cookie

Post by kaisellgren »

dbsights wrote:So what do you think, or do you have a better solution of your own.
Is this a question to everyone or just me? Sorry, but I mostly care about security questions and I think you should make a topic at Code or Theory forum as you would get much more replies there.
dbsights
Forum Newbie
Posts: 13
Joined: Thu Jun 11, 2009 6:44 pm

Re: Passworded cookie

Post by dbsights »

Hah! True. This conversation has definitely gone off topic. I will repost under code/design as suggested.
Post Reply