보안7분 분량

오탐은 모델 문제이기 이전에 문화 문제다

오탐률을 0으로 만드는 가장 쉬운 방법은 스캐너를 끄는 것입니다. 대부분의 팀은 스캐너를 끄지 않습니다. 대신 결과를 읽지 않기 시작합니다. 우리가 고객사에서 반복적으로 본 것은 모델 정확도의 문제가 아니라 운영 방식의 문제였습니다.

플롯팩토리 보안 엔지니어링팀

FlawDetector 엔진 · 리서치

3.2%도 팀을 무너뜨린다

벤치마크에서 오탐률 3.2%는 좋은 숫자입니다. 그런데 이 비율이 실제 큐에서 몇 건이 되는지는 코드베이스 규모가 결정합니다. 스캔 결과 건수는 라인 수에 거의 선형으로 비례하고, 트리아지 시간은 건수에 비례합니다.

오탐률 3.2%가 만드는 주간 트리아지 부하
코드베이스주간 결과 건수주간 오탐 건수트리아지 시간 (건당 12분)
5만 라인약 120건4건48분
40만 라인약 900건29건5.8시간
200만 라인약 4,300건138건27.6시간

건당 12분은 코드 확인·재현 시도·판정 기록까지 포함한 내부 관측 중앙값입니다.

주 27.6시간은 보안 엔지니어 0.7명분입니다. 그리고 이 시간의 산출물은 '아무 일도 일어나지 않았다'는 결론입니다. 어떤 조직도 이 작업에 전담 인력을 오래 붙이지 못합니다. 그래서 결과는 예외 없이 같은 방향으로 흘러갑니다. 큐가 밀리고, 밀린 큐는 읽히지 않고, 읽히지 않는 큐에서는 진짜 결함도 함께 묻힙니다.

오탐의 비용은 시간이 아니라 신뢰다

오탐의 비용을 계산할 때 대부분 트리아지 시간만 셉니다. 실제로 더 비싼 것은 그다음에 벌어지는 일입니다. 오탐을 세 번 연속으로 만난 개발자는 네 번째 알림을 열지 않습니다. 이 학습은 개인 차원에서 끝나지 않고 팀의 기본값이 됩니다. '보안 봇이 또 뭐라 하네'가 농담으로 자리 잡는 순간, 그 파이프라인의 탐지율은 숫자가 아무리 높아도 실질적으로 0에 수렴합니다.

알림 피로는 사람이 게을러져서 생기는 현상이 아닙니다. 신호 대비 잡음이 일정 수준을 넘으면 무시가 합리적인 전략이 되기 때문에 생깁니다.

'오탐'이라 불리는 네 가지 서로 다른 것

고객사 트리아지 기록을 분석해 보면, 개발자가 '오탐'으로 닫은 결과 중 실제로 엔진이 틀린 경우는 절반이 되지 않았습니다. 나머지는 엔진이 맞았지만 조직의 맥락에서 조치하지 않기로 한 것들이었고, 이 둘을 같은 버튼으로 닫는 것이 문제의 시작이었습니다.

닫힌 결과의 실제 성격 분류
실제로는증상올바른 처방
진짜 오탐코드에 해당 결함이 존재하지 않음엔진 피드백 제출 + 룰 조정. 여기서만 정확도 논의가 유효
도달 불가결함은 맞지만 외부 입력이 닿지 않음도달 불가 근거를 문서로 남기고 만료일 있는 억제
수용된 위험알고 있고 감수하기로 결정함소유자·보상 통제·재검토일이 붙은 예외 승인
우선순위 오류맞지만 지금 다룰 것은 아님억제가 아니라 백로그 이동. 다음 스캔에서 다시 보여야 함

팀이 만들어내는 오탐

도구가 아니라 도입 방식이 오탐 체감량을 키우는 경우가 많습니다. 세 가지 패턴이 반복적으로 관찰됩니다.

  • 정책 없는 전체 스캔 — 첫날부터 전체 코드베이스에 모든 규칙을 켜면, 수천 건의 과거 부채가 신규 결함과 함께 쏟아집니다. 개발자 입장에서 이 큐는 통째로 잡음입니다.
  • 게이트 없는 리포트 — 아무것도 막지 않는 리포트는 아무도 읽지 않습니다. 반대로 처음부터 모든 심각도를 막으면 팀은 게이트를 끕니다. 신규 코드의 Critical만 막는 좁은 게이트가 정착률이 가장 높았습니다.
  • 주인 없는 큐 — 결함에 담당자가 자동 배정되지 않으면 큐는 공유지가 됩니다. 코드 소유권 파일과 연결해 결함이 생성되는 순간 담당 팀이 정해지도록 해야 합니다.

다섯 가지 처방

  1. 01

    트리아지 SLA를 정한다

    Critical은 24시간, High는 3영업일 안에 '첫 판정'을 붙입니다. 해결이 아니라 판정입니다. 판정되지 않은 결함이 쌓이는 것이 미해결 결함이 쌓이는 것보다 위험합니다.

  2. 02

    억제에 만료일을 강제한다

    영구 억제는 허용하지 않습니다. 최대 180일로 상한을 두고, 만료되면 다음 스캔에서 자동으로 다시 열립니다. 맥락은 바뀌고, 6개월 전의 '도달 불가'는 오늘 도달 가능할 수 있습니다.

  3. 03

    도달 가능성 기준을 합의한다

    무엇을 '외부 입력이 닿는다'로 볼지 팀마다 다릅니다. 내부 관리자 API는? 배치 잡의 인자는? 이 기준을 문서로 먼저 정하지 않으면 트리아지는 매번 논쟁이 됩니다.

  4. 04

    결함에 소유자를 붙인다

    코드 소유권 정보와 연결해 생성 시점에 담당 팀을 지정합니다. 재배정은 가능하지만 무주공산 상태는 허용하지 않습니다.

  5. 05

    분기마다 오탐 회고를 한다

    '진짜 오탐'으로 분류된 사례만 모아 30분 회고를 돌립니다. 룰 조정으로 해결될 것, 프레임워크 인식 추가가 필요한 것, 우리 코드 관행을 바꿔야 할 것으로 나눕니다.

억제 규칙을 코드로

억제가 대시보드의 버튼 클릭으로만 남으면, 6개월 뒤에는 누가 왜 눌렀는지 아무도 모릅니다. 억제는 리포지토리 안의 파일로 관리하고, 리뷰를 거치고, 만료를 CI에서 강제하는 편이 안전합니다.

.flawdetector/suppressions.ymlyaml
- finding: FD-2026-05-0417
  rule: CWE-22/path-traversal
  path: internal/tools/migrate.go
  class: unreachable          # false-positive | unreachable | accepted-risk
  reason: >
    CLI 전용 경로. 입력 경로는 배포 파이프라인이 생성하며
    HTTP 표면에 노출되지 않음. 위협 모델 문서 참조.
  evidence: docs/threat-model.md#migrate-cli
  owner: platform-team
  expires: 2026-11-30         # 만료 시 다음 스캔에서 자동 재개방

- finding: FD-2026-05-0902
  rule: CWE-327/weak-hash
  path: legacy/auth/session.rb
  class: accepted-risk
  reason: "레거시 세션 호환. 2026-09 마이그레이션으로 제거 예정."
  compensating-control: "세션 TTL 15분 + IP 바인딩"
  evidence: JIRA-SEC-2841
  owner: identity-team
  expires: 2026-09-30
만료일·소유자·근거가 필수 필드인 억제 규칙

그리고 이 파일 자체를 CI에서 검사합니다. 만료된 억제, 근거 링크가 끊어진 억제, 소유 팀이 사라진 억제는 파이프라인에서 걸립니다.

bashbash
# 만료·고아 억제 검사 (주간 스케줄 잡에 배치)
flawdetector suppressions lint \
  --max-age 180d \
  --require-evidence \
  --require-owner \
  --fail-on expired,orphaned

# 억제된 결함만 다시 열어서 보기
flawdetector scan --include-suppressed --format table

무엇을 측정할 것인가

오탐률은 도구를 평가하는 지표이지 팀을 평가하는 지표가 아닙니다. 팀의 보안 운영 건강도를 보려면 다른 것을 봐야 합니다.

보안 큐 운영 지표
지표정의권장 목표
트리아지 리드타임 (P50)결함 생성 → 첫 판정까지24시간 이내
억제 만료 준수율만료일이 지났는데 남아 있는 억제 비율0%
재개방률억제 해제 후 다시 취약으로 확정되는 비율5% 미만
Critical 봉인 리드타임탐지 → sealed 판정까지72시간 이내

이 네 가지가 건강하면 오탐률이 3%든 5%든 파이프라인은 굴러갑니다. 반대로 이 네 가지가 무너져 있으면 오탐률 1%짜리 도구를 들여와도 결과는 같습니다. 큐는 계속 밀리고, 알림은 계속 무시됩니다.

정리

정확도를 올리는 일은 엔진의 몫이고, 우리는 그 일을 계속합니다. 다만 도입 현장에서 실제로 성패를 가른 변수는 대부분 운영 쪽에 있었습니다. 억제에 만료일이 있는가, 결함에 주인이 있는가, 첫 판정까지 며칠이 걸리는가. 이 세 가지를 먼저 정리한 팀은 어떤 도구를 써도 큐를 관리했고, 정리하지 않은 팀은 어떤 도구를 써도 큐에 파묻혔습니다.

스캐너를 도입하기 전에 정할 것은 임계값이 아니라 누가, 언제까지, 무엇을 근거로 닫는가입니다.

자주 묻는 질문

오탐률이 낮은 도구를 쓰면 트리아지 문제가 해결되나요?
부담은 줄지만 해결되지는 않습니다. 트리아지 큐에서 시간을 가장 많이 쓰는 항목은 '진짜 오탐'이 아니라 도달 가능성 판단과 위험 수용 결정입니다. 이 둘은 도구 정확도와 무관하게 조직이 기준을 정해야 하는 영역입니다.
억제 규칙에 만료일을 강제하면 관리 부담이 늘지 않나요?
단기적으로는 늘어납니다. 다만 만료 없는 억제는 시간이 지날수록 '왜 닫혔는지 아무도 모르는 결함' 목록으로 변하고, 감사나 사고 대응 시점에 훨씬 큰 비용으로 돌아옵니다. 상한 180일에 자동 재개방을 걸면 실제 갱신 부담은 분기당 한 번 수준입니다.
게이트를 어느 심각도부터 켜는 것이 좋은가요?
신규 코드의 Critical만 막는 구성으로 시작하는 것을 권합니다. 기존 부채까지 한 번에 막으면 팀이 게이트를 끄는 결정으로 이어지는 경우가 많습니다. 부채는 백로그로 분리해 별도 속도로 처리하는 편이 정착률이 높습니다.
도달 불가로 판단한 결함도 리포트에 남겨야 하나요?
남겨야 합니다. 도달 불가는 코드 구조에 의존하는 판단이고, 리팩터링 한 번으로 뒤집힐 수 있습니다. 근거와 만료일을 함께 기록해 두면 구조가 바뀌었을 때 다시 검토할 근거가 됩니다.

이어서 읽기

전체 글

내 리포지토리에서 루프를 직접 확인하세요

리포를 연결하면 전체 스캔, 머지 가능한 패치, 그리고 Critical 결함마다 판정 로그를 받아볼 수 있습니다. 카드 등록은 필요 없습니다.