PDA

View Full Version : تابع رندوم امن - خشت اول چون نهاد معمار کج ...!



eshpilen
چهارشنبه 31 خرداد 1391, 21: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')