الكود الخاص بك تحت المجهر: لماذا أمان TypeScript ليس خيارًا بل ضرورة
\n\nهل تساءلت يومًا ماذا يحدث عندما يدخل مستخدم في حقل البحث على موقعك ليس فقط "كتب عن Python"، بل شيئًا مثل '; DROP TABLE users; --؟ إذا كان الخادم الخلفي الخاص بك على TypeScript غير مستعد لمثل هذا السيناريو، فأنت لا تخاطر فقط بالحصول على خطأ، بل بفقدان قاعدة البيانات بأكملها. هل يبدو هذا كسيناريو فيلم رعب؟ مرحبًا بك في العالم الحقيقي لتطوير الويب، حيث أمان TypeScript ليس ميزة عصرية، بل هو النظافة الأساسية للكود.
في هذه المقالة، لن نلقي محاضرات مملة. سنقوم بتحليل العدوين الرئيسيين لأي تطبيق ويب: حقن SQL وXSS (البرمجة النصية عبر المواقع). ستتعرف على كيفية مهاجمة القراصنة لمشاريعك، والأهم من ذلك، كيفية تحويل كود TypeScript الخاص بك إلى حصن منيع باستخدام أمثلة حقيقية وأفضل الممارسات.
\n\nحقن SQL: كيف "يتحدث" المهاجمون مع قاعدة البيانات الخاصة بك
\n\nتخيل أن تطبيقك هو موظف استقبال مهذب ينفذ أي طلب من الضيف. إذا طلب الضيف: "أرني الغرفة رقم 5"، يريه الموظف. ولكن ماذا لو طلب الضيف: "أرني جميع الغرف وأعطني مفاتيح الخزنة"؟ بدون تحقق، سينفذ الموظف هذا أيضًا. حقن SQL يعمل تمامًا بهذه الطريقة: يقوم المهاجم "بتضمين" أوامره في إدخال المستخدم العادي.
\n\nلنفترض أن لديك استعلامًا بسيطًا للبحث عن مستخدم بالاسم:
\n\n// ❌ خطير: كود ضعيف\nconst userName = req.body.username; // المستخدم أدخل "admin"\nconst query = `SELECT * FROM users WHERE name = '${userName}'`;\ndb.execute(query);\n\nيبدو غير ضار؟ والآن تخيل أن المستخدم يدخل: admin' OR '1'='1. يتحول الاستعلام إلى:
SELECT * FROM users WHERE name = 'admin' OR '1'='1'\n\nتهانينا! لقد أعطيت المهاجم للتو قائمة بجميع مستخدمي النظام. وإذا أراد حذف شيء ما؟ بسهولة: admin'; DROP TABLE users; -- — ولم يعد لقاعدة البيانات الخاصة بك وجود.
كيف تحمي نفسك: الاستعلامات المعلمة هي درعك
\n\nالقاعدة الأساسية لـ الحماية من حقن SQL في TypeScript: لا تقم أبدًا بإدراج بيانات المستخدم مباشرة في سلسلة الاستعلام. استخدم الاستعلامات المعلمة (prepared statements). فهي تفصل بين كود SQL والبيانات، وتتعامل قاعدة البيانات مع إدخال المستخدم فقط كبيانات وليس كأوامر.
\n\nمثال مع مكتبة pg (لـ PostgreSQL):
// ✅ آمن: استعلام معلم\nimport { Pool } from 'pg';\n\nconst pool = new Pool({\n connectionString: process.env.DATABASE_URL\n});\n\nasync function getUserByName(userName: string): Promise<any> {\n const query = 'SELECT * FROM users WHERE name = $1';\n const result = await pool.query(query, [userName]);\n return result.rows;\n}\n\n// الاستخدام\nconst userInput = req.body.username; // حتى لو كان "admin' OR '1'='1"\nconst user = await getUserByName(userInput);\n// قاعدة البيانات ستتعامل مع هذا كبحث عن مستخدم بهذا الاسم الغريب\nconsole.log(user); // [] — لم يتم العثور على شيء\n\nنفس المبدأ يعمل مع ORM الأخرى، مثل Prisma أو TypeORM. فهي تستخدم الاستعلامات المعلمة افتراضيًا، مما يجعل أمان TypeScript ميزة مدمجة.
\n\n// ✅ آمن: استخدام Prisma\nimport { PrismaClient } from '@prisma/client';\n\nconst prisma = new PrismaClient();\n\nasync function findUser(name: string) {\n return prisma.user.findUnique({\n where: { name: name } // Prisma يقوم بهروب بيانات الإدخال بنفسه\n });\n}\n\nهجمات XSS: عندما يبدأ موقعك في بث كود شخص آخر
\n\nإذا كانت هجمات حقن SQL تضرب قاعدة البيانات، فإن XSS (البرمجة النصية عبر المواقع) تهاجم المستخدمين. تخيل أن مهاجمًا