PDA

View Full Version : سوال: آپلود فايل + امنيت



abdorreza
سه شنبه 08 شهریور 1390, 07:53 صبح
سلام

براي امنيت قسمت آپلود فايل بايد چه كاري كرد؟

چون كاربر ميتونه مثلا هر فايلي را آپلود كنه. ميتونه پسوندشو مثلا بكنه JPG و آپلود كنه.

ممنون.

A B C D
سه شنبه 08 شهریور 1390, 08:47 صبح
بیشتر آپلودها اجازهء فایل زیپ رو میدن. کاربر میتونه هر فایلی رو بذاره توی فایل زیپ. حالا فرضا پسوند فایل رو عوض کنه که چی بشه، خب بجاش زیپ میکنه!

mamali-mohammad
سه شنبه 08 شهریور 1390, 11:00 صبح
خب وقت پسوند رو jpg کنه ، ازش نمی تونه استفاده کنه
شما برای اطمینان فقط اجازه آپلود چند فایل خاص رو بده

alireza.stack
سه شنبه 08 شهریور 1390, 11:25 صبح
برای تعیین نوع آپلود شما بایستی نوع فایل آپلودی را با Mime تعیین کنید تا به جز آن فایلی آپلود نکند. البته برقراری امنیت آپلود بسیار حساس است و باید حواستان باشد. بطور مثال ممکن است در نام تصویر کاربری کد وارد کند و در سایت شما پیمایش کند و به وسیله ی Trivial سایت را هک کند. اگر تصویر آپلود می کنید پس از دریافت آن از کاربر نام آن را به اعداد تصادفی تغییر بدهید(برای امنیت بیشتر)
گاهی اوقات در هدر تصاویر کد هگز عکس را قرار می دهند و در ادامه ی آن کدهای اجرائی می نویسند که مرورگرهای قدیمی قادر به تشخیص آن نیستند.

A B C D
سه شنبه 08 شهریور 1390, 11:32 صبح
این Trivialچیه؟

alireza.stack
سه شنبه 08 شهریور 1390, 13:33 عصر
بوسیله ی Trivial می توان ساختار فهرستهای سایت را پیمایش کرد و با پیمایش و سپس حدس زدن پوشه هائی مانند include و... به پوشه های حساس سایت دسترسی داشت که البته اگر Trivial فعال باشد و دسترسی پوشه ها را محدود کرد اینکار ما را با مشکل مواجه خواهد کرد.
موفق باشید

Net So
سه شنبه 08 شهریور 1390, 13:44 عصر
Extension و MIME رو میشه Bypass کرد.
اولی رو به اشکالی نظیر » shell.php.jpg - shell.php;.jpg
دومی رو به اشکای نظیر » فایل رو با notepad باز کن و اولش این رو بزار : GIF89a;


گاهی اوقات در هدر تصاویر کد هگز عکس را قرار می دهند و در ادامه ی آن کدهای اجرائی می نویسند که مرورگرهای قدیمی قادر به تشخیص آن نیستند.
البته Code Bind زیاد کارا نیست ولی حتما فایل ها باید تغیر نام بدن و پوشه اونها تغییر کنه. Perm پوشه رو حتما Set کن. فرضا اگه شل آپ بشه و بیاد بالا اگه Dir Perm 775 نداشته باشه نمیتونه هیچ کاری بکنه . از آپلود یه فایل گرفته تا نوشتن Htaccess برای SafeMode Bypassing و ...
محدودیت رو تو سایز فایل بزار .
و اینکه میتونی فایل ها رو تو DB هم ذخیره کنی.

A B C D
سه شنبه 08 شهریور 1390, 19:53 عصر
بوسیله ی Trivial می توان ساختار فهرستهای سایت را پیمایش کرد و با پیمایش و سپس حدس زدن پوشه هائی مانند include و... به پوشه های حساس سایت دسترسی داشت که البته اگر Trivial فعال باشد و دسترسی پوشه ها را محدود کرد اینکار ما را با مشکل مواجه خواهد کرد.
موفق باشید
احتمالا منظورت Traversal نبوده؟

MMSHFE
سه شنبه 08 شهریور 1390, 21:36 عصر
من بعد از آپلودکردن فایل و چک کردن MIME و Extension، اون رو با GD و توسط تابع imagecreatefromjpeg میخونم و تصویر خونده شده رو با تابع imagejpeg در مکان دلخواه ذخیره میکنم. به نظرتون امنیت این روش چطوره؟ اگه تصویر، عکس واقعی نباشه، یک تصویر سیاه تولید خواهد شد و کدی اجرا نمیشه (امتحان کردم).

Net So
سه شنبه 08 شهریور 1390, 23:22 عصر
من بعد از آپلودکردن فایل و چک کردن MIME و Extension، اون رو با GD و توسط تابع imagecreatefromjpeg میخونم و تصویر خونده شده رو با تابع imagejpeg در مکان دلخواه ذخیره میکنم. به نظرتون امنیت این روش چطوره؟ اگه تصویر، عکس واقعی نباشه، یک تصویر سیاه تولید خواهد شد و کدی اجرا نمیشه (امتحان کردم).
روش کار آمدی هست به نظرم. ایده جالبی هم پشتش هست. Extension رو با چه الگوریتمی چک میکنید ؟ از آخر میاین به اول تا به . برسید ؟ تعداد . ها رو میشمارید ؟
یه سوال. اگر فایل ما عکس نبود چی ؟ دقیقتر بگم یعنی مثلا سیستم یه قسمت آپلود فایل داره. مثلا PDF و RAR و ... اونوقت چی ؟

abdorreza
چهارشنبه 09 شهریور 1390, 06:57 صبح
از همه ي دوستان ممنون كه پاسخ دادند

فكر كنم به جاهاي خوبي رسيد و داره ميرسه

ممنون

farhadfery
چهارشنبه 09 شهریور 1390, 11:19 صبح
من بعد از آپلودکردن فایل و چک کردن MIME و Extension، اون رو با GD و توسط تابع imagecreatefromjpeg میخونم و تصویر خونده شده رو با تابع imagejpeg در مکان دلخواه ذخیره میکنم. به نظرتون امنیت این روش چطوره؟ اگه تصویر، عکس واقعی نباشه، یک تصویر سیاه تولید خواهد شد و کدی اجرا نمیشه (امتحان کردم).
سلام. استاد می شه کدتون را اینجا بگذارید؟

idocsidocs
چهارشنبه 09 شهریور 1390, 12:06 عصر
سلام. استاد می شه کدتون را اینجا بگذارید؟
من دیدم که مرورگرهای مختلف، نوع میم متفاتی رو برمی گردونن و نمی شه به تشخیص نوع میم یکسان برای مرورگرهای متفاوت اعتماد کرد.

شما چطور این مشکل رو بر طرف می کنید؟

A B C D
چهارشنبه 09 شهریور 1390, 12:12 عصر
من بعد از آپلودکردن فایل و چک کردن MIME و Extension، اون رو با GD و توسط تابع imagecreatefromjpeg میخونم و تصویر خونده شده رو با تابع imagejpeg در مکان دلخواه ذخیره میکنم. به نظرتون امنیت این روش چطوره؟ اگه تصویر، عکس واقعی نباشه، یک تصویر سیاه تولید خواهد شد و کدی اجرا نمیشه (امتحان کردم).


Brilliant


روشش از خودتونه یا از جایی ایده گرفتید؟

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

در اینمورد هیچکس نمیتونه بدون دانش و تحلیل و تست گسترده روی مسائل فنی پیچیده و ساختارهای سطح پایین نظر بده. مثلا شاید بخاطر اینکه فرمت های فشرده سازی که در اکثر فایلهای تصویری مورد استفاده در وب بکار میرن نیاز به مشخصه های خاصی دارن تا توسط الگوریتم معکوس قابل دیکد باشن، و کد اینجکت شده این مشخصه ها رو نداره، در نتیجه دیتای معیوب از این نظر در تصویر حاصل شدهء خروجی حذف/نادیده گرفته میشه.

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

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

خلاصه هیچی بقدر کافی معلوم نیست، اما خود روش اساسا ایدهء هوشمندانه ای بوده که شاید بقدر کافی بی نقص هم باشه. جای بررسی واقعا تخصصی داره!

payamsp
چهارشنبه 09 شهریور 1390, 19:52 عصر
برای اینکه مطمئن بشیم فایلِ آپلود شده تصویر هست با تابع getimagesize طول و عرض تصویر رو چک کنیم و در صورت معتبر بودن طول و عرض اجازه آپلود بدیم .
روشی برای دور زدنش هست ؟

A B C D
چهارشنبه 09 شهریور 1390, 20:39 عصر
بنظرم روش عاقلانه و اصولی اینه که تحقیقی درمورد روشی که نرم افزارهای معروف و معتبر بکار میبرن بکنید. مثلا ویبالتین در این مورد چکار میکنه؟ مای بی بی چکار میکنه؟ سایتهای آپلود معروف چیکار میکنن؟ و اینهمه نرم افزار بازمتن وجود داره. نداره؟
آیا نیازی به استفاده از روشهای ابداعی شخصی و با شک و تردید هست؟

idocsidocs
چهارشنبه 09 شهریور 1390, 21:24 عصر
برای اینکه مطمئن بشیم فایلِ آپلود شده تصویر هست با تابع getimagesize طول و عرض تصویر رو چک کنیم و در صورت معتبر بودن طول و عرض اجازه آپلود بدیم .
روشی برای دور زدنش هست ؟
آـقای کرامتی توی سایتشون یه مقاله برای امنیت آپلود فایل گذاشتن.

در مورد این تابع هم توضیح دادن.

مقاله رو می تونید توی سایت ایشون پیدا کنید.

MMSHFE
پنج شنبه 10 شهریور 1390, 11:06 صبح
روش کار آمدی هست به نظرم. ایده جالبی هم پشتش هست. Extension رو با چه الگوریتمی چک میکنید ؟ از آخر میاین به اول تا به . برسید ؟ تعداد . ها رو میشمارید ؟
یه سوال. اگر فایل ما عکس نبود چی ؟ دقیقتر بگم یعنی مثلا سیستم یه قسمت آپلود فایل داره. مثلا PDF و RAR و ... اونوقت چی ؟
درمورد Extension با استفاده از pathinfo اطلاعات کلی درباره فایل آپلودشده رو درمیارم و توی یک آرایه میریزم و بعد با کمک اندیس extension اون آرایه، به پسوند فایل دسترسی پیدا میکنم. درمورد PDF و RAR هم کتابخانه های Open Source خوندن اونها موجوده (مثل PDFLib و...) که اگه این کتابخانه ها بتونن اون فایل آپلود شده رو بخونن، یعنی اینکه فایل درسته اما درمورد تصاویر، خودم شخصاً امتحان کردم و یکسری کد داخلش Inject کردم ولی وقتی با GD خوندمش، اونها رو Ignore کرد. البته برای اندازه تصاویر هم از imagesx و imagesy استفاده میکنم چون برخلاف getimagesize، اطلاعات رو از Header نمیخونن و از قسمت Data فایل، طول و عرض رو محاسبه میکنند. درنتیجه نمیشه با تغییر Header، اونها رو دور زد و باید واقعاً تصویری وجود داشته باشه تا اندازه واقعی اون بدست بیاد. نهایتاً اگه یکسری اطلاعات توی تصویر قرار گرفته باشه، اگه در قسمت Data باشه باعث میشه تصویر بهم بریزه و نفوذگر لو بره، درنتیجه طبیعتاً درقسمت Header فایل هست که اون هم با این روش نادیده گرفته میشه. موفق باشید.

A B C D
پنج شنبه 10 شهریور 1390, 12:04 عصر
نهایتاً اگه یکسری اطلاعات توی تصویر قرار گرفته باشه، اگه در قسمت Data باشه باعث میشه تصویر بهم بریزه و نفوذگر لو برهاستدلال شما خیلی سطحی و کلی و بنظرم چشم بسته است. بنظر من اصلا به این سادگی ها نیست. شما طوری صحبت میکنید انگار از ریزترین جزییات ساختار فرمت و روش اینجکت اطلاع دارید.
اصلا اون دیتای اینجکت دقیقا چیه؟ چه کدهاییست؟ منظورتون کدهای PHP هست؟ یا توالی خاصی از بایتها با مقدار خاص که باعث سوء استفاده از باگ مرورگر/کتابخانه ای میشن؟
آیا نیاز هست برای سوء استفاده از این باگها، اطلاعات حساب شده ای عمدا هم در قسمت هدر و هم در قسمت دیتا درج بشن؟ (بطور مثال باگهایی که از Buffer Overflow استفاده میکنن) آیا میشه این دو بخش رو از نظر مشخصه های فرمت با هم هماهنگ کرد؟ ... جواب تمام این سوالات جز با دیدن عین روش و کدها و غرق شدن در بطن مطلب پیدا نمیشن. مگر اینکه منبع روشنگری در این ارتباط پیدا کنیم که بشه بهش اعتماد کرد.
من نمیدونم شما بر چه اساسی میگید اگر در قسمت دیتا باشه باعث میشه تصویر بهم بریزه. میتونید بگید دقیقا یعنی چی و چرا؟ چون شما خودتون یه تست ساده انجام دادید این مطلب بصورت کلی ثابت شده؟

دیتای تصویر یه دیتای باینری هست و دیتای باینری فرمتهای مختلف هم در خیلی موارد میتونه هر ترکیبی از بایتهای مختلف با هر مقداری باشه و در موارد دیگه هم که محدودیتی داره بر اساس Specification اون فرمت خاص این محدودیت ها وضع میشن (و تازه شما از کجا مطمئن هستید که روش شما این محدودیتها رو چک میکنه؟).
تاجاییکه بنده اطلاع دارم و استنباط میکنم، بطور خامش دیتای یه تصویر هیچ محدودیتی نداره. بطور مثال فرمت Bitmap از نوع RGB رو درنظر بگیرید که برای هر رنگ یک بایت درنظر گرفته شده (یعنی 24 بیت در کل برای هر پیکسل)؛ مقدار هر بایت دیتای این تصویر میتونه از 0 تا 255 باشه که البته میتونه بصورت -128 تا 127 هم تفسیر بشه. بنابراین ما میتونیم در دیتای یک تصویر عملا هر چیزی رو قرار بدیم و هیچ مقدار غیرمجازی وجود نداره! نه کد PHP و نه کدهای exe از نوع Native از دیتای خام یک تصویر قابل تفکیک نیستن.
همونطور که قبلا گفتم ممکنه دلیل فیلتر شدن کدهای اینجکت شده این باشه که مشخصه های دیتای اینجکت شده قابل دیکد توسط الگوریتم Decompressor نیست. یا اینکه مشخصه های متناظر (اغلب طول) در بخش هدر و بخش دیتا با هم تطابق ندارن. و اگر اینطور باشه، بازهم فقط یک تحلیل و تحقیق فنی کامل و دقیق میتونه مشخص کنه آیا میشه بصورتی این مشخصه ها رو برای اون دیتا تنظیم کرد یا دور زد و یا خیر. اینکه تاحالا این کار رو نکردن میتونه بخاطر این بوده باشه که مجبور نبودن این کار رو بکنن و چک خاصی در این ارتباط انجام نمیشده و/یا قبل از اینکه به اون مراحل برسن توسط باگهای نرم افزار هدف نفوذ صورت گرفته بوده.

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


درمورد PDF و RAR هم کتابخانه های Open Source خوندن اونها موجوده (مثل PDFLib و...) که اگه این کتابخانه ها بتونن اون فایل آپلود شده رو بخونن، یعنی اینکه فایل درستهبنظر بنده این کاملا به باگی که ازش سوء استفاده میشه بستگی داره که آیا اینجکتی که برای سوء استفاده از اون باگ انجام میشه لزوما باعث اختلالی در فرمت به شکلی که کتابخانه های مورد استفادهء شما اون رو فیلتر کنن میشه یا خیر.
خیلی از باگها وجود داشتن که برای سوء استفاده از اونها نیازی به تخلف از فرمت و ایجاد دیتای غیراستانداردی نبوده و بنابراین توسط کتابخانه های مربوطه هم خونده میشن.
یه مثالی میزنم که مقداری از نظر نوع برای قیاس دور بنظر میاد، اما بهرحال بعنوان مثال بد نیست بنظرم و بقول معروف میگن در مثل مناقشه نیست.
چند وقت پیش بحثش بود که یک عدد خاصی رو وقتی به طریقی به انجین PHP میدادید هنگ میکرد. آیا اون عدد از نظر فرمت و دیتای برنامه های PHP غیرمجاز بود و ساختار عجیب و غریبی داشت؟ اون یک عدد کاملا معمولی بود که در هیچ بخشی اعم از PHP، HTML و غیره غیرمجاز و غیرعادی بحساب نمیامد.
نهایت هم اگر برنامه ای امنیتی میخواست جلوی چنین حمله هایی رو بگیره، باید دقیقا اون اعداد خاص رو میشناخت و فیلتر میکرد. تازه بازهم در ارتباط با این روش این سوال پیش میاد که عددی که غیرمجاز نیست و کاملا طبیعیه اگر بخواد در برنامه بصورت طبیعی استفاده بشه چطور باید استفاده کنیم. پس شاید روش درست این باشه که انجین PHP رو Patch کنیم.
ضمنا فراموش نکنید که حتی خود کتابخانه های بازمتن مربوطه هم از باگ مصون نیستن و از کجا معلوم باگی نداشته باشن یا دقیقا باگ یکسانی رو نداشته باشن؟ گاهی باگها به علت خاصی مثل یک Specification فنی نادرست، در کتابخانه های متعددی ایجاد میشن. و خیلی وقتها هم نرم افزارهای مختلف از کتابخانه های مشترکی استفاده میکنن که اگر باگی در اون کتابخانه ها باشه در نتیجه ممکنه به تمام یا خیلی از اون نرم افزارها منتقل بشه؛ چه اون کتابخانه در زبان PHP مورد استفاده باشه و چه در سمت کلاینت در یک PDF reader.
اگر اونا هم باگ داشته باشن، ممکنه دیتای مورد نظر رو فیلتر نکنن، و حتی ممکنه هکرها این بار بجای کلاینت های آسیب پذیر، بتونن به خود سرور حمله کنن.

A B C D
پنج شنبه 10 شهریور 1390, 12:15 عصر
در کل هم بنظر بنده حمله هایی که به باگ نرم افزارهای سمت کلاینت حمله میکنن، در درجهء اول مسئولیت خود کلاینت هست و قرار نیست ما تمام این حمله ها رو شناسایی و فیلتر کنیم، چون این کار میتونه بسیار دشوار و پر هزینه باشه. مرورگرها و نرم افزارهای مختلف دیگه مثل Adobe PDF reader احتمالا ده ها و صدها و حتی شاید هزاران باگ از این نوع دارن. آیا ما باید تمام اونها رو بشناسیم و چک و فیلتر کنیم؟ هیچ اطمینانی هست که روش شما تمام اونها رو فیلتر میکنه؟

بنظر من تنها مواردی رو که خیلی خطرناک هستن و نرم افزارهای مورد نظر توسط تعداد زیادی از کاربران استفاده میشن (مثلا IE6)، باید شناسایی و خنثی کنیم. تازه اونم اگر واقعا براحتی ممکن باشه. چون وقتی کاربر یه نرم افزار با باگ خطرناک داره، در درجهء اول مسئولیتش با خودشه که اون نرم افزار رو Patch کنه، ارتقا بده، یا از نرم افزار دیگری استفاده کنه.

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

idocsidocs
پنج شنبه 10 شهریور 1390, 12:19 عصر
درمورد Extension با استفاده از pathinfo اطلاعات کلی درباره فایل آپلودشده رو درمیارم و توی یک آرایه میریزم و بعد با کمک اندیس extension اون آرایه، به پسوند فایل دسترسی پیدا میکنم. درمورد PDF و RAR هم کتابخانه های Open Source خوندن اونها موجوده (مثل PDFLib و...) که اگه این کتابخانه ها بتونن اون فایل آپلود شده رو بخونن، یعنی اینکه فایل درسته اما درمورد تصاویر، خودم شخصاً امتحان کردم و یکسری کد داخلش Inject کردم ولی وقتی با GD خوندمش، اونها رو Ignore کرد. البته برای اندازه تصاویر هم از imagesx و imagesy استفاده میکنم چون برخلاف getimagesize، اطلاعات رو از Header نمیخونن و از قسمت Data فایل، طول و عرض رو محاسبه میکنند. درنتیجه نمیشه با تغییر Header، اونها رو دور زد و باید واقعاً تصویری وجود داشته باشه تا اندازه واقعی اون بدست بیاد. نهایتاً اگه یکسری اطلاعات توی تصویر قرار گرفته باشه، اگه در قسمت Data باشه باعث میشه تصویر بهم بریزه و نفوذگر لو بره، درنتیجه طبیعتاً درقسمت Header فایل هست که اون هم با این روش نادیده گرفته میشه. موفق باشید.
شما معموملا به چه پسوندهایی اجازه آپلود می دید؟ اگر امکانش هست لیست این پسوندها رو اینجا بذارید.

سوالی که دارم اینه که آیا فایل تصویری در شرایط عادی (بدون انجام تنظیمات در آپاچی یا HTACCESS) می تونه کدهای مخرب موجود توی هدر خودش رو اجرا کنه؟ یعنی امکان اجرای کدهای موجود در تصاویر باید توسط HTACCESS بوجود بیاد و بعد هکر بتونه از این روش برای هک کردن استفاده کنه. این مطلب درسته؟

A B C D
پنج شنبه 10 شهریور 1390, 12:41 عصر
سوالی که دارم اینه که آیا فایل تصویری در شرایط عادی (بدون انجام تنظیمات در آپاچی یا HTACCESS) می تونه کدهای مخرب موجود توی هدر خودش رو اجرا کنه؟ یعنی امکان اجرای کدهای موجود در تصاویر باید توسط HTACCESS بوجود بیاد و بعد هکر بتونه از این روش برای هک کردن استفاده کنه. این مطلب درسته؟ من از بحثها و حرفهایی که روش و اصول تخصصی و علمی ندارن خیلی بدم میاد.
این تاپیک هم متاسفانه به نمونه ای از همین موارد تبدیل شد.
بطور مثال در وهلهء اول ما باید مشخص کنیم منظورمون در هر موردی دقیقا چه نوع حمله هایی هست.
در این بحث تاحالا بطور صریح حتی مشخص نشده که بحث حمله ها به سمت سرور مطرح هست یا به کلاینت ها.
وقتی هم آدم یه روشی میده و یه تستی میکنه، نمیتونه همینطوری بیاد ادعا کنه اون روش جلوی همهء حمله ها رو به هر شکلی میگیره! با یه تست شخصی و ساده که نمیشه چنین چیزی رو ثابت کرد. این نیاز به تستهای خیلی زیاد و گسترده داره یا اینکه بصورت تئوریک و با استناد به Specification و غیره این مسئله اثبات بشه. مثل روشی که شما در ریاضی قضیه ای رو ثابت میکنید.

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

درمورد کلاینت هم که مطالب خیلی گسترده تره و به باگهای بی‌شمار نرم افزارهای مختلف مورد استفاده در کلاینت مربوط میشه که البته بیشتر این باگها در ارتباط با مرورگرهای مختلف مطرح هستن (ولی نرم افزارهای دیگری مثل Adobe reader هم چه بصورت Standalone و چه بصورت پلاگین در داخل مرورگر مطرح هستن). مثلا IE در طول تاریخ خودش باگهای خطرناک بسیاری داشته که توسط بعضیهاش صرفا با نمایش یک تصویر آلوده، عملا هر کد اجرایی میتونسته روی PC کلاینت اجرا بشه. اما تعداد این باگها درکل زیاد هست و لزوما فقط به نسخه های مختلف IE هم محدود نمیشه. اینکه یه روشی آیا جلوی همهء این فایلهای آلوده رو میتونه بگیره یا نه یه مسئلهء واضح کوچک و ساده ای نیست و باید یا تمام موارد (حداقل تمام باگهای خطرناک مرورگرها و نرم افزارهایی که افراد زیادی ازشون استفاده میکنن) تست بشن یا این مسئله بصورت فنی و با تمام جزییات ثابت بشه. تازه روش تست یه روش تجربی هست که بدون مکمل تئوری قابلیت اعتماد زیادی نداره و فقط برای مواردی که اثبات تئوریک مقدور نیست (یا خیلی مشکله) میشه بهش اکتفا کرد.

درمورد بحثهای این تاپیک هم باید بگم که بنده تاحالا فکر میکردم منظور حمله های به سمت کلاینت هست از طریق باگهایی که در مرورگرها و نرم افزارهای دیگر سمت کلاینت یا کتابخانه های مورد استفاده در سمت کلاینت وجود داره، چون درمورد سمت سرور و اون مورد خاص درج کدهای PHP که قبلا راه حل واضح و کامل ارائه شده و انتظار نداشتم کسی بیاد یه روش دیگری رو که مبهم و قابل تردید هم هست ارائه کنه.
روش جلوگیری از اجرای کدهای PHP در فایلهای آپلودی رو که قبلا گفتیم. چیز پیچیده ای نبود! کافیه پسوندهای چندگانه رو حذف کنید (یا بجای نقطه هاشون کاراکتر دیگری بذارید) و تنها آخرین پسوند رو باقی بذارید و پسوند آخر رو هم با یک White list چک کنید (هیچ پسوند دیگری غیر از معدود پسوندهای مجاز و بی خطر رو اجازه ندید).

A B C D
شنبه 12 شهریور 1390, 14:47 عصر
جناب MMSHFE (http://barnamenevis.member.php?55504-MMSHFE)، منتظر توضیحات بیشتر شما بودیم!
نفرمودید بالاخره منظور شما و روشی که ارائه کردید، دربارهء همون قضیهء اجرای دستورات PHP در داخل فایلهای تصویر هست یا چیز دیگه.
ضمنا نمونه کد روشتون و دیتایی (فایل تصویر) رو که تست کردید در اختیار نذاشتید تا دیگران هم بتونن براحتی بررسی کنن.

eAmin
یک شنبه 13 شهریور 1390, 00:32 صبح
http://www.phpclasses.org/browse/file/34642.html

A B C D
یک شنبه 13 شهریور 1390, 09:42 صبح
http://www.phpclasses.org/browse/file/34642.html
این دقیقا چکار میکنه؟
تست کردید؟
کدش رو خوندید؟
قابل اعتماده؟

eAmin
یک شنبه 13 شهریور 1390, 12:51 عصر
این کد رو صرفا برای نمونه قرار دادم، که البته این یکی (http://www.digitalgemstones.com/code/tools/ImgUploader.php) به نظر بهتره.
روش کار این اسکرپیت به اینصورت هست که اول توسط getimagesize ابعاد تصویر مورد نظر رو به دست می یاره، بعد به هنگام/بعد از آپلود تصویر رو توسط gd دوباره ایجاد میکنه، تقریبا مثل روش جناب MMSHFE که استفاده کردن.

A B C D
یک شنبه 13 شهریور 1390, 13:25 عصر
منم دنبال کد دقیق و دیتای مورد تست بودم که بررسیش کنم.
متاسفانه آقای MMSHFE مثل اینکه مایل به این کار نیستن که البته اصولیش این بود که وقتی این مسئله رو مطرح کردن همکاری بیشتری بکنن.
بنده میتونم از خودم کد درست کنم ولی وقتی آماده هست چرا باید وقت بذارم و بعدش هم ممکنه طرف بگه این اون چیزی نبوده که من گفتم و تست کردم.

درمورد این روشها هم باید تاحد ممکن کد رو دید و تست هم کرد. مطمئن شدن به سادگی ممکن نیست. ضمنا هرکس هرجا کدی گذاشت فکر میکنید میشه بسادگی بهش اعتماد کرد؟ اصلا اون اولی که گذاشتید و کد مختصری داشت بنظر بنده یه کار حرفه ای نبود. توضیحاتی که داده بود هیچ کامل نبود (کامنت هاش رو میگم)، غلطهای املایی و سینتاکس انگلیسی هم داشت، و خود کدها و ساختار برنامه هم بنظر اصلا حرفه ای نبود. ظاهرا کار یه برنامه نویس هندی هست.

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

ضمنا بنده در تستش به مشکل برخوردم چون باوجودیکه اکستنشن exif رو در EasyPHP فعال میکنم و dll های لازم هم در دایرکتوری نصب موجوده اما پیام خطا میده. احتمالا یه پکیج دیگه غیر از EasyPHP رو دانلود کنم.

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

این اینک دومی هم که گذاشتید چرا برای من اصلا باز نمیشه؟

MMSHFE
دوشنبه 14 شهریور 1390, 11:02 صبح
با عرض شرمندگي، الآن شهرستان هستم و به كدي كه نوشتم متأسفانه دسترسي ندارم ولي خيلي زود براتون همينجا قرارش ميدم. اميدوارم به دردتون بخوره. موفق باشيد.

هادی2020
جمعه 25 آذر 1390, 00:05 صبح
جهت یادآوری همگی:لبخند:

engmmrj
پنج شنبه 22 فروردین 1392, 14:25 عصر
با عرض شرمندگي، الآن شهرستان هستم و به كدي كه نوشتم متأسفانه دسترسي ندارم ولي خيلي زود براتون همينجا قرارش ميدم. اميدوارم به دردتون بخوره. موفق باشيد.
2 سال گذست.
پس کی برمیگردید؟

masiha68
جمعه 16 خرداد 1393, 21:05 عصر
سال سوم ....
کدی در کار نیست :دی

MMSHFE
جمعه 16 خرداد 1393, 21:42 عصر
بابا کشتین ما رو! تاپیک سه سال قبل بود اصلاً یادم رفته بود. کدی که استفاده میکنم رو ضمیمه کردم. پیچیدگی خاصی نداره و توی پکیج PHP هم نمونه ساده ترش استفاده شده بود.

MRmoon
جمعه 16 خرداد 1393, 22:32 عصر
سال سوم ....
کدی در کار نیست :دی

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

majid1605
شنبه 17 خرداد 1393, 00:25 صبح
من کد زیر رو نوشتم البته کلی کلاس آپلود فایل رو تحلیل کردم بعضی از کدها هم از اونها گرفته شده ولی بیشترش رو خودم نوشتم بدون ایراد نیست ولی خوبه



class FTPUpload
{
private $conectionID ;
private $loginIsOk ;
private $messages = array() ;
private $ftpServer ;
private $ftpUsername ;
private $ftpPassword ;
private $passive ;
private $ftptimeout ;
private $ftpPort ;
private $ftpDefaultDir;
private $valideTypes = array('gif','jpg','jpe','jpeg','png',
'zip','tar','gtar','gz','tgz','7z','rar');

public function __construct($server,$username,$password,$port=21 ,$passivemode = false ,$timeout=90)
{
if(isset($server, $username, $password))
{
$this->ftpServer = $server ;
$this->ftpUsername = $username ;
$this->ftpPassword = $password ;
$this->ftpPort = $port ;
$this->ftptimeout = $timeout ;
$this->passive = $passivemode ;
$this->ftpDefaultDir = 'uploads' ;
}
else
$this->logMessage('username or pass dose not match!');

}
public function connect()
{
//setup FTP connection
$this->conectionID = ftp_connect($this->ftpServer,$this->ftpPort );
//login FTP
$login = ftp_login($this->conectionID,$this->ftpUsername ,$this->ftpPassword);
//sets passive mode /*default off*/
ftp_pasv($this->conectionID , $this->passive);

if(!$this->conectionID || !$login)
{
$this->logMessage('FTP connection has failed!');
$this->logMessage('Attempted to connect to ' . $this->ftpServer . ' for user ' . $this->ftpUsername, true);
return false;
}
else
$this->logMessage('Connected to ' .$this->ftpServer . ' for user ' . $this->ftpUsername);
$this->loginIsOk = true;
return true;
}
//create directory on server
//$director is name and directory path
public function FTPMakeDirectory($directory)
{
if(isset($directory))
{
$paths =array();
$paths = explode("/" ,$directory);
$dirNum = count($paths);
if($dirNum >= 1)
{
foreach($paths as $newDirectory)
{
if(!$this->FTPExistDirectory($newDirectory))//check directory for exist
{
if(ftp_mkdir($this->conectionID ,$newDirectory))//make directory and check create success
{
$this->logMessage('Directory "' . $newDirectory . '" created successfully');
$this->FTPChangeDirectoryPath($newDirectory);//change directory path to new directory
//return true;
}
else
{
$this->logMessage('Failed creating directory "' . $newDirectory . '"');
//return false;
}
}
else
{
$this->FTPChangeDirectoryPath($newDirectory);//change directory path to exist directory
$this->logMessage('Directory "' . $newDirectory . '" is exist');
}
}
}
}
else
$this->logMessage('Directory name or path is not set');
}
//Changes the current directory
public function FTPChangeDirectoryPath($directory="")
{
if(isset($directory))
{
if(ftp_chdir($this->conectionID ,$directory))
{
$this->logMessage('Directory "' . $directory . '" change successfully and current directory is '.ftp_pwd($this->conectionID));
return true ;
}
else
{
$this->logMessage('Directory "' . $directory . '" change failure');
return false;
}
}
else
if(ftp_chdir($this->conectionID ,$this->ftpDefaultDir))
{
$this->logMessage('Directory "' . $directory . '" set is default');
return false;
}
else
{
$this->logMessage('Directory "' . $directory . '" do not set default ');
return false;
}
}
//check exist directory
private function FTPExistDirectory($directory)
{
$orginalDir = ftp_pwd($this->conectionID);
if(isset($directory) && @ftp_chdir($this->conectionID ,$directory))
{
ftp_chdir($this->conectionID ,$orginalDir);
return true ;
}
else
return false ;
}
//remove directory or file from ftp server
public function FTPRemoveDirectory($dirName)
{
if($this->FTPExistDirectory($dirName))
{
if(ftp_rmdir($this->conectionID , $dirName))
{
$this->logMessage('Directory "' . $directory . '" remove successfully');
return true;
}
else
{
$this->logMessage('failure remove directory "' . $directory . '"');
return false;
}
}
else
{
$this->logMessage('Directory "' . $directory . '" dose not exist');
return false;
}
}
//delete file from server
public function FTPDeleteFile($file="")
{
if(ftp_size($this->conectionID , $file) !== -1)
{
$path = ftp_pwd($this->conectionID).$file ;
if(ftp_delete($this->conectionID , $path))
{
$this->logMessage('file "' . $file . '" deleted successfully');
return false;
}
else
{
$this->logMessage('failure delete ' . $file) ;
return false ;
}
}
else
{
$this->logMessage($file.' does not exist ') ;
return false ;
}

}
// change file permission
public function FTPChangePermissionFile($permission = 0664 ,$file)
{
if(ftp_size($this->conectionID , $file) !== -1)
{
if(ftp_chmod($this->conectionID ,$permission ,$file))
{
$this->logMessage('success change permission ' . $file) ;
return true ;
}
else
{
$this->logMessage('failure change permission ' . $file) ;
return false ;
}
}
else
{
$this->logMessage($file.' does not exist ') ;
return false ;
}
}
public function FileSize($file , $convert='B')
{
if(isset($file))
{
$size = floatval(filesize($file));
$convert = strtoupper($convert);
switch($convert)
{
case 'B':
return $size ;
case 'KB':
return round( $size/1024 , 2 );
case 'MB':
return round( $size/pow(1024,2) , 2) ;
case 'GB':
return round( $size/pow(1024,3) , 2) ;
case 'TB':
return round( $size/pow(1024,4) , 2) ;
}

}
else
{
$this->logMessage($file.' does not exist ') ;
return $size ;
}
}

public function rawlistDirectory($parameters, $recursive = false)
{
$result = ftp_rawlist($this->conectionID, $parameters, $recursive);

if ($result === false) {
throw new Exception('Unable to list directory');
}
return $result;
}

public function listDirectory($directory)
{
$result = ftp_nlist($this->connection, $directory);
asort($result);

if ($result === false) {
throw new Exception('Unable to list directory');
}
return $result;
}

//upload file from local and open file stream
//$localORFileHandle Determines where are uploaded files
//local from local system defaul.the default is the local
//handle from open stream file
public function FTPUploadFile($fileFrom, $fileTo ,$localORFileHandle='local')
{
if($localORFileHandle === 'local')
{
if('text/plain' == $this->GetMimeType($fileFrom))
{
$mode = FTP_ASCII;
}
else
$mode = FTP_BINARY ;
$upload = ftp_put($this->conectionID , $fileTo, $fileFrom, $mode);
}
elseif($localORFileHandle === 'handle')
{
$mode = FTP_BINARY ;
$upload = ftp_fput($this->conectionID , $fileTo, $fileFrom, $mode);
}
// Check upload status
if (!$upload)
{
$this->logMessage('FTP upload has failed!');
return false;
}
else
{
$this->logMessage('Uploaded "' . $fileFrom . '" as "' . $fileTo);
return true;
}
}
//download file
public function FTPDownloadFile ($fileFrom, $fileTo)
{
// *** Set the transfer mode
$asciiArray = array('txt', 'csv');
$extension = end(explode('.', $fileFrom));
if (in_array($extension, $asciiArray)) {
$mode = FTP_ASCII;
} else {
$mode = FTP_BINARY;
}
// try to download $remote_file and save it to $handle
if (ftp_get($this->connectionId, $fileTo, $fileFrom, $mode, 0)) {

return true;
$this->logMessage(' file "' . $fileTo . '" successfully downloaded');
} else {

return false;
$this->logMessage('There was an error downloading file "' . $fileFrom . '" to "' . $fileTo . '"');
}
}
//rename file or directory
public function FTPRename($oldName , $newName)
{
if(ftp_size($this->conectionID , $oldName) !== -1 || $this->FTPExistDirectory($oldName))
{
if(ftp_rename($this->conectionID ,$oldName , $newName))
{
$extensions = end(explode('.', $fileFrom));
if(in_array($extensions ,$this->valideTypes))
{

}
$this->logMessage( $oldName .' successfully rename to '.$newName);
return false;
}
else
{
$this->logMessage('failure rename ' . $oldName) ;
return false ;
}
}
else
{
$this->logMessage('directory or file "' . $oldName . '" dose not exist');
return false;
}
}
//generate unique name for files
public function UniquePrefixName()
{
$Unique = uniqid(rand(),TRUE);
$Salt = uniqid(hash("sha256",$Unique),TRUE);
$UniqueName = hash("sha256", $Unique.$Salt);
return $UniqueName;
}

/**
* Sanitizes a filename replacing whitespace with underscore
* Removes special characters that are illegal in filenames on certain
* return string The sanitized filename
*/
function SanitizeFileName( $fileName )
{
$extention = '.'.end( explode('.',trim($fileName)));
$fileName = str_replace($extention,'',$fileName);
$specialChars = array("?", "[", "]", "/", "\\", "=", "<", ">", ":", ";", ",", "'",".", "\"", "&", "$", "#", "*", "(", ")", "|", "~", "`", "!", "{", "}");
$fileName = str_replace($specialChars, '_', $fileName);
$fileName = preg_replace('/[\s-]+/', '-', $fileName);
$fileName = trim($fileName);
return $fileName;
}

public function GetMimeType($fileName)
{
if(!isset($fileName))
return false;
if(function_exists('finfo_file'))
{
$finfo = finfo_open(FILEINFO_MIME_TYPE);
$mimeType = finfo_file($finfo, $fileName);
finfo_close($finfo);
return $mimeType;
}
else
{
$mimeTypes = array(

'txt' => 'text/plain',
'htm' => 'text/html',
'html' => 'text/html',
'php' => 'text/html',
'css' => 'text/css',
'js' => 'application/javascript',
'json' => 'application/json',
'xml' => 'application/xml',
'swf' => 'application/x-shockwave-flash',
'flv' => 'video/x-flv',

// images
'png' => 'image/png',
'jpe' => 'image/jpeg',
'jpeg' => 'image/jpeg',
'jpg' => 'image/jpeg',
'gif' => 'image/gif',
'bmp' => 'image/bmp',
'ico' => 'image/vnd.microsoft.icon',
'tiff' => 'image/tiff',
'tif' => 'image/tiff',
'svg' => 'image/svg+xml',
'svgz' => 'image/svg+xml',

// archives
'zip' => 'application/zip',
'rar' => 'application/x-rar-compressed',
'exe' => 'application/x-msdownload',
'msi' => 'application/x-msdownload',
'cab' => 'application/vnd.ms-cab-compressed',

// audio/video
'mp3' => 'audio/mpeg',
'qt' => 'video/quicktime',
'mov' => 'video/quicktime',

// adobe
'pdf' => 'application/pdf',
'psd' => 'image/vnd.adobe.photoshop',
'ai' => 'application/postscript',
'eps' => 'application/postscript',
'ps' => 'application/postscript',

// ms office
'doc' => 'application/msword',
'rtf' => 'application/rtf',
'xls' => 'application/vnd.ms-excel',
'ppt' => 'application/vnd.ms-powerpoint',

// open office
'odt' => 'application/vnd.oasis.opendocument.text',
'ods' => 'application/vnd.oasis.opendocument.spreadsheet',
);
$extention = strtolower(end(explode('.',strtolower($fileName)) ));
return (array_key_exists($extention, $mimeTypes) ?
$mimeTypes[$extention] : 'application/octet-stream');
}
}

public function FTPIsDiskSpace($file)
{
if(isset($file))
{
$size = $this->FTPFileSize($file);
return ftp_alloc($this->conectionID,$size);
}
else
return false;

}

private function logMessage($message)
{
$this->messages[] = $message ;
}

public function getMessage()
{
return $this->messages;
}

public function __deconstruct()
{
if ($this->connectionId)
ftp_close($this->connectionId);
}
}


فقط آپلود از طریق ftp
برای عکس هم از کلاس زیر استفاده می کنم



class Image
{
private $image;
private $origWidth;
private $origHeight;
private $newImage;
private $mimeType;

public function __construct($fileName)
{
if(isset($fileName))
{
$this->image = $this->OpenImage($fileName);
// get image size
$this->origWidth = imagesx($this->image);
$this->origHeight = imagesy($this->image);
}
else
echo 'no file';
}

private function OpenImage($fileName)
{
if(!isset($fileName))
return false ;
else
{
$this->mimeType = $this->GetMimeType($fileName);
switch($this->mimeType)
{
case 'image/jpeg':
$img = imagecreatefromjpeg($fileName);
break;
case 'image/png':
$img = imagecreatefrompng($fileName);
imagealphablending($img, false);
//Create alpha channel for transparent layer
$col=imagecolorallocatealpha($img,255,255,255,127) ;
//Create overlapping 100x50 transparent layer
imagefilledrectangle($img,0,0,100, 50,$col);
//Continue to keep layers transparent
imagealphablending($img,true);
//imagecolortransparent($img, imagecolorat($img, 0, 0));
break;
case 'image/gif':
$img = imagecreatefromgif($fileName);
break;
default :
$img = false ;
break;
}
return $img ;
}
}

public function ResizeImage($option = "default" , $newSizeW=100 , $newSizeH=100 , $percent=0)
{
if(isset($option))
switch( strtolower($option) )
{
case "exact":
$width = $newSizeW;
$height = $newSizeH;
break;

case "maxwidth":
$width = $newSizeW;
$height = $this->resizeHeightByWidth($newSizeW);
break;

case "maxheight":
$width = $this->resizeWidthByHeight($newSizeH);
$height = $newSizeH;
break;
case "percent":
$width = $this->origWidth *($percent/100);
$height = $this->origHeight *($percent/100);
break;
case "default":
$height = $this->origHeight ;
$width = $this->origWidth ;
break;
}
$this->newImage = imagecreatetruecolor($width , $height);
imagefill($this->newImage, 0, 0, 0);
return imagecopyresampled($this->newImage ,$this->image,0,0,0,0,
$width , $height ,
$this->origWidth ,$this->origHeight);
}

// Get the resized height from the width keeping the aspect ratio
private function resizeHeightByWidth($width)
{
return round(($this->origHeight / $this->origWidth) * $width);
}

// Get the resized width from the height keeping the aspect ratio
private function resizeWidthByHeight($height)
{
return round(($this->origWidth / $this->origHeight) * $height);
}

public function saveImage($savePathAndName = null, $imageQuality=100)
{

switch($this->mimeType)
{
case 'image/jpg':
case 'image/jpeg':
// Check PHP supports this file type
if (imagetypes() & IMG_JPG)
{
//ob_end_clean();
ob_start();
imagejpeg($this->newImage, $savePathAndName, $imageQuality);
$final_image['image'] = ob_get_contents();
ob_end_clean();
$final_image['type']="jpg";
}
break;

case 'image/gif':
// Check PHP supports this file type
if (imagetypes() & IMG_GIF)
{
ob_start();
imagegif($this->newImage, $savePathAndName);
$final_image['image'] = ob_get_contents();
ob_end_clean();
$final_image['type']="gif";
}
break;

case 'image/png':
$invertScaleQuality = 9 - round(($imageQuality/100) * 9);
// Check PHP supports this file type
if (imagetypes() & IMG_PNG)
{
ob_start();
imagepng($this->newImage, $savePathAndName, $invertScaleQuality);
$final_image['image'] = ob_get_contents();
ob_end_clean();
$final_image['type']="png";
}
break;
}

return $final_image;
imagedestroy($this->newImage);
}

public function GetMimeType($filename)
{
if(!isset($filename))
return false;
if(function_exists('finfo_file'))
{
$finfo = finfo_open(FILEINFO_MIME_TYPE);
$mimeType = finfo_file($finfo, $filename);
finfo_close($finfo);
return $mimeType;
}
else
{
$mimeTypes = array(

'txt' => 'text/plain',
'htm' => 'text/html',
'html' => 'text/html',
'php' => 'text/html',
'css' => 'text/css',
'js' => 'application/javascript',
'json' => 'application/json',
'xml' => 'application/xml',
'swf' => 'application/x-shockwave-flash',
'flv' => 'video/x-flv',

// images
'png' => 'image/png',
'jpe' => 'image/jpeg',
'jpeg' => 'image/jpeg',
'jpg' => 'image/jpeg',
'gif' => 'image/gif',
'bmp' => 'image/bmp',
'ico' => 'image/vnd.microsoft.icon',
'tiff' => 'image/tiff',
'tif' => 'image/tiff',
'svg' => 'image/svg+xml',
'svgz' => 'image/svg+xml',

// archives
'zip' => 'application/zip',
'rar' => 'application/x-rar-compressed',
'exe' => 'application/x-msdownload',
'msi' => 'application/x-msdownload',
'cab' => 'application/vnd.ms-cab-compressed',

// audio/video
'mp3' => 'audio/mpeg',
'qt' => 'video/quicktime',
'mov' => 'video/quicktime',

// adobe
'pdf' => 'application/pdf',
'psd' => 'image/vnd.adobe.photoshop',
'ai' => 'application/postscript',
'eps' => 'application/postscript',
'ps' => 'application/postscript',

// ms office
'doc' => 'application/msword',
'rtf' => 'application/rtf',
'xls' => 'application/vnd.ms-excel',
'ppt' => 'application/vnd.ms-powerpoint',

// open office
'odt' => 'application/vnd.oasis.opendocument.text',
'ods' => 'application/vnd.oasis.opendocument.spreadsheet',
);
$extention = strtolower(end(explode('.',strtolower($filename)) ));
return (array_key_exists($extention, $mimeTypes) ?
$mimeTypes[$extention] : 'application/octet-stream');
}
}

//get width image
public function GetWidth()
{
return $this->origWidth;
}
//get height image
public function GetHeight()
{
return $this->origHeight;
}

public function Destroy()
{
imagedestroy($this->image);
}

public function __destruct()
{
$this->Destroy();
}

}


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

کلاس عکس تووی تابع OpenImage یه مشکل با فایل های png داشتم (فقط همین قسمتش)که بعضی مواقع سیاه میشدن که اکثرا فایل هایی که ترنسپرنت بودن این اتفاق واسشون می افتاد که یکی از دوستان پیشنهاد اون کد رو دادن واسه همین هنوز اونم آماده نیست