أمان تطبيقات C++: أهم 10 ثغرات للمبتدئين
\n\nتمنح لغة C++ المطور تحكمًا هائلاً في الذاكرة والأداء، لكن هذه الحرية تأتي بمسؤولية كبيرة. وفقًا لـ OWASP و CWE، غالبًا ما تنشأ الثغرات في تطبيقات C++ بسبب سوء التعامل مع الذاكرة، وعدم التحقق من الحدود، وعدم الانتباه عند التعامل مع المؤشرات. في هذه المقالة، سنستعرض 10 من أكثر الثغرات شيوعًا التي يواجهها المبتدئون. ستتعلم كيفية اكتشافها، والأهم من ذلك، كيفية تجنبها.
\n\n1. تجاوز سعة المخزن المؤقت (Buffer Overflow)
\nكلاسيكيات هذا المجال. تنشأ هذه الثغرة عندما يكتب البرنامج بيانات تتجاوز حدود المخزن المؤقت المخصص. يمكن للمهاجم الكتابة فوق مناطق الذاكرة المجاورة، بما في ذلك عنوان إرجاع الدالة، والتحكم في تنفيذ الكود.
\n\n// كود ضعيف\nvoid vulnerable() {\n char buffer[10];\n std::cout << "أدخل سلسلة: ";\n std::cin >> buffer; // لا يوجد فحص للطول!\n std::cout << buffer << std::endl;\n}\n\n// إصدار آمن\nvoid safe() {\n char buffer[10];\n std::cout << "أدخل سلسلة: ";\n std::cin.getline(buffer, sizeof(buffer));\n std::cout << buffer << std::endl;\n}\n\nكيف تحمي نفسك: استخدم دائمًا الدوال ذات الحد الأقصى للطول (strncpy، snprintf، std::string). تجنب gets() و strcpy().
2. تسرب الذاكرة (Memory Leaks)
\nإذا قمت بتخصيص ذاكرة عبر new ولكن نسيت تحريرها عبر delete، فسيبدأ البرنامج في "استهلاك" ذاكرة الوصول العشوائي تدريجيًا. بالنسبة لتطبيقات الخادم، هذه كارثة - بعد بضع ساعات من التشغيل، سيتعطل الخدمة مع خطأ std::bad_alloc.
// مثال على تسرب\nvoid leak() {\n int* ptr = new int(42);\n // نسينا delete ptr;\n}\n\n// النهج الصحيح\nvoid noLeak() {\n std::unique_ptr<int> ptr = std::make_unique<int>(42);\n // سيتم تحرير الذاكرة تلقائيًا عند الخروج من نطاق الرؤية\n}\n\nكيف تحمي نفسك: استخدم المؤشرات الذكية (std::unique_ptr، std::shared_ptr). في الحالات القصوى - استخدم RAII (Resource Acquisition Is Initialization).
3. المؤشرات المعلقة (Dangling Pointers)
\nيشير المؤشر إلى ذاكرة تم تحريرها بالفعل. إلغاء الإشارة إلى مثل هذا المؤشر هو سلوك غير محدد (UB). قد يؤدي ذلك إلى تعطل البرنامج أو، الأسوأ من ذلك، إلى تنفيذ كود ضار.
\n\nint* dangling() {\n int x = 10;\n return &x; // خطأ: نعيد عنوان متغير محلي!\n}\n\n// بشكل صحيح - تخصيص ذاكرة في الكومة\nint* safe() {\n int* ptr = new int(10);\n return ptr;\n}\n\nكيف تحمي نفسك: بعد delete، قم دائمًا بتعيين nullptr للمؤشر. استخدم المؤشرات الذكية.
4. تجاوز سعة الأعداد الصحيحة (Integer Overflow)
\nعندما تتجاوز نتيجة عملية حسابية نطاق نوع البيانات، يحدث تجاوز. قد يؤدي ذلك إلى عواقب غير متوقعة، مثل تخصيص مخزن مؤقت صغير جدًا.
\n\nvoid check_size(size_t user_size) {\n // ثغرة: إذا كان user_size كبيرًا جدًا، فقد يصبح user_size + 1 صغيرًا\n char* buffer = new char[user_size + 1];\n // ...\n}\n\n// حماية\nvoid safe_check(size_t user_size) {\n if (user_size > std::numeric_limits<size_t>::max() - 1) {\n throw std::overflow_error("الحجم كبير جدًا");\n }\n char* buffer = new char[user_size + 1];\n}\n\nكيف تحمي نفسك: تحقق من الحدود قبل العمليات الحسابية. استخدم __builtin_add_overflow (GCC/Clang) أو مكتبات الحساب الآمن.
5. المؤشرات الفارغة (Null Pointer Dereference)
\nإلغاء الإشارة إلى مؤشر فارغ