ذخیره جلسات در پایگاه داده
شهریور ۱۲م, ۱۳۸۹
دفعه قبل، درباره دزدیدن جلسه صحبت کردم، هنوز منتشر نشده که فیدبک رو ببینم) به هر صورت، این دفعه میخوام روشی رو توضیح بدم که میشه مشکل ذخیره جلسه رو در فایل سیستم حل کرد.
این راه حل آخرین راه حل در این زمینست. اگه تنظیمات سرور درست نباشه، و یه کاربر دیگه روی سیستم شما، بتونه فایلهاتون رو از طریق کاربر خودش باز کنه، خیلی راحت میتونه سورس شما رو بدزده، به دیتابیس شما دسترسی پیدا کنه و … (تنظیمات دیتابیس رو توی PHP معمولا توی فایل متنی مینویسن، خوب اگه یکی اونو باز کنه و بخونه دیتابیس هم امن نیست :) ) اگه میخواید برنامه خودتون رو منتشر کنید و نمیخواید دیگران به سورسش دسترسی داشته باشن، حتما با یه چیزی مثل Zend Guard از اون محافظت کنید، و از اون مهمتر،قیمت مهمترین فاکتور خرید هاست نیست!!!! فکر میکنم اعتماد و امنیت مهمتر باشه!
شاید بد نباشه که اول راه حل ساده تر رو نشون بدیم.
اولین مساله، اینکه مشخص کنیم آیا واقعا از آخرین دسترسی به این جلسه، یه مدت زمان خاص گذشته یا نه؟ دوم اینکه مشخص کنیم این جلسه واقعا برای همین کاربریه که الان میخواد ازش استفاده کنه؟
این کد رو ببینید :
<?php
//session_save_path("../tmp");
session_start();
//Settings
$diff=10*60; //600 Sec time out
$salt="this-is-uniq-hash-for-any-program";
$_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'] : 'NO USER AGENT';
$browser_data=$salt.$_ip.$_agent;
$browser_hash=md5($browser_data);
$now=time();
if (isset($_SESSION['last_time']) && isset($_SESSION['browser_hash'])){
if (strcasecmp($browser_hash,$_SESSION['browser_hash'])!=0
|| $now-$_SESSION['last_time']>$diff){
foreach ($_SESSION as $key => $value){
unset($_SESSION[$key]);
}
session_destroy();
//You can pass to login page or whatever :-"
header("Location: ".$_SERVER['PHP_SELF']);
die(); //THIS IS IMPORTANT, never trust user browser,
}
}else{
//this is new session....
//Old session but without time and browser hash ?
//Just destroy it, if any, to prevent hijaking
foreach ($_SESSION as $key => $value){
unset($_SESSION[$key]);
}
$_SESSION['last_time']=$now;
$_SESSION['browser_hash']=$browser_hash;
}
این کد رو دقیقا در ابتدای برنامه بگذارید. دقیقا در ابتدای فایل index.php (معمولا) میتونید یه فایل جدا درست کنید این کد رو توش بنویسید و در ابتدای برنامه اینو include کنید. حالا دیگه هم زمان کنترل میشه هم بروزر، گرچه این متد بسیار بسیار سادست :) ولی خیلی هم موثره.
دقیقا اولین خط کد، که کامنت هم شده،میتونه این امکان رو به شما بده که جای ذخیره شدن جلسات رو عوض کنید، مثلا بیاریدشون روی پوشه های خودتون(که البته باید یه فولدری باشه که وب سرور بهش دسترسی نداشته باشه، یعنی یا با htaccess یا با هر ابزار دیگه ای کاملا قفلش کرده باشید یا اینکه اصلا خارج از پوشه ریشه باشه. البته این پوشه باید توسط PHP قابل نوشتن باشه که بتونه جلسات رو اونجا ذخیره کنه. اگر نه یکی از بیرون و به راحتی از طریق Browser میتونه فایل رو ببینه)
اما راه دوم، که به شما این امکان رو میده که مثلا جلسات رو توی دیتابیس ذخیره کنید، نه توی فایل. از PDO برای دیتابیس استفاده شده.
خواندن ادامه این مطلب »
جلسات PHP : آره یا نه؟ مساله اینه.
شهریور ۵م, ۱۳۸۹
مقدمه
باور کنید دست خودم نبود!!! هدفم هم اصلا خرابکاری نیست. بیشتر آزمایشه و اطلاع رسانی. من چیکار کنم آخه؟ وقتی اینقدر ساده میشه امنیت یه وب سایت رو به مخاطره انداخت! هدف من آموزش این نیست که چطور خرابکاری کنید، هدفم اینه که کمک کنم این مشکل برطرف بشه.
بحث اصلی : جلسات
معمولا جلسات، از اون چیزهاییه که تو تمام زبانهای برنامه نویسی تحت وب استفاده میشه. این جلسات، اطلاعاتی هستن که در طرف سرور ذخیره میشه، ولی کلید اونها در طرف کلاینت. این کلید معمولا یه رشته غیر قابل حدسه و به دو صورت رد و بدل میشه.
روش اول از طریق لینکه، یعنی مثلا یه لینکی داریم به این صورت :
http://example.com/?sessid=zGNQE1DlHGFjDZLEfdeNK3rvRC7
وقتی این درخواست میرسه به PHP (من کاری به بقیه زبانها ندارم) خودش جلسه با این شماره (zGNQE1DlHGFjDZLEfdeNK3rvRC7) رو پیدا میکنه و بارگذاری میکنه. البته اگه باشه. اگه نباشه هم که خیلی ساده چشماشو میبنده و رد میشه. راه دوم اینه که کلید جلسه از طریق کوکی منتقل شه، یه جورایی محبوب تره این روش، علتشم اینه که لینک خراب نمیشه، لزومی هم نداره هر لینکی تو صفحه هست این اطلاعات بهش الصاق بشه. (شما باید وقت اجرا این کلید جلسه رو بگیرید و هر لینکی که خواستید ایجاد کنید اینو به انتهاش اضافه کنید ولی توی روش کوکی چون این اطلاعات با کوکی رد و بدل میشه نیاز نیست.)
اگه به صورت لینک باشه، دیگه میشه گفت CSRF که قبلا دربارش نوشتم پیش نمیاد، اما معایبشو گفتم.
حالا اگه یکی تو یه کامپیوتر دیگه، به هر نحوی به این کلید دست پیدا کنه، در زمانی که شما هنوز آنلاین هستید و جلسه اعتبار داره بیاد و صفحه شما رو، با این کلید باز کنه (یعنی اگه لینکه که با ایجاد لینک شبیه سازی شده و اگه کوکیه با ایجاد کوکی مورد نظر درخواستو شبیه سازی کنه، خیلی ساده میره تو حساب شما! ایجاد کوکی هم با FireBug و FireCookie مثل آب خوردنه!!)
به همین علته که میگن هر وقت از پای صفحه خودتون بلند شدید، حتما logout کنید. فکر نکنید چون پشت کامپیوتر خودتونید همه چی آرومه!! نه، ممکنه همین الان یه نفر جلسه شما رو از اون طرف دنیا دزدیده باشه و داخل صفحه شما شده باشه!! با این logout جلسه تعطیل میشه و کلیدش بی ارزش میشه.و اینطوری اون آقا/خانم دزد،با خارج شدن شما، ناکام میمونه، اما اگه اینکارو نکنید ممکنه تا مدتها همچنان فعال باقی باشه.
این که از وسط راه جلسه دزدیده بشه، فقط و فقط یه راه حل داره اونم استفاده از https هستش. یعنی در حالت عادی و غیر https راهی نیست که از دزدیدن کلید در میانه راه جلوگیری کنیم. به عبارتی ISP شما، و تمام کسانی که در مسیر ارتباط شما به سایت قرار گرفتن با یه مانیتورینگ ساده میتونن اطلاعات جلسه رو بدزدن. خوب، اینو میتونید با یه هزینه اضافه امن کنید ولی آیا خطر این دزدیده شدن جلسه فقط همین جاست؟ یعنی اگه جلسه سالم به مقصد رسید :D اونوقت همه چی سر جاشه؟ خوب بالاخره رسیدم به اصل مطلب.
خواندن ادامه این مطلب »
CSRF یا چطور چرخ رو دوباره اختراع کردم
مرداد ۲۲م, ۱۳۸۹
امروز یه اتفاق خیلی ساده، یه درس بزرگ بهم داد. داشتم یه برنامه ساده CRUD مینوشتم، یعنی یه برنامه ای که یه سری فیلد رو توی دیتابیس بنویسه، ویرایش داشته باشه و حذف کنه و در نهایت نشون بده.
این برنامه فقط برای نمایش برای یه نفر بود که میخواست یه چیزی یاد بگیره :دی خیلی ساده.
مساله لینک حذف بود، این لینک :
http://localhost/admin.php?del=10
در حقیقت این میومد و ردیف شماره ۱۰ رو حذف میکرد. البته توی admin.php بررسی میشد که آیا کاربر اجازه دسترسی داره یا نه. اگه اجازه حذف داشت اونوقت حذف رو انجام میداد. به نظر همه چی درست بود، ولی یه اتفاق باعث شد بفهمم یه جای کار میلنگه.
این لینک رو اتفاقی کپی کردم توی کلیپ برد. خودمم همزمان میخواستم یه جایی تو یه پیغام شخصی فروم، یه عکس بگذارم. منتها اشتباها این لینک کپی شد به جای لینک عکس اصلی. از اونجایی که دقت منم در حد تیم ملیه :) عکس رو با بدون اینکه آدرسش رو چک کنم گذاشتم توی پیغام، دکمه پیشنمایش رو زدم و …
باقیش معلومه. فیلد ۱۰ حذف شد، و من حتی متوجه نشده بودم تا یه کم بعد رفتم دوباره سراغ برنامه.
خوب این یعنی که با اینکه من امنیت رو کاملا حفظ کرده بودم توی فایل admin.php ولی این امنیت خیلی راحت به هم ریخت با وادار شدن من برای دیدن یه عکس. خوب اگه اینکار رو یه نفر دیگه انجام میداد و یه پیغام حاوی لینک حذف برای من میفرستاد که مثلا به صورت عکس جاسازی شده بود، خیلی راحت به مقصودش میرسید. من اجازه حذف دارم، و فقط کافیه اون لینک یه بار توسط من ویزیت بشه، به هر صورتی.
راه چاره چیه؟؟ اینکه از متد GET استفاده نکنیم.یا اینکه تو صفحه دوم هم یه سوال بپرسیم و بعد از طریق متد پست حذف نهایی رو توی مرحله دوم انجام بدیم، یه کمی به نظر من اعصاب خورد کنه.
البته من یه پیشنهاد هم دارم، که فکر میکنم بد نباشه. یه کلمه رندوم ایجاد بشه تو هر جلسه برای هر کاربر، مثلا به اسم $salt . از هر جلسه به جلسه دیگه متفاوت باشه. به جای لینک بالا، از این لینک استفاده بشه (یا چیزی شبیه اون):
http://localhost/admin.php?del=10&salt=$slat
وقت حذف علاوه بر بررسی سطح دسترسی کاربر این “نمک” هم بررسی بشه :) اگه اونم با متغییری که تو جلسه ذخیره شده برابر بود کاری که باید انجام بشه،انجام بشه.
اینطوری، کسی نمیتونه لینک رو درست کنه. (مگه اینکه یه جوری جلسه مدیر رو بدزده، که خوب در اون صورت دسترسی خود مدیر رو داره و دیگه لازم نیست یه همچین کارهایی رو انجام بده، خودش دسترسی داره و میتونه هر کاری میخواد بکنه!!)
پ.ن : به این روش میگن CSRF یا Cross-Site Request Forgery که جالب اینه که خودم هم میدونستم ولی هیچوقت دقت نکرده بودم یعنی همیشه طور دیگه ای بهش نگاه میکردم، اینکه مثلا یه سایت خطرناک رو به خورد کاربر بدن نه سایتی که اصلا خطری نداره و اتفاقا کاربر اجازه دسترسی بهش رو داره و از دسترسی کاربر سواستفاده بشه. این روش هم من دوباره کشف کردم، قبل از من بازم کشف شده بوده :D و من فقط دوباره چرخ رو اختراع کردم!!
بیشتر درباره این قضیه که بگردید دوباره منابع بهتری رو پیدا میکنید و راه حلهای دقیقتر و بهتری.
Mysql Menu قسمت سوم
مرداد ۱۲م, ۱۳۸۹
چند وقت پیش در باره منو و طریقه ایجاد آن با mysql نوشتم. منظورم بیشتر روش دومی بود که معرفی کردم. خودم تا همین روزها نیاز نشده بود که از این منو استفاده کنم، تا امروز برای کاری ار همان پست استفاده کردم. (این وبلاگ بیشتر از هر کسی گویا به درد خودم میخورد) میخواستم از همان کوئری ها استفاده کنم و یک ساختار تشکیل شده از ul و li های تو در تو درست کنم. اول سعی کردم اطلاعات را داخل آرایه بریزم که بعد به صورت html دربیاورم که متوجه شدم دارم لقمه دور سر میچرخانم.
با توجه به اینکه ساختار منو (لطفا پست MySQL , Menu قسمت دوم رو حتما بخونید) دقیقا شبیه ساحتار تو در توی html هست میشه از این خاصیت استفاده کرد :) یعنی اونجا همه منو های یه سمت چپ داشتن یه سمت راست، تو html هم هر تگی یه سمت چپ داره یه سمت راست. من میخوام یه ساختار منوی ساده با ul و li به وجود بیارم طوری که :
- عنصر دور تا دور فقط یه ul داره.
- عناصری که زیر مجموعه ندارن دورشون li میفته
- عناصری که زیر مجموعه دارن هم li میخوان هم ul .
- اگه آیتمی هیچ زیر منو نداشت اونوقت متن اون آیتم هم نشون داده بشه (شما اینو خودتون میتونید گسترش بدید، یا برای همه منوها آیتم رو نشون بدید، من ترجیح دادم سادهتر کار کنم.)
نتیجه خیلی سادست. فقط باید حالات مختلف رو بنویسم.ساختار دیتابیس اینه (مثل مثال قبلی ، فقط فیلد name به صورت unique در اومده برای اینکه توی کد باید به یه چیزی تکیه کرد ;) )
CREATE TABLE IF NOT EXISTS `adv_menu` ( `id` int(11) NOT NULL AUTO_INCREMENT, `name` varchar(60) NOT NULL, `data` varchar(255) NOT NULL, `lside` int(11) NOT NULL, `rside` int(11) NOT NULL, PRIMARY KEY (`id`), UNIQUE KEY `name` (`name`) ) ENGINE=MyISAM;
اینبار میخوام از PDO استفاده کنم برای اتصال به mysql . ایده خیلی سادست. فقط در نظر بگیرید که همونطور که قبلا گفتم (تو پست قبلی در این مورد) هر منو یه چپ داره یه راست.
خوب چی میشه اگه سمت چپ رو شروع تگ در نظر بگیریم و سمت راست رو پایان تگ؟ چی میشه اگه یه آرایه ایجاد کنیم به تعداد دو برابر آیتمها، با ایندکس ۱ تا دوبرابر آیتمها، بعد تو آرایه ایندکس سمت چپ تگ شروع html رو بذاریم (مثلا یه li یا یه ul بعد یه li یا هر چیز دیگه ای) و در ایندکس سمت راست هم دقیقا پایان همون تگ سمت راست رو (مثلا اینبار /li )، فقط کافیه بدونیم که مثلا آیتم اگه زیر مجموعه نداشت فقط li میخواد (که این فقط در صورتیه که سمت چپی یکی کمتر از سمت راستی باشه) اگه منوی دور تا دوره فقط یه ul میخواد (که خوب این هم سمت چپش همیشه ۱ میشه) و در غیر این صورت ul و li با هم.
خوب این خیلی ساده ایتمهای توی دیتابیس رو تبدیل میکنه به یه ساختار html و کافیه که اینبار فقط این آرایه رو به هم بچسبونیم و در خروجی نشون بدیم.
کد زیر کل این کارها رو به ساده ترین وجهی انجام میده، ولی باز هم جای کار داره، خیلی زیاد هم جای کار داره.
خواندن ادامه این مطلب »
ارسال mail از طریق Zend Framework
تیر ۲۲م, ۱۳۸۹
لطفا کامنتهای داخل کدها رو هم بخونید!! بعضیاشون واقعا ضروری هستن!
Zend Framework انتخاب من برای یک فریم ورک برای کار با PHP هستش. اینبار میخوام درباره چگونگی ارسال ایمیل به صورتی که بشه براش قالب ایجاد کرد بنویسم. این کد رو من خیلی وقت نیست که نوشتم. اگه مشکل داشت خوشحال میشم برام بنویسید تا بدونم.
برای استفاده باید چند تا کار انجام بدید. اول اینکه توی فایل تنظیمات برنامه خودتون یه سری اطلاعات اضافه کنید. اگه از ini استفاده میکنید اینو به فایل تنظیمات اضافه کنید :
mail.from= "you@yoursite.com" mail.name= "Your name" mail.templatePath=APPLICATION_PATH "/../../layouts/mails" mail.host=imap.yoursite.com mail.port=25 mail.param.auth=login mail.param.username=you@yoursite.com mail.param.password=yourpassword
اینو توی بخش مناسب بنویسید مثلا برای development توی بخش development و… فقط مساله templatePath هست که اونم آدرس رو عوض کنید با آدرسی که میخواید view های مربوط به ایمیل قرار بگیره. (این رو قطعا باید بر اساس پروژه خودتون عوض کنید)
گام بعدی اینه که تنظیمات رو بخونید. اینجوری (معمولا توی bootstrap):
//Load yor config CHANGE PATH TO YOUR CONFIG FILE!!!!!!
$config=new Zend_Config_Ini(APPLICATION_PATH . '/../../configs/application.ini',APPLICATION_ENV);
Zend_Registry::set('config',$config);
//Do your stuff......
//Now its time to setup mail system :D
$config=Zend_Registry::get('config');
$mailParams=$config->mail->toArray();
Zend_Registry::set('mail_params',$mailParams);
شکه نشید ولی تا الان هیچ کاری انجام نشده :) حالا وقت ایجاد Cybits_Utils_View_Helper_SendMail هستش. توی فولدر library چند تا فولدر باید درست شه :
خواندن ادامه این مطلب »
