اختبار أمان التطبيقات الساكن (SAST) يحلل الكود المصدري أو الـ bytecode أو الثنائي دون تشغيل البرنامج. على عكس اختبار وقت التشغيل، يعمل SAST بسرعة المُصرِّف — يمكنه فحص كل pull request في ثوانٍ وإبلاغ النتائج قبل أن يصل أي سطر واحد إلى الإنتاج. على نطاق الشركات الكبرى، تُظهر بنية التحليل الساكن الداخلية لـ Google (Tricorder) ملايين النتائج أسبوعياً مباشرة في مراجعة الكود، معاملةً الأمان كخاصية من الدرجة الأولى للكود لا عائقاً في نهاية الإصدار.
يتناول هذا الدرس محركي SAST مفتوحي المصدر السائدين — CodeQL وSemgrep — ويشرح كيف يعملان تحت الغطاء، ويقدم دليلاً عملياً لدمج كليهما دون إغراق فريقك في ضجيج النتائج.
كيف تعمل محركات SAST
هناك نهجان مختلفان جوهرياً للتحليل الساكن، وفهم هذا الفرق يخبرك أي أداة تلجأ إليها:
التحليل الدلالي / تدفق البيانات (CodeQL). يُجمَّع الكود في قاعدة بيانات علائقية من الحقائق: كل استدعاء دالة وتعيين متغير وحافة تدفق بيانات ومسار تدفق تحكم مُخزَّن كعلاقة قابلة للاستعلام. يكتب مهندسو الأمان استعلامات تشبه SQL ضد هذه القاعدة للتعبير عن أنماط الثغرات — مثلاً، "ابحث عن كل المسارات حيث تتدفق البيانات من طلب HTTP إلى استعلام SQL دون المرور عبر مُعقِّم". يكتشف هذا الثغرات المعقدة متعددة الملفات التي تمتد عبر إطارات استدعاء كثيرة. التكلفة: بطيء (دقائق لكل فحص)، يتطلب بناءً كاملاً.
التحليل القائم على الأنماط / مطابقة AST (Semgrep). تكتب أنماطاً بلغة تشبه الكود الذي تبحث عنه، ويطابقها Semgrep ضد شجرة التحليل النحوي. نمط مثل $X.execute($QUERY) سيطابق أي استدعاء لأي طريقة اسمها execute بأي وسيطة اسمها QUERY. يكتشف هذا الأنماط المفهومة والمحلية بسرعة فائقة (ثوانٍ). التكلفة: قد يفوته الثغرات متعددة القفزات؛ معدل الإيجابيات الكاذبة يعتمد على جودة الأنماط.
في الممارسة العملية، تشغّل pipelines SAST الإنتاجية كليهما: Semgrep كبوابة سريعة على كل PR (النتائج تحجب الدمج)، وCodeQL كفحص ليلي أعمق تُغذّي نتائجه متأخرة أمنية بدلاً من حجب التطوير اليومي.
نظام SAST ثنائي المستوى: يعمل Semgrep على كل PR كبوابة سريعة؛ يعمل CodeQL ليلياً للتحليل العميق لتدفق البيانات. كلاهما يُبلّغ عبر SARIF إلى GitHub Advanced Security.
دمج Semgrep في GitHub Actions
مجموعة قواعد Semgrep المسماة p/owasp-top-ten تغطي أخطر عشرة مخاطر في أمان تطبيقات الويب وتُصانها فريق Semgrep. ابدأ من هنا؛ ثم أضف قواعد مخصصة تدريجياً. علامة --severity ERROR تعني أن النتائج ذات خطورة ERROR فقط هي التي تحجب الـ pipeline — نتائج WARNING وINFO تُبلَّغ لكنها لا تُفشل الوظيفة.
الفكرة الأساسية: تُحمِّل كلتا الوظيفتين النتائج بتنسيق SARIF (صيغة تبادل نتائج التحليل الساكن)، المعيار المفتوح لنتائج الأمان. يُحلِّل GitHub Advanced Security ملفات SARIF ويعرض النتائج مضمَّنة على pull requests وفي تبويب Security للمستودع. يمنح هذا مهندسي الأمان لوحة مراقبة موحدة عبر جميع الأدوات دون تكاملات مخصصة.
كتابة قاعدة Semgrep مخصصة
القوة الحقيقية لـ Semgrep تكمن في كتابة قواعد مصممة خصيصاً لقاعدة الكود الخاصة بك — أنماط لا تستطيع مجموعات القواعد العامة معرفتها. مثال شائع: فريقك يُغلِّف جميع استعلامات قاعدة البيانات في مساعد مخصص، لذا أي استدعاء مباشر لطريقة ORM الأساسية يتجاوز التحقق من المدخلات. يمكن لـ Semgrep اكتشاف ذلك بقاعدة واحدة.
# .semgrep/rules/no-raw-orm-calls.yml
# يكتشف الاستخدام المباشر لمُنشئ الاستعلامات الخام في ORM متجاوزاً
# الغلاف المعتمد db.safeQuery(). الخطورة ERROR تحجب الـ PR.
rules:
- id: no-raw-orm-query
languages: [typescript]
severity: ERROR
message: |
الاستعلام المباشر عبر ORM يتجاوز الغلاف المعتمد db.safeQuery().
استخدم db.safeQuery() الذي يتحقق من المدخلات ويُضمِّنها بشكل آمن.
metadata:
cwe: "CWE-89: SQL Injection"
owasp: "A03:2021 - Injection"
pattern-either:
- pattern: $ORM.query($QUERY, ...)
- pattern: $ORM.raw($QUERY, ...)
paths:
exclude:
- "**/*.test.ts" # ملفات الاختبار تستخدم الاستعلامات الخام بشكل مشروع
- "db/migrations/**" # الـ migrations تحتاج SQL خاماً
# شغّل هذه القاعدة فقط محلياً:
# semgrep --config .semgrep/rules/no-raw-orm-calls.yml --severity ERROR src/
إدارة النتائج دون الغرق فيها
أكبر نمط فشل لـ SAST في المؤسسات الحقيقية ليس أن الأدوات لا تجد شيئاً — بل أنها تجد الكثير جداً. فحص يُبلِّغ عن 8,000 نتيجة في اليوم الأول يُعلِّم المهندسين تجاهل الأداة بالكامل. المخرج هو استراتيجية "الفرز أولاً" التي تستخدمها شركات مثل Stripe وShopify:
ابدأ بمجموعة قواعد ضيقة عالية الدقة. استخدم p/owasp-top-ten بالإضافة إلى الحزمة الموصى بها للغتك. أوقف القواعد ذات معدل الإيجابيات الكاذبة المرتفع فوراً.
افحص النتائج الحالية وأسِّس خطاً أساسياً. شغّل الفحص الكامل على main مرة واحدة، وصدِّر SARIF، وأدرج جميع النتائج الحالية كـ مرفوضة مع مسوّغ في GitHub Security. الآن فقط النتائج الجديدة على كود جديد ستحجب الـ PRs. هذا أمر بالغ الأهمية — معاقبة المهندسين على ديون موجودة مسبقاً لم يُدخلوها تدمر تبني الأداة.
أخمد النتائج عمداً بتعليقات مضمَّنة، لا بتعطيل شامل. عندما تكون نتيجة ما إيجابية كاذبة مؤكدة، أخمد السطر المحدد مع مسوّغ. لا تعطّل فئة بأكملها بشكل عام أبداً.
تتبع ديون الإخماد. احسب عمليات الإخماد لكل مجموعة قواعد في لوحة الأمان الخاصة بك. نمو الإخماد دون إصلاحات مقابلة هو مؤشر رائد لتدهور وضع الأمان.
# إخماد نتيجة Semgrep محددة مع تعليق مسوّغ.
# يجب أن يكون التعليق على السطر مباشرة قبل السطر المُبلَّغ عنه.
# nosemgrep: no-raw-orm-query -- هذا ترحيل مخطط؛ SQL الخام مطلوب هنا.
await orm.raw('ALTER TABLE users ADD COLUMN last_login TIMESTAMP');
# لـ CodeQL، استخدم تعليق CODEQL_SUPPRESS:
# lgtm[js/sql-injection] -- المدخل مُعقَّم بواسطة validateInput() بثلاث استدعاءات قبل هذا.
# GitHub Advanced Security: ارفض نتيجة عبر API مع سبب
gh api -X PATCH \
/repos/ORG/REPO/code-scanning/alerts/42 \
-f dismissed_reason="false positive" \
-f dismissed_comment="المدخل مُتحقَّق منه بواسطة AuthMiddleware قبل هذه النقطة."
ممارسة احترافية: عامل معدل الإيجابيات الكاذبة لـ SAST كـ SLO. في Google، السياسة هي أن أي أداة تحليل بمعدل إيجابيات كاذبة يتجاوز 10% تُعطَّل حتى تُصلَح القاعدة. أداة تصرخ ذئباً تُعلِّم المهندسين تجاهل جميع التنبيهات — بما فيها الحقيقية. تتبع نسبة النتائج المرفوضة/المخمدة إلى النتائج المُتَّخذ إجراء بشأنها لكل قاعدة، واقطع القواعد عالية الضجيج من مجموعة بوابات الحجب بشكل عدواني.
مصيدة إنتاجية: خطأ شائع هو تشغيل SAST فقط على الملفات المتغيرة في diff الـ PR بدلاً من قاعدة الكود كاملة. كثير من الثغرات الأمنية تمتد عبر ملفات متعددة — مصدر محدد في ملف A يتدفق عبر ملف B ويُفرغ في ملف C. الفحص على الـ diff فقط يفوته هذه التدفقات عبر الملفات بالكامل. يفحص CodeQL دائماً قاعدة الكود الكاملة؛ بالنسبة لـ Semgrep، تأكد من تشغيل semgrep ci (الذي يستخدم سياق المستودع الكامل) بدلاً من semgrep scan --diff-filter=A,M وحده.
استعلامات CodeQL المخصصة
عندما لا تغطي حزم استعلامات CodeQL المدمجة نمط ثغرة خاصاً بمكدسك، يمكنك كتابة استعلام مخصص بلغة استعلام CodeQL (QL). هذه إمكانية متقدمة تستخدمها فرق الأمان في GitHub وMicrosoft والمؤسسات المالية الكبرى لفرض ثوابت خاصة بالمؤسسة.
// custom-queries/js/express-path-traversal.ql
// يجد معالجات مسار Express حيث تتدفق قيم req.params أو req.query
// إلى fs.readFile / fs.readFileSync دون تعقيم المسار.
/**
* @name Path traversal from Express route parameter
* @description Unsanitized route parameter used in file system access.
* @kind path-problem
* @problem.severity error
* @id custom/express-path-traversal
* @tags security
* cwe-022
*/
import javascript
import DataFlow::PathGraph
class ExpressRouteParam extends DataFlow::SourceNode {
ExpressRouteParam() {
exists(DataFlow::PropRead pr |
pr.getBase().asExpr().(PropAccess).getPropertyName() = ["params","query","body"]
and this = pr
)
}
}
class FsReadSink extends DataFlow::SinkNode {
FsReadSink() {
exists(DataFlow::CallNode call |
call.getCalleeName() = ["readFile","readFileSync","createReadStream"]
and this = call.getArgument(0)
)
}
}
from DataFlow::PathNode source, DataFlow::PathNode sink
where DataFlow::localFlowPath(source.getNode(), sink.getNode())
and source.getNode() instanceof ExpressRouteParam
and sink.getNode() instanceof FsReadSink
select sink.getNode(), source, sink, "Path traversal: route parameter flows into file read."
الفكرة الأساسية: SAST انضباط إزاحة نحو اليسار — قيمته تتضاعف عند دمجه مبكراً في دورة حياة التطوير، لا عند إضافته في نهاية الإصدار. كل نتيجة تُكشف في PR تكلف عشر دقائق للإصلاح؛ نفس النتيجة تُكشف بعد النشر تكلف ساعات بالإضافة إلى عبء الحوادث. الهدف ليس صفر نتائج في مخرجات الفحص — بل صفر نتائج غير معالجة بخطورة ERROR أو CRITICAL في كود يُشحن إلى الإنتاج.
تصنيف الخطورة واتفاقيات مستوى الخدمة
ليست جميع النتائج متساوية. تحدد برامج SAST الإنتاجية اتفاقية مستوى خدمة (SLA) حسب الخطورة:
CRITICAL (تنفيذ كود عن بُعد، تجاوز المصادقة، كشف بيانات جماعي) — احجب النشر، أصلح خلال 24 ساعة.
HIGH (حقن SQL، اجتياز المسار، SSRF) — احجب دمج الـ PR، أصلح خلال 72 ساعة.
MEDIUM (XSS، إلغاء التسلسل غير الآمن) — لا تحجب الدمج، أصلح خلال 14 يوماً، تتبَّع في متأخرة الأمان.
LOW / INFO — إرشادي فقط، أصلح وقت الفراغ أو أخمد مع مسوّغ.
رمِّز هذه الـ SLAs في سير عمل الفرز: تصنيفات GitHub Issues أو مكونات أمان Jira أو تذاكر SIEM — أيّاً كان النظام الذي يراجعه فريق الأمان فعلاً. SLA بدون آلية تتبع طموحي لا تشغيلي.
نستخدم ملفات تعريف الارتباط لتشغيل هذا الموقع وتحليل الزيارات وعرض إعلانات مخصّصة. يمكنك قبول كل ملفات تعريف الارتباط أو رفض غير الأساسية منها.
سياسة الخصوصية