رفتن به مطلب
جامعه‌ی برنامه‌نویسان مُدرن ایران

برنامه نویسی

  • نوشته‌
    7
  • دیدگاه
    2
  • مشاهده
    1,262

مشارکت‌کنندگان این وبلاگ

امنیت در نرم افزارهای تولید شده با زبان ++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("Random number is -> %ld\n", li); 
  return 0; 
} 

عدم استفاده از تابع بازگشتی جهت مقدار دهی اولیه به آرایه ای با کلاس حافظه استاتیک

در مثال زیر زمان ساخته شدن و مقدار دهی اولیه آرایه cache تابع fact مجددا فراخوانی شده واین عمل به دلیل ایستا بودن آرایه باعث بروز رفتار تعریف نشده و خطا خواهد شد.

#include <stdexcept> 
 
int fact(int i) noexcept(false) { 
  if (i < 0) { 
    throw std::domain_error("i must be >=0"); 
  } 
 
  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("i must be >=0"); 
  } 
   
  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 membe
}; 
 
//b.h 
#include "a.h" 
 
//c.c 
#include "a.h" 
#include "b.h" 

برای جلوگیری از این الحاق های مضاعف می توانید از روش زیر استفاده نمایید

//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; 
} 

اکنون مشاهده میکنید که بعد از اجرای برنامه چه مقدار هایی در متغیرها ذخیره شده است.

11.jpg.202657c3dd2805362979947c85476ce6.jpg

پس بنابراین مقدار دهی اولیه متغیرها یا باید برحسب نیاز در همان ابتدا تعریف صورت گیرد یا در صورت عدم نیاز به وجود مقدار اولیه خاص، مقدار دهی با استفاده از تابع همان نوع داده انجام خواهد شد.

int main (void) 
{ 
  int a = int(); 
  float b = float(); 
  char c = char(); 
  bool d = bool(); 
  return 0; 
} 

و بعد از اجرا به این صورت خواهد بود

image.png.297a09ad5bdaade2dbe8dd7e6758b94c.png

مقدار دهی اولیه به آرایه ها

int main (void) 
{ 
  int a[5]; 
  float b[5]; 
  char c[5]; 
  bool d[5]; 
  return 0; 
} 

که بعد از اجرا بدین صورت خواهد بود...

image.png.f687bbfb490c4d20e0d77057651966c3.png

و برای رفع این اشکال باید همیشه آرایه ها را مقدار دهی اولیه نمایید.

int main (void) 
{ 
  int a[5] = {}; 
  float b[5] = {}; 
  char c[5] = {}; 
  bool d[5] = {}; 
  return 0; 
} 

وبعد از مقدار دهی اولیه به آرایه ها خواهیم داشت ...

image.png.56336daa0361ee9f4fcb5003b2d08b26.png

ادامه خواهد داشت این مقاله...

 


  • پسندیدن 2
  • تشکر شده 1


0 دیدگاه


نظرهای پیشنهاد شده

هیچ دیدگاهی برای نمایش وجود دارد.

برای ارسال دیدگاه یک حساب کاربری ایجاد کنید یا وارد حساب خود شوید

برای اینکه بتوانید دیدگاهی ارسال کنید نیاز دارید که کاربر سایت شوید

ایجاد یک حساب کاربری

برای حساب کاربری جدید در سایت ما ثبت نام کنید. عضویت خیلی ساده است !

ثبت نام یک حساب کاربری جدید

ورود به حساب کاربری

دارای حساب کاربری هستید؟ از اینجا وارد شوید

ورود به حساب کاربری
  • کاربران آنلاین در این صفحه   0 کاربر

    هیچ کاربر عضوی،در حال مشاهده این صفحه نیست.

×