diff --git a/projects/netbis-sigmatch.md b/projects/netbis-sigmatch.md index b6243ba..bd02e52 100644 --- a/projects/netbis-sigmatch.md +++ b/projects/netbis-sigmatch.md @@ -13,17 +13,33 @@ Netbis NPM 로그(VictoriaLogs)를 실시간 분석해 **사람 개입 없이 - **배포 예정**: jp1 Incus `ai-sigmatch` 컨테이너 (systemd timer 1분) - **책임자**: kappa 직접 개발 (Heimdall 위임 X, 개발 단계) -## 핵심 설계 (v2, 2026-04-24 재설계) +## 핵심 설계 (v2.1, 2026-04-24 CUSUM + global entropy 추가) -### 탐지 트리거 2종만 +### 탐지 트리거 (OR 조건, 하나라도 충족 시 attack mode ON) -**A. 집계 기반 공격 모드** (DDoS 검출) +**A1. Static threshold** (급격한 볼륨 이상) ``` 현재 5분 총 req > baseline_시간대(UTC hour).p95 × 2.0 OR > baseline_시간대.max × 1.5 -→ attack mode ON → top N contributor IP challenge ``` +**A2. CUSUM Page-Hinkley** (점진적 ramp-up 공격) +``` +g_t = max(0, g_{t-1} + (x_t - μ_hour - δ)) , δ = μ × 0.3 +g_t > μ × 2.0 → trigger → reset g=0 +``` +- μ는 시간대별 롤링 mean. static threshold를 못 넘게 살살 올리는 low-rate DDoS 커버 +- 전역 g_t 1개 유지 (hour 경계 reset 문제 회피) + +**A3. Global src IP entropy drop** (소수 IP 집중 공격) +``` +현재 5분 트래픽의 Shannon(Counter(client_ip)) < baseline_entropy_p10(시간대별) +``` +- entropy 계산은 `min_entropy_baseline_samples=20` 확보 후부터 +- `uniq_ips < entropy_min_uniq_ips=30` 이면 판정 스킵 (트래픽 자체가 너무 적을 때) + +→ attack mode ON: top N contributor IP 일괄 challenge + **B. 개별 극단 (항상 동작)** ``` Scanner-shape: uniq_paths/reqs ≥ 0.8 AND path_entropy ≥ 7 AND reqs ≥ 300 @@ -34,25 +50,21 @@ Raw extreme: reqs ≥ 1500 / 5min ### 롤링 baseline (자동 갱신) -- 매 사이클 현재 윈도우 샘플을 `baseline_samples`에 누적 -- 시간대(hour_utc)별 p95/max를 최근 7일 롤링 기준으로 실시간 계산 +- 매 사이클 현재 윈도우 샘플을 `baseline_samples`에 누적 (total_reqs + src_ip_entropy + uniq_ips) +- 시간대(hour_utc)별 mean/p95/max/entropy_p10/entropy_p50을 최근 7일 롤링 기준으로 실시간 계산 - **공격 판정된 윈도우 샘플은 baseline에 안 들어감** (baseline 오염 방지) - 매 60 사이클마다 7일 초과 샘플 자동 prune - 초기 seed는 `baseline_aggregate.py`로 1회 수집, 롤링 12+ 샘플 쌓이면 seed 대체 -### 폐기된 이전 설계 - -- ~~IsolationForest + DBSCAN per-IP anomaly~~ → 정상 폴링 유저를 outlier로 잡아 오탐 위험 -- ~~Persistence 단독 트리거~~ → 페이지 오래 열어둔 유저 6 사이클 지속 시 오탐 -- ~~사전 정의 hard rule (R1~R6)~~ → 공격 패턴 종속, 자동 시그니처 생성 취지 어긋남 - ## 오탐 방지 특성 | 상황 | 동작 | |------|------| | 평상시 정상 트래픽 | 액션 0 (heavy user·폴링 유저 다 통과) | | 평상시 섞인 취약점 스캐너 | 개별 극단 트리거 (확정 공격) | -| DDoS·대규모 공격 | attack mode 발동 → top contributor 일괄 challenge | +| DDoS·대규모 공격 (급격) | static threshold (p95×2.0 / max×1.5) 발동 | +| Low-rate / 점진 ramp-up DDoS | CUSUM Page-Hinkley 누적으로 발동 | +| 소수 IP 집중 공격 (같은 볼륨) | global src IP entropy drop으로 발동 | | 가짜 공격 (baseline 경계선) | samples ≥ 10 확보 후에만 판정 (초기 1~2일은 판정 유예) | **사람이 쓴 공격 지식 없음**: @@ -70,11 +82,12 @@ Raw extreme: reqs ≥ 1500 / 5min - [x] Phase 1: feature 추출 (`fetch_features.py`) - [x] Phase 2: 24h retrospective baseline (`collect_baseline.py`) - [x] Phase 3-5: (폐기) IsolationForest+DBSCAN+persistence 기반 -- [x] **Phase 6 (v2): 집계 기반 공격 모드 + 개별 극단 시그니처** ← 현재 -- [x] **Phase 7: 롤링 baseline 자동 갱신** ← 현재 -- [ ] Phase 8: 장시간 관찰 (dry-run) — 진행 중 (백그라운드 `loop.py`) -- [ ] Phase 9: CF IP Access Rules 호출 (managed_challenge → block) -- [ ] Phase 10: jp1 Incus 배포 (systemd timer) +- [x] Phase 6 (v2): 집계 기반 공격 모드 + 개별 극단 시그니처 +- [x] Phase 7: 롤링 baseline 자동 갱신 +- [x] **Phase 8 (v2.1): CUSUM Page-Hinkley + global src IP entropy drop** ← 현재 +- [ ] Phase 9: 장시간 관찰 (dry-run) — 진행 중 (백그라운드 `loop.py`). entropy baseline 20+ 샘플 쌓이려면 약 20시간 +- [ ] Phase 10: CF IP Access Rules 호출 (managed_challenge → block) +- [ ] Phase 11: jp1 Incus 배포 (systemd timer) ## 파라미터 (사람 조정 가능) @@ -84,8 +97,12 @@ Raw extreme: reqs ≥ 1500 / 5min | `attack_max_multiplier` | 1.5 | 또는 max의 몇 배면 | | `attack_top_n` | 20 | attack 시 challenge할 상위 IP 수 | | `attack_contributor_min_reqs` | 200 | top IP 중 이 이상인 것만 | +| `cusum_drift_pct` | 0.3 | CUSUM 허용 드리프트 δ = μ × pct | +| `cusum_threshold_mult` | 2.0 | CUSUM 트리거 h = μ × mult | +| `min_entropy_baseline_samples` | 20 | entropy baseline 샘플 부족 시 판정 스킵 | +| `entropy_min_uniq_ips` | 30 | uniq IP 부족 시 entropy 판정 스킵 | | `scanner_uniq_ratio` | 0.8 | uniq_paths/reqs 임계 | -| `scanner_min_entropy` | 7.0 | path entropy 임계 | +| `scanner_min_entropy` | 7.0 | path entropy 임계 (per-IP) | | `scanner_min_reqs` | 300 | 스캐너 최소 요청 수 | | `extreme_reqs` | 1500 | 단일 IP 극단 rate 임계 | | `persistence_for_block` | 5 | challenge → block 승급 사이클 | @@ -94,6 +111,28 @@ Raw extreme: reqs ≥ 1500 / 5min | `baseline_rolling_days` | 7 | 롤링 윈도우 | | `min_baseline_samples` | 10 | baseline 샘플 부족 시 판정 유예 | +## 검증 결과 (simulate.py) + +- 평상시 24h 데이터: static threshold 0회 발동, **CUSUM hour별 μ 적용 시 0회 오탐** +- 마지막 5개 윈도우에 1.5x→3.5x 점진 ramp 주입: CUSUM 3회 발동 (idx 198/199/200 연속) +- CUSUM 유닛 테스트: 평상시 g=0 유지, ramp-up 시 g 누적 → h 초과 시 trigger + reset 확인 + +## 폐기된 이전 설계 + +- ~~IsolationForest + DBSCAN per-IP anomaly~~ → 정상 폴링 유저를 outlier로 잡아 오탐 위험 +- ~~Persistence 단독 트리거~~ → 페이지 오래 열어둔 유저 6 사이클 지속 시 오탐 +- ~~사전 정의 hard rule (R1~R6)~~ → 공격 패턴 종속, 자동 시그니처 생성 취지 어긋남 + +## 폐기 후보 (검토 후 불채택) + +- **Prophet / LSTM / Bi-LSTM** — 라벨 없음, 학습·추론 무거움, jp1 작은 컨테이너 오버킬 +- **CatBoost / Random Forest 지도학습** — 라벨 필요 +- **per-IP Isolation Forest / OC-SVM** — 정상 폴링 유저 오탐 (이미 폐기 이유 동일) + +향후 데이터 2주+ 쌓이면 검토 예정: +- **Holt-Winters** — level+trend+seasonality. 현재 static p95×mult 대체 가능 +- **Aggregate-window Isolation Forest** — per-IP 아닌 "윈도우 다변량 feature" anomaly + ## 파일 구조 ```