개요 — 무엇을 하나
탐지 룰셋(/security/rulesets)은 InfraGuard가 관측·판정하는 모든 탐지 규칙을 44개 룰셋 / 11개 카테고리로 정규화한 정본(定本) 라이브러리입니다. 이 화면은 세 가지를 한 자리에서 제공합니다.
- 개요 — 카테고리별 섹션 안에 룰셋 카드를 배치하고, 선택한 시간 창(1h/6h/24h) 동안 각 룰셋에 실제로 걸린 이벤트 수와 최종 관측 시각을 실측으로 표시합니다.
- 해설 — 카드/모달에서 각 룰셋을 3단(무엇을 탐지 · 왜 걸리나 · 최악의 경우)으로 서사화합니다.
- 되짚기 — 카드를 클릭하면 그 룰셋으로 실제 걸린 최근 이벤트를 엔진별 테이블로 모달에 펼칩니다.
핵심 설계는 분류 구조와 해설 텍스트의 분리입니다. 구조(카테고리·룰셋 키·엔진별 매칭 규약)는 로케일 독립 정적 SOT인 App\Support\SecurityRulebook이 소유하고(캐시 안전), 해설 텍스트(이름·의미·근거·위험)는 resources/lang/{ko,en}/secrules.php가 소유합니다. 프런트가 key+category로 둘을 병합해 렌더합니다.
이 화면은 완전한 읽기 전용입니다. 룰셋을 켜고 끄거나 편집·삭제하지 않습니다. "이 조직에서 지금 무엇이 관측되고 있으며, 그 의미는 무엇인가"를 이해시키는 참조 카탈로그입니다. 탐지 상수(임계·윈도우)를 실제로 바꾸는 곳은 규칙 임계(Admin ▸ Rules)입니다.
44 룰셋의 원천 = 실 텔레메트리
룰셋은 추상 규칙이 아니라 실제 수집 테이블에 **매칭 규약(match)**으로 결선돼 있습니다. 원천은 전부 legacy_glory 커넥션(수집기 함대가 채우는 레거시 DB)입니다.
| 엔진 | 매칭 규약 | 원천 테이블 | 룰셋 수 |
|---|---|---|---|
| Suricata IDS | sig: 시그니처 문자열 소문자 부분일치 배열 |
suricata_alert_ct |
33 |
| 웹(nginx) | classifyWeb: is_suspicious 요청을 url/ua/status로 우선순위 disjoint 분류 |
nginx_meta_ct |
8 |
| Kitsune ML | source=kitsune: 오토인코더 이상 IP |
kitsune_detail_log |
1 |
| 휴리스틱(srct) | source=srct: 스캔/레이트 휴리스틱 run |
srct_process_log |
1 |
| JA3 지문 | source=ja3: 동일 출발지의 JA3 다양성(회전) |
suricata_tls_ct |
1 |
언제 쓰나
- 트리아지 전 맥락 파악 — 위협(Threats)이나 탐지·JA3에서 낯선 시그니처를 만났을 때, 그 시그니처가 어떤 룰셋 계열이며 무엇을 의미하는지 확인합니다.
- 관측 지형 스캔 — 지난 24시간 동안 어떤 카테고리가 발화(firing)하고 있는지, 웹 공격 패턴이 정찰 수준인지 RCE 프로빙까지 왔는지 한눈에 봅니다.
- 신입 온보딩·보고 — "우리가 무엇을 탐지하는가"를 카테고리·엔진 단위로 설명하는 교육/보고 자료.
- 이벤트 되짚기 — 특정 룰셋(예:
web_secret_scan)에 최근 걸린 실제 요청 IP·URL·상태코드를 확인하고, 의심 출발지를 IP 평판 조회나 차단 목록(Blocklist)으로 이어갑니다.
화면 구성
상단 KPI
| KPI | 라벨 | 의미 |
|---|---|---|
total |
룰셋 | 카탈로그 총 룰셋 수 (항상 44) |
active |
관측된 룰셋 | 선택 창에서 이벤트가 1건 이상 발화한 룰셋 수(firing) |
events |
창 내 이벤트 | 전 룰셋 이벤트 카운트 합 |
cats |
카테고리 | 카테고리 수 (11) |
내부적으로 엔진별 합(kitsune·suricata 이벤트 합)도 함께 계산돼, Security 퍼널 서사의 "룰셋" 단계 수치와 정합합니다.
컨트롤
- 시간 창 토글 —
1h(60분) ·6h(360분) ·24h(1440분). 기본24h. - 자동 갱신(LIVE) —
/security/rulesets/live를 폴링해 카운트를 갱신(일시정지 가능). - 전체화면 — 상단 네비를 숨기고 대시보드 몰입 모드로 전환(ESC 해제).
카테고리 섹션 + 룰셋 카드
11개 카테고리가 표시 순서대로 섹션을 이루고, 각 섹션 안에 minmax(600) 카드 그리드로 룰셋이 배열됩니다. 카드에는 룰셋 이름·MDI 아이콘·심각도 배지(위급/주의/정보)·창 내 이벤트 수·최근 관측 시각·[자세히]가 표시됩니다. 이벤트가 없으면 "조용함/창 내 관측 없음"으로 표시됩니다.
| # | 카테고리 키 | 라벨 | 엔진 | 룰셋 수 |
|---|---|---|---|---|
| 1 | tcp_stream |
TCP 스트림 무결성 | Suricata | 10 |
| 2 | packet_decode |
패킷 디코딩 | Suricata | 4 |
| 3 | protocol_anomaly |
프로토콜 이상 | Suricata | 5 |
| 4 | http_anomaly |
HTTP 규약 | Suricata | 5 |
| 5 | tls_anomaly |
TLS 이상 | Suricata | 5 |
| 6 | dns_anomaly |
DNS 이상 | Suricata | 1 |
| 7 | mail_anomaly |
메일(SMTP) 이상 | Suricata | 3 |
| 8 | web_attack |
웹 공격 패턴 | nginx | 8 |
| 9 | ml_anomaly |
ML 이상탐지 | Kitsune | 1 |
| 10 | heuristic_scan |
휴리스틱 스캔 | srct | 1 |
| 11 | tls_fingerprint |
JA3 지문 | JA3 | 1 |
사용법
1) 시간 창 선택 → 지형 읽기
먼저 1h/6h/24h 중 관심 창을 고릅니다. active(관측된 룰셋)와 각 카드의 이벤트 수로 지금 무엇이 발화 중인지 파악합니다. 심각도 배지는 암시 심각도임에 유의합니다 — Suricata 엔진 이벤트는 대부분 sev3(정보성)이지만, 그 함의는 다릅니다(잡음/스캔/익스플로잇). 룰셋의 배지는 원본 sev가 아니라 이 함의 톤을 반영합니다.
2) 카드 클릭 → 최근 이벤트 모달
카드를 클릭하면 /security/rulesets/events?key=<룰셋키>&range=<창>을 호출해 모달을 엽니다. 모달 구성:
- 해설 3단 — 무엇을 탐지 · 왜 걸리나 · 최악의 경우.
- 집계 — 이벤트 수 · 고유 출발지(distinct src IP).
- 매칭 시그니처 칩(Suricata 전용) — 이 룰셋의 sig 부분일치에 걸린 실제 시그니처 문자열 + 각 카운트·최소 sev(상위 12개).
- 엔진별 이벤트 테이블(최근 60행).
- 출발지 IP → 평판 조회 — 각 src IP에 AbuseIPDB 팝업 버튼. 등록된 내부망 IP는
내부배지가 붙고 평판 버튼이 숨습니다(우리 서버를 공격자로 오인 방지).
엔진별 이벤트 테이블 컬럼:
| 엔진 | 컬럼 |
|---|---|
| Suricata | 시각 · 출발지 · 목적지 · 포트 · proto · 시그니처 · sev |
| nginx | 시각 · 출발지 · host · method · URL(rawurldecode, 180자) · 상태코드 |
| Kitsune | 시각 · IP · 이상점수 · 세션수 · 차단여부 |
| srct | 시각 · 스캔수 · 차단수 · 메모 |
| JA3 | 시각 · IP · JA3 다양성 · TLS 접속수 · SNI 다양성 |
3) 웹 공격 분류 우선순위(disjoint)
웹(nginx) 룰셋 8종은 서로 **배타(disjoint)**입니다. is_suspicious=1 요청 한 건은 카탈로그 정의 순서로 스캔해 첫 매칭 룰셋 하나에만 귀속되고, 어느 패턴에도 안 걸리면 catchall(web_suspicious_other)로 떨어집니다. 즉 같은 요청이 두 웹 룰셋에 이중 집계되지 않습니다.
| 우선순위 | 룰셋 키 | 매칭 단서(url/ua) | 심각도 |
|---|---|---|---|
| 1 | web_secret_scan |
.env .git wp-config id_rsa .aws credentials backup.sql .bak |
주의 |
| 2 | web_path_traversal |
../ ..%2f %2e%2e etc/passwd boot.ini proc/self |
위급 |
| 3 | web_sqli |
union select information_schema sleep( or 1=1 %27or |
위급 |
| 4 | web_xss |
<script onerror= javascript: document.cookie |
주의 |
| 5 | web_rce_probe |
/bin/sh exec( system( jndi: ${ phpunit |
위급 |
| 6 | web_admin_brute |
wp-login xmlrpc.php phpmyadmin /admin/login |
주의 |
| 7 | web_bad_bot |
UA: sqlmap nikto masscan nuclei python-requests curl/ |
주의 |
| 8 | web_suspicious_other |
(catchall — 위 어디에도 안 걸린 의심 요청) | 정보 |
웹 분류는 성능을 위해 최근순 표본 6,000건(리포지토리 상한 8,000)으로 집계합니다. 대량 스캔이 있으면 표본 밖 이벤트는 카운트에 반영되지 않을 수 있습니다 — 절대 카운트가 아니라 분포·경향으로 읽어라.
4) Suricata 시그니처 부분일치의 함의
Suricata 룰셋은 시그니처 문자열의 소문자 부분일치로 걸립니다. 예를 들어 stream_invalid_ts는 시그니처에 invalid timestamp가 포함된 모든 이벤트를 합산합니다. 하나의 룰셋이 여러 실제 시그니처를 묶을 수 있고(모달의 "매칭 시그니처" 칩에서 확인), 서로 다른 룰셋의 sig 키워드는 겹치지 않도록 설계됐습니다.
5) 실측 예시(참고)
24시간 창 실측 스냅샷 예: 44 룰셋 중 37개 발화, stream_invalid_ts 10,243건, web_secret_scan 3,304건(대다수 .env류 404), srct_heuristic 126,252건, ja3_rotation 300건(다양성 307/307). 값은 조직·시점에 따라 달라집니다.
데이터 소스
전 원천은 legacy_glory 커넥션(읽기 전용)이며, 하드코딩 자격 없이 config('database.connections.legacy_glory')로만 접속합니다.
| 룰셋 계열 | 테이블 | 리포지토리 메서드(발췌) |
|---|---|---|
| Suricata (33) | suricata_alert_ct |
signatureHistogram() · recentAlertsBySignatures() · distinctIpsBySignatures() |
| 웹 (8) | nginx_meta_ct (is_suspicious=1) |
suspiciousRows($since, 6000) |
| Kitsune ML (1) | kitsune_detail_log |
kitsuneCount() · kitsuneRecentInWindow() |
| 휴리스틱 (1) | srct_process_log |
srctBlockedSum() · srctRunsInWindow() |
| JA3 (1) | suricata_tls_ct |
topJa3Ips($since, N, excludePrefixes) |
성능·캐시
- 카드 개요(
build)는 30초 캐시(infr:sec:rulebook:<range>). Suricata는 시그니처 히스토그램을 1회 뽑아 44개 룰셋에 부분일치 합산해 N+1을 회피합니다. - JA3는
SecurityScope::excludePrefixes()로 **자기망(등록 인스턴스 IP /24)**을 NOT LIKE 제외하고, 다양성ja3_variety >= 2인 출발지만 오펜더로 셉니다. - 모달 이벤트는 창 조회 시 60행 상한. 각 리포지토리 쿼리는 좁은 시간 창 + LIMIT + 파라미터 바인딩.
권한
- 읽기 전용. 컨트롤러 게이트는 조회
level≥1이며, Security 역할 영역 진입 자체가 사이트맵min_req=2(보안 관제)로 보호됩니다 — 실질적으로 staff(=5)·admin(=10) 또는 보안 스코프 사용자가 접근합니다. - 쓰기 경로가 없으므로 step-up 재확인·감사 로그가 없습니다.
/rulesets·/rulesets/live·/rulesets/events전부 순수 조회입니다. events엔드포인트의key는 카탈로그 화이트리스트로만 허용됩니다. 목록 밖 키는404 unknown_ruleset을 반환합니다(임의 테이블 조회 불가).
레벨 규약: demo=1 · staff=5 · admin=10. 일부 영역은
infr.level:N미들웨어로 별도 게이팅됩니다. 자세한 등급 체계는 RBAC 레벨을 참고합니다.
문제해결 · FAQ
모든 카드가 "조용함"으로 표시됩니다
선택 창(기본 24h)에 매칭 이벤트가 없거나, legacy_glory 커넥션이 도달 불가한 경우입니다. 서비스는 원천 오류를 graceful하게 삼켜 빈 카운트(0)로 표시합니다 — 카드가 사라지지는 않습니다. 시간 창을 넓히거나 커넥션을 점검합니다.
웹 룰셋 카운트가 [위협]/[탐지]의 수치와 다릅니다
웹 분류는 최근순 표본 6,000건 기준의 분포 집계다(절대 카운트 아님). 또한 disjoint 분류라 한 요청은 한 룰셋에만 귀속됩니다. 정확한 이벤트 원본은 각 화면의 원천 쿼리를 따릅니다.
특정 시그니처가 어느 룰셋에도 안 잡힙니다
Suricata 매칭은 키워드 부분일치입니다. 카탈로그가 커버하지 않는 신규/커스텀 시그니처는 룰셋 카운트에 합산되지 않습니다. 해설·분류가 필요하면 시그니처 키워드를 SecurityRulebook의 sig 배열에 추가하는 코드 변경이 필요합니다(런타임 설정 아님).
JA3 오펜더가 0인데 TLS 트래픽은 많습니다
JA3 룰셋은 ja3_variety >= 2(지문 회전)인 출발지만 오펜더로 셉니다. 지문 회전이 없으면 정상으로 간주돼 카운트되지 않습니다. 또한 자기망 IP는 제외됩니다.
이벤트 모달에 IP가 내부 배지로 표시됩니다
SecurityScope::internalMatchers()가 판정한 등록 내부망 IP입니다. 우리 서버가 Suricata 방향 라벨상 "출발지"로 찍힐 때의 오인을 막고, 평판 조회 버튼을 숨깁니다. 내부망 정의는 운영 정책(Operations Policy)의 internal_networks에서 관리합니다.
관련 항목
- 탐지 · JA3 시퀀스 분석 — 룰셋의 상위 화면(Detections). 결정적 휴리스틱이 JA3 시퀀스로 새 룰셋을 도출.
- 위협(Threats) — IDS 위협 엔티티 트리아지 + 시그니처 해설.
- 보안 개요(Overview) — 방어 퍼널의 "룰셋" 단계와 정합.
- IP 평판 조회 — 모달 출발지 IP를 AbuseIPDB로 확인.
- 차단 목록(Blocklist) — 의심 출발지를 차단으로 연결.
- 규칙 임계(Admin ▸ Rules) — 탐지 수집기 상수(임계·윈도우)를 실제로 조정.
