배경 경보는 화면을 열어야 보였다. 상용 조기경보 서비스는 관심 기업에 변화가 생기면 먼저 알려 준다. 알림은 외부로 나가는 첫 기능이라 빠짐 · 중복 · 폭주 · SSRF · 비밀 유출을 설계에서 막아야 한다.
AlertService 가 경보를 쓰는 같은 트랜잭션(규칙 평가 청크)에서 notify.outbox 에 사건을 남긴다.
경보는 생겼는데 알림이 빠지거나, 롤백된 경보가 알려지는 경우가 없다. 메시지 브로커 없이 DB 하나로(ADR-011 · 012 와 같은 원칙).FOR UPDATE SKIP LOCKED 로 사건을 가져가 한 SQL 문으로 구독과 맞춘다(심각도 · 규칙 · 대상 유형 · 관심 목록만).
같은 사람 · 경보는 알림함에 한 번, 같은 웹훅 · 경보는 전송 한 번(유니크 키). worker 가 여럿이어도 한 번만 처리.| 위협 | 대응 |
|---|---|
| SSRF — 관리자가 적은 주소로 서버가 요청 | https · 기본 포트만, user@ · # 금지. 호스트를 풀어 모든 주소가 공인 주소일 때만(루프백 · 사설 · 169.254 메타데이터 · CGNAT · ULA · 멀티캐스트 거부, IPv4 를 품은 IPv6 — NAT64 · 6to4 · IPv4 호환 — 는 안의 IPv4 로 판단, Teredo · 문서용 대역 거부 — VERIFICATION 113). 등록할 때와 보낼 때마다 다시 검사, 리다이렉트 안 따라감, 연결 3초 · 응답 5초, 응답 본문은 읽지 않음 |
| 웹훅 주소 · 비밀 유출 (Slack 주소는 경로 자체가 토큰) | AES-256-GCM 저장 암호화(키 = NOTIFY_SECRET_KEY 를 HMAC 으로 유도, 무작위 IV, 변조는 태그로 거부). 응답 · 화면에는 호스트만(https://hooks.slack.com/…), GENERIC 서명 비밀은 만들 때 한 번만 보여 줌 |
| 위조 · 재전송된 웹훅을 받는 쪽이 믿음 | GENERIC: X-BuildRisk-Signature: sha256=HMAC(비밀, 타임스탬프 + "." + 본문) · X-BuildRisk-Timestamp · X-BuildRisk-Delivery(중복 제거용) |
Slack 메시지 주입(<!channel> · 가짜 링크) |
경보 제목 · 이름의 & < > 이스케이프 |
| 남의 알림 · 구독 보기 | /api/v1/me/** 는 조회도 로그인 필요, 주인이 아니면 404(있는지도 알리지 않음). 변경은 기존 감사 로그에 남음 |
키가 없으면 웹훅 기능만 끄고(화면에 안내) 알림함은 동작한다. make env 가 키를 만든다.
NotifySecurityTest — 내부 주소 14종 거부 · 공인 주소 허용 · localhost · 메타데이터 주소 거부, 암호문 왕복 · 변조 · 다른 키, 서명 값, Slack 이스케이프.NotificationIT — 규칙 평가 → outbox → 알림함 · 웹훅(받은 쪽에서 서명 재계산 일치, 두 경보를 메시지 하나로), 다시 평가해도 중복 없음, 503 → 백오프 · 404 → 포기, 이어진 경보는 알리지 않고 다시 열린 경보는 알림, 관심 목록 필터, 권한 · 404.localhost · 169.254.169.254 · 10.0.0.5 · http 주소 등록이 400. E2E: 종 → 구독 만들고 지우기 · 내부망 웹훅 거부 메시지.buildrisk_notify_outbox_pending · buildrisk_notify_deliveries_total{result} → Grafana 패널.덧붙임 (2026-10-09, VERIFICATION 131) X-BuildRisk-Delivery 는 같은 전송 묶음(전송 행 집합)이면 다시 보내도 같은 값(UUID v3)이다 —
전에는 매번 새 UUID 라 ‘응답 시간 초과’로 다시 보낸 것을 받는 쪽이 걸러낼 수 없었다. 결과는 아직 내 임대(lease_until)인 행에만 적고,
한 번 가져간 전송은 90초 안에 보내지 못하면 시도 횟수를 되돌려 돌려놓는다(임대 2분을 넘기면 다른 worker 가 같은 행을 다시 보냄).
덧붙임 (2026-10-09, VERIFICATION 153) 다시 열린 경보는 같은 (웹훅, 경보) 전송 행을 다시 쓰므로, Delivery ID 를 행 번호만으로 만들면 재발 알림이 첫 전송과 같은 ID 라 받는 쪽이 중복으로 버렸다. 다시 열 때 전송 행의 시작 시각을 새로 적고 ID 에 (행 번호, 시작 시각)을 넣는다 — 같은 주기의 재시도는 여전히 같은 값.