مقدمة: لماذا أمان تطبيقات JavaScript ليس خيارًا بل ضرورة
أصبحت JavaScript (خاصة على منصة Node.js) اللغة المهيمنة لإنشاء تطبيقات العميل والخادم على حد سواء. ومع ذلك، مع زيادة الشعبية، يزداد عدد الهجمات. وفقًا لـ OWASP، تحدث معظم الاختراقات بسبب أخطاء نموذجية يمكن منعها. في هذه المقالة، سنستعرض أهم 10 ثغرات أمنية في JavaScript (Node.js) مع أمثلة برمجية حقيقية وطرق الحماية. ستكون المادة مفيدة للمطورين المبتدئين وذوي الخبرة الراغبين في تعزيز أمان مشاريعهم.
1. الحقن (SQL, NoSQL, Command Injection)
تظل هجمات الحقن أخطر فئة من الثغرات الأمنية. في Node.js، غالبًا ما تتأثر MongoDB و PostgreSQL.
مثال على كود ضعيف (حقن NoSQL):
// كود ضعيفapp.post('/login', async (req, res) => { const { username, password } = req.body; const user = await db.collection('users').findOne({ username: username, password: password }); // إذا تم تمرير { "username": { "$ne": null }, "password": { "$ne": null } }، فسيتم إرجاع أول مستخدم});كيفية الإصلاح:
- استخدم ORM/ODM مع معاملات (Mongoose, Sequelize).
- تحقق من صحة البيانات المدخلة ونقّها (مكتبة
validatorأوjoi). - للأوامر، استخدم
execFileبدلاً منexec.
2. البرمجة النصية عبر المواقع (XSS)
يسمح XSS للمهاجم بإدخال نص برمجي ضار في صفحة يشاهدها مستخدمون آخرون.
مثال على كود ضعيف (XSS منعكس):
// مسار Express ضعيفapp.get('/search', (req, res) => { const query = req.query.q; res.send(`<h1>نتائج البحث عن: ${query}</h1>`); // إذا تم تمرير ?q=<script>alert('XSS')</script>، فسيتم تنفيذ النص البرمجي});الحماية:
- قم بتشفير المخرجات: استخدم
res.renderمع محركات القوالب (EJS, Pug) أوencodeURIComponent. - قم بتعيين رأس HTTP
Content-Security-Policy. - لا تستخدم
innerHTMLعلى العميل - استخدمtextContent.
3. المصادقة غير الآمنة وإدارة الجلسات
المصادقة الضعيفة هي أحد الأسباب الرئيسية لتسرب البيانات. غالبًا ما يقوم المطورون بتضمين المفاتيح بشكل ثابت في الكود أو استخدام كلمات مرور ضعيفة.
الأخطاء النموذجية:
- تخزين كلمات المرور بنص واضح (بدلاً من bcrypt/argon2).
- استخدام أسرار JWT قابلة للتخمين (
secret123). - عدم وجود حد لعدد محاولات تسجيل الدخول.
مثال على التنفيذ الصحيح:
const bcrypt = require('bcrypt');const saltRounds = 12;
app.post('/register', async (req, res) => { const hash = await bcrypt.hash(req.body.password, saltRounds); // حفظ التجزئة في قاعدة البيانات});
app.post('/login', async (req, res) => { const user = await db.getUser(req.body.username); const match = await bcrypt.compare(req.body.password, user.passwordHash); if (match) { // إنشاء JWT مع فترة صلاحية قصيرة const token = jwt.sign({ id: user.id }, process.env.JWT_SECRET, { expiresIn: '15m' }); res.json({ token }); }});4. SSRF (تزوير الطلب من جانب الخادم)
تسمح ثغرة SSRF للمهاجم بإجبار الخادم على تنفيذ طلبات إلى موارد داخلية (مثل بيانات تعريف مزود السحابة).
مثال:
// كود ضعيف: تحميل الصورة الرمزية عبر URLapp.post('/avatar', async (req, res) => { const { url } = req.body; const response = await fetch(url); // يمكن تمرير http://169.254.169.254/latest/meta-data/ // معالجة الرد});الحماية:
- أنشئ قائمة بيضاء بالنطاقات المسموح بها.
- احظر