۱۰ اشتباه رایج در کدنویسی و روشهای اجتناب از آنها

اشتباهات رایج در کدنویسی میتوانند منجر به ازکارافتادگیهای گسترده، دزدی اطلاعات، نفوذ و مشکلات زیاد دیگری در برنامه نهایی شوند. برخی از این خطاها، مختص به زبانهای خاصی مانند C یا ++C بوده و برخی دیگر نیز در زبانهای دیگری از قبیل جاوا، جاوا اسکریپت، پایتون و غیره متداول هستند. در این مقاله، رایجترین اشتباهات توسعهدهندگان در هنگام برنامهنویسی و کدنویسی را برای شما ارائه خواهیم کرد. پیشنهاد میکنیم که در حین کدنویسی، این فهرست را بررسی کنید تا از عدم وجود چنین خطاهایی در برنامه خود مطمئن شوید. این فهرست به ترتیب اهمیت از بالا به پایین مرتب شده است.
1. سرریز بافر
«سرریز بافر» (Buffer Overflow)، هنگامی رخ میدهد که دادههای نوشته شده در یک بافر، از انتهای آن عبور کنند. دلیل این مشکل میتواند محاسبات اشتباه در محل نوشتن کد یا ادامه نوشتن آن بدون بررسی طول بافر باشد. سرریز بافر، یکی از رایجترین خطاهایی است که منجر به سو استفادههای زیادی میشود. انتشار کرم اینترنتی موریس در سال 1988، کرم «نیمدا» (Nimda) در سال 2001 و خطای «Sendmail» در سال 2003، مثالهایی از عواقب مشکلساز سرریز بافر هستند.
نمونهای از این مشکل در زبان C:
char array[6] = "hello"; strcat(array, ", joe"); /* <- This line causes a buffer overflow. */
فرد مهاجم، کدی را در برنامه جاسازی میکند که منجر به سرریزبافر شده و مسیر بازگردانی را تغییر میدهد و به جای بازگردانی یک مسیر صحیح، باعث فراخوانی یک مسیر تغییریافته و اجرای کد مخرب میشود. این کد ممکن است در هر محلی از حافظه قرار گرفته باشد.
2. تزریق به پایگاه داده
«تزریق به پایگاه داده» (SQL Injection)، روشی برای تزریق دستورات درون ورودی کاربر است؛ به طوری که این دستورات، مستقیماً توسط پایگاه داده اجرا میشوند. این مشکل به مهاجمان اجازه میدهد تا اقدامات مخربی مانند حذف جداول، از کار انداختن پایگاه داده، دزدی اطلاعات و غیره را انجام دهند. دلیل اصلی موفقیت حملات تزریق به پایگاه داده این است که نرمافزار پردازشگر اطلاعات ورودی کاربر، پیش از اجازه عبور دادههای ورودی به پایگاه داده و اجرای آنها، اعتبارسنجی و بررسیهای کافی را انجام نمیدهد. نمونهای از این مشکل در زبان جاوا:
// The following is a parameter value with SQL injection String username = "joe'; delete from user where username like '%"; Connection con = ...; // create connection to database // هنگامی که این دستور اجرا شود، تمام کاربران از پایگاه داده حذف خواهند شد con.createStatement().execute("update user set logged_in = 1 where username = '" + username + "'");
3. تزریق دستور سیستمعامل
«تزریق دستور سیستمعامل» (OS Command Injection) هنگامی رخ میدهد که ورودیهای مختص به کاربر، مستقیماً و بدون ارزیابی مناسب به سیستمعامل تحویل داده شوند تا اپلیکیشن آن را اجرا کند. چنین عملیاتی ممکن است برای به کارگیری یک دستور موجود در سیستمعامل، توسط یک اپلیکیشن مورد استفاده قرار گیرد. هنگامی که اپلیکیشن، ورودیهای کاربر را بدون اعتبارسنجی مناسب عبور میدهد، مسیر برای سو استفاده مهاجمان و اجرای دستورات مخرب هموار میشود. به عنوان مثال، این دستورات مخرب میتوانند برای حذف فایلها، تغییر مجوز دسترسی به آنها و فعالیتهای مخرب دیگر به کار گرفته شوند.
4. سرریز عدد صحیح
هنگامی که تلاش برای ذخیره یک مقدار بزرگ درون یک متغیر با نوع عدد صحیح صورت گیرد، «سرریز عدد صحیح» (Integer Overflow) رخ میدهد. به این مشکل، اصطلاحاً «wraparound» نیز میگویند. در سرریز عدد صحیح، مقدار عددی بزرگ کوتاه میشود و عملیات با ذخیره آن عدد و نمایش یک نتیجه غیر قابل پیشبینی پایان مییابد. به عنوان مثال، حداکثر مقداری که یک متغیر دو بایتی از نوع «short» میتواند داشته باشد، 65535 است. حال، فرض کنید دو مقدار short مانند 65530 و 10 داشته باشیم و نتیجه را نیز در یک short ذخیره کنیم. مجموع این دو عدد (65545)، در این متغیر جا نخواهد شد و در اثر کوتاه شدن عدد، یک مقدار غیر قابل پیشبینی در متغیر نهایی قرار داده میشود. اگر در ادامه، این مقدار در یک عملیات دیگر (مانند اندیس آرایه) مورد استفاده قرار گیرد، نتایج آن عملیات نیز غیر قابل پیشبینی خواهد بود.
نمونهای از این مشکل در زبان C:
short a = 65530, b = 10; short c = a + b; // در کامپیوتر من، مقدار غیر قابل پیشبینی 4 بازگردانده شد
5. اعتبارسنجی نامناسب اندیس آرایه
یکی دیگر از خطاهای بسیار رایج در حوزه نرمافزار، اعتبارسنجی نامناسب یک اندیس آرایه است. این مشکل زمانی رخ میدهد که یک آرایه از اندیسی در خارج از محدوده خود استفاده میکند. هنگامی که شما به یک محل در خارج از محدوده معتبر دادههای برنامه دسترسی پیدا کنید، با خطای «دسترسی به حافظه» (Memory Access) مواجه خواهید شد. این خطا، به عنوان «نقض قطعهبندی» (Segmentation Violation) نیز شناخته میشود. اگر محل حافظه، درون محدوده دادهها اما بیرون از آرایه قرار گفته باشد، در هنگام نوشتن چنین محلی، با خطای «تخریب حافظه» (Memory Corruption) مواجه خواهید شد.
خطاهای ذکر شده، بیشتر در زبانهای C و ++C متداول هستند اما امکان مواجه با آنها در هر زبانی مانند جاوا، جاوا اسکریپت، پایتون و غیره که داری مدیریت حافظه خودکار هستند نیز وجود دارد. کاهش چنین خطاهایی، به برنامهنویس بستگی دارد. برنامهنویسان میتواند با داشتن توجه کافی در حین کدنویسی این مشکلات را کاهش دهند.
6. تخصیص منابع بدون تعیین محدودیت
از آنجایی که مدیریت حافظه در زبانهای C و ++C به صورت دستی انجام میشود، مشکل «تخصیص حافظه» (Memory Allocation) در این زبانها بسیار رایج است. تخصیص حافظه بدون اعتبارسنجی مناسب اندازه تخصیص یافته، میتواند منجر به عدم موفقیت این عملیات شود. به علاوه، هنگامی که نتیجه تخصیصها بررسی نشوند و مستقیماً مورد استفاده قرار گیرند، امکان مواجه با یک فاجعه وجود خواهد داشت. برخورد با چنین خطاهایی در زبانهایی مانند جاوا، جاوا اسکریپت و پایتون که مدیریت حافظه خودکار دارند نیز در هنگام تخصیص آرایهها امکان دارد. از اینرو، باید در زمان تخصیص آرایهها در چنین زبانهایی، دقت کافی به خرج داده شود. یکی دیگر از دلایل احتمالی بروز این مشکل، ایجاد و عدم بازرسی مناسب منابع دیگری مانند «دستگیرههای فایل» (File Handles) یا «دستگیرههای اتصال» (Connection Handle) است. بستن نادرست منابع پس از اتمام استفاده از آنها، شایعترین راه برای رسیدن به محدودیت این منابع است.
7. ارجاعدهی مجدد به یک اشارهگر منقضی شده
در زبانهایی مانند C و ++C، پس از اتمام کار با حافظه، امکان آزادسازی فضای استفاده شده وجود دارد. استفاده از اشارهگر برای این بخش از حافظه آزاد، یک خطا محسوب میشود. چنین خطایی به دلیل منجر شدن به کارافتادگیهای بزرگ، در خبرها نیز مورد اشاره قرار گرفته است. بنابراین، باید مطمئن شوید که چنین خطایی در کد شما رخ نخواهد داد.
8. ارجاعدهی مجدد به اشارهگر تهی
در برخی از مواقع ممکن است که یک اشارهگر، پیش از مقداردهی اولیه یا بعد از آزادسازی حافظه، به صورت «تهی» (Null) باشد. ارجاعدهی مجدد به چنین اشارهگری میتواند منجر به رخ دادن خطای «اشارهگر تهی» (Null Pointer) شود که در زبان جاوا به آن «NullPointerException» گفته میشود. این خطا، در زبانهای C و ++C و همچنین جاوا بسیار متداول است اما مطمئناً میتواند در زبانهای دیگر نیز رخ دهد. شما باید برای اجتناب از این خطا، دقت کافی را بر روی کد خود به خرج دهید.
9. مقداردهی اولیه از دست رفته
متغیر محلی، به متغیری گفته میشود که درون یک تابع یا بلوک تعریف شده است و در انتهای تابع، دیگر وجود نخواهد داشت. این متغیر در پشته قرار میگیرد و پس از اینکه برای اولین بار تعریف شد، به صورت تصادفی پاک میشود. وظیفه شما به عنوان یک برنامهنویس این است که به محض تعریف این متغیرها، مقدار مناسبی را به آنها اختصاص دهید. استفاده از این متغیرها پیش از مقداردهی اولیه، منجر به خطای «مقداردهی اولیه از دست رفته» (Missing Initialization) میشود و مطمئناً باعث کرش برنامه یا امری مخربتر خواهد شد.
نمونهای از این مشکل در زبان C:
int pos; char buffer[] = "hello world"; // از آنجایی که هیچ مقداردهی اولیهای برای متغیر عدد صحیح صورت نگرفته است، نتیجه خروجی ممکن به صورت نامشخص باشد یا اینکه برنامه کرش کند printf("Value of character at pos %d is: %c\n", pos, buffer[pos]);
10. الگوریتمهای رمزنگاری شده ناقص یا پرخطر
دنیای رمزنگاری به طور مداوم در حال پیشرفت و تکامل است. چیزی که امروز مورد قبول است شاید فردا غیر قابل قبول باشد. دلیل این امر را میتوان رشد توان پردازشی کامپیوترها در نظر گرفت. کارهایی که انجام آنها با کامپیوترهای امروزی چندین سال طول میکشد، ممکن است در آینده تنها چند دقیقه زمان ببرد. به علاوه، شاید شخصی راه نفوذ به یک الگوریتم بخصوص را پیدا کند که در این صورت، آن الگوریتم دیگر قابل استفاده نخواهد بود. از اینرو، شما باید همیشه پیشرفتهای رمزنگاری را دنبال کرده و اگر هک و آسیبپذیریهایی در زمینه الگوریتمهای مورد استفاده شما کشف شد، کد خود را بهروزرسانی کنید.
به عنوان مثال، «SHA-1» که یک «الگوریتم درهمسازی» (Hashing Algorithm) است، دیگر برای رایانش درهمسازها توصیه نمیشود. در سال 2005، حملاتی علیه این الگوریتم یافت شد و از آن به بعد، استفاده از الگوریتمهای «SHA-2» و «SHA-3» پیشنهاد میشود. اگر نرمافزاری دارید که هنوز هم استفاده میشود و درون کد آن الگوریتم SHA-1 قرار دارد، باید آن را با الگوریتمهای پیشنهادی جایگزین کنید. در غیر این صورت، با احتمال خطر حمله به اپلیکیشن خود مواجه خواهید شد.
سخن آخر
در این مقاله، برخی از متداولترین اشتباهات و خطاهای برنامهنویسی و کدنویسی را برای شما ارائه دادیم. عدم توجه و رعایت نکات پیشگیرانه در مورد این اشتباهات میتواند عواقب جبرانناپذیری را دربر داشته باشد. شما میتوانید برای اجتناب از این خطاها، فهرستی از آنها تهیه و در هنگام کدنویسی به آن رجوع کنید. امیدواریم این مقاله برایتان مفید واقع شده باشد. اگر به مطالعه در این زمینه علاقهمند هستید، مطالب زیر را به شما پیشنهاد میکنیم:
- مجموعه آموزشهای برنامهنویسی
- آموزش برنامه نویسی C
- آموزش برنامه نویسی C++
- مجموعه آموزش های کاربردی برنامه نویسی C# (سی شارپ)
- آموزش برنامه نویسی PHP
- آموزش برنامه نویسی جاوا
#
برای چند حلقه تو در تو با ابعاد زیاد که در زمان اجرا حافظه قفل میشه شما چه راهکاری دارید؟
چطور میشه تا یک نقطه ای رفت و حافظه رو خالی کرد و دوباره از همان نقطه شروع به ادامه کار کرد؟