코드 보안 분석기를 만들고 있습니다. 그런데 보안 도구가 그 안에서 치명적인 결함을 찾아냈습니다.
요약
코드 보안 분석기인 vibeanalyzer를 개발하던 중, 기존 보안 도구인 Semgrep을 통해 프로젝트 내 심각한 취약점이 발견된 사례를 다룹니다. 직접 의존성인 vitest에서 발견된 경로 탐색(Path Traversal) 취약점 등 개발 도구 체인의 보안 중요성을 강조합니다.
핵심 포인트
- 직접 의존성(Direct dependency)의 보안 취약점은 매우 치명적임
- Semgrep과 같은 기존 정적 분석 도구 활용의 중요성
- 간접 의존성(Transitive dependencies)을 통한 보안 위협 존재
- 개발 툴체인 보안이 프로덕션 환경만큼 중요할 수 있음
저는 코드를 점검하는 데 도움을 주기 위한 도구를 만들고 있습니다. 현재는 vibeanalyzer라고 부릅니다. 아이디어는 간단합니다. 우리 중 많은 이들이 '바이브 코딩 (vibe-code)'을 합니다. 즉, 에이전트가 코드를 작성하게 두고, 코드는 깔끔해 보이며 테스트도 통과하지만, 우리가 프로젝트에 무엇을 들여왔는지 실제로 전혀 모르는 상태를 말합니다. 제 주변의 누군가가 이를 완벽하게 표현했습니다: "내부에 무엇이 들어있는지는 모르겠지만, 겉으로는 작동하기를 원한다."
분석기에 완전히 몰입하기 전에, 저는 마침내 오래전에 했어야 했던 일을 했습니다. 누군가 이미 저보다 더 잘 해결한 방법이 있는지 확인하는 것이었습니다. 이미 있었습니다. Semgrep이라고 불리는 도구였죠. 그리고 그 도구가 가장 먼저 한 일은 제 보안 분석기 자체에서 치명적인 (Critical) 취약점을 찾아낸 것이었습니다.
이 포스트는 그 아이러니와 그로부터 파생된 결과에 관한 것입니다.
미리 고백하자면: 저는 표준 도구들을 몰랐습니다
저는 독학했습니다. 컴퓨터 과학 (CS) 학위도 없고, 어깨 너머로 가르쳐주는 선배도 없으며, 토론과 저 자신의 실수를 통해 배웁니다. 그래서 Semgrep, CodeQL, SonarQube, Snyk와 같이 이미 확립된 도구들이 수년 동안 이 일을 해오고 있다는 사실을 제대로 알지 못한 채 코드 분석기 구축을 시작했습니다.
그 부분은 아마 여러분이 댓글에서 가장 먼저 지적할 부분일 것이기에, 제가 먼저 말하겠습니다. 네, 저는 이미 존재하는 것들을 확인하지 않고 솔루션을 만들기 시작했습니다. 그래서 마침내 확인해 보았습니다. 보안을 지켜줘야 할 도구인 vibeanalyzer 저장소에 Semgrep을 실행해 보았습니다.
발견된 내용
몇 분 만에 다음과 같은 결과가 나왔습니다:
하나의 미관상의 문제 (cosmetic issue). 디렉토리 이름을 생성하는 데 SHA-1이 사용되었습니다. 제 경우 이것은 보안 구멍은 아닙니다 (비밀번호나 서명이 아니라, 중요하지 않은 지점에서의 해시 선택일 뿐입니다). 하지만 이것은 제가 스스로는 절대 찾아내지 못했을 바로 그런 종류의 문제입니다.
그리고 의존성 (dependencies)에서 6개의 결과가 발견되었습니다. 이 부분에서 저는 정신이 번쩍 들었습니다. 왜냐하면 이것들은 미관상의 문제가 아니었기 때문입니다:
- Critical (심각):
vitest에서 발견된 경로 탐색 (Path Traversal) 취약점입니다. UI 서버가 실행 중일 때, 누군가가 프로젝트 외부의 파일을 읽고, 쓰고, 실행할 수 있게 합니다. 그리고 이것은 제가 직접 가져온 직접 의존성 (Direct dependency)입니다. - Two High (높음) 2개:
esbuild(무결성 검사 없이 바이너리 다운로드)와vite(.env및 인증서 파일의 서빙을 막아야 하는 가드(Guard)를 우회)에서 발견되었습니다. - Three Medium (중간) 3개:
vite,esbuild,launch-editor전반에 걸친 경로 탐색 (Path traversal) 및 민감 데이터 노출 (Sensitive-data exposure) 변종들입니다.
이것이 실제로 얼마나 심각한지에 대해서는 공정하게 판단해야 합니다. 이들 대부분은 _간접 의존성 (Transitive dependencies)_입니다. 즉, 제가 직접 설치한 것이 아니라 제 의존성들의 의존성입니다. 이들은 모두 개발 툴체인 (Dev toolchain)에 위치하므로, 주로 실행 중인 개발 서버를 위협할 뿐 반드시 프로덕션 (Production)을 위협하는 것은 아닙니다. 또한 실제 공격 확률 (EPSS)은 전반적으로 1% 이하로 낮습니다. 불이 난 집과 같은 상황은 아닙니다.
하지만 그 vitest 취약점은 _직접적 (Direct)_이며 _심각 (Critical)_합니다. 그것은 제가 직접, 곧바로 프로젝트로 끌어들인 것이며, 저는 전혀 알지 못했을 것입니다.
불편한 질문
여러분이 물어볼 질문을 제가 대신 하겠습니다. 왜냐하면 곧 나올 질문이라는 것을 알기 때문입니다: 보안 점검 도구 자체가 어떻게 심각한 (Critical) 취약점을 가질 수 있는가?
답은 불편하며, 이것이 바로 이 프로젝트의 핵심입니다. 발견된 문제는 제 로직에 있는 것이 아니라 의존성 (Dependencies)에 있습니다. 제가 할 수 있는 한 정직하게 코드를 작성하더라도 공급망 (Supply chain)은 여전히 저를 공격할 수 있습니다. 왜냐하면 위험은 제가 아니라 제가 그 위에 구축하는 것들에 의해 전달되기 때문입니다. 만약 보안 도구를 실제로 만들고 있으며 이 문제에 적극적으로 관심을 기울이는 제가 이것을 잡아내지 못했다면, 에이전트(Agent)가 앱을 만들게 하고 바로 배포해 버린 '바이브 코더 (Vibecoder)'에게 무슨 기회가 있겠습니까?
없습니다. 그것이 이 프로젝트가 존재해야 하는 이유입니다.
Semgrep이 할 수 없는 것 — 그리고 그럼에도 불구하고 제가 이것을 만드는 이유
저는 여기서 "Semgrep을 사용하고 여러분의 도구는 잊으세요"라고 말하며 멈출 수도 있습니다. 그리고 솔직히 말해서, 공급망 (supply chain) 보안이나 알려진 위험한 코드 패턴에 대해서라면 그 말이 맞습니다. Semgrep은 이를 더 결정론적 (deterministically)이고 무료로, 더 잘 수행합니다. 저는 Semgrep이 잘하는 분야에서 그를 이기려고 하는 것이 아니며, 만약 제가 그렇다고 주장한다면 여러분은 저를 믿어서는 안 됩니다.
하지만 저는 Semgrep이 말하지 않은 부분에서 무언가를 발견했습니다. Semgrep은 그 코드가 제 프로젝트가 실제로 의도한 대로 작동하는지 여부를 알려주지 않았습니다. Semgrep은 제가 프로젝트를 위해 설정한 경계 (boundaries)를 알지 못합니다. 어떤 함수가 잘 작동하고 안전하더라도, 그것이 아예 존재해서는 안 될 문제를 해결하고 있다는 사실을 Semgrep은 말해줄 수 없습니다. Semgrep은 위험한 패턴은 알지만, _의도 (intent)_는 알지 못합니다.
그 사각지대 — 즉, 코드가 실제로 하는 일과 원래 의도했던 일 사이의 간극 — 가 바로 이 시리즈 전체의 주제입니다. 저는 이것을 '의도 간극 (intent gap)'이라고 부릅니다. (네, 이 용어는 마케팅이나 UX 분야에서도 사용됩니다. 하지만 제가 말하는 것은 저만의 좁은 의미에서의 용어입니다. 즉, 코드와 그 의도된 목적 사이의 거리입니다.)
그리고 그것이 바로 제가 vibeanalyzer를 구축하고 있는 핵심 원리입니다. 이 도구가 가장 먼저 하는 일은 프로젝트의 의도와 비목표 (non-goals), 즉 넘어서는 안 될 가드레일 (guardrails)을 로드하거나 사용자에게 생성을 요청하는 것입니다. 그래야 AI가 코드를 평가할 때, 실제 목표가 무엇인지 그리고 무엇이 이미 범위를 벗어났는지를 알 수 있기 때문입니다. 이것은 정적 분석기 (static analyzers)가 채워주지 못하는 간극입니다. 왜냐하면 정적 분석기는 코드 뒤에 숨겨진 아이디어인 '의도'를 읽지 못하기 때문입니다. 그리고 이것이야말로 이미 존재하는 도구들과 나란히 구축할 가치가 있다고 감히 말할 수 있는 유일한 부분입니다.
현재 진행 상황
의도(intent)와 비목표(non-goal) 로딩을 완료했습니다. TypeScript에 대한 초기 머신 분석(machine analysis)과 폴더 구조를 나타내는 Mermaid 그래프가 포함된 기본적인 마크다운(markdown) 출력을 구현했습니다. 앞으로 남은 과제는 다음과 같습니다. 머신 보안 계층(machine security layer)(참고로, 현재 Semgrep이 저보다 더 잘하고 있다는 점을 분명히 말해두어야겠습니다), AI 계층의 골격, 그리고 이 프로젝트의 핵심이자 가장 불확실한 부분인 AI 로직 그 자체입니다. AI가 의도(intent)에 따라 코드를 의미 있게 평가할 수 있을지, 아니면 그저 오탐(false alarms)의 홍수가 될지 저는 전혀 알지 못합니다. 웹사이트를 대상으로 시도해 보았지만, 저장소(repository)에서 어떻게 작동할지는 아직 확인하지 못했습니다. 설령 결과가 좋지 않더라도 그 과정에 대해 기록할 것입니다.
이 시리즈는 공개적으로 구축(building in the open)하는 것에 관한 것입니다. 완성된 제품이나 보장을 기대하지 마십시오. 대신, 마침내 표준 도구들을 실행해 보고, 자신의 프로젝트로부터 타격을 입은 뒤에도 계속 나아가기로 결심한 누군가의 기록을 기대하십시오. 왜냐하면 오늘 이후로 그가 채우고자 하는 간극이 더욱 명확해졌기 때문입니다.
다음 파트에서는 제 TS 코드의 머신 분석(machine analysis)이 실제로 무엇을 생성하는지, 그리고 그것이 AI 계층이 수행해야 하는 작업과 어디에서 갈라지기 시작하는지를 깊이 파고들 것입니다. 그 지점이 바로 '의도 간극(intent gap)'이라는 말이 단순한 멋진 문구에 그치지 않고, 작동하는 코드가 되거나 혹은 시도하다가 무너져야 하는 지점입니다.
덧붙이자면: 이 글을 쓴 후 vitest 버전을 올렸더니, 나머지 의존성들이 전이적으로(transitively) 연결되어 있었기 때문에 6개의 의존성 탐지 결과가 한꺼번에 해결되었습니다. 이제 Semgrep은 0건을 보고합니다. 이것이 프로젝트가 "안전"하다는 뜻은 아닙니다. 알려진 6개의 CVE가 사라졌다는 뜻일 뿐입니다. 알려지지 않은 취약점과 저 자신의 로직 버그는 여전히 탐지되지 않습니다. 그것이 제가 여전히 이 도구를 만들고 있는 이유 전부입니다. 그리고 네, 아주 오래전에 패치했어야 할 취약점에 대해 포스트를 작성했습니다. 그것이 바로 '바이브 코딩(vibecoding)'의 본질입니다. 심지어 그것을 감시해야 하는 도구 안에서조차 말이죠.
AI 자동 생성 콘텐츠
본 콘텐츠는 Dev.to AI tag의 원문을 AI가 자동으로 요약·번역·분석한 것입니다. 원 저작권은 원저작자에게 있으며, 정확한 내용은 반드시 원문을 확인해 주세요.
원문 바로가기