فا

‫ برنامه‌نويسي امن با زبان C - قوانين و پيشنهادات محيط

IRCAR201103090
 
عنصر اصلي در برنامه‌نويسي امن با زبان‌هاي مختلف برنامه‌نويسي، مستندسازي خوب و استفاده از استانداردهاي قابل اجرا است. استانداردهاي كدنويسي، برنامه نويسان را ترغيب به پيروي از مجموعه‌اي متحدالشكل از قوانين و راهنمايي‌هايي مي‌كند كه بر اساس نيازمندي‌هاي پروژه و سازمان تعيين شده است، نه بر اساس سلايق و مهارت‌هاي مختلف برنامه‌نويسان. به محض تعيين استانداردهاي مذكور، ميتوان از آن به عنوان معياري براي ارزيابي كدهاي منبع، چه به صورت دستي و چه به صورت اتوماتيك استفاده كرد.
از استانداردهاي معروف در اين زمينه مي‌توان به استانداردCERT براي كدنويسي امن اشاره كرد كه يك سري از قوانين و پيشنهادات را براي كدنويسي امن با زبان‌هاي برنامه‌نويسي C، C++ و جاوا ارائه مي‌دهد. هدف از اين قوانين و پيشنهادات، حذف عادت‌هاي كدنويسي ناامن و رفتارهاي تعريف نشده است كه منجر به آسيب‌پذيري‌هاي قابل سوءاستفاده مي‌شود. به كارگيري استانداردهاي مذكور منجر به توليد سيستم‌هاي با كيفيت بالاتر مي‌شود كه در برابر حملات بالقوه، پايدارتر و مقاوم‌تر هستند.
در مقاله "آشنايي با استاندارد CERT براي برنامه‌نويسي امن"، كليات استاندارد CERT در زمينه مزبور را توضيح داديم و در سري مقاله‌هاي برنامه‌نويسي امن به زبان C به صورت تخصصي‌تر شيوه برنامه‌نويسي امن با اين زبان را مورد بررسي قرار مي‌دهيم. قابل ذكر است كه در اين استاندارد 89 قانون و 134 پيشنهاد براي برنامه‌نويسي امن با زبان C ارائه شده است كه در اين سري مقالات، مهمترين آنها را كه در سطح يك قرار دارند، شرح خواهيم داد. براي كسب اطلاعات بيشتر در مورد سطح‌بندي قوانين و پيشنهادات به مقاله "آشنايي با استاندارد CERT براي برنامه نويسي امن" مراجعه فرماييد. در مقاله حاضر به پيشنهادات و قوانين ارائه شده سطح اول در مورد محيط خواهيم پرداخت. 
 
37. ENV01-C – در مورد اندازه يك متغير محيطي هيچ پيش فرضي نداشته باشيد.
 
هيچگونه پيش‌فرضي در مورد اندازه متغيرهاي محيطي نداشته باشيد زيرا ممكن است برنامه هاي رقيب تمام فضاي حافظه اينگونه متغيرها را پر كرده باشند. در صورتي كه لازم باشد يك متغير محيطي ذخيره شود، بايد فضاي حافظه مورد نياز محاسبه شده و به صورت دايناميك به آن اختصاص داده شود.
در زير يك برنامه را مشاهده مي كنيد كه از قانون مذكور پيروي نكرده است و رشته اي را كه توسط getenv() بازگردانده مي شود به يك بافر با اندازه ثابت كپي مي كند.
 
 
در نمونه اصلاح شده اين برنامه، از تابع strlen() براي محاسبه اندازه رشته استفاده شده و حافظه مورد نياز به صورت دايناميك اختصاص داده شده است.
 
38. ENV04-C – در صورتي كه به يك پردازشگر فرمان نياز نداريد، تابع system() را فراخواني نكنيد.
 
مفسرين فرمان مانند POSIX يا cmd.exe علاوه بر اجراي فرمان هاي ساده، عملياتي نيز هستند. در صورتي كه عملياتي ضرورتاً مورد نياز برنامه شما نيست، بهتر است از تابع system() يا هر تابع ديگري كه يك مفسر فرمان را براي شما اجرا مي كند، استفاده نكنيد زيرا اين كار پاكسازي رشته-فرمان را بسيار پيچيده مي سازد و در نتيجه ميزان خطر برنامه را بالا مي برد.
در زير قطعه كدي را مشاهده مي كنيد كه از اين قانون پيروي نكرده است. در اين مثال از تابع system() براي اجراي any_cmd در محيط ميزبان استفاده شده است. در اينجا نيازي به فراخواني پردازشگر فرمان وجود ندارد.
 
 
در صورتي كه اين برنامه براي مثال توسط يك كاربر با دسترسي مديريتي در لينوكس (superuser) اجرا شود، مهاجم مي تواند با وارد كردن رشته زير يك حساب كاربري براي خود ايجاد كند.
 
happy'; useradd 'attacker
پوسته اين رشته را به عنوان دو دستور جداگانه تفسير خواهد كرد:
 
any_cmd 'happy';
useradd 'attacker'
 
و به اين ترتيب يك حساب كاربري جديد را ايجاد مي كند كه مهاجم توسط آن مي تواند به سيستم قرباني دسترسي پيدا كند.
در زير برنامه اصلاح شده مذكور را مشاهده مي كنيد. در اينجا فراخواني system() با فراخواني تابع execve() جايگزين شده است. خانواده توابع exec مي توانند براي اجراي دستورات اجرايي خارجي با انواع پارامترها و توسط روش هاي مختلف مورد استفاده قرار گيرند.
البته در استفاده از اين توابع بايد دقت شود فايل هاي اجرائي خارجي قابل تغيير از طريق يك كاربر تأييد نشده نباشند. براي مثال بايد دقت كرد كه اينگونه توابع قابليت نوشتن را نداشته باشند.
 
اين برنامه اصلاح شده تفاوت هاي مهمي با برنامه قبلي دارد. اولاً input با آرايه args تركيب شده و به عنوان يك آرگومان به exexve() فرستاده مي شود. اين مسئله هر گونه نگراني را در مورد سرريز بافر يا حذف رشته در هنگام شكل دهي به رشته فرمان از بين مي برد. ثانياً اين برنامه نياز دارد يك پردازه جديد را قبل از اجراي پردازه فرزند "/usr/bin/any_exe" فراخواني كند. با وجودي كه اين برنامه پيچيده تر از فراخواني system() است، اما كاملاً به نسبت امنيت اضافه شده مي ارزد.
 
39. ENV32-C – تمام توابع atexit بايد به صورت طبيعي بازگردانده شوند.
 
هيچ تابع ثبت شده به عنوان atexit() نبايد با روشي غير از روند طبيعي برنامه، از برنامه خارج شود. بسيار مهم و از لحاظ امنيتي حياتي است كه به توابع atexit() اجازه داده شود، پاكسازي خود را به طور كامل انجام دهند. اين قضيه از آن جهت اهميت دارد كه معمولاً برنامه نويسان اطلاعي در مورد handler هايي كه ممكن است توسط كتابخانه هاي پشتيبان نصب شده باشند، ندارند. دو مشكل عمده در اين رابطه شامل فراخواني هاي تودرتوي exit() و تمام كردن فراخواني يك تابع ثبت شده توسط atexit() از طريق فراخواني longjmp مي شود.
در C99 تابع exit() براي خروج نورمال از برنامه تعريف شده است و فراخواني هاي تو در تو از اين تابع منجر به رفتار تعريف نشده برنامه خواهد شد. اين مسئله زماني اتفاق مي افتد كه تابع exit() از درون يك تابع كه توسط atexit() ثبت شده است فراخواني شود، يا زماني كه از درون يك رسيدگي كننده به سيگنال فراخواني شود.
در صورتي كه يك فراخواني از longjmp انجام شود كه بتواند به كار يك تابع ثبت شده توسط atexit() پايان دهد، آنگاه رفتار برنامه تعريف نشده خواهد بود.
 
در زير برنامه اي را مشاهده مي كنيد كه از قانون فوق پيروي نكرده است. توابع exit1() و exit2()  توسط atexit() ثبت شده اند تا پاكسازي هاي لازم را قبل از خروج از كل برنامه انجام دهند. در صورتي كه شرايط (conditions) صحيح (true) ارزيابي شود، exit() براي بار دوم فراخواني خواهد شد و اين موضوع منجر به رفتار تعريف نشده در برنامه مي شود.
 
از آنجايي كه همه توابعي كه توسط atexit() ثبت شده اند، برعكس ترتيب ثبتشان فراخواني مي شوند، بنابراين اگر exit2() با هر روشي به جز return از برنامه خارج شود، تابع exit1() فراخواني نخواهد شد. اين مسئله براي رسيدگي كننده هاي (handler) خروجي كه توسط كتابخانه هاي پشتيبان ثبت شده باشند، نيز درست است.
تابعي كه به عنوان يك رسيدگي كننده به خروج توسط atexit() ثبت شده است، تنها بايد به وسيله return و نه هيچ روش ديگري خارج شود. در زير برنامه اي را كه با اين قانون سازگار است، مشاهده مي كنيد.
 

نظرات

بدون نظر
شما برای نظر دادن باید وارد شوید

نوشته

 
تاریخ ایجاد: 18 مرداد 1393

دسته‌ها

امتیاز

امتیاز شما
تعداد امتیازها:0