أمان التطبيقات
مراجعة الكود الآمن
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}`);
}
});
الإجابة:
- حقن SQL: إدراج مباشر لـ
userId - XSS:
user.nameغير مهرب في استجابة HTML - كشف المعلومات: رسالة الخطأ الكاملة مكشوفة
- AuthZ مفقود: لا فحص إذا كان الطالب يمكنه الوصول لهذا المستخدم
نصيحة المقابلة: عند إعطائك كود للمراجعة، صِف عملية تفكيرك بصوت عالٍ. يريد المحاورون رؤية كيف تجد الثغرات، وليس فقط أنك تجدها.
في الدرس التالي، سنغطي منهجيات نمذجة التهديدات. :::