add the main file structure requirements
هذا الالتزام موجود في:
156
q2-attack-simulation /brute-force-alert.yaml
Normal file
156
q2-attack-simulation /brute-force-alert.yaml
Normal file
@@ -0,0 +1,156 @@
|
||||
# Alert Rules لاكتشاف هجمات Brute Force
|
||||
# للتطبيقات المنشورة على غيمة (ghaymah.systems)
|
||||
|
||||
# ═══════════════════════════════════════════════════════════════
|
||||
# Prometheus Alert Rules
|
||||
# ═══════════════════════════════════════════════════════════════
|
||||
groups:
|
||||
- name: ghaymah_brute_force_detection
|
||||
interval: 30s
|
||||
rules:
|
||||
# ─────────────────────────────────────────────────────────
|
||||
# 1. اكتشاف محاولات تسجيل دخول فاشلة متكررة
|
||||
# ─────────────────────────────────────────────────────────
|
||||
- alert: BruteForceDetected
|
||||
expr: |
|
||||
sum(rate(http_requests_total{
|
||||
path="/api/login",
|
||||
status=~"401|403"
|
||||
}[5m])) by (source_ip) > 10
|
||||
for: 2m
|
||||
labels:
|
||||
severity: critical
|
||||
team: security
|
||||
platform: ghaymah
|
||||
annotations:
|
||||
summary: "🚨 هجوم Brute Force محتمل"
|
||||
description: |
|
||||
IP {{ $labels.source_ip }} أرسل أكثر من 10 محاولات فاشلة
|
||||
في آخر 5 دقائق على /api/login
|
||||
value: "{{ $value }} محاولة/دقيقة"
|
||||
runbook_url: "https://wiki.internal/runbooks/brute-force"
|
||||
action: |
|
||||
1. حظر IP فوراً
|
||||
2. مراجعة الـ logs
|
||||
3. التحقق من الحسابات المستهدفة
|
||||
|
||||
# ─────────────────────────────────────────────────────────
|
||||
# 2. اكتشاف نجاح تسجيل دخول بعد محاولات فاشلة
|
||||
# ─────────────────────────────────────────────────────────
|
||||
- alert: SuspiciousLoginAfterFailures
|
||||
expr: |
|
||||
(
|
||||
sum(increase(http_requests_total{
|
||||
path="/api/login",
|
||||
status="200"
|
||||
}[1m])) by (source_ip) > 0
|
||||
)
|
||||
and
|
||||
(
|
||||
sum(increase(http_requests_total{
|
||||
path="/api/login",
|
||||
status="401"
|
||||
}[10m])) by (source_ip) > 5
|
||||
)
|
||||
for: 0m
|
||||
labels:
|
||||
severity: critical
|
||||
team: security
|
||||
annotations:
|
||||
summary: "⚠️ تسجيل دخول مشبوه بعد محاولات فاشلة"
|
||||
description: |
|
||||
IP {{ $labels.source_ip }} نجح في تسجيل الدخول
|
||||
بعد عدة محاولات فاشلة - قد يكون هجوم ناجح!
|
||||
action: |
|
||||
1. مراجعة الحساب المستخدم فوراً
|
||||
2. التحقق من النشاط بعد الدخول
|
||||
3. النظر في إلغاء الـ session
|
||||
|
||||
# ─────────────────────────────────────────────────────────
|
||||
# 3. اكتشاف طلبات كثيرة من IP واحد
|
||||
# ─────────────────────────────────────────────────────────
|
||||
- alert: HighRequestRateFromSingleIP
|
||||
expr: |
|
||||
sum(rate(http_requests_total[1m])) by (source_ip) > 100
|
||||
for: 1m
|
||||
labels:
|
||||
severity: warning
|
||||
team: security
|
||||
annotations:
|
||||
summary: "📊 معدل طلبات عالي من IP واحد"
|
||||
description: |
|
||||
IP {{ $labels.source_ip }} يرسل {{ $value }} طلب/دقيقة
|
||||
قد يكون هجوم DDoS أو scraping
|
||||
action: "مراقبة وحظر إذا استمر"
|
||||
|
||||
# ─────────────────────────────────────────────────────────
|
||||
# 4. اكتشاف محاولات الوصول لـ endpoints حساسة
|
||||
# ─────────────────────────────────────────────────────────
|
||||
- alert: SensitiveEndpointAccess
|
||||
expr: |
|
||||
sum(rate(http_requests_total{
|
||||
path=~"/api/admin.*|/api/users/export|/api/config"
|
||||
}[5m])) by (source_ip, path) > 5
|
||||
for: 1m
|
||||
labels:
|
||||
severity: warning
|
||||
team: security
|
||||
annotations:
|
||||
summary: "🔐 محاولات وصول لـ endpoints حساسة"
|
||||
description: |
|
||||
IP {{ $labels.source_ip }} يحاول الوصول لـ {{ $labels.path }}
|
||||
action: "التحقق من صلاحيات المستخدم"
|
||||
|
||||
# ─────────────────────────────────────────────────────────
|
||||
# 5. اكتشاف User-Agent مشبوه
|
||||
# ─────────────────────────────────────────────────────────
|
||||
- alert: SuspiciousUserAgent
|
||||
expr: |
|
||||
sum(rate(http_requests_total{
|
||||
user_agent=~".*curl.*|.*python.*|.*wget.*|.*nikto.*|.*sqlmap.*"
|
||||
}[5m])) by (source_ip, user_agent) > 10
|
||||
for: 2m
|
||||
labels:
|
||||
severity: warning
|
||||
team: security
|
||||
annotations:
|
||||
summary: "🤖 User-Agent مشبوه"
|
||||
description: |
|
||||
IP {{ $labels.source_ip }} يستخدم {{ $labels.user_agent }}
|
||||
قد يكون أداة هجوم آلية
|
||||
|
||||
---
|
||||
# ═══════════════════════════════════════════════════════════════
|
||||
# Alertmanager Configuration
|
||||
# ═══════════════════════════════════════════════════════════════
|
||||
# alertmanager.yml
|
||||
global:
|
||||
resolve_timeout: 5m
|
||||
|
||||
route:
|
||||
group_by: ['alertname', 'severity']
|
||||
group_wait: 10s
|
||||
group_interval: 10s
|
||||
repeat_interval: 1h
|
||||
receiver: 'security-team'
|
||||
routes:
|
||||
- match:
|
||||
severity: critical
|
||||
receiver: 'security-critical'
|
||||
continue: true
|
||||
|
||||
receivers:
|
||||
- name: 'security-team'
|
||||
email_configs:
|
||||
- to: 'security@company.com'
|
||||
subject: '[Ghaymah Alert] {{ .GroupLabels.alertname }}'
|
||||
|
||||
- name: 'security-critical'
|
||||
email_configs:
|
||||
- to: 'security-oncall@company.com'
|
||||
webhook_configs:
|
||||
- url: 'https://hooks.slack.com/services/xxx/yyy/zzz'
|
||||
send_resolved: true
|
||||
# إرسال SMS للحالات الحرجة
|
||||
# pagerduty_configs:
|
||||
# - service_key: 'your-pagerduty-key'
|
||||
209
q2-attack-simulation /incident-response-plan.md
Normal file
209
q2-attack-simulation /incident-response-plan.md
Normal file
@@ -0,0 +1,209 @@
|
||||
# خطة الاستجابة للحوادث - Incident Response Plan
|
||||
|
||||
## نوع الحادثة
|
||||
هجوم Brute Force ناجح على API Login أدى إلى تسريب بيانات
|
||||
|
||||
---
|
||||
|
||||
## المرحلة 1: الاحتواء (Containment) - أول 30 دقيقة
|
||||
|
||||
### الإجراءات الفورية:
|
||||
|
||||
```bash
|
||||
# 1. إلغاء جميع الـ JWT tokens النشطة
|
||||
# في قاعدة البيانات أو Redis
|
||||
redis-cli FLUSHDB # إذا كانت sessions في Redis
|
||||
|
||||
# أو عبر API إداري
|
||||
curl -X POST https://app.ghaymah.systems/api/admin/invalidate-all-tokens \
|
||||
-H "Authorization: Bearer $ADMIN_TOKEN"
|
||||
|
||||
# 2. حظر IP المهاجم
|
||||
# في iptables
|
||||
sudo iptables -A INPUT -s 192.168.1.100 -j DROP
|
||||
|
||||
# أو في nginx
|
||||
echo "deny 192.168.1.100;" >> /etc/nginx/conf.d/blocked-ips.conf
|
||||
sudo nginx -s reload
|
||||
|
||||
# 3. تعطيل الحساب المخترق
|
||||
UPDATE users SET status = 'suspended',
|
||||
password_hash = 'COMPROMISED_RESET_REQUIRED'
|
||||
WHERE username = 'admin';
|
||||
```
|
||||
|
||||
### قائمة التحقق:
|
||||
- [ ] إلغاء جميع الـ tokens
|
||||
- [ ] حظر IP المهاجم
|
||||
- [ ] تعطيل الحساب المخترق
|
||||
- [ ] إبلاغ فريق الأمن
|
||||
- [ ] توثيق وقت الاكتشاف
|
||||
|
||||
---
|
||||
|
||||
## المرحلة 2: التحقيق (Investigation) - 1-4 ساعات
|
||||
|
||||
### جمع الأدلة:
|
||||
|
||||
```bash
|
||||
# 1. حفظ الـ logs
|
||||
cp /var/log/nginx/access.log /incident/access.log.$(date +%Y%m%d)
|
||||
cp /var/log/nginx/error.log /incident/error.log.$(date +%Y%m%d)
|
||||
|
||||
# 2. تحليل الطلبات المشبوهة
|
||||
grep "POST /api/login" /var/log/nginx/access.log | \
|
||||
awk '{print $1}' | sort | uniq -c | sort -rn | head -20
|
||||
|
||||
# 3. تتبع نشاط المهاجم بعد الدخول
|
||||
grep "192.168.1.100" /var/log/nginx/access.log | \
|
||||
grep -E "200|201" > /incident/attacker-activity.log
|
||||
|
||||
# 4. تحديد البيانات المسربة
|
||||
grep "192.168.1.100" /var/log/nginx/access.log | \
|
||||
grep -E "/api/users|/api/export"
|
||||
```
|
||||
|
||||
### الأسئلة المطلوب الإجابة عليها:
|
||||
1. متى بدأ الهجوم؟
|
||||
2. كم محاولة تمت قبل النجاح؟
|
||||
3. ما البيانات التي تم الوصول إليها؟
|
||||
4. هل يوجد backdoors؟
|
||||
5. هل تأثرت أنظمة أخرى؟
|
||||
|
||||
---
|
||||
|
||||
## المرحلة 3: الاستئصال (Eradication) - 4-8 ساعات
|
||||
|
||||
### الإجراءات:
|
||||
|
||||
```bash
|
||||
# 1. إعادة تعيين كلمات مرور جميع المستخدمين
|
||||
# إرسال بريد إلكتروني لجميع المستخدمين
|
||||
|
||||
# 2. فحص النظام من backdoors
|
||||
# فحص cron jobs
|
||||
crontab -l
|
||||
cat /etc/crontab
|
||||
|
||||
# فحص authorized_keys
|
||||
cat ~/.ssh/authorized_keys
|
||||
|
||||
# فحص الملفات المعدلة مؤخراً
|
||||
find /app -mtime -1 -type f
|
||||
|
||||
# 3. تحديث التبعيات
|
||||
npm audit fix
|
||||
# أو
|
||||
pip install --upgrade -r requirements.txt
|
||||
```
|
||||
|
||||
### قائمة التحقق:
|
||||
- [ ] إعادة تعيين كلمات المرور
|
||||
- [ ] فحص backdoors
|
||||
- [ ] مراجعة صلاحيات المستخدمين
|
||||
- [ ] تحديث التبعيات الأمنية
|
||||
|
||||
---
|
||||
|
||||
## المرحلة 4: الاستعادة (Recovery) - 8-24 ساعة
|
||||
|
||||
### خطوات الاستعادة:
|
||||
|
||||
1. **تفعيل الحماية الجديدة:**
|
||||
```javascript
|
||||
// إضافة Rate Limiting
|
||||
const rateLimit = require('express-rate-limit');
|
||||
app.use('/api/login', rateLimit({
|
||||
windowMs: 15 * 60 * 1000,
|
||||
max: 5,
|
||||
message: 'محاولات كثيرة، حاول لاحقاً'
|
||||
}));
|
||||
```
|
||||
|
||||
2. **تفعيل Account Lockout:**
|
||||
```javascript
|
||||
// قفل الحساب بعد 5 محاولات فاشلة
|
||||
if (failedAttempts >= 5) {
|
||||
await lockAccount(userId, 30 * 60 * 1000); // 30 دقيقة
|
||||
}
|
||||
```
|
||||
|
||||
3. **إعادة تشغيل الخدمات:**
|
||||
```bash
|
||||
# إعادة نشر التطبيق على غيمة
|
||||
git push origin main # النشر التلقائي
|
||||
```
|
||||
|
||||
### قائمة التحقق:
|
||||
- [ ] تفعيل Rate Limiting
|
||||
- [ ] تفعيل Account Lockout
|
||||
- [ ] إضافة CAPTCHA
|
||||
- [ ] تفعيل MFA للحسابات الإدارية
|
||||
- [ ] إعادة تشغيل الخدمات
|
||||
- [ ] اختبار الحماية الجديدة
|
||||
|
||||
---
|
||||
|
||||
## المرحلة 5: إبلاغ المتأثرين - 24-48 ساعة
|
||||
|
||||
### نموذج البريد الإلكتروني:
|
||||
|
||||
```
|
||||
الموضوع: إشعار أمني مهم - [اسم التطبيق]
|
||||
|
||||
عزيزي المستخدم،
|
||||
|
||||
نود إبلاغك بحادثة أمنية وقعت بتاريخ [التاريخ].
|
||||
تم الوصول غير المصرح به إلى بعض بيانات المستخدمين.
|
||||
|
||||
البيانات المتأثرة:
|
||||
- الاسم
|
||||
- البريد الإلكتروني
|
||||
- [أي بيانات أخرى]
|
||||
|
||||
الإجراءات المتخذة:
|
||||
1. تم إيقاف الهجوم فوراً
|
||||
2. تم تعزيز الحماية
|
||||
3. تم إعادة تعيين جميع كلمات المرور
|
||||
|
||||
ما يجب عليك فعله:
|
||||
1. تغيير كلمة المرور فوراً
|
||||
2. تفعيل المصادقة الثنائية
|
||||
3. مراقبة حساباتك الأخرى
|
||||
|
||||
نعتذر عن أي إزعاج.
|
||||
|
||||
فريق الأمن
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## المرحلة 6: الدروس المستفادة - أسبوع
|
||||
|
||||
### اجتماع Post-Mortem:
|
||||
|
||||
| السؤال | الإجابة |
|
||||
|--------|---------|
|
||||
| ما الذي حدث؟ | هجوم brute force ناجح |
|
||||
| لماذا حدث؟ | عدم وجود rate limiting + كلمة مرور ضعيفة |
|
||||
| كيف اكتشفناه؟ | مراجعة يدوية للـ logs |
|
||||
| كم استغرق الاحتواء؟ | [X] دقيقة |
|
||||
| ما الذي يجب تحسينه؟ | إضافة تنبيهات تلقائية |
|
||||
|
||||
### التحسينات المطلوبة:
|
||||
1. [ ] تفعيل Rate Limiting على جميع APIs
|
||||
2. [ ] فرض سياسة كلمات مرور قوية
|
||||
3. [ ] تفعيل MFA إلزامياً للإداريين
|
||||
4. [ ] إضافة تنبيهات للنشاط المشبوه
|
||||
5. [ ] تدريب الفريق على الاستجابة للحوادث
|
||||
|
||||
---
|
||||
|
||||
## جهات الاتصال
|
||||
|
||||
| الدور | الاسم | الهاتف | البريد |
|
||||
|-------|-------|--------|--------|
|
||||
| قائد الحادثة | [الاسم] | [الرقم] | [البريد] |
|
||||
| فريق DevOps | [الاسم] | [الرقم] | [البريد] |
|
||||
| المستشار القانوني | [الاسم] | [الرقم] | [البريد] |
|
||||
| دعم غيمة | - | - | support@ghaymah.systems |
|
||||
206
q2-attack-simulation /prevention-policies.yaml
Normal file
206
q2-attack-simulation /prevention-policies.yaml
Normal file
@@ -0,0 +1,206 @@
|
||||
# Network Policies & Container Security لمنع هجمات Brute Force
|
||||
# للتطبيقات المنشورة على غيمة (ghaymah.systems)
|
||||
|
||||
---
|
||||
# ═══════════════════════════════════════════════════════════════
|
||||
# 1. Network Policy - عزل قاعدة البيانات
|
||||
# ═══════════════════════════════════════════════════════════════
|
||||
apiVersion: networking.k8s.io/v1
|
||||
kind: NetworkPolicy
|
||||
metadata:
|
||||
name: database-isolation
|
||||
namespace: ghaymah-app
|
||||
labels:
|
||||
app: security-policy
|
||||
spec:
|
||||
podSelector:
|
||||
matchLabels:
|
||||
app: postgresql
|
||||
policyTypes:
|
||||
- Ingress
|
||||
- Egress
|
||||
ingress:
|
||||
# السماح فقط للـ backend بالوصول لقاعدة البيانات
|
||||
- from:
|
||||
- podSelector:
|
||||
matchLabels:
|
||||
app: backend-api
|
||||
ports:
|
||||
- protocol: TCP
|
||||
port: 5432
|
||||
egress:
|
||||
# منع قاعدة البيانات من الاتصال بالخارج
|
||||
- to:
|
||||
- podSelector:
|
||||
matchLabels:
|
||||
app: backend-api
|
||||
ports:
|
||||
- protocol: TCP
|
||||
port: 5432
|
||||
|
||||
---
|
||||
# ═══════════════════════════════════════════════════════════════
|
||||
# 2. Network Policy - حماية API Login
|
||||
# ═══════════════════════════════════════════════════════════════
|
||||
apiVersion: networking.k8s.io/v1
|
||||
kind: NetworkPolicy
|
||||
metadata:
|
||||
name: api-login-protection
|
||||
namespace: ghaymah-app
|
||||
spec:
|
||||
podSelector:
|
||||
matchLabels:
|
||||
app: login-api
|
||||
policyTypes:
|
||||
- Ingress
|
||||
ingress:
|
||||
# السماح فقط من خلال API Gateway/Ingress
|
||||
- from:
|
||||
- namespaceSelector:
|
||||
matchLabels:
|
||||
name: ingress-nginx
|
||||
- podSelector:
|
||||
matchLabels:
|
||||
app: api-gateway
|
||||
ports:
|
||||
- protocol: TCP
|
||||
port: 8080
|
||||
|
||||
---
|
||||
# ═══════════════════════════════════════════════════════════════
|
||||
# 3. Network Policy - عزل Redis
|
||||
# ═══════════════════════════════════════════════════════════════
|
||||
apiVersion: networking.k8s.io/v1
|
||||
kind: NetworkPolicy
|
||||
metadata:
|
||||
name: redis-isolation
|
||||
namespace: ghaymah-app
|
||||
spec:
|
||||
podSelector:
|
||||
matchLabels:
|
||||
app: redis
|
||||
policyTypes:
|
||||
- Ingress
|
||||
ingress:
|
||||
- from:
|
||||
- podSelector:
|
||||
matchLabels:
|
||||
app: backend-api
|
||||
ports:
|
||||
- protocol: TCP
|
||||
port: 6379
|
||||
|
||||
---
|
||||
# ═══════════════════════════════════════════════════════════════
|
||||
# 4. Pod Security Policy - منع تشغيل root
|
||||
# ═══════════════════════════════════════════════════════════════
|
||||
apiVersion: policy/v1beta1
|
||||
kind: PodSecurityPolicy
|
||||
metadata:
|
||||
name: restricted-psp
|
||||
spec:
|
||||
privileged: false
|
||||
runAsUser:
|
||||
rule: MustRunAsNonRoot
|
||||
runAsGroup:
|
||||
rule: MustRunAs
|
||||
ranges:
|
||||
- min: 1000
|
||||
max: 65535
|
||||
fsGroup:
|
||||
rule: MustRunAs
|
||||
ranges:
|
||||
- min: 1000
|
||||
max: 65535
|
||||
seLinux:
|
||||
rule: RunAsAny
|
||||
volumes:
|
||||
- 'configMap'
|
||||
- 'emptyDir'
|
||||
- 'secret'
|
||||
- 'persistentVolumeClaim'
|
||||
readOnlyRootFilesystem: true
|
||||
allowPrivilegeEscalation: false
|
||||
requiredDropCapabilities:
|
||||
- ALL
|
||||
|
||||
---
|
||||
# ═══════════════════════════════════════════════════════════════
|
||||
# 5. Security Context للـ Deployment
|
||||
# ═══════════════════════════════════════════════════════════════
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: secure-api
|
||||
namespace: ghaymah-app
|
||||
spec:
|
||||
replicas: 2
|
||||
selector:
|
||||
matchLabels:
|
||||
app: backend-api
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
app: backend-api
|
||||
spec:
|
||||
securityContext:
|
||||
runAsNonRoot: true
|
||||
runAsUser: 1000
|
||||
runAsGroup: 1000
|
||||
fsGroup: 1000
|
||||
containers:
|
||||
- name: api
|
||||
image: myapp:latest
|
||||
securityContext:
|
||||
allowPrivilegeEscalation: false
|
||||
readOnlyRootFilesystem: true
|
||||
capabilities:
|
||||
drop:
|
||||
- ALL
|
||||
ports:
|
||||
- containerPort: 8080
|
||||
resources:
|
||||
limits:
|
||||
cpu: "500m"
|
||||
memory: "512Mi"
|
||||
requests:
|
||||
cpu: "100m"
|
||||
memory: "128Mi"
|
||||
volumeMounts:
|
||||
- name: tmp
|
||||
mountPath: /tmp
|
||||
volumes:
|
||||
- name: tmp
|
||||
emptyDir: {}
|
||||
|
||||
---
|
||||
# ═══════════════════════════════════════════════════════════════
|
||||
# 6. Rate Limiting في Ingress (nginx)
|
||||
# ═══════════════════════════════════════════════════════════════
|
||||
apiVersion: networking.k8s.io/v1
|
||||
kind: Ingress
|
||||
metadata:
|
||||
name: api-ingress
|
||||
namespace: ghaymah-app
|
||||
annotations:
|
||||
# Rate limiting
|
||||
nginx.ingress.kubernetes.io/limit-rps: "10"
|
||||
nginx.ingress.kubernetes.io/limit-connections: "5"
|
||||
# حماية من الهجمات
|
||||
nginx.ingress.kubernetes.io/server-snippet: |
|
||||
# حظر User-Agents المشبوهة
|
||||
if ($http_user_agent ~* (curl|wget|python|nikto|sqlmap)) {
|
||||
return 403;
|
||||
}
|
||||
spec:
|
||||
rules:
|
||||
- host: app.ghaymah.systems
|
||||
http:
|
||||
paths:
|
||||
- path: /api
|
||||
pathType: Prefix
|
||||
backend:
|
||||
service:
|
||||
name: backend-api
|
||||
port:
|
||||
number: 8080
|
||||
224
q2-attack-simulation /rate-limiter.js
Normal file
224
q2-attack-simulation /rate-limiter.js
Normal file
@@ -0,0 +1,224 @@
|
||||
/**
|
||||
* Rate Limiter Middleware للحماية من Brute Force
|
||||
* للتطبيقات المنشورة على غيمة (ghaymah.systems)
|
||||
*/
|
||||
|
||||
const rateLimit = require('express-rate-limit');
|
||||
const RedisStore = require('rate-limit-redis');
|
||||
const Redis = require('ioredis');
|
||||
|
||||
// ═══════════════════════════════════════════════════════════════
|
||||
// إعداد Redis (من غيمة)
|
||||
// ═══════════════════════════════════════════════════════════════
|
||||
const redis = new Redis(process.env.GHAYMAH_REDIS_URL || 'redis://localhost:6379');
|
||||
|
||||
// ═══════════════════════════════════════════════════════════════
|
||||
// Rate Limiter لـ Login API
|
||||
// ═══════════════════════════════════════════════════════════════
|
||||
const loginLimiter = rateLimit({
|
||||
store: new RedisStore({
|
||||
sendCommand: (...args) => redis.call(...args),
|
||||
prefix: 'rl:login:'
|
||||
}),
|
||||
|
||||
// 5 محاولات كل 15 دقيقة
|
||||
windowMs: 15 * 60 * 1000,
|
||||
max: 5,
|
||||
|
||||
// رسالة الخطأ
|
||||
message: {
|
||||
error: 'محاولات كثيرة جداً',
|
||||
message: 'تم حظرك مؤقتاً. حاول مرة أخرى بعد 15 دقيقة',
|
||||
retryAfter: 15 * 60
|
||||
},
|
||||
|
||||
// Headers قياسية
|
||||
standardHeaders: true,
|
||||
legacyHeaders: false,
|
||||
|
||||
// تخطي الطلبات الناجحة
|
||||
skipSuccessfulRequests: false,
|
||||
|
||||
// معالج مخصص عند تجاوز الحد
|
||||
handler: (req, res, next, options) => {
|
||||
// تسجيل للمراقبة
|
||||
console.warn(`[SECURITY] Rate limit exceeded`, {
|
||||
ip: req.ip,
|
||||
path: req.path,
|
||||
userAgent: req.get('User-Agent'),
|
||||
timestamp: new Date().toISOString()
|
||||
});
|
||||
|
||||
// إرسال metric لـ Prometheus
|
||||
if (global.metrics) {
|
||||
global.metrics.rateLimitExceeded.inc({
|
||||
path: '/api/login',
|
||||
ip: req.ip
|
||||
});
|
||||
}
|
||||
|
||||
res.status(429).json(options.message);
|
||||
},
|
||||
|
||||
// تحديد المفتاح (IP + username إن وجد)
|
||||
keyGenerator: (req) => {
|
||||
const username = req.body?.username || 'unknown';
|
||||
return `${req.ip}:${username}`;
|
||||
}
|
||||
});
|
||||
|
||||
// ═══════════════════════════════════════════════════════════════
|
||||
// Rate Limiter عام للـ API
|
||||
// ═══════════════════════════════════════════════════════════════
|
||||
const apiLimiter = rateLimit({
|
||||
store: new RedisStore({
|
||||
sendCommand: (...args) => redis.call(...args),
|
||||
prefix: 'rl:api:'
|
||||
}),
|
||||
|
||||
// 100 طلب كل دقيقة
|
||||
windowMs: 60 * 1000,
|
||||
max: 100,
|
||||
|
||||
message: {
|
||||
error: 'طلبات كثيرة',
|
||||
message: 'تجاوزت الحد المسموح. حاول لاحقاً'
|
||||
},
|
||||
|
||||
standardHeaders: true
|
||||
});
|
||||
|
||||
// ═══════════════════════════════════════════════════════════════
|
||||
// Rate Limiter للـ endpoints الحساسة
|
||||
// ═══════════════════════════════════════════════════════════════
|
||||
const sensitiveEndpointLimiter = rateLimit({
|
||||
store: new RedisStore({
|
||||
sendCommand: (...args) => redis.call(...args),
|
||||
prefix: 'rl:sensitive:'
|
||||
}),
|
||||
|
||||
// 10 طلبات كل 5 دقائق
|
||||
windowMs: 5 * 60 * 1000,
|
||||
max: 10,
|
||||
|
||||
message: {
|
||||
error: 'وصول محدود',
|
||||
message: 'هذا الـ endpoint محمي. حاول لاحقاً'
|
||||
}
|
||||
});
|
||||
|
||||
// ═══════════════════════════════════════════════════════════════
|
||||
// Account Lockout بعد محاولات فاشلة
|
||||
// ═══════════════════════════════════════════════════════════════
|
||||
const failedAttempts = new Map(); // في الإنتاج استخدم Redis
|
||||
|
||||
const accountLockout = async (req, res, next) => {
|
||||
const username = req.body?.username;
|
||||
if (!username) return next();
|
||||
|
||||
const key = `lockout:${username}`;
|
||||
const lockoutData = await redis.get(key);
|
||||
|
||||
if (lockoutData) {
|
||||
const data = JSON.parse(lockoutData);
|
||||
|
||||
// التحقق من القفل
|
||||
if (data.locked && Date.now() < data.lockedUntil) {
|
||||
const remainingMinutes = Math.ceil((data.lockedUntil - Date.now()) / 60000);
|
||||
return res.status(423).json({
|
||||
error: 'الحساب مقفل',
|
||||
message: `تم قفل الحساب بسبب محاولات فاشلة متكررة. حاول بعد ${remainingMinutes} دقيقة`,
|
||||
lockedUntil: new Date(data.lockedUntil).toISOString()
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
next();
|
||||
};
|
||||
|
||||
// تسجيل المحاولة الفاشلة
|
||||
const recordFailedAttempt = async (username) => {
|
||||
const key = `lockout:${username}`;
|
||||
let data = await redis.get(key);
|
||||
|
||||
if (data) {
|
||||
data = JSON.parse(data);
|
||||
data.attempts += 1;
|
||||
} else {
|
||||
data = { attempts: 1, locked: false };
|
||||
}
|
||||
|
||||
// قفل بعد 5 محاولات
|
||||
if (data.attempts >= 5) {
|
||||
data.locked = true;
|
||||
data.lockedUntil = Date.now() + (30 * 60 * 1000); // 30 دقيقة
|
||||
|
||||
console.warn(`[SECURITY] Account locked: ${username}`, {
|
||||
attempts: data.attempts,
|
||||
lockedUntil: new Date(data.lockedUntil).toISOString()
|
||||
});
|
||||
}
|
||||
|
||||
// حفظ لمدة ساعة
|
||||
await redis.setex(key, 3600, JSON.stringify(data));
|
||||
|
||||
return data;
|
||||
};
|
||||
|
||||
// مسح المحاولات بعد نجاح الدخول
|
||||
const clearFailedAttempts = async (username) => {
|
||||
await redis.del(`lockout:${username}`);
|
||||
};
|
||||
|
||||
// ═══════════════════════════════════════════════════════════════
|
||||
// Exports
|
||||
// ═══════════════════════════════════════════════════════════════
|
||||
module.exports = {
|
||||
loginLimiter,
|
||||
apiLimiter,
|
||||
sensitiveEndpointLimiter,
|
||||
accountLockout,
|
||||
recordFailedAttempt,
|
||||
clearFailedAttempts
|
||||
};
|
||||
|
||||
// ═══════════════════════════════════════════════════════════════
|
||||
// مثال الاستخدام في Express
|
||||
// ═══════════════════════════════════════════════════════════════
|
||||
/*
|
||||
const express = require('express');
|
||||
const {
|
||||
loginLimiter,
|
||||
apiLimiter,
|
||||
accountLockout,
|
||||
recordFailedAttempt,
|
||||
clearFailedAttempts
|
||||
} = require('./rate-limiter');
|
||||
|
||||
const app = express();
|
||||
|
||||
// تطبيق على جميع الـ APIs
|
||||
app.use('/api', apiLimiter);
|
||||
|
||||
// تطبيق على Login
|
||||
app.post('/api/login',
|
||||
loginLimiter,
|
||||
accountLockout,
|
||||
async (req, res) => {
|
||||
const { username, password } = req.body;
|
||||
|
||||
const user = await authenticate(username, password);
|
||||
|
||||
if (!user) {
|
||||
await recordFailedAttempt(username);
|
||||
return res.status(401).json({ error: 'بيانات خاطئة' });
|
||||
}
|
||||
|
||||
// نجاح - مسح المحاولات الفاشلة
|
||||
await clearFailedAttempts(username);
|
||||
|
||||
const token = generateToken(user);
|
||||
res.json({ token });
|
||||
}
|
||||
);
|
||||
*/
|
||||
57
q2-attack-simulation /timeline-analysis.md
Normal file
57
q2-attack-simulation /timeline-analysis.md
Normal file
@@ -0,0 +1,57 @@
|
||||
# تحليل Timeline لهجوم Brute Force على API Login
|
||||
|
||||
## السيناريو
|
||||
هجوم brute force ناجح على API login أدى إلى تسريب بيانات.
|
||||
|
||||
---
|
||||
|
||||
## 1. تحليل الهجوم خطوة بخطوة
|
||||
|
||||
| الوقت | المرحلة | الحدث | التفاصيل الفنية |
|
||||
|-------|---------|-------|-----------------|
|
||||
| T+0 | الاستطلاع | المهاجم يكتشف التطبيق | فحص `app.ghaymah.systems` واكتشاف API endpoints |
|
||||
| T+2min | جمع المعلومات | تحديد نقطة الدخول | اكتشاف `/api/login` يقبل POST requests |
|
||||
| T+3min | التحضير | تجهيز الأدوات | إعداد قائمة كلمات مرور شائعة + أداة Hydra/Burp |
|
||||
| T+5min | بدء الهجوم | إطلاق Brute Force | بدء إرسال طلبات متكررة على `/api/login` |
|
||||
| T+10min | التصعيد | زيادة المحاولات | 1000+ طلب POST بكلمات مرور مختلفة |
|
||||
| T+15min | استمرار | لا يوجد حظر | عدم وجود Rate Limiting سمح بالاستمرار |
|
||||
| T+20min | النجاح | تخمين كلمة المرور | نجاح الدخول بـ `admin:admin123` |
|
||||
| T+21min | الوصول | الحصول على Token | استلام JWT token صالح |
|
||||
| T+25min | الاستكشاف | فحص الصلاحيات | اكتشاف `/api/users` و `/api/export` |
|
||||
| T+30min | التسريب | سحب البيانات | تحميل بيانات المستخدمين |
|
||||
| T+35min | الاكتشاف | تنبيه أمني | فريق الأمن يلاحظ نشاط غير طبيعي |
|
||||
| T+40min | الاحتواء | بدء الاستجابة | حظر IP + إلغاء tokens |
|
||||
|
||||
---
|
||||
|
||||
## 2. المؤشرات التي كان يجب اكتشافها (IOCs)
|
||||
|
||||
### مؤشرات في الـ Logs:
|
||||
```
|
||||
# نمط الهجوم في access.log
|
||||
192.168.1.100 - - [26/Jul/2026:14:05:01] "POST /api/login HTTP/1.1" 401 45
|
||||
192.168.1.100 - - [26/Jul/2026:14:05:01] "POST /api/login HTTP/1.1" 401 45
|
||||
192.168.1.100 - - [26/Jul/2026:14:05:02] "POST /api/login HTTP/1.1" 401 45
|
||||
# ... مئات المحاولات من نفس IP
|
||||
192.168.1.100 - - [26/Jul/2026:14:20:15] "POST /api/login HTTP/1.1" 200 512
|
||||
192.168.1.100 - - [26/Jul/2026:14:25:30] "GET /api/users HTTP/1.1" 200 15234
|
||||
```
|
||||
|
||||
### العلامات الحمراء:
|
||||
1. **عدد طلبات غير طبيعي**: 100+ طلب/دقيقة من IP واحد
|
||||
2. **نمط متكرر**: نفس الـ endpoint مع status 401
|
||||
3. **User-Agent مشبوه**: أدوات مثل `python-requests` أو `curl`
|
||||
4. **توقيت منتظم**: طلبات كل 100ms (آلي وليس بشري)
|
||||
|
||||
---
|
||||
|
||||
## 3. الثغرات التي استُغلت
|
||||
|
||||
| الثغرة | الوصف | التأثير |
|
||||
|--------|-------|---------|
|
||||
| عدم وجود Rate Limiting | لا يوجد حد لعدد المحاولات | سمح بآلاف المحاولات |
|
||||
| كلمة مرور ضعيفة | `admin123` سهلة التخمين | نجاح الهجوم |
|
||||
| عدم وجود Account Lockout | الحساب لم يُقفل بعد محاولات فاشلة | استمرار الهجوم |
|
||||
| عدم وجود CAPTCHA | لا يوجد تحقق بشري | أتمتة الهجوم |
|
||||
| عدم وجود MFA | طبقة حماية واحدة فقط | الوصول بكلمة المرور فقط |
|
||||
| صلاحيات واسعة | حساب admin يصل لكل البيانات | تسريب كامل |
|
||||
المرجع في مشكلة جديدة
حظر مستخدم