InfraGuard

탐지 + JA3 시퀀스 룰셋

탐지 알고리즘(Kitsune·srct·nginx·JA3) 서사와 결정적 휴리스틱 JA3 시퀀스 분석으로 이상행동 룰셋(infr_sec_ruleset)을 도출·축적하는 플래그십 화면.

개요 — 무엇을 하나

Security ▸ Detections(/security/detections)는 InfraGuard 의 탐지 플래그십 화면입니다. 세 가지를 한 화면에서 묶습니다.

  1. 탐지 알고리즘 서사 — 어떤 원리로 위협을 잡는지 교육용 popover 로 설명(Kitsune ML · srct 휴리스틱 · nginx is_suspicious · JA3).
  2. 탐지 후 서사 — 각 탐지기가 최근 창에서 잡은 이상 IP · 의심 웹 · run 이력을 실데이터로 표면화.
  3. ★ JA3 시퀀스 룰셋 — 한 출발지 IP 의 시간순 JA3 핑거프린트 시퀀스를 결정적 휴리스틱이 읽어 "연속적 이상행동"을 전문 용어 룰셋(infr_sec_ruleset)으로 열거·저장합니다.

수집·자동차단은 이미 백엔드에서 돌지만(stats-design 시절 UI 는 방화벽·블랙리스트 표뿐이었습니다) 그 숨은 텔레메트리를 신뢰도 있는 서사로 표면화하는 것이 이 화면의 목적입니다.

이 화면은 읽기 위주 + 온디맨드 분석입니다. 탐지기 자체(kts_tx·srct_tx·eve_ingest)는 대상 호스트에서 pm2 로 도는 Node 수집기이며, 앱은 그들이 채운 테이블을 읽습니다. 앱이 직접 트래픽을 스니핑하지 않습니다.

AI 나레이션(LLM) 경로는 2026-09-04 제거되었습니다. 예전에는 시퀀스 요약을 AI 어댑터에 보내 자유서술 답변을 저장했습니다. 실측으로 확인된 세 가지가 이유입니다 — LLM 의 결론("JA3 로테이션")은 비율 계산 3줄이 이미 내던 것과 같은데 1회 약 9,100 토큰이 들었고, 자유서술을 그대로 저장해 쓴 사람의 언어가 굳어 영문 세션이 한글 카드를 읽었으며, 폴백 경로에서도 provider/model 을 어댑터 값으로 찍어 저장된 13행 중 10행이 model=kimi-k2.6 라벨을 달고 실제로는 템플릿 출력이었습니다. 지금은 휴리스틱만 돌아 외부 호출·토큰 비용이 없고, provider/model 은 실제로 무엇이 돌았는지만 기록합니다.


언제 쓰나


화면 구성

KPI 스트립

KPI 의미 원천
AI 이상 선택 창 내 Kitsune 이상탐지 건수 kitsune_detail_log count
AI 차단 그중 is_blocked=1(임계+veto 충족) kitsune_detail_log where is_blocked
의심 웹 창 내 nginx is_suspicious=1 요청 수 nginx_meta_ct count
룰셋 현재 활성(status=active) 룰셋 수 infr_sec_ruleset

앞의 두 KPI 가 "AI" 인 것은 Kitsune 이 실제로 머신러닝 오토인코더이기 때문입니다 — 이 라벨은 사실이라 그대로 둡니다.

탐지 알고리즘 패널 (edu popover)

각 탐지기 카드에 원리 · 무엇을 보나 · tip 을 여는 교육 popover. 텍스트는 security.det.edu.* 정적 lang 팩이 소유합니다.

pm2 배치 안내

탐지 수집기 5종을 대상 호스트에 pm2 cron 으로 배치하는 명령·소스코드를 제공(§데이터 소스 · pm2 표).

탐지 로그 & run 이력

JA3 오펜더 & 룰셋


사용법

1) 탐지 현황 훑기

상단 창 토글(1h / 6h / 24h) 로 관측 창을 바꿉니다(기본 24h). KPI·탐지 로그가 그 창으로 재계산되고, 화면은 30초 폴링·서비스는 30초 캐시입니다.

오펜더 목록만은 창과 무관하게 항상 최근 6시간입니다. JA3 로테이션은 수 시간에 걸쳐 누적되므로 짧은 창에서는 패턴이 드러나지 않습니다(자동 차단의 360분 JA3 창과 정합).

2) JA3 시퀀스 분석 실행 (★)

  1. 오펜더 목록에서 IP 를 고르거나(Overview·Threats 딥링크) [분석] 을 누릅니다.
  2. 서버가 suricata_tls_ct 에서 그 IP 의 TLS JA3 시퀀스를 시간순으로 인출합니다(창 = 운영정책 sec_ai_window_min, 기본 60분 · 최대 200행).
  3. 시퀀스 길이가 최소치(sec_ai_min_seq, 기본 5) 미만이면 insufficient_sequence 로 건너뜁니다.
  4. 휴리스틱이 시퀀스를 findings 로 분류합니다(아래 규칙 표).
  5. 결과가 룰셋으로 저장되고 목록과 Overview 퍼널이 즉시 갱신됩니다.

응답 예시:

{
  "ok": true,
  "provider": "heuristic",
  "model": null,
  "saved": 1,
  "seq_len": 37,
  "findings": [
    { "key": "ja3_rotation",
      "params": { "conns": 37, "variety": 34 },
      "category": "tls_anomaly",
      "severity": "crit", "action": "block", "confidence": 91 }
  ]
}

분석은 결정적이고 무료입니다 — 같은 시퀀스는 항상 같은 룰셋을 내고, 외부 서비스를 호출하지 않으며, 네트워크가 없어도 동작합니다. 실행 비용이 없으니 오펜더 수치가 바뀔 때마다 부담 없이 재분석하세요.

휴리스틱 규칙

findings 는 시퀀스 통계로 만듭니다(rotation = ja3_variety / seq_len).

조건 룰셋 분류 심각도 권고 신뢰도
variety ≥ 3 그리고 rotation ≥ 0.5 JA3 로테이션(자동화 스캐너/봇넷) tls_anomaly rotation≥0.9 → crit, 그 외 warn block 50 + round(rotation×45), 최대 95
민감 URL 관측(.env·.git·wp-config…) 자격/설정 파일 조회 시도 credential_access crit block 88
variety = 1 그리고 conns ≥ 10 그리고 sni_variety ≥ 4 단일 JA3 다중 표적 스캔 scan warn observe 72
위 어느 것도 아님 TLS 기준 활동 other info observe 40

민감 URL 은 정규식 .env .git wp-config .aws /actuator phpunit /.. shell.php wp-login xmlrpc 로 매칭합니다(nginx is_suspicious + suricata_http 컨텍스트).

분류·심각도·권고는 삽입 전에 컬럼 enum 으로 검증됩니다(recon · credential_access · exfil · bruteforce · c2 · tls_anomaly · scan · other / info · warn · crit / observe · block · isolate · report).

룰셋 문구와 언어

finding 은 문장이 아니라 정체성으로 저장됩니다. heuristicFindings() 가 {key, params} 를 내고, 행은 rule_key + params(마이그레이션 000783)를 보관하며, 문구는 행을 읽는 시점에 읽는 사람의 언어로 입혀집니다.

000783 이전에 만들어진 행은 원래 텍스트를 유지합니다. 마이그레이션은 각 행의 evidence 로 구 템플릿을 재구성해 정확히 일치할 때만 rule_key 를 복원합니다(추측하지 않습니다). 구 LLM 경로가 쓴 자유서술 행은 해당 IP 를 다시 분석해 교체되기 전까지 그대로 남습니다.

3) 룰셋 관리

액션 효과
활성화 status=active(Overview 퍼널·차단 서사에 반영)
보류 status=dismissed
삭제 행 제거

재분석은 그 IP 의 기존 기계 유래 룰셋만 교체합니다(entity_type=ip, entity_key=ip, source=ai 삭제 후 재생성) — 수동 작성 룰셋은 보존됩니다.

룰셋의 action 필드는 권고이지 명령이 아닙니다. 이 값으로 동작하는 코드는 없습니다 — 자동 차단은 suricata_tls_ct 에서 JA3 로테이션을 직접 계산하며 infr_sec_ruleset 을 읽지 않습니다. action=block 인 룰셋이 있어도 아무것도 차단되지 않습니다. 차단은 Blocklist 에서 하거나 자동 차단 스케줄러가 수행합니다.

4) 스케줄 축적(옵트인)

infr:sec-analyze 커맨드가 상위 JA3 오펜더를 순회하며 룰셋을 축적합니다. 단발 실행 후 종료하는 커맨드라 스케줄에 안전하게 걸 수 있습니다(데몬 아님).

# 게이트 무시하고 1회 실행(디버그)
php artisan infr:sec-analyze --force

# 단일 IP 분석(게이트 무관)
php artisan infr:sec-analyze --ip=203.0.113.10

5) pm2 배치 명령 복사

pm2 카드는 [명령 복사](pm2 실행 줄), [소스코드 복사](스니펫 body), [인스턴스 값으로 채워 복사](HOSTNAME/IP 토큰 치환)를 제공합니다. 소스는 시드된 Inventory ▸ Tools 스니펫(infr_snippet)에서 옵니다.


데이터 소스

읽는 레거시 테이블(수정 금지)

테이블 용도 공급 수집기
kitsune_detail_log 이상 IP·anomaly_score·is_blocked kts_tx
kitsune_process_log Kitsune run 이력 kts_tx
srct_process_log srct run 이력 srct_tx
nginx_meta_ct 의심 웹 요청(is_suspicious) nginx_shipper/aggregator
suricata_tls_ct JA3 시퀀스(ja3_hash·sni·version) eve_ingest
suricata_http_ct 분석용 HTTP 컨텍스트 eve_ingest
blacklist_info 자동 차단 결과(참조) kts_tx · srct_tx
ip_info_loop geo/ASN 인리치 —

앱 소유 신규 테이블

pm2 배치 수집기

key 공급 배치 명령(cron-restart)
eve_ingest suricata_*_ct pm2 start infr_eve_ingest.cjs --name infr-eve --cron-restart="0 0 * * 6"
kts_tx kitsune_*_log · blacklist_info pm2 start infr_kts_tx.cjs --name infr-kitsune --cron-restart="0 0 * * 2"
srct_tx srct_process_log · blacklist_info pm2 start infr_srct_tx.cjs --name infr-srct --cron-restart="0 0 * * 4"
sync_black ipset · iptables pm2 start infr_sync_black.mjs --name infr-syncblack --cron-restart="*/5 * * * *"
sync_firewall firewall_status pm2 start infr_sync_firewall.cjs --name infr-firewall --cron-restart="0 0 * * 0"

서비스 & 라우트

메서드 라우트
GET /security/detections(index)
GET /security/detections/live(폴링)
POST /security/detections/analyze(분석)
POST /security/detections/ruleset/save
POST /security/detections/ruleset/delete

접근 권한

작업 게이트
화면 조회 Security 역할영역 접근(min_req 2) — demo(1)는 보안 영역 자체가 보이지 않고, staff(5)·admin(10)은 조회 가능
분석 / 룰셋 저장·삭제 user_level ≥ 2(canWrite) + infr_audit_log 감사

문제 해결 & FAQ

분석이 insufficient_sequence 로 건너뜀 — 그 IP 의 suricata_tls_ct 시퀀스가 sec_ai_min_seq(기본 5) 미만입니다. 창(sec_ai_window_min, 기본 60분)을 넓히거나 오펜더 목록(6시간 창)에서 더 활발한 IP 를 고르세요.


관련 문서

탐지 · AI JA3 시퀀스 분석
탐지 · AI JA3 시퀀스 분석