I suppose I better write about this now, as I encountered these errors a while ago, and the details already started slipping my mind. So, what’s happening? I am writing some PHP code. As a testing server, I’m using my ODROID U3. An ARMv7 board, running ArchLinuxARM. Therefore, we have a 32 bit machine running PHP 5.6. Now, the (soon-to-be) production server is an x86_64 machine, running Ubuntu 14.04LTS. So, this is a 64 bit machine running PHP 5.5.
Let’s have a look at the
mcrypt_create_iv() function. This function produces random data. Now, there is a change introduced in PHP 5.6.0. First, let’s have a look on how random data works on a Linux platform. (Other *NIX platform might differ from this)
/dev/random Gives random data, and blocks until there is enough entropy available.
/dev/urandom Gives random data, but never blocks. This means possible less entropy in the result.
Basically, we’re interested in the most possible entropy when we’re doing crypto, then we use
/dev/random. That’s why, for example, generating PGP keys takes so long. It’s just waiting for enough entropy to become available. When we need some randomness in a setting that doesn’t require crypto-grade entropy,
/dev/urandom is sufficient.
Now, back to PHP. We have the
mcrypt_create_iv() function to generate random data. Now, what happened. Since PHP 5.6.0 the default source for randomness is
/dev/urandom. In prior versions it was
/dev/random. So…. what happens? Code that runs fine on the development machine hangs on the production server, as it’s waiting for entropy. That’s not what we want to happen on a website. So, when using the
mcrypt_create_iv() function, always specify the entropy source as the default differs per PHP version.
Well… the difference between 32 and 64 bit PHP versions, it’s all in the size of the int, or rather, using the
PHP_INT_MAX for purposes it wasn’t intended for. So, we’re looking at setting a cookie. The idea is to have a “stay logged in” option, so we set the cookie to a date/time way in the future. Considering the Year 2038 problem, the INT_MAX for 32 bit, results in 19 January 2038. However, this is far enough in the future to be used as a “remember me” function. As systems turn 64 bit, this problem is solved, and why not setting the date even to a later date? It just needs to be way ahead in the future to simulate a “never expire” cookie. Well… let’s have a closer look on how cookies work. In PHP, the
setcookie() takes a unix timestamp, so, it’s just an INT value containing the number of seconds since the epoch (1 Jan 1970, 00:00 GMT). So far so good, so, if we give it the maximal int value for a 64 bit int….. we end up 293 billion years in the future. (The English Language uses the Short Scale for Numbers, where Continental European Languages uses the Long Scale, note this is “293 miljard” in Dutch. See “Names of Large Numbers” on Wikipedia) So, we’re setting a cookie to a date probably beyond the end of the universe. So, basically, it will never expire. That’s the intention, so…. what went wrong?
Setting a cookie to a date where it will survive the end of the universe causes issues with the Year 10000 problem. The problem is, the server doesn’t just sent that number to the client, it sends a formatted string, it’s format is
Wdy, DD Mon YYYY HH:MM:SS GMT Spot the problem? The year is encoded as
YYYY, meaning 9999 is last year it supports. So…. setting a cookie to the end of time is not an option.
I don’t know how many 32 bit clients are still out there, but considering the fact they might suffer from the year 2038 problem, I suppose it’s safer to use 2038 as the the-end-of-time value.