LLM을 이용한 서비스 취약점 분석 자동화 #1

표상영 · 토스 Security Researcher
2025년 12월 24일

안녕하세요. 토스 Security Researcher 표상영입니다.

요즘 LLM이 정말 다양한 분야에서 활용되고 있죠. 토스에서도 여러 업무에 LLM을 접목하며 효율성을 높이고 있는데요. 그중에서도 LLM을 이용해 서비스 취약점 분석을 자동화한 경험을 공유해 보려고 합니다. 이번 글에서는 이 프로젝트를 진행하면서 마주했던 문제점과 그에 대한 해결책을 소개해 드리고, 이어서 2편에서 실제로 어떻게 구현했는지를 공유할 예정이에요.

Google, Project Naptime

구글에는 재미있는 프로젝트가 하나 있습니다.

LLM을 이용해 취약점 분석을 자동화하고 낮잠 시간을 만들자!”라는 목적의 Naptime(낮잠) 프로젝트가 바로 그것인데요.

https://googleprojectzero.blogspot.com/2024/06/project-naptime.html / Naptime architecture

당시 ‘LLM이 자율적으로 소스코드를 분석하고 취약점을 찾는다’라는 것은 분석가들 머릿속에만 있는 상상 속의 무언가 였습니다. 저 또한 그런 생각을 갖고있던 찰나, 꽤 구체적인 아이디어와 함께 실제로 유의미한 결과를 도출한 Project Naptime 글을 흥미롭게 읽게 되었어요.

글을 읽고 난 뒤, ‘정말 가능할까?’에서 ‘진짜 가능하구나!’라는 가능성을 확인하고, 곧 바로 길드(토스의 작은 프로젝트 그룹)를 만들고 길드원을 모으기 시작했습니다. 모든 팀원분들이 그랬지만, 새롭게 나오는 기술들로 공부해야할 것들은 너무 많았고 자원은 한정되어 있어서 리소스 절약이 정말 절실했거든요.

시작부터 만만치 않았던 여정

"LLM으로 취약점 분석? 간단하겠네!" 라고 생각하며 시작했지만, 현실은 녹록치 않았어요.

첫 번째 벽: 대용량 소스코드를 어떻게 넣을까?

가장 먼저 부딪힌 문제는 대용량 소스코드를 LLM에 넣어주는 것이었어요. 토스의 서비스들은 대부분 상당한 규모의 소스코드를 가지고 있는데, 이 모든 코드를 LLM의 토큰 한계 안에 맞추는 것부터 쉽지 않았습니다.

처음에는 RAG(Retrieval Augmented Generation)를 시도해 봤어요. 코드를 벡터로 임베딩해서 필요한 부분만 가져오는 방식이었는데, 코드 간 연관관계를 제대로 파악하기 어려웠어요. 그래서 Repomix 같은 툴로 전체 소스코드를 압축해서 던져주는 것도 시도해 봤습니다. 하지만 결국 모든 시도가 토큰 문제로 실패했어요.

설사 토큰 Context Size 안에 맞춘다고 해도 더 큰 문제가 있었어요. 바로 Hallucination이었습니다. 너무 많은 정보를 한 번에 처리하다 보니 LLM이 실제로는 없는 취약점을 만들어내거나, 코드의 흐름을 잘못 이해하는 경우가 빈번했거든요.

해결책을 찾다: MCP로 자유롭게 소스코드 탐색하기

여러 논문과 레퍼런스를 찾아본 결과, LLM Agent가 자유롭게 소스코드를 참조할 수 있도록 하는 MCP(Model Context Protocol)를 만드는 방향으로 해결할 수 있다는 걸 알게 됐어요.

이를 위해 개발한 SourceCode Browse MCP는 다음과 같은 구조로 동작해요:

이렇게 소스코드를 사전에 인덱싱 해두면, LLM이 필요할 때마다 MCP Tool Calling을 통해 "이 함수는 어디서 정의되지?", "이 변수는 어디서 사용되지?" 같은 정보들을 효율적으로 참조할 수 있게 만들 수 있었어요.

두 번째 벽: 일관성 없는 결과와 정확도

MCP 덕분에 소스코드 참조 문제는 해결됐지만, 이제 정확도 문제가 발목을 잡았어요. 사용한 모델은 Claude-Sonnet-4 였는데, 취약점을 잘 찾긴 했지만 일관성이 없었거든요.

예를 들어 XSS 취약점이 총 10개 있으면, 어떨 때는 8개만 찾고 2개를 빼먹고, 어떨 때는 10개를 다 찾기도 하고, 심지어 9개를 찾았다가 다시 돌리면 7개만 찾기도 하는 등 결과에 일관성이 없었어요. 이렇게는 도저히 LLM을 신뢰할 수 없었죠.

SAST 도구와의 만남: CodeQL vs Semgrep

이 문제를 해결하기 위해 SAST(Static Application Security Testing) 도구를 결합하기로 했습니다. 대표적인 오픈소스 SAST 도구로는 CodeQL과 Semgrep이 있습니다. 저는 평소 취약점 분석을 할 때, CodeQL을 애용하기 때문에 당연하게도 CodeQL을 사용하려고 했었는데요.

CodeQL의 경우 다음과 같은 한계가 있었어요:

Semgrep은 훨씬 가벼웠어요:

왜냐하면 제가 SAST 도구로 하려는 건 취약점 분석이 아니라 모든 Source→Sink 경로를 수집하는 것이었거든요.

핵심 아이디어: 모든 입력 경로를 빠짐없이 분석하기

취약점은 결국 신뢰할 수 없는 입력(Untrusted Input)으로부터 발생합니다. 그래서 다음과 같은 전략을 세웠어요:

실제로 이 방식을 적용하니 항상 취약점을 놓치지 않고 찾아내는 결과를 얻을 수 있었어요!

세 번째 벽: 비용의 현실

그런데 문제가 또 생겼어요. 모든 입력 경로를 다 분석하게 했더니, 분석할 필요가 없는 코드까지 분석하느라 쓸데없는 토큰이 너무 많이 소모되는 거예요.

예를 들어 총 100개의 경로가 있으면 XSS가 발생할 수 있는 경로는 10개 뿐인데, 나머지 90개의 의미없는 경로도 분석하는 상황이었죠. 토큰은 곧 비용이니까 반드시 해결해야 하는 문제였어요.

Multi-Agent로 효율성 높이기

이 문제는 Multi-Agent 시스템으로 해결할 수 있었어요. 3개의 에이전트로 구성했습니다:

이렇게 구성하니 Discovery 에이전트가 거름망 역할을 해줘서, 실질적으로 취약점 분석이 필요한 경로만 Analysis 에이전트가 처리할 수 있었어요. 덕분에 비용도 줄이고 전체 분석 시간도 단축할 수 있었습니다.

네 번째이자 가장 큰 벽: 지속 가능성

여기까지는 기술적으로 성공적이었지만, 가장 중요한 문제가 남아있었어요. 바로 지속 가능성이었죠.

토스에는 수백 개의 서비스가 있어요. 계산해보니:

1회 비용을 100원으로 줄인다고 해도 월 270만원이니, 결코 지속 가능한 비용이 아니었어요.

Open Model로의 전환

이 문제를 해결하려면 Cloud Model이 아닌 Open Model을 사용해야 했습니다. OpenAI도 GPT-OSS 같은 오픈 모델을 공개하고 있고, 이런 모델을 직접 호스팅하면 LLM 비용이 발생하지 않거든요.

물론 Cloud Model보다 성능이나 안정성은 떨어지기 때문에, 이를 보완하기 위한 추가적인 노력이 필요했어요.

후보 모델 선별과 ROI 비교

취약점 분석에 적합하고 MCP 사용이 가능한 모델을 찾아본 결과, 3개 모델로 압축할 수 있었어요:

  • Qwen3:30B
  • gpt-oss:20B
  • llama3.1:8B

실제 여러 유형의 취약점이 존재하는 샘플 프로젝트에서 3개의 IDOR 취약점을 찾는 테스트를 진행했어요. 결과는 다음과 같았습니다:

모델
분석률
정탐률
오탐률
Input 토큰
Output 토큰
Qwen3:30B
100% (14/14)
100% (3/3)
35.7% (5/14)
20,925
2,569
gpt-oss:20B
57.1% (8/14)
100% (3/3)
0% (0/8)
51,558
4,396
llama3.1:8B
28.6% (4/14)
16.7% (0.5/3)
87.5% (3.5/4)
6,563
1,413

비용 순위 (적은 토큰 소모)

1️⃣ llama3.1:8B 2️⃣ Qwen3:30B 3️⃣ gpt-oss:20B

정확도 순위

1️⃣ Qwen3:30B 2️⃣ gpt-oss:20B 3️⃣ llama3.1:8B

여기서 분석률은 모든 입력 경로에 대한 Coverage, 정탐률은 실제 취약점을 정확히 찾은 비율, 오탐률은 취약점이 아닌 것을 취약점으로 잘못 판단한 비율이에요.

발생 비용과 정확도를 종합적으로 고려했을 때 Qwen3:30B가 가장 준수한 성능을 보였어요. gpt-oss는 정탐률과 오탐률에서 완벽한 결과를 보였지만, 커버리지가 떨어지고 MCP Tool Calling이 실패하는 경우가 잦았거든요.

Qwen3:30B 관련 논문이나 시장 평가를 보니 취약점 분석에서 좋은 성능을 보인다는 평가가 많아서 최종적으로 선택했어요.

성능 보완 작업

Open Model은 아무래도 Cloud Model보다 성능이 떨어지기 때문에 보완하는 작업이 필요했어요:

우리가 이뤄낸 결과

이런 과정을 거쳐 최종적으로 정확도 95% 이상의 LLM 취약점 분석 자동화를 구현할 수 있었어요. 처음에는 단순히 ‘LLM으로 취약점 찾기’라는 아이디어로 시작했지만, 실제로는:

이렇게 4단계의 벽을 차례대로 넘어야 했어요. 각 단계마다 기술적 해결책 뿐만 아니라 비용과 운영 측면까지 고려해야 했고, 때로는 성능을 일부 포기하더라도 실용성을 택해야 하는 선택의 순간들이 있었죠.

실제로 어떻게 이 프로젝트를 구현했는지에 대한 더 자세한 내용은, 2편에서 이어서 소개해 드릴게요. 많은 기대 부탁드립니다!

마치며

이번 프로젝트를 통해 배운 가장 중요한 교훈은 "기술적으로 가능한 것"과 "실제로 운영 가능한 것" 사이에는 큰 차이가 있다는 점이었어요. LLM 기술 자체는 충분히 성숙했지만, 이를 실제 서비스에 적용하려면 비용, 성능, 안정성, 지속 가능성을 모두 고려한 종합적인 설계가 필요했거든요.

앞으로도 토스에서는 이런 실용적인 AI 활용 사례들을 더 많이 만들어갈 예정이에요. 기술은 결국 사람을 위해 존재하는 것이니까, 화려한 기술보다는 정말 도움이 되는 기술을 만드는 데 계속 집중하려고 합니다.

긴 글 읽어주셔서 감사합니다.

댓글 0댓글 관련 문의: toss-tech@toss.im
㈜비바리퍼블리카 Copyright © Viva Republica, Inc. All Rights Reserved.