أمان تطبيقات TypeScript: أهم 10 ثغرات أمنية
TypeScript، كونه طبقة فوق JavaScript، يرث العديد من نقاط الضعف الخاصة به، مع إضافة مخاطر محددة تتعلق بنظام الأنواع والترجمة. حتى الكتابة الصارمة لا تضمن الأمان — فالأخطاء في المنطق والممارسات غير الآمنة يمكن أن تؤدي إلى عواقب وخيمة. في هذه المقالة، سنستعرض أهم 10 ثغرات أمنية في تطبيقات TypeScript، وسنقدم أمثلة على الكود وتوصيات للحماية.
1. Cross-Site Scripting (XSS) عبر dangerouslySetInnerHTML
واحدة من أكثر الثغرات الأمنية شيوعًا في تطبيقات React/TypeScript هي إدراج HTML غير مُنقّى عبر dangerouslySetInnerHTML.
// ❌ كود ضعيفfunction UserComment({ comment }: { comment: string }) { return <div dangerouslySetInnerHTML={{ __html: comment }} />;}
// ✅ خيار آمنimport DOMPurify from 'dompurify';
function SafeComment({ comment }: { comment: string }) { const sanitized = DOMPurify.sanitize(comment); return <div dangerouslySetInnerHTML={{ __html: sanitized }} />;}2. SQL-Injections عبر استعلامات غير مُنقّاة
استخدام السلاسل النصية القالبية في استعلامات SQL مع إدخال المستخدم هو طريق مباشر للحقن.
// ❌ كود ضعيفasync function getUser(id: string) { const query = `SELECT * FROM users WHERE id = '${id}'`; return db.execute(query);}
// ✅ خيار آمن (استخدام الاستعلامات المُعلمة)async function safeGetUser(id: string) { const query = 'SELECT * FROM users WHERE id = $1'; return db.execute(query, [id]);}3. Prototype Pollution عبر دمج الكائنات العميق
TypeScript يسمح بنسخ الكائنات بسهولة، لكنه لا يتحقق من سلسلة النماذج الأولية.
// ❌ كود ضعيفfunction merge(target: any, source: any) { for (const key in source) { if (typeof source[key] === 'object') { target[key] = merge(target[key] || {}, source[key]); } else { target[key] = source[key]; } } return target;}
// ✅ خيار آمنfunction safeMerge(target: Record<string, any>, source: Record<string, any>) { const result = { ...target }; for (const key of Object.keys(source)) { if (key === '__proto__' || key === 'constructor') continue; if (typeof source[key] === 'object' && !Array.isArray(source[key])) { result[key] = safeMerge(result[key] || {}, source[key]); } else { result[key] = source[key]; } } return result;}4. NoSQL-Injections (MongoDB)
عند العمل مع MongoDB، يمكن أن يؤدي تمرير كائن مباشرة من الطلب إلى حقن عوامل تشغيل $where أو $regex.
// ❌ كود ضعيفapp.post('/login', async (req, res) => { const { username, password } = req.body; const user = await User.findOne({ username, password }); // إذا تم تمرير { "$gt": "" }، يمكن تجاوز التحقق});
// ✅ خيار آمنimport { sanitize } from 'mongo-sanitize';
app.post('/login', async (req, res) => { const cleanBody = sanitize(req.body); const user = await User.findOne({ username: cleanBody.username, password: cleanBody.password });});5. أوامر نظام التشغيل عبر child_process
تنفيذ إدخال المستخدم عبر exec دون تنقية هو ثغرة أمنية حرجة.
// ❌ كود ضعيفimport { exec } from 'child_process';
function runCommand(input: string) { exec(`echo ${input}`, (err, stdout) => { console.log(stdout); });}
// ✅ خيار آمنimport { execFile } from 'child_process';
function safeCommand(input: string) { // نستخدم execFile مع مصفوفة من الوسائط execFile('echo', [input], (err, stdout) => { console.log(stdout); });}