امنیت در نرم افزارهای تولید شده با زبان ++C

با توجه به اهمیت امنیت نرم افزار، شرکت های بزرگ دنیا به ارائه راهکارهایی چون طراحی زبان ها و محیط های برنامه نویسی و مفسر و مترجم هایی با قابلیت های کنترل امنیتی بر روی سیستم عامل و بسیاری از راهکارهای دیگر پرداخته اند اما با توجه به عدم توانایی راهکارها در کنترل تمامی موارد امنیتی، عدم امکان پیاده سازی راهکارهای امنیتی بر روی برخی ساختارها، ایجاد محدودیت برای دسترسی به برخی منابع و امکانات و مشکلات کوچک و بزرگ دیگر برنامه نویسی یک برنامه به صورت ایمن بهترین راهکار برای محافظت از یک برنامه است.یکی از زبان هایی که در کنار محبوبیت در میان برنامه نویسان، همیشه یکی از زبان های پر بحث در برنامه نویسی ایمن بوده است، خانواده زبان های C به خصوص ++C است.در این زبان ها عمده مدیریت منابع به برنامه نویس واگذار شده که در صورت عدم مدیریت درست آن ها، آسیب پذیری های مختلفی رخ می دهد.بهترین راهکار برای جلوگیری از بروز آسیب پذیری نرم افزارها، برنامه نویسی پدافندی و ایمن آن نرم افزار از ابتداست.دراین مستندات، باتوجه به جامعیت و کاربرد فراوان زبان ++C درکنار محبوبیت، مباحث ونکات اساسی در برنامه نویسی پدافندی و ایمن این زبان مطرح شده و انواع آسیب پذیری و شیوه جلوگیری از بروز آن ها و رفع آن ها در صورت بروز، توضیح داده می شود. همچنین سعی می شود تا راهکارهای ارائه شده تا حد امکان قابل پیاده سازی در زبان C نیز باشند.با توجه به گستردگی ابزارهای برنامه نویسی این زبان و وجود کامپایلرهای مختلف، زبان معیاری برای این مستند مدنظر قرار گرفته شده است و ساختار ارائه شده مربوط به ابزار یا کامپایلر خاصی نیست اما بنا بر نیاز مثال هایی در کنار زبان معیار از ابزارهایی خاص نیز ارائه می گردد.رفتار تعریف نشده ممکن است شامل مختل شدن عملکرد برنامه(Crash) خروجی نامربوط و غلط، بروز آسیب پذیری های نرم افزاری و موارد دیگر می باشد.وجود رفتار نامتعارف در یک برنامه نه تنها امنیت خود آن برنامه ، بلکه ممکن است امنیت سیستم عامل، شبکه را نیز به خظر بیندازد.جلوگیری از بروز رفتارهای تعریف نشده و مقابله با آن ها از مباحث مهم برنامه نویسی تدافعی و ایمن است.توابع بدون آرگومانبرای تعریف یک تابع بدون آرگومان باید از کلمه کلیدی void در زمان تعریف تابع استفاده نمایید.با این کار تزریق کد توسط هکرها را مختل می کنید.int getValue(void) { return 1; }اعداد تصادفیدر صورت نیاز به اعداد تصادفی از تابع ()rand استفاده نکنید به این علت که خروجی این تابع در تکرارهای بالا دچار تکرار می شود. بهتراست از تابع ()srand استفاده کنید می توانید برای آن seed تعریف کنید تا احتمال تکرار را به حداقل برسانید.در ویندوز هم می توانید از تابع ()CryptGenRandom استفاده کنید و در لینوکس هم تابع ()random و تابع ()srandom استفاده نمایید.#include <windows.h> #include <wincrypt.h> #include <iostream> int main(void) { HCRYPTPROV hcp; CryptAcquireContext(&prov, NULL, NULL, PROV_RSA_FULL, 0); long int li = 0ک CryptGenRandom(hcp, sizeof(li), (BYTE *)&li); printf(&quotRandom number is -> %ld\n&quot, li); return 0; }عدم استفاده از تابع بازگشتی جهت مقدار دهی اولیه به آرایه ای با کلاس حافظه استاتیکدر مثال زیر زمان ساخته شدن و مقدار دهی اولیه آرایه cache تابع fact مجددا فراخوانی شده واین عمل به دلیل ایستا بودن آرایه باعث بروز رفتار تعریف نشده و خطا خواهد شد.#include <stdexcept> int fact(int i) noexcept(false) { if (i < 0) throw std::domain_error(&quoti must be >=0&quot); static const int cache[] = { fact (0), fact(1), fact(2), fact(3), fact(4), fact (5), fact (6), fact(7), fact(8), fact(9), fact(10), fact (11), fact (12), fact(13), fact(14), fact(15), fact(16) }; if (i < (sizeof(cache) / sizeof(int))) return cache[i]; return i > 0 ? i * fact (i - 1) : 1; }حال برای رفع این اشکال طبق نمونه کد زیر آرایه را بدون استفاده initializer list با استفاده از یک متغیر ثابت که تعداد عضو های آرایه را معین میکند تعریف شده است و ازآنجا که کامپایلر آرایه های از جنس کلاس حافظه استاتیک را خود با عدد 0 مقداردهی میکند دیگر یک آرایه از قبل پر شده نخواهیم داشت و در مرحله با استفاده از تکنیک lazy به هر یک از عضوهای آرایه مقدار مناسب را با استفاده از تابع بازگشتی مقدار دهی خواهیم کرد.#include <stdexcept> const int arraySize = 17 int fact(int i) noexcept(false) { if (i < 0) throw std::domain_error(&quoti must be >=0&quot); static int cache[arraySize]; if (i < (sizeof(cache) / sizeof(int))) { if (0 == cache[i]) cache[i] = i > 0 ? i * fact(i - 1) : 1; return cache[i]; } return i > 0 ? i * fact(i - 1) : 1;الحاق مضاعف هدر فایل هاالحاق مضاعف زمانی رخ می دهد که یک هدر دو ویا چند بار به برنامه اضافه شوند.در مثال زیر در کلاس c هدرهای a , b الحاق می شوند در حالی که در کلاس b هم هدر a الحاق شده است که الحاق مضاعف رخ داده است.//a.h struct a { int member }; //b.h #include &quota.h&quot //c.c #include &quota.h&quot #include &quotb.h&quotبرای جلوگیری از این الحاق های مضاعف می توانید از روش زیر استفاده نمایید//a.h #ifdef A_H #define A_H struct a { int member; }; #endifویا می توانید از دستور pragma استفاده کنید البته این دستور جز دستورات استاندارد ++c / c نمی باشد ولی اکثر کامپایلرها این دستور را اجرا میکنند.//a.h #pragma once struct a { int member; };رمزنگاری اصولیجهت رمزنگاری داده های حساس در برنامه های خود می توانید از کتابخانه ++Crypto و همچنین کتابخانه libcrypto از OpenSSL نیز استفاده نمایید.رمز نگاری به چند دسته اصلی تقسیم می شود:1- رمزنگاری درهم سازHashکه میتوان به الگوریتم های MD6 , MD5 , SHA-1,SHA-0 اشاره کرد.2- رمزنگاری با کلید متقارنکه می توان به الگوریتم های RC4 , AES , DES , 3DES اشاره کرد.3- رمزنگاری با کلید عمومی نا متقارنکه می توان به الگوریتم های RSA , DSA , DSS اشاره کرد.4- کد گذاری دودویی به متنکه می توان به الگوریتم های Base32 , Base58 , Base64 ,Base85 اشاره کرد.مدیریت مقدار و نوع داده ها و مقدار دهی اولیهدر مثال زیر متغیر هایی تعریف شده اند که مقدار اولیه ندارند (البته درست است که در برخی از کامپایلرها این متغیرها را مقدار دهی خواهند کرد، ولی توجه داشته باشید که تکنیک های برنامه نویسی تدافعی جدای از امکانات کامپایلر می باشد)int main (void) { int a; float b; char c; bool d; return 0; }اکنون مشاهده میکنید که بعد از اجرای برنامه چه مقدار هایی در متغیرها ذخیره شده است.پس بنابراین مقدار دهی اولیه متغیرها یا باید برحسب نیاز در همان ابتدا تعریف صورت گیرد یا در صورت عدم نیاز به وجود مقدار اولیه خاص، مقدار دهی با استفاده از تابع همان نوع داده انجام خواهد شد.int main (void) { int a = int(); float b = float(); char c = char(); bool d = bool(); return 0; }و بعد از اجرا به این صورت خواهد بود...int main (void) { int a[5]; float b[5]; char c[5]; bool d[5]; return 0; }که بعد از اجرا بدین صورت خواهد بود...و برای رفع این اشکال باید همیشه آرایه ها را مقدار دهی اولیه نمایید.int main (void) { int a[5] = {}; float b[5] = {}; char c[5] = {}; bool d[5] = {}; return 0; }وبعد از مقدار دهی اولیه به آرایه ها خواهیم داشت ...موفق باشید.