- URL: https://www.laruence.com/en/2012/09/24/2810.html
- Please include attribution when republishing.
This morning, a colleague came to me saying that a script using mcrypt that went live last week was responding very slowly, even though all the server metrics looked normal, and he didn't know why.
After looking into it, here is a simple script that reproduces the issue:
<?php $dmcryptText = "dummy"; $key = "foobar"; $size = mcrypt_get_iv_size(MCRYPT_BLOWFISH,MCRYPT_MODE_ECB); $iv = mcrypt_create_iv($size); // note this line $m = mcrypt_ecb(MCRYPT_BLOWFISH, $key, $dmcryptText, MCRYPT_DECRYPT, $iv); var_dump($m);
When 20 concurrent requests hit this script, you'll notice Apache's response time shoots up dramatically...
Since this problem may be quite common, I figured I should write an article about this pitfall, to keep others from stepping on it again.
PHP's Mcrypt extension function mcrypt_create_iv, if you don't specify a source, defaults to using /dev/random (on Linux) as the random number generator. (Maybe some of you already know the reason, hehe — feel free to skip ahead)
The problem here is /dev/random. Its random pool depends on system interrupts to generate entropy. When the system's interrupt count is low and not enough random numbers can be produced, any process trying to read from it will wait — that is, it will hang. Here's a simple example:
$ dd if=/dev/random bs=1024k count=1
When your machine isn't busy enough, you'll notice the output comes out slowly, with occasional stalls...
That's where the problem lies. When 20 concurrent requests come in, the server's interrupts aren't sufficient to produce enough random numbers for mcrypt, causing the PHP process to wait, which shows up as longer response times.
The fix is to switch to /dev/urandom. /dev/urandom is also a random number device, but it doesn't depend on system interrupts.
<?php $dmcryptText = "dummy"; $key = "foobar"; $size = mcrypt_get_iv_size(MCRYPT_BLOWFISH,MCRYPT_MODE_ECB); $iv = mcrypt_create_iv($size, MCRYPT_DEV_URANDOM); // note this line $m = mcrypt_ecb(MCRYPT_BLOWFISH, $key, $dmcryptText, MCRYPT_DECRYPT, $iv); var_dump($m);
After the change and retesting, the problem was gone, everything normal....
@Xuxin ops from SAE on Weibo provided a solution that doesn't require modifying the PHP code:
Xuxin ops: We ran into this problem at SAE in Feb/March — one command kills it:
$ rngd -r /dev/urandom -o /dev/random -t 1It fills the entropy pool with results from urandom, which both guarantees the pool's fill level and preserves randomness.
So why does PHP use /dev/random by default? Because in theory, /dev/urandom can become predictable under certain conditions (see: /dev/random), so /dev/urandom is generally considered less secure than /dev/random.
Postscript: when you read the manual, always read the comments below it too, hehe. A lot of things are only mentioned in the comments, like this one from mcrypt_create_iv:
If you use /dev/random you need a well filled entropy pool or the application will block until enough good entropy comes available
Be First to Comment