أمان C++: الحماية من حقن SQL وXSS في 2024

مدرب بايثون أونلاين للمبتدئين

تعلم Python بسهولة دون إرهاق نظري. حل مهام عملية مع التحقق التلقائي واكتب الكود مباشرة في المتصفح.

ابدأ الدورة

عندما يلتقي C++ بالويب: لماذا الأمان هو صداعك الرئيسي

\n\n

هل تساءلت يومًا لماذا يحب المتسللون تطبيقات C++ كثيرًا؟ لا، ليس لأن الكود معقد. بل لأنه في السعي وراء الأداء، غالبًا ما ينسى المطورون أمان C++. ويدفعون ثمن ذلك ببيانات المستخدمين وسمعتهم وأموالهم.

\n\n

تخيل: لقد كتبت خادمًا رائعًا بلغة C++ يعالج الطلبات أسرع من البرق. لكن إدخالًا واحدًا غير مُهرب - ويقوم المهاجم بتفريغ قاعدة البيانات بأكملها. أو ما هو أسوأ، يقوم بإدراج سكريبت يسرق جلسات جميع زوار الموقع. هل هذا مألوف؟ إذا لم يكن كذلك - رائع، فهذا يعني أنك تقرأ هذه المقالة في الوقت المناسب.

\n\n

في هذه المادة، سنقوم بتحليل أخطر هجومين على تطبيقات الويب - حقن SQL والبرمجة النصية عبر المواقع (XSS) - ونوضح كيفية الحماية منهما تحديدًا في C++. بدون سحر، مع أمثلة كود واقعية. هيا بنا.

\n\n

حقن SQL: كيف يحول المتسللون استعلامك إلى سلاح

\n\n

حقن SQL هو عندما يقوم المهاجم بإدخال جزء من كود SQL في حقل الإدخال (مثل اسم المستخدم أو استعلام البحث). إذا قام تطبيقك بتعويض هذا الإدخال بشكل أعمى في الاستعلام، فبدلاً من البحث عن مستخدم بالاسم، ستنفذ قاعدة البيانات شيئًا مثل DROP TABLE users. أو ما هو أسوأ، ستقوم بإخراج جميع كلمات المرور.

\n\n

خطأ المبتدئ النموذجي (وكيفية تجنبه)

\n\n

انظر إلى هذا الكود. يبدو غير ضار، لكنه قنبلة موقوتة:

\n\n
// خطير! لا تفعل هذا أبدًا\nstd::string username = getUserInput(); // المستخدم أدخل: ' OR '1'='1\nstd::string query = "SELECT * FROM users WHERE username = '" + username + "'";\n// النتيجة: SELECT * FROM users WHERE username = '' OR '1'='1'\n// سيعيد جميع المستخدمين!
\n\n

هل ترى المشكلة؟ إذا أدخل المستخدم ' OR '1'='1، سيعيد الاستعلام الجدول بأكمله. وإذا أدخل '; DROP TABLE users; --، فوداعًا أيتها القاعدة.

\n\n

النهج الصحيح: الاستعلامات المعلمة

\n\n

الطريقة الوحيدة الموثوقة للحماية من حقن SQL في C++ هي استخدام الاستعلامات المعلمة. قاعدة البيانات نفسها تميز بين الكود والبيانات. إليك كيف يبدو ذلك مع مكتبة libpqxx (لـ PostgreSQL) أو MySQL Connector/C++:

\n\n
#include <iostream>\n#include <pqxx/pqxx> // libpqxx لـ PostgreSQL\n\nvoid safeQuery(const std::string& username) {\n    try {\n        pqxx::connection conn("dbname=mydb user=myuser password=mypass");\n        pqxx::work txn(conn);\n        \n        // استعلام معلم: $1 هو المعامل\n        std::string query = "SELECT * FROM users WHERE username = $1";\n        pqxx::result result = txn.exec_params(query, username);\n        \n        for (auto row : result) {\n            std::cout << "User: " << row["username"].c_str() << std::endl;\n        }\n        txn.commit();\n    } catch (const std::exception &e) {\n        std::cerr << "Error: " << e.what() << std::endl;\n    }\n}\n\nint main() {\n    std::string input;\n    std::cout << "Enter username: ";\n    std::getline(std::cin, input);\n    safeQuery(input);\n    return 0;\n}
\n\n

لاحظ: نحن نستخدم txn.exec_params بدلاً من دمج السلاسل النصية. قاعدة البيانات نفسها تقوم بتهريب الأحرف الخاصة. حتى إذا أدخل المستخدم ' OR '1'='1، سيتم التعامل معه كـ سلسلة نصية، وليس كجزء من أمر SQL.

\n\n

ماذا تفعل إذا كنت تستخدم SQLite الخام؟

\n\n

حتى مع SQLite يمكنك العمل بأمان. استخدم sqlite3_prepare_v2 و sqlite3_bind_text:

\n\n
#include <sqlite3.h>\n#include <iostream>\n\nvoid safeSQLiteQuery(sqlite3* db, const std::string& username) {\n    sqlite3_stmt* stmt;\n    const char* sql = "SELECT * FROM users WHERE username = ?";\n    \n    if (sqlite3_prepare_v2                

المدونات

توصيات الكتب