أمان التطبيقات

مراجعة الكود الآمن

4 دقيقة للقراءة

مراجعة الكود الآمن هي مهارة AppSec أساسية يتم اختبارها في معظم مقابلات الأمن. يغطي هذا الدرس الأدوات والتقنيات والثغرات التي تحتاج لتحديدها.

SAST مقابل DAST مقابل SCA

نوع الأداة ما تحلله متى تعمل أمثلة
SAST الكود المصدري وقت البناء Semgrep، CodeQL، SonarQube
DAST التطبيق قيد التشغيل وقت التشغيل Burp Suite، OWASP ZAP، Nuclei
SCA التبعيات وقت البناء Snyk، Dependabot، Trivy
IAST التشغيل + المُجهز وقت التشغيل Contrast Security

سؤال المقابلة

س: "ما هي قيود أدوات SAST؟"

الإجابة:

  • الإيجابيات الكاذبة: تعليم الكود الآمن كثغرة
  • لا سياق وقت التشغيل: لا يمكن اكتشاف مشاكل التكوين
  • خاصة باللغة: كل لغة تحتاج قواعد مختلفة
  • عمياء عن منطق العمل: لا تفهم السلوك المقصود

منهجية مراجعة الكود

نهج STRIDE لكل عنصر

عند مراجعة الكود، تحقق بشكل منهجي من:

1. معالجة المدخلات
   └── أين تدخل البيانات الخارجية؟
   └── هل يتم التحقق منها، تنظيفها، تهريبها؟

2. المصادقة
   └── كيف يتم التعامل مع بيانات الاعتماد؟
   └── هل تُدار الجلسات بأمان؟

3. التفويض
   └── هل يتم فرض ضوابط الوصول؟
   └── هل هناك فحص للأدوار/الصلاحيات؟

4. حماية البيانات
   └── هل البيانات الحساسة مشفرة؟
   └── هل الأسرار مكتوبة في الكود؟

5. معالجة الأخطاء
   └── هل الأخطاء تسرب معلومات؟
   └── هل يتم التعامل مع الفشل بأمان؟

اكتشاف الثغرات في الكود

حقن SQL

# ثغرة
def get_user(username):
    query = f"SELECT * FROM users WHERE username = '{username}'"
    return db.execute(query)

# آمن
def get_user(username):
    query = "SELECT * FROM users WHERE username = %s"
    return db.execute(query, (username,))

حقن الأوامر

# ثغرة
import os
def ping_host(host):
    os.system(f"ping -c 1 {host}")

# آمن
import subprocess
def ping_host(host):
    # التحقق من المدخلات
    if not re.match(r'^[\w.-]+$', host):
        raise ValueError("Invalid hostname")
    subprocess.run(["ping", "-c", "1", host], check=True)

اجتياز المسار

# ثغرة
def read_file(filename):
    with open(f"/uploads/{filename}") as f:
        return f.read()

# آمن
import os
def read_file(filename):
    base_dir = "/uploads"
    full_path = os.path.normpath(os.path.join(base_dir, filename))
    # ضمان بقاء المسار ضمن الدليل الأساسي
    if not full_path.startswith(base_dir):
        raise ValueError("Invalid path")
    with open(full_path) as f:
        return f.read()

إلغاء التسلسل غير الآمن

# ثغرة
import pickle
def load_session(data):
    return pickle.loads(base64.b64decode(data))

# آمن
import json
def load_session(data):
    return json.loads(base64.b64decode(data))

تمرين المقابلة: ابحث عن الأخطاء

// كم عدد الثغرات التي يمكنك اكتشافها؟
app.get('/user/:id', async (req, res) => {
    const userId = req.params.id;
    const query = `SELECT * FROM users WHERE id = ${userId}`;

    try {
        const user = await db.query(query);
        res.send(`<h1>Welcome ${user.name}</h1>`);
    } catch (err) {
        res.send(`Error: ${err.message}`);
    }
});

الإجابة:

  1. حقن SQL: إدراج مباشر لـ userId
  2. XSS: user.name غير مهرب في استجابة HTML
  3. كشف المعلومات: رسالة الخطأ الكاملة مكشوفة
  4. AuthZ مفقود: لا فحص إذا كان الطالب يمكنه الوصول لهذا المستخدم

نصيحة المقابلة: عند إعطائك كود للمراجعة، صِف عملية تفكيرك بصوت عالٍ. يريد المحاورون رؤية كيف تجد الثغرات، وليس فقط أنك تجدها.

في الدرس التالي، سنغطي منهجيات نمذجة التهديدات. :::

اختبار

الوحدة 3: أمان التطبيقات

خذ الاختبار