eshpilen
چهارشنبه 31 خرداد 1391, 22:56 عصر
میدونید که در وب از توابع رندوم برای مقاصد امنیتی خیلی زیاد استفاده میشه.
رشته های رندوم در وب کاربرد گسترده و اساسی دارن. ما واقعا بهشون نیازمندیم.
مثلا کد فعال سازی، انواع Token های امنیتی، حتی کلیدهای رمزنگاری، ... .
اینها معمولا با استفاده از توابع استاندارد PHP مثل rand, mt_rand, microtime و البته روشهای ترکیبی ای و پیشرفته تری که بعضی ها از خودشون ابداع میکنن تولید میشن.
اما آیا این توابع و روشها بقدر کافی امن هستند که بتونیم برای تمام این موارد و در تمام سطوح حساسیت امنیتی بهشون اعتماد کنیم؟
میتونم براحتی ادعا کنم که پاسخ قطعی یک حرفه ای و متخصص به این پرسش کلی این است: خیر!
چرا؟
چون توابع رندوم PHP از نوع توابع رندوم امنیتی و مناسب استفاده در مصارف رمزنگاری نیستند. و چون روشهای شخصی اختراع شده توسط برنامه نویسان هم گرچه بعضا میتوانند به امنیت بالاتری دست بیابند اما هنوز از نظر اصول علم امنیت و رمزنگاری ضعفهای زیاد و بزرگی دارند و امنیت آنها درحد استانداردهای رسمی و حرفه ای نیست؛ چون توسط افراد دارای تخصص لازم (تخصص در علم رمزنگاری/Cryptography) طراحی نشده و این روشها تحقیق و دلیل و سند خاصی هم بعنوان پشتوانه ندارند.
و اما بنده در اینجا یک تابع رندوم امن رو براتون میذارم، که توصیه میکنم برای تولید تمام دیتای رندوم خودتون که اهمیت امنیتی داره ازش استفاده کنید:
<?php
if(ini_get('register_globals')) exit("<center><h3>Error: Turn that damned register globals off!</h3></center>");
/**
* Random Number Generator
*
* @category Crypt
* @package Crypt_Random
* @author Jim Wigginton <terrafrost@php.net>
* @copyright MMVII Jim Wigginton
* @license http://www.opensource.org/licenses/mit-license.html MIT License
* @version $Id: Random.php,v 1.9 2010/04/24 06:40:48 terrafrost Exp $
* @link http://phpseclib.sourceforge.net
*/
// crypt_random modified by hamidreza_mz -=At=- yahoo -=Dot=- com
@$entropy=sha1(microtime().$pepper.$_SERVER['REMOTE_ADDR'].$_SERVER['REMOTE_PORT'].$_SERVER['HTTP_USER_AGENT'].serialize($_POST).serialize($_GET).serialize($_CO OKIE));
function crypt_random($min = 0, $max = 0x7FFFFFFF)
{
if ($min == $max) {
return $min;
}
global $entropy;
if (function_exists('openssl_random_pseudo_bytes')) {
// openssl_random_pseudo_bytes() is slow on windows per the following:
// http://stackoverflow.com/questions/1940168/openssl-random-pseudo-bytes-is-slow-php
if ((PHP_OS & "\xDF\xDF\xDF") !== 'WIN') { // PHP_OS & "\xDF\xDF\xDF" == strtoupper(substr(PHP_OS, 0, 3)), but a lot faster
extract(unpack('Nrandom', pack('H*', sha1(openssl_random_pseudo_bytes(4).$entropy.micro time()))));
return abs($random) % ($max - $min) + $min;
}
}
// see http://en.wikipedia.org/wiki//dev/random
static $urandom = true;
if ($urandom === true) {
// Warning's will be output unles the error suppression operator is used. Errors such as
// "open_basedir restriction in effect", "Permission denied", "No such file or directory", etc.
$urandom = @fopen('/dev/urandom', 'rb');
}
if (!is_bool($urandom)) {
extract(unpack('Nrandom', pack('H*', sha1(fread($urandom, 4).$entropy.microtime()))));
// say $min = 0 and $max = 3. if we didn't do abs() then we could have stuff like this:
// -4 % 3 + 0 = -1, even though -1 < $min
return abs($random) % ($max - $min) + $min;
}
if(function_exists('mcrypt_create_iv') and version_compare(PHP_VERSION, '5.3.0', '>=')) {
@$tmp16=mcrypt_create_iv(4, MCRYPT_DEV_URANDOM);
if($tmp16!==false) {
extract(unpack('Nrandom', pack('H*', sha1($tmp16.$entropy.microtime()))));
return abs($random) % ($max - $min) + $min;
}
}
/* Prior to PHP 4.2.0, mt_srand() had to be called before mt_rand() could be called.
Prior to PHP 5.2.6, mt_rand()'s automatic seeding was subpar, as elaborated here:
http://www.suspekt.org/2008/08/17/mt_srand-and-not-so-random-numbers/
The seeding routine is pretty much ripped from PHP's own internal GENERATE_SEED() macro:
http://svn.php.net/viewvc/php/php-src/tags/php_5_3_2/ext/standard/php_rand.h?view=markup */
static $seeded;
if (!isset($seeded) and version_compare(PHP_VERSION, '5.2.5', '<=')) {
$seeded = true;
mt_srand(fmod(time() * getmypid(), 0x7FFFFFFF) ^ fmod(1000000 * lcg_value(), 0x7FFFFFFF));
}
extract(unpack('Nrandom', pack('H*', sha1(mt_rand(0, 0x7FFFFFFF).$entropy.microtime()))));
return abs($random) % ($max - $min) + $min;
}
//################################################## #######################
function random_bytes($length) {
$bytes = '';
for($i = 0; $i < $length; $i++) $bytes.=chr(crypt_random(0, 255));
return $bytes;
}
//################################################## #######################
function random_string($length, $chars=null) {
if(is_null($chars)) $chars='ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnop qrstuvwxyz0123456789';
$random_string='';
for($i=0; $i<$length; $i++)
$random_string.=$chars[crypt_random(0, strlen($chars)-1)];
return $random_string;
}
//################################################## #######################
?>
این تابع رو از کتابخانهء رمزنگاری phpseclib کش رفتم و البته یک مقدار قابل توجهی بهبود و تغییراتی هم درش داده شده که این کار رو با همکاری و کمک و تایید خود توسعه دهندهء کتابخانهء phpseclib انجام دادم.
نکته: چون نرم افزار فروم کدها رو خراب میکنه، کد تابع رندوم امن رو ضمیمه کردم، و بنابراین برای اطمینان از صحت کدها، بجای کپی پیست کردن از کدهای نمایش داده شده، از فایل ضمیمه دریافتش کنید. مثلا در تابع random_string بین محتوای رشتهء متغییر chars یک فاصله افتاده که در واقعیت وجود نداره و نباید باشه.
کد رو هم گذاشتم بمونه تا بشه همینطور هم تابع رو دید زد و بررسی کرد (ضمنا بعضی افراد که عضو نیستن و نمیتونن فایل ضمیمه رو دانلود کنن بهش دسترسی داشته باشن).
در کد بالا سه تابع مشاهده میکنید، اما تابع اصلی تابع crypt_random است که میشه گفت معادل امن تابع mt_rand است؛ بقیهء توابع ارائه شده از crypt_random بعنوان هستهء خود استفاده میکنند.
ضمنا اون خط $entropy=sha1... هم که قبل از تابع اومده جزیی از سیستم تولید اعداد رندوم هست ولی باید خارج از تابع قرار بگیره.
و اما مثالهای استفاده:
تولید یک عدد رندوم از 1 تا 10:
crypt_random(1, 10)
یک عدد رندوم از 0 تا 2147483647:
crypt_random()
یک رشتهء باینری رندوم با طول 16 بایت (128 بیت):
random_bytes(16)
توضیح اینکه اینطور رشته های رندوم باینری اغلب بعنوان کلیدهای رمزنگاری متقارن استفاده میشن (توسط الگوریتم هایی مثل AES).
یک رشتهء رندوم به طول 22 کاراکتر، متشکل از حروف بزرگ و کوچک و اعداد:
random_string(22)
یک رشتهء رندوم به طول 12 کاراکتر، که فقط از کاراکترهای A, B, C و 1, 2, 3 تشکیل شده باشه:
random_string(12, '123ABC')
رشته های رندوم در وب کاربرد گسترده و اساسی دارن. ما واقعا بهشون نیازمندیم.
مثلا کد فعال سازی، انواع Token های امنیتی، حتی کلیدهای رمزنگاری، ... .
اینها معمولا با استفاده از توابع استاندارد PHP مثل rand, mt_rand, microtime و البته روشهای ترکیبی ای و پیشرفته تری که بعضی ها از خودشون ابداع میکنن تولید میشن.
اما آیا این توابع و روشها بقدر کافی امن هستند که بتونیم برای تمام این موارد و در تمام سطوح حساسیت امنیتی بهشون اعتماد کنیم؟
میتونم براحتی ادعا کنم که پاسخ قطعی یک حرفه ای و متخصص به این پرسش کلی این است: خیر!
چرا؟
چون توابع رندوم PHP از نوع توابع رندوم امنیتی و مناسب استفاده در مصارف رمزنگاری نیستند. و چون روشهای شخصی اختراع شده توسط برنامه نویسان هم گرچه بعضا میتوانند به امنیت بالاتری دست بیابند اما هنوز از نظر اصول علم امنیت و رمزنگاری ضعفهای زیاد و بزرگی دارند و امنیت آنها درحد استانداردهای رسمی و حرفه ای نیست؛ چون توسط افراد دارای تخصص لازم (تخصص در علم رمزنگاری/Cryptography) طراحی نشده و این روشها تحقیق و دلیل و سند خاصی هم بعنوان پشتوانه ندارند.
و اما بنده در اینجا یک تابع رندوم امن رو براتون میذارم، که توصیه میکنم برای تولید تمام دیتای رندوم خودتون که اهمیت امنیتی داره ازش استفاده کنید:
<?php
if(ini_get('register_globals')) exit("<center><h3>Error: Turn that damned register globals off!</h3></center>");
/**
* Random Number Generator
*
* @category Crypt
* @package Crypt_Random
* @author Jim Wigginton <terrafrost@php.net>
* @copyright MMVII Jim Wigginton
* @license http://www.opensource.org/licenses/mit-license.html MIT License
* @version $Id: Random.php,v 1.9 2010/04/24 06:40:48 terrafrost Exp $
* @link http://phpseclib.sourceforge.net
*/
// crypt_random modified by hamidreza_mz -=At=- yahoo -=Dot=- com
@$entropy=sha1(microtime().$pepper.$_SERVER['REMOTE_ADDR'].$_SERVER['REMOTE_PORT'].$_SERVER['HTTP_USER_AGENT'].serialize($_POST).serialize($_GET).serialize($_CO OKIE));
function crypt_random($min = 0, $max = 0x7FFFFFFF)
{
if ($min == $max) {
return $min;
}
global $entropy;
if (function_exists('openssl_random_pseudo_bytes')) {
// openssl_random_pseudo_bytes() is slow on windows per the following:
// http://stackoverflow.com/questions/1940168/openssl-random-pseudo-bytes-is-slow-php
if ((PHP_OS & "\xDF\xDF\xDF") !== 'WIN') { // PHP_OS & "\xDF\xDF\xDF" == strtoupper(substr(PHP_OS, 0, 3)), but a lot faster
extract(unpack('Nrandom', pack('H*', sha1(openssl_random_pseudo_bytes(4).$entropy.micro time()))));
return abs($random) % ($max - $min) + $min;
}
}
// see http://en.wikipedia.org/wiki//dev/random
static $urandom = true;
if ($urandom === true) {
// Warning's will be output unles the error suppression operator is used. Errors such as
// "open_basedir restriction in effect", "Permission denied", "No such file or directory", etc.
$urandom = @fopen('/dev/urandom', 'rb');
}
if (!is_bool($urandom)) {
extract(unpack('Nrandom', pack('H*', sha1(fread($urandom, 4).$entropy.microtime()))));
// say $min = 0 and $max = 3. if we didn't do abs() then we could have stuff like this:
// -4 % 3 + 0 = -1, even though -1 < $min
return abs($random) % ($max - $min) + $min;
}
if(function_exists('mcrypt_create_iv') and version_compare(PHP_VERSION, '5.3.0', '>=')) {
@$tmp16=mcrypt_create_iv(4, MCRYPT_DEV_URANDOM);
if($tmp16!==false) {
extract(unpack('Nrandom', pack('H*', sha1($tmp16.$entropy.microtime()))));
return abs($random) % ($max - $min) + $min;
}
}
/* Prior to PHP 4.2.0, mt_srand() had to be called before mt_rand() could be called.
Prior to PHP 5.2.6, mt_rand()'s automatic seeding was subpar, as elaborated here:
http://www.suspekt.org/2008/08/17/mt_srand-and-not-so-random-numbers/
The seeding routine is pretty much ripped from PHP's own internal GENERATE_SEED() macro:
http://svn.php.net/viewvc/php/php-src/tags/php_5_3_2/ext/standard/php_rand.h?view=markup */
static $seeded;
if (!isset($seeded) and version_compare(PHP_VERSION, '5.2.5', '<=')) {
$seeded = true;
mt_srand(fmod(time() * getmypid(), 0x7FFFFFFF) ^ fmod(1000000 * lcg_value(), 0x7FFFFFFF));
}
extract(unpack('Nrandom', pack('H*', sha1(mt_rand(0, 0x7FFFFFFF).$entropy.microtime()))));
return abs($random) % ($max - $min) + $min;
}
//################################################## #######################
function random_bytes($length) {
$bytes = '';
for($i = 0; $i < $length; $i++) $bytes.=chr(crypt_random(0, 255));
return $bytes;
}
//################################################## #######################
function random_string($length, $chars=null) {
if(is_null($chars)) $chars='ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnop qrstuvwxyz0123456789';
$random_string='';
for($i=0; $i<$length; $i++)
$random_string.=$chars[crypt_random(0, strlen($chars)-1)];
return $random_string;
}
//################################################## #######################
?>
این تابع رو از کتابخانهء رمزنگاری phpseclib کش رفتم و البته یک مقدار قابل توجهی بهبود و تغییراتی هم درش داده شده که این کار رو با همکاری و کمک و تایید خود توسعه دهندهء کتابخانهء phpseclib انجام دادم.
نکته: چون نرم افزار فروم کدها رو خراب میکنه، کد تابع رندوم امن رو ضمیمه کردم، و بنابراین برای اطمینان از صحت کدها، بجای کپی پیست کردن از کدهای نمایش داده شده، از فایل ضمیمه دریافتش کنید. مثلا در تابع random_string بین محتوای رشتهء متغییر chars یک فاصله افتاده که در واقعیت وجود نداره و نباید باشه.
کد رو هم گذاشتم بمونه تا بشه همینطور هم تابع رو دید زد و بررسی کرد (ضمنا بعضی افراد که عضو نیستن و نمیتونن فایل ضمیمه رو دانلود کنن بهش دسترسی داشته باشن).
در کد بالا سه تابع مشاهده میکنید، اما تابع اصلی تابع crypt_random است که میشه گفت معادل امن تابع mt_rand است؛ بقیهء توابع ارائه شده از crypt_random بعنوان هستهء خود استفاده میکنند.
ضمنا اون خط $entropy=sha1... هم که قبل از تابع اومده جزیی از سیستم تولید اعداد رندوم هست ولی باید خارج از تابع قرار بگیره.
و اما مثالهای استفاده:
تولید یک عدد رندوم از 1 تا 10:
crypt_random(1, 10)
یک عدد رندوم از 0 تا 2147483647:
crypt_random()
یک رشتهء باینری رندوم با طول 16 بایت (128 بیت):
random_bytes(16)
توضیح اینکه اینطور رشته های رندوم باینری اغلب بعنوان کلیدهای رمزنگاری متقارن استفاده میشن (توسط الگوریتم هایی مثل AES).
یک رشتهء رندوم به طول 22 کاراکتر، متشکل از حروف بزرگ و کوچک و اعداد:
random_string(22)
یک رشتهء رندوم به طول 12 کاراکتر، که فقط از کاراکترهای A, B, C و 1, 2, 3 تشکیل شده باشه:
random_string(12, '123ABC')