PDA

View Full Version : حرفه ای: ذخيره Session در DB به جاي فايل



MMSHFE
چهارشنبه 21 اردیبهشت 1390, 13:05 عصر
با سلام، لطفاً كلاسي كه براي اين كار نوشتم رو بررسي كنيد. مثال نمونه هم درباره نحوه استفاده از اون گذاشتم. لطفاً اگه سؤالي داشتين، بپرسين.
توضيح خلاصه:
با ايجاد يك شئ از اين كلاس قبل از فراخواني تابع session_start در هر اسكريپتي كه نياز به كار با Session داره، بدون نياز به هيچ كار اضافه ديگه، Sessionها توي ديتابيس ذخيره ميشن نه توي فايل. كافيه از اين كلاس يك شئ ايجاد كنيد و ساير كارهاي لازم براي استفاده از Session مثل قبل خواهد بود و هيچ تفاوتي در كد شما ايجاد نخواهد شد. براي مثال، با كمك آرايه SESSION_$ ميتونيد توي Session يك داده ذخيره كنيد يا يك داده رو حذف كنيد و كلي كارهاي ديگه.
خوشحال ميشم نظراتتون رو بدونم.

i-php-i
چهارشنبه 21 اردیبهشت 1390, 14:13 عصر
الان دانلود می کنم و تست می کنم.

i-php-i
چهارشنبه 21 اردیبهشت 1390, 14:35 عصر
دوست عزیز فکر کنم که یه مدت وقتتون رو با سوالهام در این مورد بگیرم.:چشمک:

سوال اول اینکه کد زیر چه نقشی داره و اینکه آیا روی همه سرورها می شه این مقدار رو تنظیم کرد یا نه؟

گزینه های جایگزین user چی هستن؟


ini_set('session.save_handler','user');

MMSHFE
پنج شنبه 22 اردیبهشت 1390, 07:32 صبح
با سلام، اين تابع باعث ميشه كه كنترل كننده Session، كاربر معرفي بشه. اين گزينه توي php.ini با مقدار پيشفرض files مقداردهي شده كه باعث ميشه توي فايلها ذخيره بشه. تنها مقادير مجاز براي اين قسمت، files و user هستند.
موفق باشيد.

MMSHFE
پنج شنبه 22 اردیبهشت 1390, 10:40 صبح
ضمن عرض سلام مجدد، ديدم بد نيست يكسري توضيحات درباره اين كلاس ارائه بدم:
اين كلاس فكر ميكنم خيلي به درد ميخوره. بخصوص براي جلوگيري از حملاتي به سايت مثل سرقت Session و... در سرورهاي اشتراكي كه همه به پوشه tmp ميتونن دسترسي داشته باشن. ابتدا كد كلاس رو مشاهده كنيد:


<?PHP
//Copyright محمد مصطفي شهركي @ http://www.ncis.ir
ini_set('session.save_handler','user');

class MySessionHandler
{
private $time_out; //Time out for session
private $salt; //Salt, an uniq string
private $browser_hash; //Browser hash
private $server; //Server name
private $user; //User name
private $pass; //Pass word
private $db; //Database name

public function Open($save_path, $session_name)
{
//Initialize your need here.
//In my case, I need nothing.
return true;
}

public function Close()
{
//Just deinitialize your resources
return true;
}

public function Write($id,$data)
{
$safe_id=mysql_real_escape_string($id);
$safe_data=mysql_real_escape_string($data);
$hash=mysql_real_escape_string($this->browser_hash);
$now=date("Y-m-d H:i:s");
$query="INSERT INTO `sessions` (`id`,`data`,`modified`,`hash`) VALUES ('$safe_id','$safe_data','$now','$hash') ON DUPLICATE KEY UPDATE `data`='$safe_data', `modified`='$now',`hash`='$hash'";
try
{
mysql_query($query);
}
catch(Exception $e)
{
return false;
}
return true;
}

public function Read($id)
{
$query='SELECT * FROM `sessions` WHERE (`id`=\''.mysql_real_escape_string($id).'\')';
try
{
$result=mysql_query($query);
}
catch(Exception $e)
{
return '';
}
//Is there any???
if (mysql_num_rows($result)!=1)
{
return '';
}
$data=mysql_fetch_assoc($result);
//Now its time to validate...
$time=strtotime($data['modified']);
$hash=$data['hash'];
//Check for time out and browser data
if(time()-$time > $this->time_out || strcasecmp($this->browser_hash,$hash)!=0)
{
return '';
}
//Anything is ok, return data
return $data['data'];
}

public function Destroy($id)
{
$safe_id=mysql_real_escape_string($id);
$query="DELETE FROM `sessions` WHERE (`id`='$safe_id')";
try
{
mysql_query($query);
}
catch(Exception $e)
{
return false;
}
$this->GC($this->time_out);
return true;
}

public function GC($maxlifetime)
{
//You can use your timeout instead of this.
$date=time()-$maxlifetime;
$date_str=date("Y-m-d H:i:s",$date);
$query="DELETE FROM `sessions` WHERE (`modified`<'$date')";
try
{
mysql_query($query);
}
catch(Exception $e)
{
return false;
}
return true;
}

private function connect()
{
mysql_connect($this->server,$this->user,$this->pass) or die('Connection Error');
mysql_select_db($this->db) or die('Database does not exit.');
mysql_query('SET NAMES \'utf8\'');
}

public function __construct($server,$user,$pass,$db,$time_out=600, $salt='')
{
date_default_timezone_set('Asia/Tehran');
if($salt=='')
{
$salt=md5('http://www.ncis.ir');
}
$this->server=$server;
$this->user=$user;
$this->pass=$pass;
$this->db=$db;
$this->time_out=$time_out;
$this->salt=$salt;
$this->calcHash();
$this->connect();
session_set_save_handler(array(&$this,'Open'),array(&$this,'Close'),array(&$this,'Read'),array(&$this,'Write'),array(&$this,'Destroy'),array(&$this,'GC'));
}

private function calcHash()
{
$ip=isset($_SERVER['HTTP_CLIENT_IP'])?$_SERVER['HTTP_CLIENT_IP']:"Unknown";
$ip.=isset($_SERVER['HTTP_X_FORWARDED_FOR'])?$_SERVER['HTTP_X_FORWARDED_FOR']:"Unknown";
$ip.=isset($_SERVER['REMOTE_ADDR'])?$_SERVER['REMOTE_ADDR']:"Unknown";
$agent=isset($_SERVER['HTTP_USER_AGENT'])?$_SERVER['HTTP_USER_AGENT']:'NoUserAgent';
$browser_data=$this->salt.$_ip.$_agent;
$this->browser_hash=md5($browser_data);
}
}
?>
توي اين كلاس، بخشهاي زير وجود داره:
تنظيم session.save_handler با مقدار user تا ديگه موقع ساخت Session از فايلها استفاده نشه.
متد Open كه براي ايجاد يك Session جديد يا بازكردن Session موجود به كار ميره. همونطور كه ميبينيد، در كلاسي كه نوشتم، اين متد قرار نيست كاري انجام بده!
متد Close كه براي بستن Session كاربرد داره و همونطور كه ميبينيد، در كلاسي كه نوشتم، اين متد هم كاري انجام نميده!
متد Write كه براي نوشتن Session كاربرد داره و بعد از انجام كنترلهاي امنيتي، اطلاعات Session رو در بانك اطلاعاتي ذخيره ميكنه.
متد Read كه براي خواندن Session به كار ميره و بعد از اينكه مطمئن شد Session ID واردشده معتبره و زمان اعتبار Session تمام نشده، اطلاعات Session رو از بانك اطلاعاتي استخراج كرده و بر ميگردونه.
متد Destroy كه براي تخريب Session كاربرد داره و همونطور كه ميبينيد، بعد از حذف Session از بانك اطلاعاتي، متد GC رو صدا ميزنه كه به زباله روب معروفه!
متد GC همونطور كه گفتم، به زباله روب معروفه و قاعدتاً بايد هر چند وقت يكبار بطور خودكار فراخواني بشه ولي تابحال نديدم اين اتفاق بيفته. شايد علتش اين باشه كه PHP براي استفاده بهينه از منابع سرور، فقط وقتي صداش ميزنه كه با كمبود حافظه مواجه بشه. به هر حال، با فراخواني اين تابع، Sessionهايي كه مدت اعتبارشون منقضي شده، از بانك اطلاعاتي حذف ميشن تا حافظه اشغال شده، آزاد بشه. براي اطمينان، توي متد Destroy اين متد رو خودمون فراخواني ميكنيم تا هرموقع يك Session تخريب شد، ساير Sessionهاي منقضي شده هم از بانك اطلاعاتي حذف بشن.
متد Connect كه private هست و فقط داخل كلاس براي اتصال به بانك اطلاعاتي ازش استفاده ميكنيم.
متد سازنده كلاس كه كارش معلومه : مقداردهي اوليه به فيلدهاي كلاس و فراخواني تابع session_set_save_handler و ارسال متدهاي كلاس بعنوان پارامتر براي اون تا به PHP بگيم از اين متدها براي كار با Session استفاده كن.
و در آخر، متد calcHash كه private هست و با تركيب IP كاربر و IP پشت Proxy و IP پشت *** و همچنين Agent كه معرف سيستم عامل و مرورگر مورد استفاده است، يك كد Hash منحصر به فرد براي هر كاربر ايجاد ميكنه كه براي شناسايي كاربر استفاده ميشه. براي مثال، اگه IP فرد عوض بشه يا با مرورگر ديگه همزمان وارد سايت بشه، نميتونه از Session قبلي در اون مرورگر يا با IP جديد استفاده كنه.
حالا ببينيم چطور از اين كلاس استفاده مي كنيم:
اول يك جدول در بانك اطلاعاتي با ساختار زير ايجاد كنيد:


CREATE TABLE IF NOT EXISTS `sessions` (
`id` varchar(100) COLLATE utf8_bin NOT NULL,
`data` text COLLATE utf8_bin NOT NULL,
`modified` datetime NOT NULL,
`hash` varchar(60) COLLATE utf8_bin NOT NULL,
UNIQUE KEY `id` (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin;


<!doctype html public "-//w3c//dtd xhtml 1.0 strict//en"
"http://www.w3.org/tr/xhtml1/dtd/xhtml1-strict.dtd">
<!-- Copyright محمد مصطفي شهركي @ http://www.ncis.ir -->
<HTML xmlns="http://www.w3.org/1999/xhtml">
<HEAD>
<TITLE>Session Set Save Handler DEMO</TITLE>
<META http-equiv="content-type" content="text/html; charset=utf-8"/>
<?PHP
require_once('sssh.class.php');
?>
</HEAD>
<BODY>
<?PHP
$session=new MySessionHandler('localhost','root','','session_db ');
session_start();
echo isset($_SESSION['test'])?$_SESSION['test']:'Test session is not defined';
$_SESSION['test']='http://www.ncis.ir';
echo '<BR/>';
echo isset($_SESSION['value'])?$_SESSION['value']:'value session is not defined';
$_SESSION['value']='http://www.NCIS.ir';
?>
</BODY>
</HTML>
خيلي ساده است، نه؟ كافيه يك شئ از كلاس ايجاد كنيم و بقيه كارها دقيقاً مثل قبله. انگار نه انگار كه Session توي DB ذخيره ميشه. مثل قبل با session_start و SESSION_$ و ساير توابع Session كار ميكنيم ولي ديگه فايلي براي ما ساخته نميشه. بنابراين، هرجا خواستين Session رو توي DB ذخيره كنيد، از اين كلاس يك شئ ايجاد كنيد و بعد با Session مثل قبل كار كنيد.
اميدوارم به دردتون بخوره.
موفق باشيد.

i-php-i
جمعه 23 اردیبهشت 1390, 18:49 عصر
خيلي ساده است، نه؟ كافيه يك شئ از كلاس ايجاد كنيم و بقيه كارها دقيقاً مثل قبله. انگار نه انگار كه Session توي DB ذخيره ميشه. مثل قبل با session_start و SESSION_$ و ساير توابع Session كار ميكنيم ولي ديگه فايلي براي ما ساخته نميشه. بنابراين، هرجا خواستين Session رو توي DB ذخيره كنيد، از اين كلاس يك شئ ايجاد كنيد و بعد با Session مثل قبل كار كنيد.
اميدوارم به دردتون بخوره.
موفق باشيد.
مرسی دوست عزیز، واقعا کلاس مفیدی هست.

یه سوال برام پیش اومده:

اگر از این کلاس استفاده کنیم، می شه گفت که توی هر صفحه باید یبار با پایگاه داده اتصال برقرار کنیم، سسشن ها رو توی دیتابیس ذخیره کنیم و مجددا اتصال رو ببندیم.

حالا برای سایر کارهامون هم مجددا باید یه اتصال با پایگاه داده ایجاد کنیم، کوئری ها رو بفرستیم و بعد اتصال رو ببندیم.

به نظرتون این فرایند (ایجاد دو اتصال در هر صفحه) با اضافی روی سرور ایجاد نمی کنه و آیا راهی نیست که بتونیم با یه اتصال هم سسشن ها رو ذخیره کنیم و هم سایر کوئری ها رو بفرستیم؟

لطفا توضیح بدید.

MMSHFE
شنبه 24 اردیبهشت 1390, 07:30 صبح
با سلام، بعيد ميدونم مشكلي براي سرور پيش بياد. بخصوص طبق ادعاي سايت رسمي MySQL مبني بر اينكه در نسخه 5.3 به بعد، هربار كه درخواست اتصال به پايگاه داده ارسال ميشه، اگه از قبل اتصالي موجود باشه، لينك همون ارسال ميشه و اتصال جديد ايجاد نخواهد شد. درنتيجه اصلاً نيازي به بستن اتصال هم نخواهيد داشت. موفق باشيد.

i-php-i
شنبه 24 اردیبهشت 1390, 13:05 عصر
با سلام، بعيد ميدونم مشكلي براي سرور پيش بياد. بخصوص طبق ادعاي سايت رسمي MySQL مبني بر اينكه در نسخه 5.3 به بعد، هربار كه درخواست اتصال به پايگاه داده ارسال ميشه، اگه از قبل اتصالي موجود باشه، لينك همون ارسال ميشه و اتصال جديد ايجاد نخواهد شد. درنتيجه اصلاً نيازي به بستن اتصال هم نخواهيد داشت. موفق باشيد.
آیا تابعی که توی یه کلاس ایجاد می شه، توی سایر کلاسها هم کاربرد داره؟

سوال دوم اینکه تابع mysql یه تابع قدیمی هست و توصیه می شه از mysqli استفاده کنیم. نظر شما چیه؟

MMSHFE
شنبه 24 اردیبهشت 1390, 20:30 عصر
اگه اون تابع static نباشه، فقط درصورتي ميشه ازش استفاده كرد كه يك شئ از اون كلاس ايجاد بشه و ازطريق اون شئ، تابع مربوطه رو صدا بزنيم. درمورد mysqli حق با شماست ولي من سعي كردم اين كلاس رو سريع بسازم و احتمالاً يك نسخه با mysqli هم خواهم ساخت. موفق باشيد.

i-php-i
دوشنبه 26 اردیبهشت 1390, 17:26 عصر
اگه اون تابع static نباشه، فقط درصورتي ميشه ازش استفاده كرد كه يك شئ از اون كلاس ايجاد بشه و ازطريق اون شئ، تابع مربوطه رو صدا بزنيم. درمورد mysqli حق با شماست ولي من سعي كردم اين كلاس رو سريع بسازم و احتمالاً يك نسخه با mysqli هم خواهم ساخت. موفق باشيد.
اینطوری کمی پیچیده می شه.

فرض کنید ما یه کلاس برای ذخیره سسشن ها ایجاد کردیم، حالا می خوایم اتصالی که توی این کلاس ایجاد کردیم رو برای بقیه کلاسها هم استفاده کنیم.

در این صورت با اتمام کار کلاس ذخیره سازی سسشن ها و فراخوانی تابع destruct اتصال از بین می ره و بقیه کلاسها نمی تونن از اتصال استفاده کنن.

لطفا توضیح بدید.

MMSHFE
سه شنبه 27 اردیبهشت 1390, 07:45 صبح
با سلام، دوست گرامي نميدونم چه دليل خاصي دارين براي اينكه از همون اتصال استفاده كنيد؟ خوب يك اتصال جديد براي ساير كارها ايجاد كنيد و به اتصالي كه مربوط به كار Session هست كاري نداشته باشيد. بعلاوه، توي نسخه هاي 5.3 به بعد MySQL اگه يك اتصال رو صراحتاً نبنديم، موقع ايجاد اتصالهاي جديد، از همون اتصال قبلي استفاده ميشه.
موفق باشيد.

i-php-i
سه شنبه 27 اردیبهشت 1390, 09:45 صبح
با سلام، دوست گرامي نميدونم چه دليل خاصي دارين براي اينكه از همون اتصال استفاده كنيد؟ خوب يك اتصال جديد براي ساير كارها ايجاد كنيد و به اتصالي كه مربوط به كار Session هست كاري نداشته باشيد. بعلاوه، توي نسخه هاي 5.3 به بعد MySQL اگه يك اتصال رو صراحتاً نبنديم، موقع ايجاد اتصالهاي جديد، از همون اتصال قبلي استفاده ميشه.
موفق باشيد.
سایر دوستان می گن که اتصال با دیتابیس فشار زیادی به سرور می یاره و باید زود اتصال رو قطع کرد.

من به این دلیل روی این مسئله حساس هستم.

i-php-i
سه شنبه 27 اردیبهشت 1390, 12:46 عصر
با سلام، حقيقتش اولين باري هست كه اين مسئله رو ميشنوم. اگه اينطور بود، مطمئن باشين طراحان MySQL و PHP خودشون اين مسئله رو رعايت ميكردن و اتصالات به طور پيشفرض با خروج از اسكريپت، قطع ميشد. در هر حال، ميتونيد بعد از انجام هر كاري، با mysql_close اتصال رو ببنديد و هرموقع دوباره لازم بود كاري انجام بشه، اتصال مجدد برقرار كنيد. هرچند حقيقتاً نيازي بهش نديدم. توي سايتهاي بزرگي ديدم كه بازديدكنندگان زيادي هم داشتن ولي اتصالات رو نميبستن تا در مراجعات بعدي، دوباره از همون اتصالات استفاده كنند. يك مثال ساده: اگه بخواين 1 دقيقه اتومبلتون رو يك جا نگه دارين و دوباره راه بيفتين، خاموشش ميكنيد؟ مگه غير از اينه كه خاموش كردن و روشن كردن مجدد در بازه هاي زماني كوتاه، خودش به تنهايي بار اضافه ايجاد ميكنه؟ در اينجا هم وضع به همين منوال هست. سايتي كه آمار بازديد بالا داره، در هر لحظه يك اتصال به ديتابيس ميخواد ايجاد بشه كه اين خودش بار زيادي به سرور وارد ميكنه. چرا اجازه نديم درخواستهاي بعدي اتصال هم از همون اتصال موجود استفاده كنن؟
موفق باشيد.

یکی از دوستانی که روی بستن اتصال تاکید داشتن آقای کرامتی بود.

منظورتون اینه که وقتی اتصال رو توی یه صفحه باز کردیم، دیگه اتصال رو نبندیم؟

آیا با این کار، توی صفحات دیگه که احتیاج به ایجاد اتصال جدید هست، mysqli همون اتصالی که توی صفحات قبلی باز شده رو استفاده می کنه؟

MMSHFE
چهارشنبه 28 اردیبهشت 1390, 07:43 صبح
با سلام، من هم نميگم اتصال رو نبنديد. صحبت من سر اينه كه در كارهايي مثل Session يا كلاً سايتهايي كه بازديد زياد دارن، بستن و بازكردن اتصال، بار بيشتري رو به سرور وارد ميكنه. بله، وقتي يك اتصال در يك صفحه باز بشه و اون رو نبنديم و در صفحات بعد دوباره اقدام به برقراري اتصال كنيم، همون اتصال قبلي مورد استفاده قرار خواهد گرفت. ميتونيد درمورد دستور mysql_pconnect هم تحقيق كنيد. توي مستندات خود دستور mysqli_connect و mysql_connect ميتونيد اين مطلب رو مشاهده كنيد. درصورت وجود يك اتصال باز، همون اتصال مجدداً مورد استفاده قرار ميگيره و مجدداً اتصال جديد ايجاد نخواهد شد.
موفق باشيد.

i-php-i
شنبه 31 اردیبهشت 1390, 12:25 عصر
با سلام، من هم نميگم اتصال رو نبنديد. صحبت من سر اينه كه در كارهايي مثل Session يا كلاً سايتهايي كه بازديد زياد دارن، بستن و بازكردن اتصال، بار بيشتري رو به سرور وارد ميكنه. بله، وقتي يك اتصال در يك صفحه باز بشه و اون رو نبنديم و در صفحات بعد دوباره اقدام به برقراري اتصال كنيم، همون اتصال قبلي مورد استفاده قرار خواهد گرفت. ميتونيد درمورد دستور mysql_pconnect هم تحقيق كنيد. توي مستندات خود دستور mysqli_connect و mysql_connect ميتونيد اين مطلب رو مشاهده كنيد. درصورت وجود يك اتصال باز، همون اتصال مجدداً مورد استفاده قرار ميگيره و مجدداً اتصال جديد ايجاد نخواهد شد.
موفق باشيد.
از کدتون به خوبی استفاده می کنم.
الان 6 مرورگر سافاری، کروم، اینترنت اکسپلورر، اپرا و نت اسکیپ رو استفاده کردم و وارد وب سایت شدم و بعد با زدن دکمه خروج از وب سایت خارج شدم.

مسئله ای که هست اینه که بین این 6 مرورگر فقط مرورگر اینترنت اکسپلورر سطر مربوط به سسشن های خودش رو از دیتابیس حذف می کنه و بقیه مرورگرها سطر رو بصورت کامل حذف نمی کنن و فقط ستون data رو به کمک آپدیت کردن خالی می کنن.

نظر شما در این مورد این مسئله چیه؟ آیا این مسئله مشکلی به وجود نمی یاره؟

MMSHFE
یک شنبه 01 خرداد 1390, 07:56 صبح
با سلام، مشكل خاصي پيش نمياد. من خودم هم به اين مسئله دقت كردم و ديدم وقتي از فايلها استفاده ميشه هم موقع خروج، اون فايل به طور خودكار حذف نميشه. احتمالاً اين مسئله بخاطر طراحي مرورگر هست. البته دارم روي كدي كار ميكنم كه اون رو از طريق Cron Jobs تنظيم كنيم و هرموقع اجرا بشه، Sessionهاي Expireشده رو حذف كنه. موفق باشيد.

i-php-i
یک شنبه 01 خرداد 1390, 11:06 صبح
با سلام، مشكل خاصي پيش نمياد. من خودم هم به اين مسئله دقت كردم و ديدم وقتي از فايلها استفاده ميشه هم موقع خروج، اون فايل به طور خودكار حذف نميشه. احتمالاً اين مسئله بخاطر طراحي مرورگر هست. البته دارم روي كدي كار ميكنم كه اون رو از طريق Cron Jobs تنظيم كنيم و هرموقع اجرا بشه، Sessionهاي Expireشده رو حذف كنه. موفق باشيد.
من یه کلاس نوشتم و توی سازنده کلاس کد زیر رو قرار دادم. سوال اولم اینه که این نوع فراخوانی کلاس مربوط به ذخیره کردن سسشن ها توی دیتابیس درست هست؟

سوال دوم اینه که چطور می تونم بخش مربوط به ایجاد اتصال توی کلاس MySessionHandler رو حذف کنم، بطوری که از اتصال ایجاد شده توی سازنده استفاده کنم؟


function __construct()
{
date_default_timezone_set('Asia/Tehran');
$this->db = new mysqli('localhost','root','','data') or die ("امکان انجام درخواست شما وجود ندارد، لطفا مجددا تلاش کنيد");
require_once('./includes/sssh.class.php');
$session=new MySessionHandler('localhost','root','','data');
session_start();
}

MMSHFE
دوشنبه 02 خرداد 1390, 07:07 صبح
با سلام، فكر نميكنم تغيير زيادي لازم باشه. كافيه توي سازنده كلاس يك پارامتر هم براي اتصال اضافه كنيد و چك كنيد كه اگه خالي نبود، اتصال جديد ايجاد نشه و از همون اتصال استفاده كنه. يعني يك فيلد اتصال هم اضافه كنيد و توي سازنده اون رو با پارامتر دريافتي مقداردهي كنيد. بعد، توي تابع connect، دستور ايجاد اتصال جديد رو حذف كرده و براي دستور انتخاب ديتابيس هم بعنوان پارامتر دوم، از فيلد اتصال كه توي سازنده مقداردهي شده، استفاده كنيد.
موفق باشيد.

mamali-mohammad
شنبه 19 شهریور 1390, 13:15 عصر
این کلاس رو فقط برای کاربران ثبت نامی انجام بدیم ؟
یا برای ادمین هم انجام بدیم خوبه ؟
ممنون

MMSHFE
شنبه 19 شهریور 1390, 13:18 عصر
كلاً هر جا احساس كردين نياز به امنيت نسبتاً بيشتري دارين، ميتونيد از اين روش استفاده كنيد. قاعدتاً بخش مديريت سايت هم بهتره از اين روش استفاده كنه. موفق باشيد.

idocsidocs
شنبه 26 شهریور 1390, 11:03 صبح
كلاً هر جا احساس كردين نياز به امنيت نسبتاً بيشتري دارين، ميتونيد از اين روش استفاده كنيد. قاعدتاً بخش مديريت سايت هم بهتره از اين روش استفاده كنه. موفق باشيد. من از این کلاس استفاده می کنم.

داشتم کمی این کلاس رو تست می کردم که دیدم وقتی کوکی PHPSESSID رو حذف می کنم، هیچ مشکلی توی کار سسشن ها به وجود نمی یاد و سسشن ها به کار خودشون ادامه می دن !

امکانش هست در این مورد توضیح بدید؟ آیا این مشئله نمی تونه یه خطر امنیتی باشه؟

MMSHFE
دوشنبه 28 شهریور 1390, 10:52 صبح
اين مسئله مشكل خاصي نيست چون اين كلاس Session ID رو هم توي DB ذخيره ميكنه. علت اينكه Cookie ساخته ميشه هم حالت پيشفرض PHP هست كه مربوط به نحوه كارش با Sessionها در سمت كلاينت ميشه و ربطي به كد سمت سرور نداره. اتفاقاً به نظرم اينكه كلاس اصلاً به تنظيمات كلاينت و فايلهاي Cookie اون كاري نداره، خودش يك مزيت محسوب ميشه. البته اگه ميخواين كلاً ذخيره Cookie روي كلاينت براي انتقال Session ID رو از كار بندازين، كد زير رو به سازنده كلاس اضافه كنيد:


ini_set('session.use_cookies', 0);
ini_set('session.use_only_cookies', 0);

موفق باشيد.

idocsidocs
دوشنبه 28 شهریور 1390, 11:34 صبح
اين مسئله مشكل خاصي نيست چون اين كلاس Session ID رو هم توي DB ذخيره ميكنه. علت اينكه Cookie ساخته ميشه هم حالت پيشفرض PHP هست كه مربوط به نحوه كارش با Sessionها در سمت كلاينت ميشه و ربطي به كد سمت سرور نداره. اتفاقاً به نظرم اينكه كلاس اصلاً به تنظيمات كلاينت و فايلهاي Cookie اون كاري نداره، خودش يك مزيت محسوب ميشه. البته اگه ميخواين كلاً ذخيره Cookie روي كلاينت براي انتقال Session ID رو از كار بندازين، كد زير رو به سازنده كلاس اضافه كنيد:تا اونجاییکه من می دونم از ای دی سسشن برای شناسایی کاربر مورد نظر استفاده می شه. سسشن های اصلی هم توی دیتابیس ذخیره می شن.

اگر این کوکی غیر فعال باشه، کار شناسایی کاربر به چه شکل انجام می شه؟ مثلا اگر 1000 نفر از ایران وارد بشن، از اونجاییکه آی پی های ایران تکراری هم داره، و اکثرا از مرورگرهای ie استفاده می کنن، کار شناسایی کاربر به چه صورت انجام می شه؟


لطفا توضیح بدید.

MMSHFE
دوشنبه 28 شهریور 1390, 12:14 عصر
عزيز دل برادر، يك نگاه دوباره به كلاس بندازين! كد Hash مخصوص كاربر كه با تركيب IP و مرورگر و سيستم عامل ساخته ميشه توي DB ذخيره ميشه و از اون طريق كاربر شناسايي ميشه. اين روش خيلي مطمئن تر هست چون حتي با Session Fixation هم نميشه بهش نفوذ كرد چون كد هشي كه براي كامپيوتر نفوذگر ساخته ميشه متفاوت خواهد بود و نميتونه صرفاً با داشتن يك Session ID معتبر، وارد سايت بشه. موفق باشيد.

idocsidocs
دوشنبه 28 شهریور 1390, 13:42 عصر
عزيز دل برادر، يك نگاه دوباره به كلاس بندازين! كد Hash مخصوص كاربر كه با تركيب IP و مرورگر و سيستم عامل ساخته ميشه و زمان بازديد ساخته ميشه توي DB ذخيره ميشه و از اون طريق كاربر شناسايي ميشه. اين روش خيلي مطمئن تر هست چون حتي با Session Fixation هم نميشه بهش نفوذ كرد چون كد هشي كه براي كامپيوتر نفوذگر ساخته ميشه متفاوت خواهد بود و نميتونه صرفاً با داشتن يك Session ID معتبر، وارد سايت بشه. موفق باشيد.

مشکل منم همینه دیگه !
ای پی های ایرانی که تعدادشون محدود هست و این امکان وجود داره که چندین کاربر روی یه آی پی باشن (مگر اینکه بگید اینطور نیست) ! مرورگر و سیستم عامل همه کاربرها هم که مشخصه.

هنوزم روی نظرتون هستید؟

MMSHFE
دوشنبه 28 شهریور 1390, 13:51 عصر
دوست گرامي، خط آخر تابع calcHash رو اينطوري تغيير بدين:


$this->browser_hash=md5($browser_data.session_id());

حله؟ :چشمک:
اينطوري ديگه اگه همه اطلاعاتشون هم يكسان باشه، Session ID باعث تفاوت كد Hash ميشه. موفق باشيد.

idocsidocs
دوشنبه 28 شهریور 1390, 15:18 عصر
اينطوري ديگه اگه همه اطلاعاتشون هم يكسان باشه، Session ID باعث تفاوت كد Hash ميشه. موفق باشيد.

داداش منکه نمی خوام به زور مجبورتون کنم که نظرتون رو عوض کنید، به هر حال این نکته هم که گفتم ممکنه پیش بیاد و احتمالش زیاده.

MMSHFE
دوشنبه 28 شهریور 1390, 18:46 عصر
دوست عزيز، من هم روي نظرم پافشاري بيجا ندارم. شما گفتين كه ممكنه IP و OS و Browser يكسان باشه، من هم كدي ارائه دادم كه PHPSESSID رو توي توليد Hash كاربر دخالت ميده تا اگه همه اون موارد قبلي يكسان بود، اين مورد آخر باعث بشه براي هر كاربر كد جداگانه اي توليد بشه. موفق باشيد.

idocsidocs
سه شنبه 29 شهریور 1390, 00:13 صبح
دوست عزيز، من هم روي نظرم پافشاري بيجا ندارم. شما گفتين كه ممكنه IP و OS و Browser يكسان باشه، من هم كدي ارائه دادم كه PHPSESSID رو توي توليد Hash كاربر دخالت ميده تا اگه همه اون موارد قبلي يكسان بود، اين مورد آخر باعث بشه براي هر كاربر كد جداگانه اي توليد بشه. موفق باشيد.

مطمئنن از کلاس شما توی همه پروژه هام استفاده می کنم. اما باید این مطلب آخری رو تغییر بدم. با توجه به طراحی خوب شما، بود و نبود ای دی سسشن روی کار تاثیر چندانی نمی ذاره ولی باید همه شرایط رو در نظر گرفت.

اگر مجددا مشکلی پیش اومد برام توی این تاپک مطرح می کنم.

Ali0Boy
دوشنبه 29 اسفند 1390, 23:13 عصر
باسلام و تبریک سال نو
استاد گرامی 'MMSHFE (http://barnamenevis.org/member.php?55504-MMSHFE)' یک سوال داشتم که نمیدونم آیا قابل بیان هست اینجا و یا خیر/
من می خواستم بدونم الان با تغیر خط آخر Hash به browser_data.session_id حال اگر کاربر browser خود را ببندد بار بعدی که وارد سایت می شود ناشناس می باشد و یک شماره session جدید می گیرد در صورتی که در پایگاه داده هنوز از ایشان رکورد مربوط به session قبلی بر جاست و این در طولانی مدت که هیچ در کوتاه مدت باعث سنگین شدن بانک و مشکلات بعدی می شود. می خواستم چطور برای صفحه ام کدی بنویسم که اگر کاربر صفحه را بست اون اطلاعات بانک نیز از بین برود.

باتشکر

MMSHFE
دوشنبه 29 اسفند 1390, 23:27 عصر
با سلام، حقیقتش مرورگرهایی مثل IE خودشون رکورد مربوطه رو حذف میکنن ولی درمورد مرورگرهای FF و Chrome و... نمیدونم چرا حذف نمیشه. درهرحال میتونید موقع ایجاد شئ از کلاس مذکور، اگه timeout پیشفرض رو تغیر نداده باشین، مشابه مثال زیر، سشنهای Expireشده رو حذف کنید:


$sssh = new MySessionHandler('localhost', 'root', '', 'dbname');
$sssh->GC(600);

اینطوری، سشنهایی که 10دقیقه از آخرین فعالیتشون گذشته و تغییری نداشتن، حذف میشن. موفق باشید.

AmirSky
جمعه 04 فروردین 1391, 16:21 عصر
باسلام و ممنون از آقای شهرکی بابت کلاس خوبتون
یه سوال : در تابع GC در خط 97 چرا از $date استفاده شده؟ چرا از $date_str استفاده نشده؟ آیا دلیل حذف نشدن ها این نبوده؟
در خط 141 به خطا برخوردم که دیدم متغیر $_ip و $_agent در بالاتر به صورت $ip تعریف شده. ($browser_data=$this->salt.$_ip.$_agent;)
آیا کس دیگری هم در این موارد به خطا برخورده یا نه؟

Ali0Boy
جمعه 04 فروردین 1391, 16:37 عصر
بله دوست عزیز در خطی که گفتید دو متغییر ip , agent به صورت $_ip و $_agent صدا زده شده. خودتان دستی درست کنید. من اینکار رو کردم جواب داد. درضمن آقا شهرکی قرار شده این کلاس رو کامل کنند و اون مشکلاتی که داشت رو برطرف کنند که بعد از بستن پنجره از بانک حذف شود. انشاءالله اون موقع می توانید در بهترین حالت استفاده کنید.

lady64
شنبه 30 اردیبهشت 1391, 10:34 صبح
با سلام، حقیقتش مرورگرهایی مثل IE خودشون رکورد مربوطه رو حذف میکنن ولی درمورد مرورگرهای FF و Chrome و... نمیدونم چرا حذف نمیشه. درهرحال میتونید موقع ایجاد شئ از کلاس مذکور، اگه timeout پیشفرض رو تغیر نداده باشین، مشابه مثال زیر، سشنهای Expireشده رو حذف کنید:


$sssh = new MySessionHandler('localhost', 'root', '', 'dbname');
$sssh->GC(600);

اینطوری، سشنهایی که 10دقیقه از آخرین فعالیتشون گذشته و تغییری نداشتن، حذف میشن. موفق باشید.

سلام. من با ie امتحان میکنم اما رکورد ها حذف نمیشن . کد آخری هم که گذاشتید رو امتحان کردم ولی نشد.چکار کنم که رکوردهای درج شده با بستن مرورگر ie حذف بشن ؟

MMSHFE
یک شنبه 31 اردیبهشت 1391, 00:32 صبح
چه نسخه ای از IE هست؟ نسخه های جدید اومدن درستش کنن، بدترش کردن! دارم روی نسخه جدید اسکریپت کار میکنم. به زودی آماده میشه و توی همه مرورگرها به درستی و بطور کامل کار خواهد کرد. موفق باشید.

AmirSky
یک شنبه 31 اردیبهشت 1391, 19:03 عصر
با تشکر از آقای شهرکی
حدود دو ماه است که دارم ازش استفاده میکنم و مشکل خاصی نداره
با کروم هم عالی کار میکنه

lady64
یک شنبه 31 اردیبهشت 1391, 22:35 عصر
چه نسخه ای از IE هست؟ نسخه های جدید اومدن درستش کنن، بدترش کردن! دارم روی نسخه جدید اسکریپت کار میکنم. به زودی آماده میشه و توی همه مرورگرها به درستی و بطور کامل کار خواهد کرد. موفق باشید.

هم با ie7 هم با firefox امتحان کردم ولی رکوردها در بانک اطلاعاتی هستند و حذف نشدند.

davood1237
یک شنبه 07 خرداد 1391, 14:07 عصر
سلام به همه دوستان
من همه ارسال های شما را در مورد این کلاس خوندم . یه سوال داشتم . آیا با استفاده از این کلاس من می تونم کاربرای آنلاین در سامانه را نمایش بدم یا نه ؟؟
اگر می شود به چه صورت و اگر نمی شود لطفاً در این مورد راهنمایی کنید ممنون میشم !!

MMSHFE
دوشنبه 08 خرداد 1391, 14:39 عصر
فکر نمیکنم مشکلی وجود داشته باشه. فقط باید تعداد Sessionهای Expireنشده رو بشمارین که یک کوئری ساده برای این کار کافیه.

davood1237
دوشنبه 08 خرداد 1391, 16:24 عصر
مرسی ولی منظور من تعداد کاربران آنلاین به همراه نام یا نام کاربریشون هست . آیا می شه ؟؟
یه سوال دیگه هم که داشتم اینه که من الان با یه تیکه کد session id کاربری که login می کنه را در db نگه داری می کنم . می خوام بعد از یه تایمی چک کنم این session id هنوز وجود داره یا نه . چطور باید این کارو انجام بدم ؟؟؟ نکته ای هم که وجود داره اینه که session ها در db ذخیره نمی شوند .
ممنون از راهنماییتون

lady64
چهارشنبه 21 تیر 1391, 13:47 عصر
با سلام. آقای شهرکی من هنوز نتونستم کاری کنم که با بستن مرورگر سشن ها از پایگاه حذف بشن . میشه کمک کنید .
کسی این کد رو استفاده کرده ؟ میشه راهنمایی کنید ......

colors
چهارشنبه 04 مرداد 1391, 15:52 عصر
درود

به نظرتون چطوره یه تابعی چیزی بنویسیم که مثلا هر روز یا ... جدول سشن هارو چک کنه و هر رکوردی که modifiedش از تاریخ و ساعت فعلی یکی دوساعت جلوتر بود ( بستگی به مدت زمانی که برای expire شدن سشن در نظر میگیریم ) بیادو اون رکورد رو حذف کنه. اینجوری مرورگرهای که با این کلاس برای حذف سشن سازگار نیستن، دیگه مشکلی نخواهند داشت.

چون مطمئنا رکوردی که تو پایگاه داده از مدت زمان آخرین ویرایشش چند ساعتی گذشته باشه، یعنی اینکه رکورد بی مصرف شده و دیگه به درد نمیخوره. نظرتون چیه؟

colors
چهارشنبه 04 مرداد 1391, 22:56 عصر
اگه از نظر آقای شهرکی اشکالی نداره، کلاسو ویرایش کنم و این قابلیتو بهش اضافه کنم؟

//Copyright محمد مصطفي شهركي @ http://www.ncis.ir

mbf5923
جمعه 06 مرداد 1391, 02:51 صبح
خوب اینکار رو میشه با هر بار لود شدن پیج به سادگی انجامش داد

mahsa.norozy
دوشنبه 13 آذر 1391, 11:31 صبح
سلام

من وقتی صفحه index رو اجرا می کنم با این خطا مواجه میشم:
Parse error: parse error, unexpected T_STRING, expecting T_OLD_FUNCTION or T_FUNCTION or T_VAR or '}' in D:\Xamp 1.6.1\xampp\htdocs\mywebsite\sssh.class.php on line 7

من از php ورژن 4 استفاده می کنم ممکنه مشکل از این باشه؟

من یکم سرچ کردم، کلمه pubic رو از اول توابع حذف کردم و به جای private از var استفاده کردم و سازنده رو به صورت function MySessionHandler تعریف کردم حالا ارور به صورت زیر میاد:
Parse error: parse error, unexpected '{' in D:\Xamp 1.6.1\xampp\htdocs\mywebsite\sssh.class.php on line 37

MMSHFE
دوشنبه 13 آذر 1391, 19:55 عصر
اگه از نظر آقای شهرکی اشکالی نداره، کلاسو ویرایش کنم و این قابلیتو بهش اضافه کنم؟

//Copyright محمد مصطفي شهركي @ http://www.ncis.ir
هیچ اشکالی نداره دوست عزیز، پروژه Open Source هست :چشمک:

MMSHFE
دوشنبه 13 آذر 1391, 19:56 عصر
سلام

من وقتی صفحه index رو اجرا می کنم با این خطا مواجه میشم:
Parse error: parse error, unexpected T_STRING, expecting T_OLD_FUNCTION or T_FUNCTION or T_VAR or '}' in D:\Xamp 1.6.1\xampp\htdocs\mywebsite\sssh.class.php on line 7

من از php ورژن 4 استفاده می کنم ممکنه مشکل از این باشه؟

من یکم سرچ کردم، کلمه pubic رو از اول توابع حذف کردم و به جای private از var استفاده کردم و سازنده رو به صورت function MySessionHandler تعریف کردم حالا ارور به صورت زیر میاد:
Parse error: parse error, unexpected '{' in D:\Xamp 1.6.1\xampp\htdocs\mywebsite\sssh.class.php on line 37
این کلاس با PHP4 سازگار نیست. کلاً PHP4 پشتیبانی خوب و کاملی از شئ گرایی نداره.

navid3d_69
جمعه 17 آذر 1391, 12:09 عصر
ذخیره سشن در سیستم های بزرگ باعث نمیشه که سرعت پایین بیاد؟

mtchabok
جمعه 17 آذر 1391, 15:48 عصر
ذخیره سشن در سیستم های بزرگ باعث نمیشه که سرعت پایین بیاد؟
نه عزیز.
اگه شما مدیریت بر اطلاعات سشن در دیتابیس رو کنار سشن در فایل بزارید و با هم مقایسه گنید ، اونوقت متوجه میشید که استفاده از دیتابیس هرچه سیستم بزرگتر باشه دارای سرعت بالاتری هست .

iraitc
جمعه 10 خرداد 1392, 11:33 صبح
نه عزیز.
اگه شما مدیریت بر اطلاعات سشن در دیتابیس رو کنار سشن در فایل بزارید و با هم مقایسه گنید ، اونوقت متوجه میشید که استفاده از دیتابیس هرچه سیستم بزرگتر باشه دارای سرعت بالاتری هست .

پس معلوم میشه دوست عزیز هنوز کارای بزرگ دیتابیسی انجام ندادی .
معمولا بهتر است برای سیستم های بزرگ فایل سرور رو از دیتابیس جدا میکنند و عملیات پردازش سمت دیتابیس رو تا آنجایی که میتونن میارن پتیین . و باید بگم سرعت select و update به میزار رکورد ها در دیتابیس خیلی بستگی داره هرچه رکورد ها زیاد تر باشه سرعت اجرای این دستورات پایین تر میاد . پس کار اشتباهی است که بخوام برای سیستم های بزرگ از ذخیره سشن در دیتابیس استفاده کنیم .

MMSHFE
جمعه 10 خرداد 1392, 11:57 صبح
بزرگ از نظر شما یعنی چی؟ یعنی چند رکورد در جدول رو حداقل معیار برای بزرگی جدول میدونید؟ بد نیست بدونید ساختار MyISAM به نحوی هست که عملاً تا حدود چند میلیارد رکورد، افت محسوس سرعت نخواهیم داشت (اگه از مواردی مثل Limit با Offset و... استفاده نشه و دیتابیس هم به خوبی نرمالسازی شده باشه). بعلاوه سیستم کاری سشن به نحوی هست که رکوردهای بلا استفاده (سشنهای منقضی شده) حذف میشن. حداقل میشه با کمی تغییر، چنین قابلیتی رو اضافه کرد. بنابراین، چنین مسئله ای محل ایراد نیست. همچنین اگه آسیبهایی مثل Session Hijacking و... رو بررسی کرده باشین، خواهید دید که ارزش داره کمی افت سرعت (که اونهم با روشهایی که گفتم قابل رفعه) رو فدای امنیت بیشتر کنیم. تجربه هم نشون داده سایتها بیشتر بخاطر ناامنی مشتریهاشون رو از دست دادن تا افت سرعت در حد چند صدم ثانیه.

navid3d_69
جمعه 10 خرداد 1392, 19:27 عصر
من با این کلاس توی ie مشکل دارم سشن هارو بعد از بستن مرورگر وب پاک نمی کنه

MMSHFE
جمعه 10 خرداد 1392, 23:33 عصر
IE توی نسخه های قبلیش درست کار میکرد که این هم بخاطر آپدیتهای فوق العاده مایکروسافت، بهم ریخت! بهرحال باید یکسری تغییرات توی کد ایجاد بشه که سشنهایی که تاریخ انقضای اونها گذشته، از دیتابیس حذف بشن.

Veteran
شنبه 11 خرداد 1392, 09:45 صبح
IE توی نسخه های قبلیش درست کار میکرد که این هم بخاطر آپدیتهای فوق العاده مایکروسافت، بهم ریخت!
مایکروسافت مایکروسافت ننگ به نیرنگ تو
خون برنامه های ما میچکد از چنگ تو !
جناب شهرکی
یعنی امکانش هست یک مرورگر بری روی کد های php تاثیر بزاره ؟!
یعنی ممکنه ما یک برنامه بنویسیم که توی یک سری مرورگر ها درست کار بده توی بقیه نداره ؟

MMSHFE
شنبه 11 خرداد 1392, 10:45 صبح
ببینید، یکسری کدهای سمت سرور هست که وابسته به هدرهایی هست که توسط مرورگر باید ارسال بشه تا کار کنه و اگه مرورگر اونها رو نفرسته یا درست ارسال نکنه، کدها هم طبیعتاً به درستی کار نمیکنن. البته اینجور کدها خیلی کم هستن (مثل بخش مدیریت سشن و...) ولی بهرحال وجود دارن. برای اینکه مطمئن بشین کلاس درسته و مشکل از مرورگر هست، بدون استفاده از این کلاس با سشن کار کنید و پوشه tmp مسیر Wamp رو چک کنید. خواهید دید که فایل سشن که ساخته میشه، با بسته شدن مرورگر از بین نمیره. معمولاً توی سرورها روی این پوشه Quota میگذارن تا اگه سایزش از یه حد خاصی بیشتر شد، بطور خودکار پاک بشه یا اینکه یک برنامه دائماً درحال اجراست که سایز این پوشه رو چک میکنه و اگه از حد خاصی بیشتر شد، به ترتیب از فایلهای قدیمیتر به جدید، شروع میکنه به حذف کردن تا فضای خالی کافی ایجاد بشه و سایز پوشه هم از حد مجاز بیشتر نشه. اما چون با این کلاس، محل ذخیره سازی سشن رو به دیتابیس منتقل میکنیم، طبیعتاً باید اینکار هم دستی انجام بشه یعنی خودمون براش اسکریپت بنویسیم.

navid3d_69
شنبه 11 خرداد 1392, 15:34 عصر
خب پس می شه اینکارو با حذف اطلاعات کل اون جدول در آخر هر شب این مشکل رو حل کرد

MMSHFE
یک شنبه 12 خرداد 1392, 08:37 صبح
هم میشه همه جدول رو حذف کرد، هم فقط رکوردهایی که از زمان آخرین فعالیتشون بیش از زمان مشخصی گذشته.

engmmrj
دوشنبه 17 تیر 1392, 18:07 عصر
استاد میشه درباره این query توضیح بدین (تا قسمت insert رو متوجه میشم از on به بعد):

$query="INSERT INTO `sessions` (`id`,`data`,`modified`,`hash`) VALUES ('$safe_id','$safe_data','$now','$hash') ON DUPLICATE KEY UPDATE `data`='$safe_data', `modified`='$now',`hash`='$hash'";

MMSHFE
سه شنبه 18 تیر 1392, 08:55 صبح
میگه اگه رکوردی که میخواد ثبت بشه، کلید تکراری داشته باشه (فیلد `id`)، دستور UPDATE رو اجرا کنه.

idocsidocs
چهارشنبه 19 تیر 1392, 01:30 صبح
میگه اگه رکوردی که میخواد ثبت بشه، کلید تکراری داشته باشه (فیلد `id`)، دستور UPDATE رو اجرا کنه.


وقتی که کاربر موقع لاگین تیک "مرا بخاطر بسپارید" رو زده باشه و بصورت دستی سسشنها رو از دیتابیس حذف می کنم، کاربر از سایت خارج نمی شه و بدون مشکل به کار ادامه می ده

آیا این موضوع یه حفره امنیتی هست؟

اگر تیک "مرا بخاطر بسپارید" رو نزده باشه و سسشن ها رو بصورت دستی حذف می کنم کاربر از سایت خارج می شه

MMSHFE
چهارشنبه 19 تیر 1392, 07:41 صبح
این مسئله بخاطر اینه که درصورت فعال کردن اون گزینه، برای کاربر کوکی میسازین و داره با استفاده از کوکی لاگین میکنه. ارتباطی به مدیریت سشن نداره.

redhat2
سه شنبه 25 تیر 1392, 20:22 عصر
ممنون ، بابت کلاس ، نظرتون در مورد این 2 تا چیه :

حالت اول :

بعد از این که login کامل انجام شد ، من 3 تا Session درست می کنم ، اولی username را در خودش نگه می داره ، دومی user_id و سومی یک hash هست که این هش توی ه دتابیس ذخیره میشه ، بعد وجود Session های کاربر چک میشه ، و Session سوم هم با دتیابیس تطبیق داده میشه. برای rememberme هم کوکی با همین حالت درست می کنیم یعنی یه کوکی به نام ایمیل درست می کنیم ، یه کوکی هش درتس می کنیم که در دیتابیس هم ذخیره میشه .

حالت دوم :
قسمت Session مثل بالا است ، ولی تو قسمت Cookie ، میام یه Cookie از user_agent یا ip درست می کنیم ، و کوکی Hash را هم در دیتابیس ذخیره نمی کنیم .

به نظر شما از لحاظ امنیت و سرعت کدوم بهتر هستش ؟ در ضمن من برای حالت زمان cookie از setcookie استفاده می کنم ، این روش مطمین هست ؟

MMSHFE
چهارشنبه 26 تیر 1392, 07:42 صبح
سؤالتون ارتباطی به این تاپیک نداره. لطفاً تاپیک جداگانه ایجاد کنید.

ahmad156
جمعه 06 دی 1392, 22:56 عصر
چه نسخه ای از IE هست؟ نسخه های جدید اومدن درستش کنن، بدترش کردن! دارم روی نسخه جدید اسکریپت کار میکنم. به زودی آماده میشه و توی همه مرورگرها به درستی و بطور کامل کار خواهد کرد. موفق باشید.
سلام آقای شهرکی.نسخه جدید اسکریپت آماده نشده؟

MMSHFE
شنبه 07 دی 1392, 00:04 صبح
متأسفانه فرصت نکردم تو این مدت روی نسخه جدید کار کنم. اگه خدا بخواد بعد از نشست سوم نهاد تو شیراز روش وقت میگذارم.

id1385
یک شنبه 08 دی 1392, 21:58 عصر
با سلام
من در برنامه هام این شکلی استفاده می کنم
من یک کلاسی دارم که در تعدادی متد نوشتم که به ضمیمه آمده است.
سپس در یک کلاس دیگه من اونو گلوبال مینامم و در هر صفحه ام اولین کلاسی است که اینکلود می شه میام و یک مت دارم بعنوان چک کردن اینکه کاربر ورود کرده یا نه به این صورت:


<?php
class customer {

public static function is_customer_loged_in() {
if (isFile::check(class_path . 'class.enc.inc.php')) {
include_once (class_path . 'class.enc.inc.php');
} else {
return false;
}
;
$sess = new the_session;
$enc = new Encryption;
$sess->ses_free_olds();
$ses_id = $sess->ses_id();
$customer_id = $enc->unProtect($sess->ses_read('customer_loged_id'));
$customer_login = $enc->unProtect($sess->ses_read('customer_loged_as'));
db::load_db();
$sql = "SELECT `ID` FROM `customers` WHERE `customer_login` = '{$customer_login}' AND `ID` = {$customer_id} LIMIT 1";
$found = db::recordCount($sql);
$keep_me = @mysql_fetch_array(mysql_query("SELECT `KeepMe` FROM `sessions` WHERE `RndKey`= '$ses_id';"));
if ($keep_me['KeepMe'] == 1 && isset($customer_id) && !empty($customer_id) &&
isset($customer_login) && !empty($customer_login) && $found > 0) {
return true;
}

if (isset($customer_id) && !empty($customer_id) && isset($customer_login) && !
empty($customer_login) && $found > 0) {
return true;
} else {
return false;
}
}
}


فقط همین.
--
در این متد یک $sess->ses_free_olds(); داریم که کارش اینه که جلسات منقضی رو حذف می کنه و اگر در موردی که کاربر «مرا بخاطر بسپار» را اگر تیک زده باشه دیگه وارد بررسی ماهوی نمی شه و دایرکت میکنه به صفحه اصلی

سوالی در این باره بود در خدمت هستم


موفق باشید

reza303
جمعه 16 اسفند 1392, 14:14 عصر
دوستان یه سوال ...
به جای این همه کار نمی شه از دستورات زیر موقع لوگین استفاده کرد :

if (isset($_SESSION['admin_last_ip']) === false)
{
$_SESSION['admin_last_ip'] = $_SERVER['REMOTE_ADDR'];
}
if ($_SESSION['admin_last_ip'] !== $_SERVER['REMOTE_ADDR'])
{
session_unset();
session_destroy();
}

می یایم می گیم اگر سیشن دزدیده شد و طرف خواست استفاده کنه چون آی پیش عوض شد سیشن منقضی بشه .

numberone1
سه شنبه 23 اردیبهشت 1393, 14:20 عصر
سلام
من از این کلاس میخوام با pdo استفاده کنم ولی توی _constuct نمیتونه اتصال و پیدا کنه!


private function connect()
{
$db = database::getInstance();
}

public function __construct($time_out = 1800, $salt = '') {
$db = database::getInstance();
date_default_timezone_set('Asia/Tehran');
if ($salt == '') {
$salt = md5('123456789');
}
//$this -> server = $server;
//$this -> user = $user;
//$this -> pass = $pass;
//$this -> db = $db;
$this -> time_out = $time_out;
$this -> salt = $salt;
$this -> calcHash();
$this -> connect();
session_set_save_handler(array(&$this, 'Open'), array(&$this, 'Close'), array(&$this, 'Read'), array(&$this, 'Write'), array(&$this, 'Destroy'), array(&$this, 'GC'));
}


حتما باید توی تمام متد ها این instance ایجاد بشه تا کار کنه!
موقع اجرای کوئری این ارور میاد
Fatal error: Call to a member function query() on a non-object
اگر مثلا تو متد read یا write این خط اولش نوشته بشه دیگه ارور نمیده!!!
$db = database::getInstance();

majid1605
سه شنبه 23 اردیبهشت 1393, 20:44 عصر
فقط واسه استفاده بقیه دوستان
چندتا تغییر تووی کدها دادم که دوتا شون روی همین کلاس فعلی قابل استفاده است
یکی کوئری مربوط به تابع cg

DELETE FROM mysessions WHERE MINUTE(TIMEDIFF(NOW(),modified))>=:date

گه من date رو برحسب دقیقه تنظیم کردم

دومی تابع write یک شرط ساده اضافه کردم


if(isset($data) && !empty($data))

واسه جلوگیری از ساخت سشن خالی و اجرای کوئری write وقتیه که کاربر وارد سایت نشده البته این یکی بستگی به برنامه شما و نحوه استفاده از این کلاس

چندتا تغییر دیگه هم هست ولی بیشتر جنبه شخصی و نحوه نوشتن کلاس داره

numberone1
چهارشنبه 24 اردیبهشت 1393, 01:34 صبح
سلام
من از این کلاس میخوام با pdo استفاده کنم ولی توی _constuct نمیتونه اتصال و پیدا کنه!


private function connect()
{
$db = database::getInstance();
}

public function __construct($time_out = 1800, $salt = '') {
$db = database::getInstance();
date_default_timezone_set('Asia/Tehran');
if ($salt == '') {
$salt = md5('123456789');
}
//$this -> server = $server;
//$this -> user = $user;
//$this -> pass = $pass;
//$this -> db = $db;
$this -> time_out = $time_out;
$this -> salt = $salt;
$this -> calcHash();
$this -> connect();
session_set_save_handler(array(&$this, 'Open'), array(&$this, 'Close'), array(&$this, 'Read'), array(&$this, 'Write'), array(&$this, 'Destroy'), array(&$this, 'GC'));
}


حتما باید توی تمام متد ها این instance ایجاد بشه تا کار کنه!
موقع اجرای کوئری این ارور میاد
Fatal error: Call to a member function query() on a non-object
اگر مثلا تو متد read یا write این خط اولش نوشته بشه دیگه ارور نمیده!!!
$db = database::getInstance();

کسی نمیتونه کمک کنه مشکلم حل بشه با این؟

MMSHFE
چهارشنبه 24 اردیبهشت 1393, 08:41 صبح
داخل کلاس از ()self::getInstance استفاده کنید. برای اینکه بهتر بشه راهنمایی کنیم، کد کامل کلاستون رو بگذارین.

numberone1
چهارشنبه 24 اردیبهشت 1393, 10:13 صبح
سینگلتون هست کلاس اتصال به دیتابیس
این کد کاملش هست


class Config {
public $driver = 'sqlsrv';
public $dbhost = '127.0.0.1';
public $dbuser = 'test';
public $dbpass = '123456';
public $dbname = 'db';
}
class database {
private static $instance;
private $stmt;
private $error = array();
private $sentinel = 1;
private $affectedRows = 0;
private $countRows = 0;
private function __construct() {
$config = new Config();
try {
$this->instance = new PDO($config->driver.":Server=".$config->dbhost.';Database='.$config->dbname, $config->dbuser, $config->dbpass);
return $this->instance;
} catch (Exception $e) {
echo "can not connect.";
}
}
public static function getInstance() {
if (!self::$instance instanceof self) {
self::$instance = new self;
}
return self::$instance;
}
public function query($sql,$params = null) {
$this->stmt = $this->instance->prepare($sql);
$this->stmt->execute($params);
$this->checkQuery();
}
public function loadResult(){
$singleResult = $this->stmt->fetch(PDO::FETCH_NUM);
return $singleResult[0];
}
public function loadColumn(){
$columnList = array();
while($row = $this->stmt->fetch(PDO::FETCH_NUM)){
$columnList[] = $row[0];
}
$this->countRows = count($columnList);
return $columnList;
}
public function loadObject($class_name = "stdClass"){
$object = $this->stmt->fetchObject($class_name);
return $object;
}
public function loadObjectList($class_name = "stdClass"){
$objectList = array();
while($object = $this->stmt->fetchObject($class_name)){
$objectList[] = $object;
}
$this->countRows = count($objectList);
return $objectList;
}
public function loadAssocRow(){
$assocRow = $this->stmt->fetch(PDO::FETCH_ASSOC);
return $assocRow;
}
public function loadAssocList(){
$assocList = array();
while($row = $this->stmt->fetch(PDO::FETCH_ASSOC)){
$assocList[] = $row;
}
$this->countRows = count($assocList);
return $assocList;
}
public function loadIndexedRow(){
$indexedRow = $this->stmt->fetch(PDO::FETCH_NUM);
return $indexedRow;
}
public function loadIndexedList(){
$indexedList = array();
while($row = $this->stmt->fetch(PDO::FETCH_NUM)){
$indexedList[] = $row;
}
$this->countRows = count($indexedList);
return $indexedList;
}
public function loadJsonObject(){
$jsonObject = json_encode($this->stmt->fetch(PDO::FETCH_ASSOC));
return $jsonObject;
}
public function loadJsonObjectList(){
$index = 1;
$jsonObjectList = "";
while($row = $this->loadAssocList()){
$jsonObjectList .= json_encode($row);
if($index <= (count($this->loadAssocList())-1)){
$jsonObjectList .= ",";
}
}
return $jsonObjectList;
}
public function loadXmlDocument($file = null, $root = 'query',$elementName = 'entry'){
$xml = new DOMDocument('1.0','utf-8');
$table = $xml->createElement($root);
foreach ($this->loadAssocList() as $entry){
$element = $xml->createElement($elementName);
foreach ($entry as $node => $value) {
if ($this->valideXmlValue($value)) {
$field = $xml->createElement($node,$value);
$element->appendChild($field);
} else {
$field = $xml->createElement($node);
$cdata = $xml->createCDATASection($value);
$field->appendChild($cdata);
$element->appendChild($field);
}
}
$table->appendChild($element);
}
$xml->appendChild($table);
if ($file != null) {
return file_put_contents($file, $xml->saveXML());
} else {
return $xml->saveXML();
}
}
public function loadCSVFile($file = null){
if($file == null){
$file = date('Ymd-His');
}
$writer = fopen($file.'.csv', 'w');
$temp = $this->loadIndexedList();
foreach ($temp as $row){
fputcsv($writer, $row);
}
return fclose($writer);
}
public function startTransaction(){
$this->instance->beginTransaction();
$this->sentinel = 1;
$this->affectedRows = 0;
}
public function endTransaction(){
if ($this->sentinel == 1) {
$this->instance->commit();
} else {
$this->instance->rollBack();
}
return $this->sentinel;
}
public function getAffectedRows(){
return $this->affectedRows;
}
public function getCountRows(){
return $this->countRows;
}
public function getError(){
$e = array();
$e['ref'] = $this->error[0];
$e['code'] = $this->error[1];
$e['desc'] = $this->error[2];
return $e;
}
private function checkQuery(){
$this->error = $this->stmt->errorInfo();
if ($this->error[0] != 00000) {
$this->sentinel = 0;
}
// simpre que se realiza un SELECT esto pone $sentinel a 1
$this->affectedRows = $this->stmt->rowCount();
if ($this->affectedRows == 0){
$this->sentinel = 0;
}
}
private function valideXmlValue($value){
$chars = array('<','>','&');
foreach($chars as $ilegal) {
$state = strpos($value, $ilegal);
if($state !== FALSE){
return FALSE;
}
}
return TRUE;
}
}

MMSHFE
چهارشنبه 24 اردیبهشت 1393, 10:31 صبح
اینو تست کنید:

<?php
class Config {
public $driver = 'sqlsrv';
public $dbhost = '127.0.0.1';
public $dbuser = 'test';
public $dbpass = '123456';
public $dbname = 'db';
}
class database {
private static $instance;
private $stmt;
private $error = array();
private $sentinel = 1;
private $affectedRows = 0;
private $countRows = 0;
private function __construct() {
if(!self::$instance) {
try {
$config = new Config();
self::$instance = new PDO($config->driver.":Server=".$config->dbhost.';Database='.$config->dbname, $config->dbuser, $config->dbpass);
}
catch (Exception $e) {
throw new Exception('Cannot connect.');
}
}
return self::$instance;
}
public static function getInstance() {
return $this->__construct();
}
public function query($sql,$params = null) {
$this->stmt = self::$instance->prepare($sql);
$this->stmt->execute($params);
$this->checkQuery();
}
public function loadResult() {
$singleResult = $this->stmt->fetch(PDO::FETCH_NUM);
return $singleResult[0];
}
public function loadColumn() {
$columnList = array();
while($row = $this->stmt->fetch(PDO::FETCH_NUM)){
$columnList[] = $row[0];
}
$this->countRows = count($columnList);
return $columnList;
}
public function loadObject($class_name = 'stdClass'){
$object = $this->stmt->fetchObject($class_name);
return $object;
}
public function loadObjectList($class_name = 'stdClass'){
$objectList = array();
while($object = $this->stmt->fetchObject($class_name)){
$objectList[] = $object;
}
$this->countRows = count($objectList);
return $objectList;
}
public function loadAssocRow(){
$assocRow = $this->stmt->fetch(PDO::FETCH_ASSOC);
return $assocRow;
}
public function loadAssocList(){
$assocList = array();
while($row = $this->stmt->fetch(PDO::FETCH_ASSOC)){
$assocList[] = $row;
}
$this->countRows = count($assocList);
return $assocList;
}
public function loadIndexedRow(){
$indexedRow = $this->stmt->fetch(PDO::FETCH_NUM);
return $indexedRow;
}
public function loadIndexedList(){
$indexedList = array();
while($row = $this->stmt->fetch(PDO::FETCH_NUM)){
$indexedList[] = $row;
}
$this->countRows = count($indexedList);
return $indexedList;
}
public function loadJsonObject(){
$jsonObject = json_encode($this->stmt->fetch(PDO::FETCH_ASSOC));
return $jsonObject;
}
public function loadJsonObjectList(){
$index = 1;
$jsonObjectList = '';
while($row = $this->loadAssocList()){
$jsonObjectList .= json_encode($row);
if($index <= (count($this->loadAssocList())-1)){
$jsonObjectList .= ',';
}
}
return $jsonObjectList;
}
public function loadXmlDocument($file = null, $root = 'query',$elementName = 'entry'){
$xml = new DOMDocument('1.0','utf-8');
$table = $xml->createElement($root);
foreach ($this->loadAssocList() as $entry){
$element = $xml->createElement($elementName);
foreach ($entry as $node => $value) {
if ($this->valideXmlValue($value)) {
$field = $xml->createElement($node,$value);
$element->appendChild($field);
} else {
$field = $xml->createElement($node);
$cdata = $xml->createCDATASection($value);
$field->appendChild($cdata);
$element->appendChild($field);
}
}
$table->appendChild($element);
}
$xml->appendChild($table);
if ($file != null) {
return file_put_contents($file, $xml->saveXML());
} else {
return $xml->saveXML();
}
}
public function loadCSVFile($file = null){
if($file == null){
$file = date('Ymd-His');
}
$writer = fopen($file.'.csv', 'w');
$temp = $this->loadIndexedList();
foreach ($temp as $row){
fputcsv($writer, $row);
}
return fclose($writer);
}
public function startTransaction(){
self::$instance->beginTransaction();
$this->sentinel = 1;
$this->affectedRows = 0;
}
public function endTransaction(){
if ($this->sentinel == 1) {
self::$instance->commit();
} else {
self::$instance->rollBack();
}
return $this->sentinel;
}
public function getAffectedRows(){
return $this->affectedRows;
}
public function getCountRows(){
return $this->countRows;
}
public function getError(){
$e = array();
$e['ref'] = $this->error[0];
$e['code'] = $this->error[1];
$e['desc'] = $this->error[2];
return $e;
}
private function checkQuery(){
$this->error = $this->stmt->errorInfo();
if ($this->error[0] != 00000) {
$this->sentinel = 0;
}
// simpre que se realiza un SELECT esto pone $sentinel a 1
$this->affectedRows = $this->stmt->rowCount();
if ($this->affectedRows == 0){
$this->sentinel = 0;
}
}
private function valideXmlValue($value){
$chars = array('<','>','&');
foreach($chars as $ilegal) {
$state = strpos($value, $ilegal);
if($state !== FALSE){
return FALSE;
}
}
return TRUE;
}
}
کلاً وقتی static میگذارین با this$ دسترسی پیدا نکنید و از ::self استفاده کنید.

numberone1
چهارشنبه 24 اردیبهشت 1393, 10:37 صبح
الان به این خط اشکال میگیره!
Fatal error: Using $this when not in object context


public static function getInstance() {
return $this->__construct();
}

MMSHFE
چهارشنبه 24 اردیبهشت 1393, 11:33 صبح
خوب این کد رو بجاش بگذارین:

public static function getInstance() {
return new self;
}

numberone1
چهارشنبه 24 اردیبهشت 1393, 11:56 صبح
باز هم ارور میده!
الان دیگه کلا با همه چیز مشکل داره!


Fatal error: Uncaught exception 'Exception' with message 'Cannot connect.' in \db.php:24 Stack trace: #0 \db.php(30): database->__construct() #1 sssh.class.php(121): database::getInstance() #2 sssh.class.php(136): MySessionHandler->connect() #3 index.php(4): MySessionHandler->__construct() #4 {main} thrown in db.php on line 24

MMSHFE
چهارشنبه 24 اردیبهشت 1393, 12:07 عصر
خوب اطلاعات اتصال رو تنظیم کنید. الان میگه نمیتونم وصل بشم.

numberone1
چهارشنبه 24 اردیبهشت 1393, 12:14 عصر
درست کردم اطلاعات و الان این ارور و نمایش میده!
Fatal error: Call to a member function query() on a non-object
مربوط به این کد هست


public function Read($id) {
//$db = database::getInstance();
$sql = "SELECT * FROM sessions WHERE id = '$id' ";
//$params = array(':id' => $id);
//$query = 'SELECT * FROM `sessions` WHERE (`id`=\'' . mysql_real_escape_string($id) . '\')';
try {
$db->query($sql);
//$result = mysql_query($query);
} catch(Exception $e) {
return '';
}
/**if (mysql_num_rows($result) != 1) {
* return '';
**}
**/
$count = $db->getCountRows();
if($count == 0){
//return '';
var_dump($count);
echo $sql;
}
//$data = mysql_fetch_assoc($result);
$data = $db->loadResult();
$time = strtotime($data['modified']);
$hash = $data['hash'];
if (time() - $time > $this -> time_out || strcasecmp($this -> browser_hash, $hash) != 0) {
return '';
}
return $data['data'];
}

Hamed Beyranvand
یک شنبه 02 شهریور 1393, 12:01 عصر
سلام.استاد ممکنه بگین چقدر sessionها قابل اعتماد هستن؟؟مثلا اگه فلان session set شده بود panel مدیریت رو نشون بده یا مشخصات کاربر رو تو فلان session set کن که فلان جا مقادیرشو صدا بزنیم یا مدیریت نمایش پیغام های خطا(مثلا اگه session کد captcha set شده بود فلان پیغام رو نمایش بده)آیا ریسک نیست کار هایی از این قبیل رو با session انجام بدیم(هرچند در بانک ذخیره شن)؟؟؟ممکنه مثلا یبار Session set نشه و یه بخش سایت از کار بیوفته؟؟؟

MMSHFE
یک شنبه 02 شهریور 1393, 14:27 عصر
امکان نداره سشن ست نشه. مثل اینه که بگین آیا احتمال داره فلان متغیر توی رم ساخته نشه؟ حتی اگه 0.1 درصد هم چنین اتفاقی بیفته فوقش طرف لاگین نمیشه و دوباره تلاش میکنه تا لاگین کنه.

abolfazl-z
دوشنبه 03 شهریور 1393, 10:25 صبح
سلام.استاد ممکنه بگین چقدر sessionها قابل اعتماد هستن؟؟مثلا اگه فلان session set شده بود panel مدیریت رو نشون بده یا مشخصات کاربر رو تو فلان session set کن که فلان جا مقادیرشو صدا بزنیم یا مدیریت نمایش پیغام های خطا(مثلا اگه session کد captcha set شده بود فلان پیغام رو نمایش بده)آیا ریسک نیست کار هایی از این قبیل رو با session انجام بدیم(هرچند در بانک ذخیره شن)؟؟؟ممکنه مثلا یبار Session set نشه و یه بخش سایت از کار بیوفته؟؟؟

دوست عزیز شرط را برای همین مواقع گذاشتن.

اگر ست شده بود و اطلاعاتی سشن درست بود اطلاعات لازم را نشان میده در غیر این صورت ادامه کارا !


مهندس ذخیره سشن در دیتابیس کار بهینه ایی است ؟ از چه لحاظ ؟ از نظر امنیت جی ؟

Mori Bone
سه شنبه 04 شهریور 1393, 11:28 صبح
سلام. من با هماهنگی جناب مهندس شهرکی این کلاس رو به روش pdo تغییر ساختار دادم. امیدوارم لذت ببرید
ذخیره سازی سشن ها در دیتابیس به روش PDO (http://barnamenevis.org/showthread.php?466527-%D8%B0%D8%AE%DB%8C%D8%B1%D9%87-%D8%B3%D8%A7%D8%B2%DB%8C-%D8%B3%D8%B4%D9%86-%D9%87%D8%A7-%D8%AF%D8%B1-%D8%AF%DB%8C%D8%AA%D8%A7%D8%A8%DB%8C%D8%B3-%D8%A8%D9%87-%D8%B1%D9%88%D8%B4-PDO)

behnamy01
پنج شنبه 17 اردیبهشت 1394, 12:19 عصر
آقای شهرکی من یک مشکل عجیبی با این کلاس پیدا کردم، و اون اینکه وقتی که لاگین شدیم و مثلا ریدایرکت شدیم از login.php به index.php ، حالا اگر توی این صفحه index.php دکمه ctrl+F5 رو پشت سرهم تند تند بزنی، یک دفعه لاگ اوت میشی و به صفحه login.php ریدارکت میشی اتوماتیک، که دوباره باید لاگین کنی!!! دیتابیس رو هم این موقع اگر نگاه کنی، میبنی چند تا رکورد با هش های برابر توی دیتابیس موجوده، در حالی که ما یک کاربر بیشتر نبودیم که توی سایت لاگین بودیم!
من اومدم توی کلاس تغییراتی رو ایجاد کردم و قبل و بعد از صدا زدن متود اصلی کلاس در متود constructor دیتابیس رو قفل و بعدش آزاد کردم، مشکل رکوردهای تکراری حل شد ولی بازم با ctrl+f5 زدن و رفرش کردن هر صفحه ای که سشن توش استفاده میشه به login.php ریدارکت میشیم!

به نظرتون مشکل از کجاست؟ اینم اضافه کنم که من از session_regenerate_id هم بعد از ساخت سشن و توی هر صفحه ای که از سشن استفاده داره، استفاده میکنم.

MMSHFE
پنج شنبه 17 اردیبهشت 1394, 12:28 عصر
راستش فکر میکنم (مطمئن نیستم) چون با دیتابیس کار میکنه، یکم نسبت به فایل کندتره و درنتیجه با درخواستهای پشت سر هم، یه جا ممکنه قبل از اینکه به یه درخواست جواب داده بشه و اطلاعات از دیتابیس خونده بشه، درخواست بعدی بیاد و درخواست قبلی لغو بشه و مشکلاتی مثل Max Connection Limit Reached ایجاد کنه. بهرحال توی سایت واقعی فکر نمیکنم کسی بیاد تند تند Ctrl+F5 بزنه و تازه اگه بزنه هم رسیدن درخواست به سرور یکم کندتر از localhost هست و سرور فرصت داره درخواست قبلی رو کامل کنه.

abolfazl-z
پنج شنبه 17 اردیبهشت 1394, 12:35 عصر
راستش فکر میکنم (مطمئن نیستم) چون با دیتابیس کار میکنه، یکم نسبت به فایل کندتره و درنتیجه با درخواستهای پشت سر هم، یه جا ممکنه قبل از اینکه به یه درخواست جواب داده بشه و اطلاعات از دیتابیس خونده بشه، درخواست بعدی بیاد و درخواست قبلی لغو بشه و مشکلاتی مثل Max Connection Limit Reached ایجاد کنه. بهرحال توی سایت واقعی فکر نمیکنم کسی بیاد تند تند Ctrl+F5 بزنه و تازه اگه بزنه هم رسیدن درخواست به سرور یکم کندتر از localhost هست و سرور فرصت داره درخواست قبلی رو کامل کنه.

چه مزیتی داره که ما در دیتابیس بگذاریم ؟

behnamy01
پنج شنبه 17 اردیبهشت 1394, 12:41 عصر
چه مزیتی داره که ما در دیتابیس بگذاریم ؟
واسه امنیته، اگر کلاس رو نگاه کنید میبیید مثلا ip و خیلی چیزای دیگه رو از کاربر میگیرید و توی دیتابیس ذخیره میکنید با اینکار مسائلی زیادی مثل session fixation و ... حل میشه.
اگر آقای شهرکی اجازه بدن، این کلاس رو خودم دارم روش کار میکنم و یک سری ایرادی که وجود داشت رو میخوام حل بکنم و یک بهبودی بدم، بعدش صبر کنید همینجا قرار میدمش واستون.

MMSHFE
پنج شنبه 17 اردیبهشت 1394, 13:00 عصر
یکی دیگه از مزایاش، دسترسی به اطلاعات بیشتری از اطلاعات کاربران هست. مثلاً الان دقیقاً با کمک کوئری میتونید بفهمید کیا آنلاین هستن (مثلاً آخرین آمار فعالیتشون رو توی دیتابیس دارین و کسانی که کمتر از 20 دقیقه از آخرین فعالیتشون گذشته رو آنلاین نشون بدین) و...

MMSHFE
پنج شنبه 17 اردیبهشت 1394, 17:27 عصر
الان که بیشتر فکر میکنم میبینم شما بخاطر استفاده از session_regenerate_id به این مشکل برخوردین. موضوع از این قراره که شما یه درخواست میدین و سرور میخواد سشن رو تغییر بده و توی دیتابیس، id عوض میشه ولی قبل از اینکه جواب به کلاینت برسه و اونم توی کوکی، PHPSESSID رو عوض کنه، یه درخواست دیگه میفرستین که چون همون id قبلی با کوکی ارسال شده، یه درخواست جدید درنظر میگیره و رکورد جدید میسازه یا لاگین نشده حساب میکنه چون سمت سرور id سشن عوض شده ولی مهلت ندادین جواب از سرور بیاد و id جدید توی کوکی کلاینت ذخیره بشه. بهرحال توی سرور واقعی بعید میدونم چنین سناریویی پیش بیاد.

MMSHFE
پنج شنبه 17 اردیبهشت 1394, 17:29 عصر
درمورد تغییر کلاس هم مشکلی نیست. کلاس Open Source هست. یادمه قبلاً هم یکی دیگه از دوستان اون رو با PDO بازنویسی کرده بود.

behnamy01
پنج شنبه 17 اردیبهشت 1394, 18:04 عصر
الان که بیشتر فکر میکنم میبینم شما بخاطر استفاده از session_regenerate_id به این مشکل برخوردین. موضوع از این قراره که شما یه درخواست میدین و سرور میخواد سشن رو تغییر بده و توی دیتابیس، id عوض میشه ولی قبل از اینکه جواب به کلاینت برسه و اونم توی کوکی، PHPSESSID رو عوض کنه، یه درخواست دیگه میفرستین که چون همون id قبلی با کوکی ارسال شده، یه درخواست جدید درنظر میگیره و رکورد جدید میسازه یا لاگین نشده حساب میکنه چون سمت سرور id سشن عوض شده ولی مهلت ندادین جواب از سرور بیاد و id جدید توی کوکی کلاینت ذخیره بشه. بهرحال توی سرور واقعی بعید میدونم چنین سناریویی پیش بیاد.

راه حلی به ذهنتون نمیرسه واسه حل این مسئله؟ خودم که دیدم با مثلا lock کردن دیتابیس هیچ کاری نمیتونم بکنم، چون به هر جهت یک جایی باید قفل رو بردارم که باز طرف میتونه ctrl+F5 بزنه! ولی یک راه دیگه ای که به ذهنم میرسه، استفاده از sleep قبل از صدا زدن کلاس هستش! که خیلی راه بدیه:لبخند:

پیشنهاد میکنید کلا session_regenerate_id رو بردارم؟ چون من به توصیه دوستان توی این (http://barnamenevis.org/showthread.php?378853-طریقه-استفاده-از-Session_Regenerate_id&p=1675578&viewfull=1#post1675578)تاپیک از این تابع استفاده کردم.

abolfazl-z
پنج شنبه 17 اردیبهشت 1394, 18:11 عصر
یکی دیگه از مزایاش، دسترسی به اطلاعات بیشتری از اطلاعات کاربران هست. مثلاً الان دقیقاً با کمک کوئری میتونید بفهمید کیا آنلاین هستن (مثلاً آخرین آمار فعالیتشون رو توی دیتابیس دارین و کسانی که کمتر از 20 دقیقه از آخرین فعالیتشون گذشته رو آنلاین نشون بدین) و...

خوب برای آنلاین بودن هم میشه یک فیلد activity گذاشت.


واسه امنیته، اگر کلاس رو نگاه کنید میبیید مثلا ip و خیلی چیزای دیگه رو از کاربر میگیرید و توی دیتابیس ذخیره میکنید با اینکار مسائلی زیادی مثل session fixation و ... حل میشه.

خوب سربار اضافی میشه دیگه

دوست عزیز session fixation یعنی سرقت سشن که حمله کننده به راحتی نمیتواند نفوذ کند. درضمن من خیلی بار گفتم که اگر کسی session id را بتونه از روی حدث در بیاره احتمال خیلی کم شاید هم صفر داشته باشد.

برای جلوگیری از این روش ها خیل راحت میشه با چند کار ساده این کار را انجام داد.

اصلا ذخیره سشن در پایگاه داده از نظر من اشکال امنیتی و بهینه سازی داره

اصلا هدف از سشن چیه ؟ هدف اش این است که پروتکل http فراموش کار را بهش یادآوری بدهد و خود سازنده های PHP این همه توابع ننوشتن که ما بیاییم در دیتابیس ذخیره کنیم.

شما فکر کنید ما فقط لازم داریم یک session id یا حداقل یک user id و یک سری پارامتر های کمی را در سشن ذخیره کنیم کفایت می کند.

حالا برای مثال بالا خودتون قضاوت کنید :

فرض ما بر این است که dbms روی سرور دیگری است و ما بیاییم برای خواندن فقط چند کیلوبایت داده عملیات زیر را انجام دهیم :

1. ابتدا به dbms وصل شویم
2. درخواست را بفرستیم
3.درخواست بررسی بشود
4. جستجو انجام بشود
5. واکشی بشود
6. برای ما از بستر نت ارسال بشود
و ...

حالا جزئیات را نمیدونم ولی اگر بخواهیم جزئیات را هم ذکر کنیم که دیگه کلی مرحله میشه

شاید برای یک الی چند نفر سریع انجام بشود ولی برای n کاربر نه !

و همچنین درخواست های همزمان هم که الان دوستمان باهاش با مشکل مواجه شده که باید برای اینکار جدول و اون رکورد را قفل کنیم.

در کل میتونه خیلی مسائلی دیگه که من و شما نمیدونیم پیش بیاید.

از این طرف این همه در پایگاه داده بهینه سازی هایی انجام دادن و آنقدر رویش کار کردن که بتونه کوئری شما را در بهترین پیچیدگی زمانی ارائه بدهند پس ما نباید خراب اش کنیم و سربار اضافی برایش درست کنیم.

از این رو خواندن یک فایل session اونم با خود توابع c که PHP استفاده می کند سرعت بی نظیری دارد چرا که ما نمی خواهیم عملیات جستجو ایی را انجام دهیم و نیازی به الگوریتمی برای جستجو و ... نیست !

behnamy01
پنج شنبه 17 اردیبهشت 1394, 18:33 عصر
خوب سربار اضافی میشه دیگه

دوست عزیز session fixation یعنی سرقت سشن که حمله کننده به راحتی نمیتواند نفوذ کند. درضمن من خیلی بار گفتم که اگر کسی session id را بتونه از روی حدث در بیاره احتمال خیلی کم شاید هم صفر داشته باشد.



قرار نیست کسی session id رو حدس بزنه، بلکه اگر پروژه ای که ما میسازیم رو کاربری که ازش استفاده میکنه اونو توی یک هاستینگ اشتراکی استفاده کنه و اون هاستینگ امنیتش کامل نباشه، هکر بدون اینکه session id ها رو بخواد حدس بزنه میتونه به فایل سشن php که برای همه اعضای هاستینگ مشترکه دست پیدا کنه و اینجوری همه سشن رو برداره!! بعدشم شما که میگید این سربار اضافه هستش یک مسئله ای هستش که هر کسی واسه پروژه خودش باید تصمیم بگیره، من که نگفتم حتما از این به بعد سشن ها رو توی دیتابیس ذخیره کنید دیگه! من چون خودم دارم یک پروژه ای رو روش کار میکنم که واسم مهمه و این پروژه یک ادمین به جز خود بنده نداره، که فقط واسم امنیتش مهمه نه اینکه من یک دونه کاربر به سیستم فشار بیارم! خب توی این شرایط من روی امنیت تاکید بیشتری دارم، ولی ممکنه توی یک پروژه دیگه ای از سشن به صورت معمولی استفاده کنم توی یک پروژه ای هم کوکی!

hamedarian2009
پنج شنبه 17 اردیبهشت 1394, 18:39 عصر
چه بسا روی هاست هایی که shared هستند ذخیره روی فایل فلت امن تر هم باشه چون معمولا بالای http root میشینه

MMSHFE
پنج شنبه 17 اردیبهشت 1394, 20:28 عصر
دوست عزیز، این قابلیت، اگه مفید نبود خود PHP اجازه تغییر مکانیزم کاری سشن رو به برنامه نویس نمیداد. حملات سشن فقط محدود به Fixation نیست. اگه سرور درست کانفیگ نشده باشه، توی هاست اشتراکی اجازه دسترسی به فایلهای سشن سایر کاربران هم داده میشه. بعلاوه برای فیلد activity هم یکسری مشکلات هست. مثلاً کاربر بدون خارج شدن، لاگ اوت کنه. یا مسئله حذف رکوردهای Expireشده در روشهای دیگه که میان فعالیتها رو توی یک جدول جدا ذخیره میکنن که در اینجا خود PHP با کمک GC میاد اینها رو مدیریت میکنه.
برای مدیریت سشن هم باید چندین پارامتر مثل IP و Agent و... رو با هم ترکیب کنید و باهاش هش سشن رو بسازین تا هرکدوم از این موارد تغییر کرد، سشن قبلی معتبر نباشه. مثلاً اگه کاربر توی فایرفاکس لاگین کرد، توی کروم لاگین نباشه و...

بعلاوه این موضوع (ذخیره سشن در دیتابیس) ابداً چیز بدی نیست و بعنوان یک Option و قابلیت میشه بهش نگاه کرد. حالا اگه توی یک پروژه لازم بود، استفاده میکنید و اگه نیازی نداشتین، همونجا توی فایل بگذارین. این قابلیت اگه خوب نبود، فریمورکهای مطرح امروزی مثل Yii هم داخل خودشون تعبیه نمیکردنش.

مسئله نگهداری دیتابیس روی سرور دیگه رو هم نمیدونم چرا مطرح کردین چون عملاً توی 99٪ سایتهای ایرانی، Remote DB نداریم.

اینقدر یک مسئله ساده رو بزرگ و بغرنج نکنید!

MMSHFE
پنج شنبه 17 اردیبهشت 1394, 20:31 عصر
چه بسا روی هاست هایی که shared هستند ذخیره روی فایل فلت امن تر هم باشه چون معمولا بالای http root میشینه

اگه Permissionها روی اون هاست به خوبی تنظیم نشده باشه و open_basedir خاموش باشه، اجازه دسترسی به فایلها رو به همه کاربران اون هاست میده (چند مورد از هاستهای ایرانی و خارجی رو دیدم که این مشکل رو داشتن) درحالی که دیتابیس، حداقل یک مرحله امنیت بیشتر (نام کاربری و رمز و نام دیتابیس) داره. فکر نکنید همینکه یک فایل رو بالاتر از public_html قرار دادین یا htaccess. گذاشتین، دیگه امنیت تأمینه. خیلی وقتها حمله از داخل خود هاست انجام میشه (توسط یک نفر دیگه که روی همون هاست اکانت داره).

بعلاوه ذخیره سشن در دیتابیس به شما اجازه یکسری کنترلهای امنیتی بیشتر هم میده. مثلاً بطور خودکار موقعی که اطلاعات میخواد توی سشن ذخیره بشه، با الگوریتم AES و با کلید اختصاصی که خودتون توی برنامه تعریف کردین، رمزگذاری کنید و موقع خوندن باز بطور خودکار با همین الگوریتم و با کلید اختصاصی خودتون، رمزگشایی کنید. اینطوری اگه کسی به دیتابیس هم دسترسی پیدا کنه، اطلاعات موجود بدون داشتن اون کلید اختصاصی، به دردش نمیخوره.

abolfazl-z
پنج شنبه 17 اردیبهشت 1394, 23:42 عصر
دوست عزیز، این قابلیت، اگه مفید نبود خود PHP اجازه تغییر مکانیزم کاری سشن رو به برنامه نویس نمیداد. حملات سشن فقط محدود به Fixation نیست. اگه سرور درست کانفیگ نشده باشه، توی هاست اشتراکی اجازه دسترسی به فایلهای سشن سایر کاربران هم داده میشه. بعلاوه برای فیلد activity هم یکسری مشکلات هست. مثلاً کاربر بدون خارج شدن، لاگ اوت کنه. یا مسئله حذف رکوردهای Expireشده در روشهای دیگه که میان فعالیتها رو توی یک جدول جدا ذخیره میکنن که در اینجا خود PHP با کمک GC میاد اینها رو مدیریت میکنه.
برای مدیریت سشن هم باید چندین پارامتر مثل IP و Agent و... رو با هم ترکیب کنید و باهاش هش سشن رو بسازین تا هرکدوم از این موارد تغییر کرد، سشن قبلی معتبر نباشه. مثلاً اگه کاربر توی فایرفاکس لاگین کرد، توی کروم لاگین نباشه و...

بعلاوه این موضوع (ذخیره سشن در دیتابیس) ابداً چیز بدی نیست و بعنوان یک Option و قابلیت میشه بهش نگاه کرد. حالا اگه توی یک پروژه لازم بود، استفاده میکنید و اگه نیازی نداشتین، همونجا توی فایل بگذارین. این قابلیت اگه خوب نبود، فریمورکهای مطرح امروزی مثل Yii هم داخل خودشون تعبیه نمیکردنش.

مسئله نگهداری دیتابیس روی سرور دیگه رو هم نمیدونم چرا مطرح کردین چون عملاً توی 99٪ سایتهای ایرانی، Remote DB نداریم.

اینقدر یک مسئله ساده رو بزرگ و بغرنج نکنید!

داریم گفت و گو می کنیم دیگه(اگر ناراحت نمیشین :) ) و این مسئله ایی که شما دارین می گویید ساده ام نیست.
در کل که من باز هم نفهمیدم دلیل برتری اش را. معادل همه اینکار ها را میشه در session file هم انجام داد.


اگه مفید نبود خود PHP اجازه تغییر مکانیزم کاری سشن رو به برنامه نویس نمیداد.

همه اش ام نباید در این میدان دید قرار داشته باشد شاید استفاده های دیگری هم بشود.


اگه سرور درست کانفیگ نشده باشه، توی هاس...

بحث امنیت سرور را دیگر در نظر نگیرید. ما فرض را بر این می گذاریم که امنیت چه سمت سرور و چه سمت کلاینت بالا است.


بعلاوه برای فیلد activity هم یکسری مشکلات هست. مثلاً کاربر بدون خارج شدن، لاگ اوت کنه

بدون خارج شدن لاگ اوت کنه ؟ چطوری ؟ فکر کنم منظورتون این است که browser را ببندد اگر این هست مشکل اش چیه ؟ چطوری است ؟ منظورتان را متوجه نشده ام.


این قابلیت اگه خوب نبود، فریمورکهای مطرح امروزی مثل Yii هم داخل خودشون تعبیه نمیکردنش.
این دلیل نمیشه.


مسئله نگهداری دیتابیس روی سرور دیگه رو هم نمیدونم چرا مطرح کردین چون عملاً توی 99٪ سایتهای ایرانی، Remote DB نداریم.

99 درصد ندارن ! نمیدونستم.

چند راهنمایی در مورد کلاس سشن (خودم توسعه دهنده اش شدم :)) )

1. یک متدی بگذارین که بشه خود کانکشن را گرفت من معمولا تو تمام پروزه ها از یک کانکشن استفاده می کنم.
2. بهتره در ساخت هش نام سرور + http_host را هم اضافه کنید اینطوری باعث جلوگیری از حمله بر روی ساب دامنه یا دامنه های مختلف میشه.(نمیدونم من روی PHP تست کردم جواب گرفتم یعنی شناسه سشن دامنه a.example.com را برای کوکی b.example.com تغییر دادم عمل کرد)

MMSHFE
جمعه 18 اردیبهشت 1394, 07:30 صبح
داریم گفت و گو می کنیم دیگه(اگر ناراحت نمیشین :) ) و این مسئله ایی که شما دارین می گویید ساده ام نیست.
در کل که من باز هم نفهمیدم دلیل برتری اش را. معادل همه اینکار ها را میشه در session file هم انجام داد.
فکر نمیکنم بتونید انکار کنید که با در دست گرفتن کنترل سشن، اختیارات بیشتری در تنظیم اون و اینکه چه چیزهایی رو توی بحث سشن و تولید هش دخالت بدین خواهید داشت (حتی میتونید برای مثال، توی یک فایل لوگ کلیه اعمال کاربر رو ذخیره کنید و این موضوع - Logging - رو به خود سشن بسپارین و توی کدتون درگیرش نشین).

همه اش ام نباید در این میدان دید قرار داشته باشد شاید استفاده های دیگری هم بشود.
قطعاً استفاده های دیگری هم داره ولی یکی از رایجترین کاربردهاش، همین موضوع هست. شما توی اینترنت دنبال Session Security in PHP بگردین میبینید که یکی از رایج ترین راهکارها که مورد اتفاق نظر برنامه نویسان بزرگی هم هست، همین ذخیره سشن در دیتابیس هست.

بحث امنیت سرور را دیگر در نظر نگیرید. ما فرض را بر این می گذاریم که امنیت چه سمت سرور و چه سمت کلاینت بالا است.
شما سایت مشتری رو با فرضیات بالا میارین؟

بدون خارج شدن لاگ اوت کنه ؟ چطوری ؟ فکر کنم منظورتون این است که browser را ببندد اگر این هست مشکل اش چیه ؟ چطوری است ؟ منظورتان را متوجه نشده ام.
بله منظورم همون بستن مرورگر بود که اشتباهاً نوشتم لاگ اوت. منظورم اینه که برای فیلد activity اگه کاربر مرورگر رو ببنده، اون فیلد دست نخورده باقی میمونه ولی درمورد سشن، بهرحال بعد از تایم مشخصی که قابل تنظیمه، GC میاد سشنهای Expireشده رو حذف میکنه. ضمناً اگه برای هرکاری بخوایم فیلد به دیتابیس اضافه کنیم، از یکسری امتیازات PHP (مثل بروزرسانی خودکار Timestamp سشن) و... خودمون رو محروم کردیم. قرار نیست برای هر کاری که ابزار آماده اش هست، خودمون دیتابیس رو سنگین کنیم و منطق برنامه رو دستکاری کنیم.

این دلیل نمیشه.
پس چی دلیل میشه؟ پس دلیل اینکه این قابلیت رو توی فریمورک گذاشتن چی بوده؟ غیر از اینه که قابلیت مفید و سودمندی دونستنش؟ غیر از اینه که برای امنیت سشن، یکی از راهکارهای محبوب و کارآمد بوده؟

99 درصد ندارن ! نمیدونستم.
نمیگم هاستها این قابلیت رو ندارن. میگم 99 درصد پروژه های ایرانی، Remote DB توی سناریوی کاریشون نیست و دیتابیس و فایلها یکجا قرار گرفتن.

چند راهنمایی در مورد کلاس سشن (خودم توسعه دهنده اش شدم :)) )

1. یک متدی بگذارین که بشه خود کانکشن را گرفت من معمولا تو تمام پروزه ها از یک کانکشن استفاده می کنم.
2. بهتره در ساخت هش نام سرور + http_host را هم اضافه کنید اینطوری باعث جلوگیری از حمله بر روی ساب دامنه یا دامنه های مختلف میشه.(نمیدونم من روی PHP تست کردم جواب گرفتم یعنی شناسه سشن دامنه a.example.com را برای کوکی b.example.com تغییر دادم عمل کرد)
این کلاس رو توی پکیج PHP بازنویسی کردم و یکم امکاناتش اضافه شد:

class DBSessionHandler {
public $sessionTable = 'DBSession';
public $autoCreateSessionTable = true;
public $expire = 1200; // 20 minutes
public $autoStart = true;

/**
* Create the session table
*/
private function _createTable() {
DB::Query("
CREATE TABLE `{$this->sessionTable}` (
`id` char(32) COLLATE utf8_bin NOT NULL,
`expire` int(11) DEFAULT NULL,
`data` longblob,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin
");
}

/**
* Deletes expired session
*/
private function _deleteExpired() {
$expire = time();
DB::Query("DELETE FROM '{$this->sessionTable}` WHERE (`expire`<'{$expire}')");
}

/**
* Constructor
*/
public function __construct($autoStart = true) {
ini_set('session.save_handler', 'user');
$this->autoStart = $autoStart;
if($this->autoStart) {
$this->start();
}
register_shutdown_function(array($this, 'sessionClose'));
}

/**
* Update the current session ID with a newly generated one.
* @param boolean $deleteOldSession Whether to delete the old associated session values or not
*/
public function regenerateId($deleteOldSession = false) {
$oldId = session_id();
if(empty($oldId)) {
return;
}
session_regenerate_id();
$newId = session_id();
if(!$deleteOldSession) {
DB::Query("UPDATE `{$this->sessionTable}` SET `id`='{$newId}' WHERE (`id`='{$oldId}')");
}
else {
$expire = time() + $this->expire;
DB::Query("INSERT INTO `{$this->sessionTable}` VALUES ('{$newId}','{$expire}','')");
}
}

/**
* Actually start the session
*/
public function start() {
@session_set_save_handler(
array($this, 'sessionOpen'),
array($this, 'sessionClose'),
array($this, 'sessionRead'),
array($this, 'sessionWrite'),
array($this, 'sessionDestroy'),
array($this, 'sessionGC')
);
@session_start();
if(session_id() == '') {
throw new Exception('Failed to start session.');
}
}

/**
* Ends the current session and store session data
* Do not call this method directly.
*/
public function sessionClose() {
$this->_deleteExpired();
if(session_id() !== '') {
@session_write_close();
}
}

/**
* Session destroy handler
* Do not call this method directly.
* param string $id session ID
* @return boolean whether session is destroyed successfully
*/
public function sessionDestroy($id) {
DB::Query("DELETE FROM `{$this->sessionTable}` WHERE (`id`='{$id}')");
return (DB::AffectedRows() > 0);
}

/**
* Session GC (garbage collector) handler
* Do not call this method directly.
* @param integer $maxLifetime The number of seconds after which data will be seen as 'garbage' and cleaned up.
* @return boolean whether session is GCed successfully.
*/
public function sessionGC($id) {
$this->_deleteExpired();
return true;
}

/**
* Session open handler
* Do not call this method directly.
* @param string @savePath session save path
* @param @sessionName session name
* @return boolean Whether session is opened successfully
*/
public function sessionOpen($savePath, $sessionName) {
if($this->autoCreateSessionTable) {
$this->_deleteExpired();
if(DB::AffectedRows() < 0) {
$this->_createTable();
}
}
return true;
}

/**
* Session read handler
* Do not call this method directly.
* @param string $id session ID
* @return string the session data
*/
public function sessionRead($id) {
$expire = time();
$data = DB::ArrayQuery("SELECT `data` FROM `{$this->sessionTable}` WHERE (`id`='{$id}' AND `expire`>='{$expire}')");
return (count($data) > 0 ? base64_decode($data[0]['data']) : null);
}

/**
* Session write hanlder
* Do not call this method directly.
* @param string $id Session ID
* @param string $data session data
* @return boolean Whether session write is successful
*/
public function sessionWrite($id, $data) {
$data = base64_encode($data);
$expire = time() + $this->expire;
DB::Query("INSERT INTO `{$this->sessionTable}` VALUES ('{$id}','{$data}','{$expire}') ON DUPLICATE KEY UPDATE `data`='{$data}',`expire`='{$expire}'");
}

/**
* Count online users
*/
public function onlineCount() {
$expire = time();
$data = DB::ArrayQuery("SELECT COUNT(*) AS `total` FROM `{$this->sessionTable}` WHERE (`expire`>='{$expire}')");
return $data[0]['total'];
}

/**
* Return online users
*/
public function onlineUsers() {
$expire = time();
$data = DB::ArrayQuery("SELECT `data` FROM `{$this->sessionTable}` WHERE (`expire`>='{$expire}')");
$users = array();
foreach($data as $item) {
$item = base64_decode($item['data']);
if(preg_match('#username.*?"(.*?)"#i', $item, $match)) {
$users[] = $match[1];
}
}
return $users;
}
}

کلاس DB هم اینه کدش:

class DB {
/**
* @property Resource The connection field
*/
protected static $con = null;
private static $host = 'localhost';
private static $user = 'root';
private static $pass = '';
private static $db = 'test';

public function __construct() {
throw new Exception('Cannot create an object from this class.');
}

/**
* Database connector
* Initializes the object and connects to MySQL
*/
public static function Connect() {
self::$con = new mysqli(self::$host, self::$user, self::$pass, self::$db);
if (self::$con->connect_errno) {
die('Connect failed');
}
self::Query('SET NAMES \'utf8\'');
self::$con->set_charset('utf8');
}

/**
* How many rows affected?
* @return The number of affected rows by the last excuted query
*/
public static function AffectedRows() {
if(!self::$con) {
self::Connect();
}
return self::$con->affected_rows;
}

/**
* Executes a select query and return the result as standard PHP array
* @param string $query The select query to execute
* @return array The result array
*/
public static function ArrayQuery($query) {
if(!self::$con) {
self::Connect();
}
$result = array();
$rows = self::Query($query);
if($rows && ($rows->num_rows > 0)) {
while($row = $rows->fetch_assoc()) {
$result[] = $row;
}
}
return $result;
}

/**
* Escape a value to use safely in queries
* @param string $value The value to escape
* @return string|boolean The escaped value if connection exists, false otherwise
*/
public static function Escape($value) {
if(!self::$con) {
self::Connect();
}
if($value !== null) {
return self::Quote(self::$con->real_escape_string($value));
}
return 'NULL';
}

/**
* Execute a query and return the result as a MySQL resource
* @param string $query The query to execute
* @return resource|boolean The result resource if connection exists, false otherwise
*/
public static function Query($query) {
if(!self::$con) {
self::Connect();
}
return self::$con->query($query);
}

/**
* Get the last automatic generated id in insert queries
*/
public static function InsertId() {
if(!self::$con) {
self::Connect();
}
return self::$con->insert_id;
}

/**
* Quote the values
* @param string @value The value to quote
* @return string The quoted value
*/
public static function Quote($value) {
if($value !== null) {
return '\'' . trim($value, '\'') . '\'';
}
return $value;
}
}

اینم مثال از نحوه استفاده:

<?php
function AutoLoad($class) {
require_once 'class.' . $class . '.php';
}
spl_autoload_register('AutoLoad');
$dbsh = new DBSessionHandler();
?>
<!doctype html>
<html>
<head>
<title>Database Session Handler</title>
<meta charset="utf-8" />
</head>
<body>
<?php
echo '<p>There are ' . $dbsh->onlineCount() . ' user(s) online.</p>' . PHP_EOL;
echo 'Online users: <br />' . PHP_EOL;
foreach($dbsh->onlineUsers() as $user) {
echo $user . '<br />' . PHP_EOL;
}
?>
</body>
</html>
البته بدیهیه که میشه به شکل دلخواه متدهای کلاس رو تغییر داد و مواردی که مدنظرتون هست رو میتونید بهش اضافه کنید.

abolfazl-z
جمعه 18 اردیبهشت 1394, 11:56 صبح
فکر نمیکنم بتونید انکار کنید که با در دست گرفتن کنترل سشن، اختیارات بیشتری در تنظیم اون و اینکه چه چیزهایی رو توی بحث سشن و تولید هش دخالت بدین خواهید داشت (حتی میتونید برای مثال، توی یک فایل لوگ کلیه اعمال کاربر رو ذخیره کنید و این موضوع - Logging - رو به خود سشن بسپارین و توی کدتون درگیرش نشین).

قطعا.


شما سایت مشتری رو با فرضیات بالا میارین؟

مگر امنیت سرور و سیستم مشتری با برنامه نویس است ؟(دقت کنید باگ برنامه نویسی را نمیگم)


بهرحال بعد از تایم مشخصی که قابل تنظیمه، GC میاد سشنهای Expireشده رو حذف میکنه. ضمناً اگه برای هرکاری بخوایم فیلد به دیتابیس اضافه کنیم، از یکسری امتیازات PHP (مثل بروزرسانی خودکار Timestamp سشن) و... خودمون رو محروم کردیم. قرار نیست برای هر کاری که ابزار آماده اش هست، خودمون دیتابیس رو سنگین کنیم و منطق برنامه رو دستکاری کنیم.

بازم مفهومتان را متوجه نشدم.


ضمناً اگه برای هرکاری بخوایم فیلد به دیتابیس اضافه کنیم از یکسری امتیازات PHP (مثل بروزرسانی خودکار Timestamp سشن) و... خودمون رو محروم کردیم

خوب الان که شما دارین یک دیتابیس اضافه می کنید.


قرار نیست برای هر کاری که ابزار آماده اش هست، خودمون دیتابیس رو سنگین کنیم و منطق برنامه رو دستکاری کنیم.

به عنوان مثال با بروزرسانی کردن یک فیلد activity چه منطقی را دستکاری کردیم ؟ یعنی منظورتون کاهش بهینه سازی یا کد نویسی اضافی ؟


پس چی دلیل میشه؟ پس دلیل اینکه این قابلیت رو توی فریمورک گذاشتن چی بوده؟ غیر از اینه که قابلیت مفید و سودمندی دونستنش؟ غیر از اینه که برای امنیت سشن، یکی از راهکارهای محبوب و کارآمد بوده؟

نگاه کنید می خواهیم دو ویژگی را از لحاظ security ، performance و advantage بررسی کنیم. (از نظر من امنیت در درجه اول قرار میگیره ولی بین مزایا و بهنیه سازی را نمیدونم ولی فکر کنم بهینه سازی)

حالا من سوال ام از همان اول همین بود و به جوابم هم نرسیدم.

حالا مهندس شما از دید خودتون و همچنین اطلاعت کلی که وجود دارد در مورد هر یک دلایلی بیان کنید نسبت به هم بدون حاشیه (اون بحثی که در بالا داشتیم را کنار بگذارید)

MMSHFE
جمعه 18 اردیبهشت 1394, 18:15 عصر
مگر امنیت سرور و سیستم مشتری با برنامه نویس است ؟(دقت کنید باگ برنامه نویسی را نمیگم)
توی ایران زندگی میکنیم. دقت کنید که هنوز به اون مرحله نرسیدیم که کار تیمی داشته باشیم و خیلی جاها، مسئولیت تأمین امنیت هاست هم از دید مشتری با شماست. وقتی میشه جلوی یکسری خطاهای بقیه رو ازطریق برنامه خودمون بگیریم، بهتره امید به بقیه نداشته باشیم و خودمون دست به کار بشیم. گاهی اوقات هم خوندن سشنهای سایتها و... توسط کسانی که رابطه دوستانه با مدیر هاست دارن یا تطمیع مالی میکنن انجام میشه. خیلی از اسکریپتهایی که نال شدن، از همین روشها آسیب خوردن.

بازم مفهومتان را متوجه نشدم.
ببینید، مدیریت سشن PHP خودش Timestamp سشن رو اصلاح میکنه و نیاز نیست شما انجام بدین ولی اگه یه فیلد اضافه کنید، خودتون باید توی هر درخواست، آپدیت دیتابیس رو هم انجام بدین.

خوب الان که شما دارین یک دیتابیس اضافه می کنید.
دیتابسی که نه، احتمالاً منظورتون جدوله. بله جدول اضافه میشه ولی این جدول بطور خودکار پاکسازی میشه و بار اضافه ای روی جدول کاربران که توی سیستم، معمولاً خیلی زیاد باهاش سروکار داریم، گذاشته نمیشه.

به عنوان مثال با بروزرسانی کردن یک فیلد activity چه منطقی را دستکاری کردیم ؟ یعنی منظورتون کاهش بهینه سازی یا کد نویسی اضافی ؟
منظورم اینه که توی برنامه باید مدام کدهایی رو ببینیم که داره اون فیلد رو اصلاح میکنه درحالی که اون فیلد جزو منطق برنامه نیست و فقط برای بحث کاربران آنلاین و... که میشد با مدیریت صحیح سشن، بهش برسیم داره بروزرسانی میشه. این یعنی توی هر بخش از برنامه و هر قابلیت جدیدی که اضافه میکنیم، باید حواسمون به بروزرسانی این فیلد هم باشه.

نگاه کنید می خواهیم دو ویژگی را از لحاظ security ، performance و advantage بررسی کنیم. (از نظر من امنیت در درجه اول قرار میگیره ولی بین مزایا و بهنیه سازی را نمیدونم ولی فکر کنم بهینه سازی)

حالا من سوال ام از همان اول همین بود و به جوابم هم نرسیدم.

حالا مهندس شما از دید خودتون و همچنین اطلاعت کلی که وجود دارد در مورد هر یک دلایلی بیان کنید نسبت به هم بدون حاشیه (اون بحثی که در بالا داشتیم را کنار بگذارید)
امنیت و کارآیی هر دو به یک اندازه اهمیت دارن. سایت امنی که دیر بالا میاد به درد مشتری نمیخوره. سایت سریعی که امنیت نداره هم به همون اندازه بلا استفاده است. درمورد مزایای یک سیستم هم به نوعی میشه گفت باید امنیت و کارآیی رو با هم درنظر گرفت. امتیازها و ویژگیهای خوبی که توی یک سیستم وجود داره بدون امنیت و راندمان مناسب، بدرد نمیخوره. شما حاضرین یک موبایل که کلی امکانات داره ولی به راحتی میشه اطلاعات شما رو از داخلش با بلوتوث بیرون کشید و برای هر درخواست، 5 دقیقه شما رو معطل میکنه بخرین، فقط به این خاطر که قابلیتهای زیادی داره؟ همه اینها در کنار هم بدرد میخورن و هرکدوم نباشه، یه جای کار میلنگه.

abolfazl-z
جمعه 18 اردیبهشت 1394, 20:43 عصر
ببینید، مدیریت سشن PHP خودش Timestamp سشن رو اصلاح میکنه و نیاز نیست شما انجام بدین ولی اگه یه فیلد اضافه کنید، خودتون باید توی هر درخواست، آپدیت دیتابیس رو هم انجام بدین.

اون وقت اگر من سشن های متفاوتی داشته باشم چی ؟


منظورم اینه که توی برنامه باید مدام کدهایی رو ببینیم که داره اون فیلد رو اصلاح میکنه درحالی که اون فیلد جزو منطق برنامه نیست و فقط برای بحث کاربران آنلاین و... که میشد با مدیریت صحیح سشن، بهش برسیم داره بروزرسانی میشه. این یعنی توی هر بخش از برنامه و هر قابلیت جدیدی که اضافه میکنیم، باید حواسمون به بروزرسانی این فیلد هم باشه.

خوب البته سیستمی که من باهاش کار می کنم یک سیستم کاملا شخصی است و از صفر نوشته شده و شما فقط یکبار اونم بصورت خیلی زیبا آپدیت را انجام میدهید.(تا آخر تابستان اگر بشه میزارم تو گیتهاب)

من این رو نمیتونم قبول کنم که این یک سربار یا روش غیرمعقولی است !



امنیت و کارآیی هر دو به یک اندازه اهمیت دارن. سایت امنی که دیر بالا میاد به درد مشتری نمیخوره. سایت سریعی که امنیت نداره هم به همون اندازه بلا استفاده است. درمورد مزایای یک سیستم هم به نوعی میشه گفت باید امنیت و کارآیی رو با هم درنظر گرفتم. امتیازها و ویژگیهای خوبی که توی یک سیستم وجود داره بدون امنیت و راندمان مناسب، بدرد نمیخوره. شما حاضرین یک موبایل که کلی امکانات داره ولی به راحتی میشه اطلاعات شما رو از داخلش با بلوتوث بیرون کشید و برای هر درخواست، 5 دقیقه شما رو معطل میکنه بخرین، فقط به این خاطر که قابلیتهای زیادی داره؟ همه اینها در کنار هم بدرد میخورن و هرکدوم نباشه، یه جای کار میلنگه.

باز رفتین حاشیه یا اینکه فکر کنم من منظورم را بد بیان کردم. منظور من در مورد session in dbms با session in file بود.

MMSHFE
شنبه 19 اردیبهشت 1394, 08:35 صبح
اون وقت اگر من سشن های متفاوتی داشته باشم چی ؟
منظورتون رو متوجه نمیشم. شما اگه هزاران سشن هم داشته باشین، PHP خودش اونها رو مدیریت میکنه.

خوب البته سیستمی که من باهاش کار می کنم یک سیستم کاملا شخصی است و از صفر نوشته شده و شما فقط یکبار اونم بصورت خیلی زیبا آپدیت را انجام میدهید.(تا آخر تابستان اگر بشه میزارم تو گیتهاب)

من این رو نمیتونم قبول کنم که این یک سربار یا روش غیرمعقولی است !
اگه این رو قبول داشته باشین که این کارها رو خود PHP میتونه به روش بهینه تر و اصولی تری مدیریت کنه، اونوقت میتونید بپذیرید که این روش بهرحال یک سربار داره روی کدها میگذاره چون PHP درهرصورت مدیریت سشن خودش رو داره و فقط ما اومدیم گفتیم برای امنیت بیشتر و دراختیار داشتن امکانات بیشتر، بجای فایلها و مسیر پیشفرضش، توی دیتابیس و جدولی که ما میگیم ذخیره کنه.

باز رفتین حاشیه یا اینکه فکر کنم من منظورم را بد بیان کردم. منظور من در مورد session in dbms با session in file بود.
مشخصه که سرعت Session in File بیشتره ولی امنیت و امکانات و انعطاف پذیری Session in DB و بطور کلی User Session Handling بیشتره.

من بحثی ندارم، این قابلیت رو مطرح کردم. حالا هر کسی اون رو سودمند میدونه میتونه استفاده کنه. هرکی هم معتقده مفید نیست یا به کارش نمیاد، دلیلی نداره استفاده کنه. بهره گیری از امکانات مختلف، یک Option هست. همونطور که توی فریمورکها، این قابلیت وجود داره ولی اجباری یا حتی پیشفرض نیست. نمیدونم چرا دنبال برنده توی بحث هستین.

abolfazl-z
شنبه 19 اردیبهشت 1394, 17:11 عصر
منظورتون رو متوجه نمیشم. شما اگه هزاران سشن هم داشته باشین، PHP خودش اونها رو مدیریت میکنه.

منظورم این هست که چند نوع کاربر مختلف آنلاین داشته باشم. اون وقت برای بدست آوردن تعداد آنلاین ها چیکار می کنیم ؟

مثلا سیستم ما قابلیتی داره که با یک session دو نوع کاربر را آنلاین می کند یعنی مثلا یکی کاربر فروشگاه یکی دیگه کاربر انجمن. (برای مثال عرض کردم)

حالا ما می خواهیم تعداد آنلاین بودن کاربران فروشگاه و کاربران انجمن را جدا جدا حساب کنیم. چطوری باید انجام بدهیم ؟ آیا میشود ؟


من بحثی ندارم، این قابلیت رو مطرح کردم. حالا هر کسی اون رو سودمند میدونه میتونه استفاده کنه. هرکی هم معتقده مفید نیست یا به کارش نمیاد، دلیلی نداره استفاده کنه. بهره گیری از امکانات مختلف، یک Option هست. همونطور که توی فریمورکها، این قابلیت وجود داره ولی اجباری یا حتی پیشفرض نیست. نمیدونم چرا دنبال برنده توی بحث هستین.

مهندس من اصلا قصد جسارت ندارم ولی می خواهم برای سوال هایم جواب های علمی باشد و خیلی دوست دارم با افراد با تجربه ایی چون شما بحث و گفتگو چه کوچیک چه بزرگ داشته باشم.

من روی سیستمی که دارم می سازم(با اون سیستمی اصلی نوشته میشه) یکم حساسم و باید پایه اش خوب ساخته بشه و دارم بحث می کنم که ببینم نظر بالا و پایین چطوریه و چه دیدی دارن شاید بدرد پروژه ام خورد و به بار علمی افزوده شد.


مشخصه که سرعت Session in File بیشتره ولی امنیت و امکانات و انعطاف پذیری Session in DB و بطور کلی User Session Handling بیشتره.

کاملا باهاتون موافقم اما انعطاف پذیری را میتوانم قبول کنم ولی امنیت را نه.


اگه Permissionها روی اون هاست به خوبی تنظیم نشده باشه و open_basedir خاموش باشه، اجازه دسترسی به فایلها رو به همه کاربران اون هاست میده (چند مورد از هاستهای ایرانی و خارجی رو دیدم که این مشکل رو داشتن) درحالی که دیتابیس، حداقل یک مرحله امنیت بیشتر (نام کاربری و رمز و نام دیتابیس) داره. فکر نکنید همینکه یک فایل رو بالاتر از public_html قرار دادین یا htaccess. گذاشتین، دیگه امنیت تأمینه. خیلی وقتها حمله از داخل خود هاست انجام میشه (توسط یک نفر دیگه که روی همون هاست اکانت داره).

خوب اگر قرار باشه به فایل دسترسی داشته باشند یعنی فاجعه. دیگه سشن و دیتابیس و ... امنیتشان به صفر می رسد. به عنوان مثال در سی ام اس های وردپرس راحت میتونن فایل wp-config را بخونند و اطلاعات دیتابیس را بدست بیاورند. درواقع همه کاری میتونند انجام بدهند دیگه.

MMSHFE
یک شنبه 20 اردیبهشت 1394, 09:00 صبح
منظورم این هست که چند نوع کاربر مختلف آنلاین داشته باشم. اون وقت برای بدست آوردن تعداد آنلاین ها چیکار می کنیم ؟

مثلا سیستم ما قابلیتی داره که با یک session دو نوع کاربر را آنلاین می کند یعنی مثلا یکی کاربر فروشگاه یکی دیگه کاربر انجمن. (برای مثال عرض کردم)

حالا ما می خواهیم تعداد آنلاین بودن کاربران فروشگاه و کاربران انجمن را جدا جدا حساب کنیم. چطوری باید انجام بدهیم ؟ آیا میشود ؟
چرا فکر میکنید کاری هست که با سشن عادی میتونید انجام بدین و با این سشن نمیشه؟ اینجا فقط اومدیم محل ذخیره سازی رو عوض کردیم. هر کاری توی پروژه خودتون با سشن خود PHP انجام دادین، اینجا هم میتونید حتی با انعطاف بیشتر انجام بدین. برای مثال، توی سشن کاربران، شناسه خاصی رو ذخیره کنید یا برای متغیرهای سشن اونها، پیشوند جداگانه تعریف کنید و...

مهندس من اصلا قصد جسارت ندارم ولی می خواهم برای سوال هایم جواب های علمی باشد و خیلی دوست دارم با افراد با تجربه ایی چون شما بحث و گفتگو چه کوچیک چه بزرگ داشته باشم.

من روی سیستمی که دارم می سازم(با اون سیستمی اصلی نوشته میشه) یکم حساسم و باید پایه اش خوب ساخته بشه و دارم بحث می کنم که ببینم نظر بالا و پایین چطوریه و چه دیدی دارن شاید بدرد پروژه ام خورد و به بار علمی افزوده شد.
این خیلی خوبه.

کاملا باهاتون موافقم اما انعطاف پذیری را میتوانم قبول کنم ولی امنیت را نه.
خوب اگر قرار باشه به فایل دسترسی داشته باشند یعنی فاجعه. دیگه سشن و دیتابیس و ... امنیتشان به صفر می رسد. به عنوان مثال در سی ام اس های وردپرس راحت میتونن فایل wp-config را بخونند و اطلاعات دیتابیس را بدست بیاورند. درواقع همه کاری میتونند انجام بدهند دیگه.
نه دوست عزیز، بحث امنیت فایلها فقط مربوط به کانفیگ کاربران و گروهبندی اونها و مجوزهای دسترسی میشه ولی توی دیتابیس، یکم اوضاع فرق میکنه. معمولاً توی هاستها دسترسی به پوشه tmp که محل پیشفرض ذخیره سشنهاست، برای تمام کاربران فراهم هست و اگه سرور خوب تنظیم نشده باشه، کاربران به تمام فایلهای این پوشه دسترسی دارن ولی این معناش این نیست که به پوشه public_html شما هم دسترسی دارن و میتونن تنظیمات دیتابیس و... رو بخونن.