← 블로그 목록

Developer Knowledge

개발자가 알아야 할 지식: 위협 모델링, 보안 리뷰를 설계 습관으로 바꾸는 법

위협 모델링을 거창한 보안 문서가 아니라 기능 설계와 코드 리뷰에서 바로 쓸 수 있는 실무 사고법으로 정리한다.

OWASP Cheat Sheet Series
  • 개발자가 알아야 할 지식
  • Software Engineering
  • Security
  • Threat Modeling

왜 개발자가 알아야 하나

보안 사고는 대개 “몰랐다”보다 “그렇게 연결될 줄 몰랐다”에서 시작된다. 로그인 API는 안전해 보였고, 파일 업로드도 확장자 검사를 했고, 관리자 화면도 사내망 뒤에 있었다. 그런데 공격자는 기능을 하나씩 보지 않는다. 사용자가 입력한 값이 어디로 흐르는지, 어떤 권한 경계를 지나가는지, 실패했을 때 어떤 예외 경로가 열리는지, 팀이 당연하다고 생각한 전제를 어디서 깨뜨릴 수 있는지를 본다. 위협 모델링은 바로 이 관점을 개발 프로세스 안으로 가져오는 방법이다.

많은 팀에서 보안 리뷰는 배포 직전 체크리스트나 외부 진단 보고서로 등장한다. 이 방식은 필요한 안전망이지만, 너무 늦게 도착하면 비용이 커진다. 이미 API 계약이 굳어졌고, 데이터 모델이 퍼졌고, 권한 구조가 여러 서비스에 복제된 뒤에는 작은 수정도 제품 일정 전체를 흔든다. 반대로 설계 초기에 “이 기능에서 무엇이 잘못될 수 있나”를 묻는다면 보안은 별도 행사가 아니라 아키텍처 품질의 일부가 된다.

개발자가 위협 모델링을 알아야 하는 이유는 보안 전문가 흉내를 내기 위해서가 아니다. 실무 개발자는 시스템의 실제 제약을 가장 잘 안다. 어떤 필드가 사용자 입력인지, 어떤 배치 작업이 권한 검사를 우회하는지, 어떤 캐시가 오래 살아남는지, 장애 대응 중 어떤 플래그를 켜는지 같은 정보는 코드와 운영을 만지는 사람에게 있다. 보안팀이 도와줄 수는 있지만, 개발자가 이 정보를 구조적으로 꺼내지 않으면 좋은 질문도 표면에 머문다.

또 하나의 이유는 커뮤니케이션이다. “보안상 위험해 보인다”는 말은 쉽게 추상적 논쟁이 된다. 하지만 “이 데이터 흐름은 외부 사용자가 만든 값을 관리자 권한 작업 큐로 넘긴다”, “여기에는 인증된 사용자와 결제 승인 시스템 사이의 신뢰 경계가 있다”, “토큰 탈취 시 재사용을 막는 통제가 없다”처럼 표현하면 논의가 구체화된다. 위협 모델링은 불안을 문장으로 바꾸고, 문장을 설계 결정으로 바꾸는 공통 언어다.

핵심 개념

위협 모델링은 시스템을 보안 관점에서 모델링하고, 그 모델을 기준으로 가능한 위협과 대응을 식별하는 활동이다. 핵심은 완벽한 도표가 아니라 “무엇을 보호해야 하는가”, “어디서 신뢰가 바뀌는가”, “무엇이 잘못될 수 있는가”, “그 위험을 어떻게 줄일 것인가”를 반복해서 묻는 데 있다. OWASP의 Threat Modeling Cheat Sheet도 다양한 방법론을 소개하지만, 실무에서 중요한 공통 뼈대는 이 네 질문으로 정리할 수 있다.

첫째, 보호할 자산과 중요한 동작을 정한다. 자산은 데이터베이스 전체처럼 큰 덩어리일 수도 있고, 세션 쿠키, 결제 승인 이벤트, 관리자 초대 링크, 웹훅 서명 키처럼 작지만 치명적인 값일 수도 있다. 중요한 동작은 “주문을 확정한다”, “파일을 공개한다”, “사용자 권한을 변경한다”처럼 시스템 상태를 바꾸는 흐름이다. 위협 모델링을 시작할 때 범위를 넓게 잡으면 회의가 금방 산만해진다. 새 기능 하나, API 하나, 데이터 흐름 하나처럼 작게 시작하는 편이 낫다.

둘째, 시스템의 경계와 데이터 흐름을 그린다. 여기서 말하는 그림은 예쁜 아키텍처 다이어그램이 아니라 질문을 위한 도구다. 외부 사용자, 브라우저, 모바일 앱, API 서버, 큐, 워커, 데이터베이스, 외부 결제사, 로그 저장소 같은 요소를 놓고 데이터가 어디서 어디로 이동하는지 표시한다. 특히 신뢰 경계를 찾아야 한다. 익명 사용자에서 인증 사용자로, 사용자 영역에서 내부 서비스로, 애플리케이션에서 외부 SaaS로, 일반 권한에서 관리자 권한으로 넘어가는 지점은 공격자가 노릴 가능성이 높다.

셋째, 각 경계와 흐름에서 가능한 위협을 묻는다. 대표적인 도구가 STRIDE다. Spoofing은 누군가를 가장하는 문제, Tampering은 데이터나 요청을 변조하는 문제, Repudiation은 행위를 부인할 수 있는 문제, Information Disclosure은 정보 노출, Denial of Service는 서비스 거부, Elevation of Privilege는 권한 상승을 뜻한다. STRIDE를 외우는 것 자체가 목적은 아니다. 중요한 것은 “이 요청을 누가 보냈다고 믿는가”, “이 값은 중간에 바뀔 수 있는가”, “나중에 누가 무엇을 했는지 증명할 수 있는가”, “실패가 반복되면 시스템이 버틸 수 있는가” 같은 구체적 질문으로 바꾸는 것이다.

넷째, 대응을 설계하고 우선순위를 정한다. 모든 위협을 제거할 수는 없다. 어떤 것은 인증 강화로 줄이고, 어떤 것은 입력 검증과 출력 인코딩으로 막고, 어떤 것은 속도 제한이나 큐 격리로 완화하고, 어떤 것은 로그와 알림으로 탐지한다. 위험을 받아들이는 경우도 있다. 다만 “괜찮을 것 같다”와 “영향도와 가능성을 보고 이번 범위에서는 받아들인다”는 다르다. 위협 모델링의 산출물은 두꺼운 문서가 아니라, 코드 변경, 설계 변경, 테스트 케이스, 운영 알림, 명시적인 리스크 수용이어야 한다.

좋은 위협 모델은 살아 있다. 기능이 바뀌면 데이터 흐름도 바뀐다. 새 외부 연동을 붙이면 신뢰 경계가 생긴다. 캐시나 비동기 큐를 도입하면 권한 확인 시점과 실행 시점이 분리될 수 있다. 그래서 위협 모델링은 프로젝트 초기에 한 번 하고 끝내는 행사가 아니라, 큰 설계 변경, 민감 데이터 추가, 권한 모델 변경, 외부 API 연결, 장애 대응 절차 변경 때 다시 꺼내는 설계 노트에 가깝다.

작은 예시 또는 체크리스트

예를 들어 사용자가 프로필 이미지를 업로드하고, 서버가 이미지를 리사이즈한 뒤 CDN에 공개하는 기능을 만든다고 하자. 겉보기에는 흔한 기능이지만 데이터 흐름을 쓰면 질문이 늘어난다.

  • 사용자는 브라우저에서 파일을 선택해 API 서버로 업로드한다.
  • API 서버는 파일을 임시 스토리지에 저장하고 리사이즈 작업을 큐에 넣는다.
  • 워커는 파일을 읽어 이미지 라이브러리로 처리한 뒤 공개 버킷에 저장한다.
  • CDN URL은 사용자 프로필 응답에 포함된다.
  • 업로드 실패, 리사이즈 실패, 기존 이미지 교체 시나리오가 있다.

이 흐름에서 바로 보이는 질문은 다음과 같다.

  • 파일 타입 검사는 확장자, MIME 타입, 실제 파일 시그니처 중 무엇을 기준으로 하는가?
  • 이미지 처리 라이브러리에 매우 큰 이미지나 악성 포맷이 들어오면 CPU와 메모리를 얼마나 쓸 수 있는가?
  • 임시 스토리지 객체를 다른 사용자가 추측해서 읽을 수 있는가?
  • 공개 버킷에 저장하기 전에 사용자 권한과 소유권을 다시 확인하는가?
  • 기존 이미지를 교체할 때 이전 URL이 계속 접근 가능한가, 아니면 삭제나 만료가 필요한가?
  • 업로드 실패 로그에 원본 파일명, 내부 경로, 서명 URL 같은 민감 정보가 남는가?
  • 워커가 관리자급 권한을 가진다면, 큐 메시지 변조가 권한 상승으로 이어질 수 있는가?

이 질문들은 보안팀만의 질문이 아니다. 개발자가 구현하면서 결정해야 하는 세부사항이다. 체크리스트로 바꾸면 더 실용적이다.

  • 새 기능 설명을 한 문장으로 쓴다: 누가 어떤 자산에 어떤 변경을 가하는가?
  • 외부 입력, 내부 처리, 외부 출력의 경계를 표시한다.
  • 인증, 권한, 소유권 검사가 필요한 지점을 분리해서 적는다.
  • 실패 경로와 재시도 경로에서도 같은 검사가 유지되는지 본다.
  • 로그, 메트릭, 알림에 민감 정보가 섞이지 않는지 확인한다.
  • 가장 큰 피해를 만들 수 있는 위협 2~3개를 골라 대응을 이슈로 만든다.

이 정도만 해도 “보안 리뷰를 했다”는 모호한 말보다 훨씬 낫다. 특히 작은 팀은 처음부터 정교한 방법론을 도입하려고 하기보다, 중요한 변경마다 20~30분짜리 미니 위협 모델링을 반복하는 편이 지속 가능하다.

실무에서 자주 생기는 오해

  • “위협 모델링은 보안팀 일이니 개발자는 결과만 받으면 된다”는 오해가 있다. 실제로는 개발자가 시스템의 예외 경로와 운영 습관을 가장 잘 안다. 보안팀은 방법과 관점을 제공하고, 개발팀은 구체적 흐름과 제약을 제공해야 한다.

  • “다이어그램을 잘 그리면 끝”이라는 오해도 흔하다. 다이어그램은 질문을 돕는 도구일 뿐이다. 아무 위협도 식별하지 못하고, 아무 설계 결정도 바뀌지 않았다면 예쁜 그림은 보안 통제가 아니다.

  • “STRIDE 항목을 모두 채워야 한다”는 부담 때문에 시작을 미루는 팀도 있다. 모든 칸을 균등하게 채우는 것보다, 실제 피해가 큰 흐름을 깊게 보는 것이 낫다. 결제, 개인정보, 관리자 권한, 인증 토큰, 외부 웹훅처럼 위험한 영역부터 시작하자.

  • “위협 모델링을 하면 모든 위험을 제거해야 한다”는 생각도 비현실적이다. 실무에서는 비용, 일정, 사용자 경험, 운영 복잡도 사이에서 결정한다. 중요한 것은 위험을 모른 채 남기는 것이 아니라, 알고 우선순위를 정하는 것이다.

  • “배포 전에 한 번 하면 충분하다”는 오해는 특히 위험하다. 권한 모델, 데이터 저장 위치, 외부 연동, 비동기 처리, 캐시 정책이 바뀌면 기존 위협 모델은 낡는다. 설계 변경이 보안 전제를 바꾸는지 확인해야 한다.

  • “체크리스트만 있으면 된다”는 오해도 있다. 체크리스트는 반복 실수를 줄이는 데 좋지만, 시스템 고유의 데이터 흐름을 보지 못하면 사각지대가 생긴다. 체크리스트와 흐름 기반 질문을 함께 써야 한다.

오늘 바로 적용해보기

가장 쉬운 시작점은 다음 코드 리뷰에서 “이 변경의 신뢰 경계는 어디인가”를 한 번 묻는 것이다. 새 API가 외부 입력을 받는다면 입력이 어디까지 이동하는지, 어떤 권한으로 실행되는지, 실패했을 때 무엇이 기록되는지 확인한다. 리뷰 코멘트는 추상적인 불안보다 구체적인 질문이어야 한다. “보안 괜찮나요?”보다 “이 큐 메시지는 사용자가 만든 값인데 워커에서 소유권을 다시 확인하나요?”가 낫다.

설계 문서가 있다면 작은 섹션을 추가하자. “보안 고려사항”이라는 제목 아래 긴 문장을 쓰기보다, 보호할 자산, 신뢰 경계, 주요 위협, 대응 또는 남은 리스크를 네 줄로 적는다. 민감한 기능이라면 이 네 줄만으로도 리뷰 품질이 크게 달라진다.

운영 관점도 함께 보자. 위협 모델링은 예방만 다루지 않는다. 어떤 공격은 완전히 막기 어렵지만 빨리 알아차릴 수는 있다. 관리자 권한 변경, 비정상적인 토큰 재발급, 업로드 실패 폭증, 웹훅 서명 검증 실패 같은 이벤트는 로그와 알림으로 연결할 가치가 있다. 설계 단계에서 탐지 가능성을 생각하면 사고 대응이 훨씬 쉬워진다.

마지막으로, 너무 큰 회의로 만들지 말자. 처음부터 조직 표준과 도구를 완성하려고 하면 부담이 커진다. 새 기능 하나를 골라 30분 동안 데이터 흐름을 그리고, 가장 위험한 위협 세 개와 대응 세 개를 적어보자. 반복하면서 팀에 맞는 템플릿과 깊이가 생긴다. 보안 습관은 거창한 선언보다 작고 일관된 질문에서 자란다.

더 알아보기

오늘의 takeaway

위협 모델링은 보안을 늦게 검사하는 절차가 아니라, “무엇이 잘못될 수 있는가”를 설계 초기에 구체적인 코드와 운영 결정으로 바꾸는 개발자의 사고 습관이다.