수십 년 동안 정적 애플리케이션 보안 테스트(SAST)는 보안 팀이 코드 리뷰를 확장하는 가장 효과적인 방법 중 하나였습니다.
하지만 Codex Security를 구축할 때 우리는 의도적으로 설계 방향을 선택했습니다. 정적 분석 보고서를 가져와 에이전트가 이를 분류하도록 하는 방식에서 시작하지 않았습니다. 우리는 시스템이 리포지터리 자체, 즉 아키텍처, 신뢰 경계, 의도된 동작에서 시작하도록 설계했으며, 사람이 시간을 들이기 전에 발견된 내용을 검증하도록 했습니다.
이유는 간단합니다. 가장 어려운 취약점은 대개 데이터 흐름 문제가 아닙니다. 코드가 보안 검사를 수행하는 것처럼 보이지만, 그 검사가 시스템이 의존하는 속성을 실제로 보장하지 않을 때 발생합니다. 다르게 말하면, 과제는 단순히 데이터가 프로그램을 통해 어떻게 이동하는지를 추적하는 데 그치지 않고, 코드 내 방어 장치가 실제로 제대로 작동하는지를 판단하는 것입니다.
SAST는 종종 깔끔한 파이프라인으로 설명됩니다. 신뢰할 수 없는 입력의 소스를 식별하고, 프로그램을 통해 데이터를 추적한 뒤, 해당 데이터가 정제(sanitization) 없이 민감한 싱크에 도달하는 경우를 표시합니다. 간결하고 효율적인 모델이며, 실제 버그를 폭넓게 포착합니다.
실제로 SAST는 대규모에서도 처리 가능하도록 근사 방식을 사용할 수밖에 없습니다. 특히 간접 참조, 동적 디스패치, 콜백, 리플렉션, 프레임워크 중심의 제어 흐름이 많은 실제 코드베이스에서는 더욱 그렇습니다. 이러한 근사는 SAST의 한계라기보다, 코드를 실행하지 않고 추론해야 할 때 마주하는 현실입니다.
그 자체만으로는 Codex Security가 SAST 보고서로 시작하지 않는 이유가 되지는 않습니다.
더 중요한 문제는 소스를 싱크까지 성공적으로 추적한 이후에 무엇이 일어나는가입니다.
정적 분석이 여러 함수와 레이어를 거쳐 입력을 올바르게 추적하더라도, 결국 취약점의 존재 여부를 결정하는 질문에 답해야 합니다.
일반적인 패턴을 하나 살펴보겠습니다. 코드는 신뢰할 수 없는 콘텐츠를 렌더링하기 전에 sanitize_html() 같은 함수를 호출합니다. 정적 분석기는 해당 정화 함수가 실행된 것을 확인할 수 있습니다. 하지만 해당 새니타이저가 특정 렌더링 컨텍스트, 템플릿 엔진, 인코딩 방식, 그리고 이후 변환 과정까지 고려했을 때 실제로 충분한지 여부는 판단하지 못하는 경우가 많습니다.
바로 그 지점부터 문제가 복잡해집니다. 문제는 단순히 데이터가 싱크에 도달하는지 여부가 아닙니다. 코드 내 검사가 시스템이 가정하는 방식대로 실제로 값을 제한하고 있는지 여부입니다.
다르게 말하면, “코드가 새니타이저를 호출한다” 와 “시스템이 안전하다.”사이에는 큰 차이가 있습니다.
실제 시스템에서 매우 자주 나타나는 패턴입니다.
웹 애플리케이션이 JSON 페이로드를 수신하고 redirect_url을 추출한 다음, 이를 허용 목록 정규식과 대조하여 검증하고 URL 디코딩한 뒤, 결과를 리디렉션 핸들러에 전달합니다.
고전적인 소스~싱크 보고서는 다음과 같은 흐름을 설명할 수 있습니다.
신뢰할 수 없는 입력 → 정규식 검사 → URL 디코딩→ 리디렉션
하지만 핵심 질문은 해당 검사가 존재하는지 여부가 아닙니다. 중요한 것은 그 검사가 이후 변환 과정을 거친 뒤에도 여전히 값을 제한하고 있는지 여부입니다.
정규식이 디코딩 전에 실행된다면, 리디렉션 핸들러가 해석하는 방식대로 실제로 디코딩된 URL 을 제약하나요?
이에 답하려면 전체 변환 체인을 기준으로 추론해야 합니다. 즉, 정규식이 허용하는 범위, 디코딩과 정규화의 동작 방식, URL 파싱이 엣지 케이스를 처리하는 방식, 그리고 리디렉션 로직이 스킴과 권한을 어떻게 해석하는지를 포함합니다.
실제로 중요한 취약점은 다음과 같은 형태를 보입니다. 작업 순서 오류, 부분적 정규화, 파싱의 모호성, 그리고 검증과 해석 간의 불일치입니다. 데이터 흐름은 눈에 보입니다. 약점은 제약 조건이 변환 체인을 통해 전파되는 방식 또는 전파되지 못하는 방식에 있습니다.
이는 단순한 이론적 패턴에 그치지 않습니다. CVE-2024-29041(새 창에서 열기)에서 Express는 리디렉션 대상이 인코딩된 후 해석되는 방식 때문에 잘못된 형식의 URL이 일반적인 허용 목록 구현을 우회할 수 있는 오픈 리디렉션 문제의 영향을 받았습니다. 데이터 흐름 자체는 단순했습니다. 더 어려운 질문이자 버그 존재 여부를 결정한 핵심은 변환 체인 이후에도 검증이 여전히 유효한지 여부였습니다.
Codex Security는 단순한 목표를 중심으로 설계되었습니다. 더 강력한 근거를 기반으로 문제를 드러내어 분류 작업을 줄이는 것입니다. 제품에서는 repo별 컨텍스트(위협 모델 포함)를 활용하고, 고신호 문제를 노출하기 전에 격리된 환경에서 검증하는 것을 의미합니다
Codex Security는 “검증” 또는 “정제”처럼 보이는 경계를 마주하더라도 이를 단순한 체크 항목으로 취급하지 않습니다. 코드가 무엇을 보장하려 하는지 이해하려고 시도한 다음, 그 보장을 반증하려고 합니다.
실제로는 대개 다음과 같은 요소가 혼합된 형태로 나타납니다.
- 전체 리포지터리 맥락에서 관련 코드 경로를 보안 연구자처럼 분석하고, 의도와 구현 간의 불일치를 찾습니다. 여기에는 코멘트가 포함되지만 모델이 이를 그대로 신뢰하는 것은 아니므로 실제로 버그가 있다면 코드 위에 //Halvar says: this is not a bug 를 추가하더라도 모델이 혼란을 겪지는 않습니다.
- 문제를 가장 작고 테스트 가능한 단위(예: 단일 입력의 변환 파이프라인)로 축소해, 시스템의 다른 부분에 방해받지 않고 이를 추론할 수 있도록 합니다. 이러한 방식으로 Codex Security는 작은 코드 조각을 추출한 뒤, 이를 위한 마이크로 퍼저를 생성합니다.
- 각 검사를 개별적으로 다루기보다는 변환 전반에 걸쳐 제약 조건을 추론하는 방식입니다. 적절한 경우 이는 충족 가능성 문제로 정식화하는 과정도 포함될 수 있습니다. 다시 말해 우리는 모델에 z3-solver가 포함된 Python 환경에 대한 접근 권한을 부여하며, 특히 복잡한 입력 제약 조건 문제를 해결할 때 인간처럼 필요에 따라 이를 활용하도록 합니다. 이는 특히 비표준 아키텍처에서 정수 오버플로와 같은 버그를 분석할 때 유용합니다.
- 가능한 경우 샌드박스 검증 환경에서 가설을 실행해 “문제가 될 수 있음”과 “실제 문제임”을 구분합니다. 디버그 모드로 컴파일된 코드가 포함된 완전한 엔드투엔드 PoC만큼 확실한 증거는 없습니다.
이것이 핵심적인 전환점입니다. “검사가 존재한다”에서 멈추는 대신, 시스템은 “불변 조건이 성립하는지 여부와 그 근거”를 확인하는 방향으로 나아갑니다. 그리고 모델은 해당 작업에 가장 적합한 도구를 선택합니다.
합리적인 반응은 “둘 다 하면 되지 않을까?”일 수 있습니다. SAST 보고서로 시작한 다음, 에이전트를 활용해 더 깊이 추론할 수도 있습니다.
사전 계산된 결과가 유용한 경우도 있습니다. 특히 범위가 좁고 이미 알려진 버그 유형에서 그렇습니다. 하지만 컨텍스트 기반으로 취약점을 발견하고 검증하도록 설계된 에이전트의 경우, SAST 보고서에서 시작하면 세 가지 예측 가능한 실패 모드가 발생합니다.
첫째, 탐색 범위를 지나치게 빨리 좁히게 만들 수 있습니다. 결과 목록은 도구가 이미 탐색한 영역을 보여주는 지도입니다. 이를 출발점으로 삼으면 동일한 영역에 과도한 노력을 집중하게 되고, 같은 추상화에 의존하며, 도구의 관점에 맞지 않는 문제 유형을 놓칠 수 있습니다.
둘째, 되돌리기 어려운 암묵적인 판단이 개입될 수 있습니다. 많은 SAST 결과에는 정제, 검증 또는 신뢰 경계에 대한 가정이 내포되어 있습니다. 이러한 가정이 잘못되었거나 불완전한 경우 이를 추론 루프에 입력하면 에이전트가 ‘조사’가 아니라 ‘확인 또는 기각’으로 치우칠 수 있으며, 이는 우리가 원하는 방식이 아닙니다.
셋째, 추론 시스템을 평가하기가 더 어려워질 수 있습니다. 파이프라인이 SAST 출력으로 시작하면 에이전트가 자체 분석을 통해 발견한 내용과 다른 도구로부터 상속받은 내용을 구분하기가 어려워집니다. 시스템의 역량을 정확하게 측정하려면 이러한 분리가 중요하며 이는 시간이 지남에 따라 시스템이 개선되는 데 필요합니다.
그래서 Codex Security는 보안 리서치가 시작되는 지점인 코드와 시스템의 의도에서 출발하도록 설계되었습니다. 또한 사람의 개입 전에 검증을 통해 신뢰도를 높입니다.
SAST 도구는 본래 설계된 목적에 매우 뛰어납니다. 안전한 코딩 표준을 강제하고, 단순한 소스-싱크 문제를 포착하며, 예측 가능한 트레이드오프를 기반으로 알려진 패턴을 대규모로 탐지할 수 있습니다. 이들은 심층 방어 전략에서 중요한 역할을 할 수 있습니다.
이 글은 범위를 더 좁혀, 행동을 기반으로 추론하고 결과를 검증하는 에이전트가 정적인 결과 목록에 의존해 작업을 시작해서는 안 되는 이유를 다룹니다.
또한 순수한 소스-싱크 관점에는 한계가 있다는 점도 짚고 넘어갈 필요가 있습니다. 모든 취약점이 데이터 흐름 문제인 것은 아닙니다. 많은 실제 장애는 상태와 불변 조건 문제입니다. 워크플로우 우회, 권한 격차, 그리고 ‘시스템이 잘못된 상태에 있다’와 같은 버그가 여기에 해당합니다. 이러한 유형의 버그에서는 오염된 값이 특정 “위험한 싱크”에 도달하지 않는 경우가 많습니다. 위험은 프로그램이 항상 참일 것이라고 가정하는 부분에 있습니다.
보안 도구 생태계는 계속 발전할 것으로 예상됩니다. 정적 분석, 퍼징, 런타임 보호 기법, 그리고 에이전트 기반 워크플로가 모두 각자의 역할을 하게 될 것입니다.
Codex Security가 잘 수행하기를 바라는 부분은 보안 팀에 가장 큰 비용이 드는 영역입니다. 즉, “수상해 보인다”는 판단을 “실제 문제이며, 실패 방식은 이렇고, 시스템 의도에 맞는 해결 방법은 이것이다”로 전환하는 것입니다.
Codex Security가 리포지터리를 스캔하고 발견 사항을 검증하며 수정안을 제안하는 방식에 대해 자세히 알아보려면 문서(새 창에서 열기)를 참고하세요.


