عنصر اصلی در برنامهنویسی امن با زبانهای مختلف برنامهنویسی، مستندسازی خوب و استفاده از استانداردهای قابل اجرا است. استانداردهای كدنویسی، برنامهنویسان را ترغیب به پیروی از مجموعهای متحدالشكل از قوانین و راهنماییهایی میكند كه بر اساس نیازمندیهای پروژه و سازمان تعیین شده است، نه بر اساس سلایق و مهارتهای مختلف برنامهنویسان. به محض تعیین استانداردهای مذكور، میتوان از آن به عنوان معیاری برای ارزیابی كدهای منبع، چه به صورت دستی و چه به صورت اتوماتیك استفاده كرد.
از استانداردهای معروف در این زمینه میتوان به استانداردCERT برای كدنویسی امن اشاره كرد كه یك سری از قوانین و پیشنهادات را برای كدنویسی امن با زبانهای برنامهنویسی C، C++ و جاوا ارائه میدهد. هدف از این قوانین و پیشنهادات، حذف عادتهای كدنویسی ناامن و رفتارهای تعریف نشده است كه منجر به آسیبپذیریهای قابل سوءاستفاده میشود. به كارگیری استانداردهای مذكور منجر به تولید سیستمهای با كیفیت بالاتر میشود كه در برابر حملات بالقوه، پایدارتر و مقاومتر هستند.
در مقالههای قبلی، كلیات استاندارد CERT در زمینه مزبور را توضیح دادیم و در سری مقالههای برنامهنویسی امن به زبان C به صورت تخصصیتر شیوه برنامهنویسی امن با این زبان را مورد بررسی قرار میدهیم. قابل ذكر است كه در این استاندارد 89 قانون و 134 پیشنهاد برای برنامهنویسی امن با زبان C ارائه شده است كه در این سری مقالات، مهمترین آنها را كه در سطح یك قرار دارند، شرح خواهیم داد. برای كسب اطلاعات بیشتر در مورد سطحبندی قوانین و پیشنهادات به مقاله "
آشنایی با استاندارد CERT برای برنامه نویسی امن" مراجعه فرمایید. سری مقالات حاضر با عنوان قوانین و پیشنهادات تكمیلی (Miscellaneous) آخرین سری مقالات برنامهنویسی امن به زبان C است كه در آن قوانین و پیشنهاداتی توضیح داده میشوند كه در دسته بندیهای قبلی نگنجیدهاند.
42. MSC15-C – به رفتارهای تعریف نشده وابسته نشوید.
در C99، بخش 3.4.3 رفتار نامشخص به صورت زیر تعریف شده است:
"رفتاری كه بر اثر استفاده از برنامهای دارای ساختار نادرست یا داده های نادرست به وجود میآید و این استاندارد یك نیازمندی را در مورد آن اعمال نمیكند."
در بخش چهارم استاندارد C99 در مورد چگونگی تشخیص رفتار تعریف نشده توضیح داده شده است:" در صورتی كه نیازمندی "باید" یا "نباید" (shall or shall not) كه در بیرون یك محدودیت آمده است، نقض شود، رفتار تولید شده تعربفنشده خواهد بود. همچنین رفتار تعریفنشده در این استاندارد یا با كلمات "undefined behaviour"مشخص شده است یا با حذف هر گونه تعریف روشنی از رفتار معین میشود. هیچ اولویتی در مورد این سه روش وجود ندارد و هر سه نشاندهنده رفتار تعریفنشده هستند."رفتار تعریفنشده میتواند تحت شرایط بسیار متنوعی اتفاق بیفتد كه از آن جمله میتوان به نادیده گرفتن برخی شرایط كه منجر به نتایج پیشبینی نشده میشود اشاره كرد، برای مثال در نظر نگرفتن شرایط استثنائی. از آنجایی كه كامپایلرها ملزم به تولید كد برای رفتارهای نامشخص نیستند، این رفتارها كاندیدای خوبی برای بهینهسازیها هستند. با اطمینان از اینكه رفتارهای نامشخص اتفاق نخواهند افتاد، كامپایلرها میتوانند كدهایی را با كارایی بالاتر تولید كنند.متأسفانه رفتارهای نامشخص اتفاق میافتند به خصوص اگر مهاجمی حضور داشته باشد. بهینه سازیها تشخیص رفتار سیستم در حضور رفتارهای نامشخص را مشكل میسازند. این مسئله همچنین برای كسی كه در حال مرور كد است، مشكل ایجاد میكند زیرا در صورتی كه كد كامپایل و اجرا شود، تشخیص خطا بسیار سخت است. از طرف دیگر اینكه كامپایلر در حال حاضر كد اجرایی برای یك رفتار نامشخص تولید می كند، دلیل نمیشود كه نسخههای بعدی كامپایلر نیز این كار را در آینده انجام دهند. همچنین كامپایلرها از اساس برای تشخیص رفتارهای تعریف نشده طراحی نشدهاند و بنابراین همان طور كه گفتیم تشخیص این نوع رفتارها را در كد سخت میكنند.همه مسائل فوق بار سنگینی را بر دوش برنامهنویس میگذارند تا كدی را كه دقیقاً از استانداردهای بینالمللی پیروی میكند(strictly conforming)، تولید كند.در زیر نمونه كدی را مشاهده میكنید كه دارای یك رفتار نامشخص است كه مربوط به سرریز عدد صحیح علامتدار میشود. این برنامه به رفتار مذكور برای مدیریت سرریزبافر وابسته است.
#include
int foo(int a) {
assert(a + 100 > a);
printf("%d %d
", a + 100, a);
return a;
}
int main(void) {
foo(100);
foo(INT_MAX);
}
این برنامه از طریق دستور شرطی if a+1>a آزمایش میكند كه آیا سرریز عدد صحیح اتفاق میافتد یا خیر. این آزمون هیچگاه نتیجه نادرست را بر نمیگرداند مگر اینكه سرریز عدد صحیح اتفاق بیفتد. از آنجایی كه یك پیاده سازی استاندارد الزامی به تولید كد برای رفتار نامشخص ندارد و سرریز عدد صحیح علامتدار نیز یك رفتار نامشخص است، در نتیجه كد می تواند كامپایل شود. برای مثال GCC نسخه 4.1.1 این كد را برای همه سطوح بهینه سازی بهینه میكند.
از طرف دیگر در برخی از سكوها، سرریز عدد صحیح، قبل از اینكه فرصت آزمون آن به وجود آید، منجر به خروج از برنامه میشود.
در زیر كد اصلاح شده را مشاهده می كنید كه وابسته به رفتار تأیید نشده نیست:
#include
int foo(int a) {
assert(a < (INT_MAX - 100));
printf("%d %d
", a + 100, a);
return a;
}
int main(void) {
foo(100);
foo(INT_MAX);
}
با وجود اینكه تقریباً محال است كه همه برنامه كاربردی كاملاً از استانداردها پیروی كند ولی لازم است هدف این باشد كه اغلب برنامه اینگونه طراحی شده باشد (به این معنی كه از رفتارهای نامشخص اجتناب كند) و همچنین برنامهنویس دقت داشته باشد كه ماژولها و قسمتهایی كه به پیادهسازی وابسته هستند و باید با تغییر سكو تغییر پیدا كنند، حتماً تغییرات لازم بر آنها اعمال شده باشد.
منبع:http://www.certcc.ir
نظرات شما عزیزان: