공격 표면을 자동으로 분석하고 줄이면 가까운 시일 안에 실제 배포된 소프트웨어의 보안을 의미 있게 높일 수 있습니다. 이 글에서는 Claude를 활용해 대규모 오픈소스 코드베이스에 공격 표면 축소 기법을 적용해 얻은 데이터를 미리 공개합니다.
지난 1년간 우리는 최첨단 AI 모델이 보안 취약점을 발견하고 악용하는 데 충분한 능력이 있음을 보여 주는 데이터를 다수 공개했습니다. 하지만 알려지지 않은 취약점이 어떻게 악용될 수 있는지 보여 주는 것은 사이버 방어에서 최첨단 AI 모델이 할 수 있는 일의 출발점일 뿐입니다. 이 글에서는 공격 표면 자동 분석 및 축소에 관한 초기 연구를 요약합니다. 공격 표면 자동 분석 및 축소란 AI 모델로 외부에서 도달 가능한 코드를 파악한 뒤, 특정 배포 환경에서 굳이 노출할 필요가 없는 부분을 제거하거나 접근을 제한하는 과정입니다.
공격 표면을 자동으로 분석하고 줄이면 가까운 시일 안에 실제 배포된 소프트웨어의 보안을 의미 있게 높일 수 있습니다. 개별 취약점을 찾아 고치는 방식만으로는 보안을 확보하기 어렵습니다. 심각한 취약점 하나를 고쳐도 남아 있는 다른 취약점에 대해서는 아무것도 알 수 없기 때문입니다. 반면 코드의 도달 가능성을 줄이면 아직 알려지지 않은 취약점, 심지어 지금 실제로 악용되고 있을 수 있는 취약점의 심각도까지 낮출 수 있습니다. 이 기법은 코드 속 미지의 취약점을 없애지는 못하지만, 취약점에 닿기 어렵게 만들어 심각도를 떨어뜨립니다. 코드베이스의 공격 표면을 최소화하면 개별 취약점을 찾거나 고치지 않고도 보안이 개선됩니다.
아래에서는 Claude를 활용해 대규모 오픈소스 코드베이스에 공격 표면 축소 기법을 적용해 얻은 데이터를 미리 공개합니다. 우리가 제시하는 방법으로 Claude가 소프트웨어 보안을 의미 있게, 그리고 경제적으로 개선할 수 있음을 보여 주려는 것입니다. 구체적으로 다양한 언어로 작성된 25개 프로젝트에 Claude를 돌린 결과 493건의 문제가 발견되었습니다. 이 결과를 바탕으로, 개발자가 자신의 코드에 비슷한 기법을 적용하는 방법도 함께 다룹니다.
소프트웨어는 시간이 지날수록 복잡해지기 마련입니다. 새로운 추상화 계층, 새로운 의존성, 새로운 기능이 조금씩 겹겹이 쌓이기 때문입니다. 그 결과 소프트웨어가 처음 개발자가 의도한 것과 전혀 다르게 동작하기도 합니다. 공격자가 성공하는 이유도 상당 부분 여기에 있습니다. 공격자는 개발자가 의도하거나 설계한 동작이 아니라, 코드가 실제로 어떻게 동작하는지를 정확히 파고들기 때문입니다.
공격 표면 자동 분석 및 축소 기법은 불필요한 공격 표면을 찾아내 해당 코드를 적절한 관문 뒤로 옮기는 것을 목표로 합니다. 예를 들어 인증 전 단계의 코드를 인증 후로, 0-click을 1-click으로, 기본 활성화를 명시적 opt-in으로, 프로토콜의 첫 메시지에서 처리되던 코드를 세션 상태 머신의 더 깊은 단계로 옮기는 식입니다. 이런 단계 이전에도 도달 가능해야 하는 코드는 분명 있지만, 그렇지 않은 코드가 많습니다. 수천, 수백만 줄에 걸쳐 코드가 계속 바뀌다 보면 기능이 불필요하게 노출되어도 눈치채지 못하는 경우가 흔합니다.
코드의 공격 표면을 줄이는 방법은 여러 가지입니다. 우리 연구에서는 특히 다음 세 가지에 집중했습니다.
우리는 Claude가 이 세 가지 공격 표면 기준을 바탕으로 크고 복잡한 코드베이스에서 실제 문제를 얼마나 잘 찾아내는지 알고 싶었습니다. 그래서 C, C++, Java, PHP, Python 등으로 작성된 오픈소스 프로젝트 25개(총 500만 줄 이상)를 분석했습니다. Claude는 이들 프로젝트에서 1만 7,000개가 넘는 설정 옵션의 목록을 만들었고, 이 가운데 3,842개가 보안과 관련된 통제 또는 기능의 설정이었습니다.
연구를 위해 이 오픈소스 프로젝트를 크게 세 범주로 나눴습니다.
이 범주는 결과를 해석하는 데 도움이 됩니다. 예를 들어 Public 코드베이스는 누구든 이용할 수 있게 설계되었으므로 기본 설정에서 도달 가능한 코드의 비중이 클 것으로 예상되지만, Gated 범주의 프로젝트는 그 반대일 것으로 기대합니다.
Claude는 위의 세 가지 방법으로 493건의 문제를 찾았습니다. 결과에서 눈에 띄는 흐름이 몇 가지 있었습니다.
첫째, Gated 프로젝트 11개 모두에서 의도치 않게 도달 가능한 코드가 발견되었습니다(어느 프로젝트에서도 전체 코드 줄 수에서 차지하는 비중이 크지는 않았습니다). 구체적으로 Gated 그룹의 기본 설정에서 불필요한 인증 전 공격 표면이 93건, 인증 전에 코드가 실행되거나 상태가 기록되는 지점이 37곳, 관리자가 설정한 인증 전 통제를 조용히 약화하는 설정 조합이 61건 발견되었습니다. 이 가운데 그 자체로 즉시 악용 가능한 문제는 없었지만, 필요 이상으로 훨씬 넓은 공격 표면을 만들고 있었습니다.

둘째, Claude가 설정 간 상호작용 문제를 다룬 작업에서 (모든 프로젝트 유형에 걸쳐) 가장 흥미롭고 파급력 큰 발견이 일부 나왔습니다. Claude는 오류나 경고 없이 받아들여지고, 상호작용 시 예상 동작에 대한 문서도 없는 보안 관련 설정 조합을 162건 찾았습니다. 이 중 94건은 해당 조합이 보안 통제를 조용히 꺼 버렸습니다. 나머지 68건은 같은 보안 메커니즘에서의 불일치였습니다. 예를 들어 설정상 비활성화되었는데도 리스너나 로그인 경로에 계속 도달할 수 있거나, 두 설정이 충돌하는데 어느 쪽이 우선하는지가 설정 파일에서의 순서에 따라 달라지거나, 관리자가 설정한 보호 기능이 아무 일도 하지 않으면서 그 코드는 도달 가능한 상태로 남아 있는 경우입니다. 이런 설정 조합 중에는 표준적이지 않거나 드물게 쓰이는 것도 있겠지만, 관리자가 해당 프로젝트를 그렇게 설정해 배포하는 것을 막을 방법은 없습니다.
Claude는 서로 무관한 네 프로젝트에서 각각 독립적으로 나타난 특정 패턴도 발견했습니다. 동작 방식은 이렇습니다. 관리자가 원격 클라이언트에 대한 접근 규칙을 IP 주소 대신 호스트명이나 와일드카드 패턴으로 작성합니다. 클라이언트가 접속하면 서버는 이 규칙을 클라이언트의 호스트명과 비교합니다. 그런데 서버가 호스트명을 알 수 있는지는 역방향 DNS 조회를 제어하는 별도 옵션에 달려 있고, 대부분의 프로젝트에서 이 옵션은 꺼져 있었습니다. 조회가 꺼져 있으면 서버가 아는 것은 클라이언트의 IP 주소뿐이고, 서버는 규칙을 그 주소의 문자열과 비교합니다. 하지만 호스트명 패턴은 IP 주소와 결코 일치할 수 없습니다. 따라서 거부 규칙은 절대 작동하지 않고, 목록은 fail open 상태가 됩니다. 영향받는 프로젝트 중 일부는 문서에 호스트명 규칙이 DNS 조회 활성화에 의존한다고 적어 두었지만, 이렇게 작성한 거부 목록이 fail open으로 동작한다고 밝힌 곳은 없었습니다.
셋째, Claude는 두 설정 옵션이 충돌해 보안 검사를 조용히 빠뜨리는 경우를 자주 발견했습니다. 한 사례에서 관리자는 접속하는 모든 클라이언트에게 인증서로 신원을 증명하도록 요구할 수 있었습니다. 다른 옵션은 그 검사를 필요한 시점까지 늦추려는 의도였지만, 실제로는 검사를 완전히 꺼 버렸습니다. 그 결과 신뢰할 수 있는 클라이언트만 받도록 설정한 서버가 누구의 접속이든 받아들이게 되었습니다. 인증서 검사 뒤에 있어야 할 방대한 요청 처리 코드가 인증되지 않은 클라이언트에 노출된 것입니다. 소프트웨어는 기본 설정에서 두 옵션을 경고 없이 함께 받아들였고, 문서에도 인증서 요구가 무효화된다는 언급은 없었습니다. 이렇게 충돌하는 설정 조합은 21년 동안 이 소프트웨어에 존재해 왔습니다.
마지막으로, Claude는 인증을 기본적으로 꺼 두고(관리자가 직접 켜는 것을 전제로 하는) 프로젝트에서도 문제를 발견했습니다. TLS 클라이언트 인증서를 요구하는 옵션이 프로젝트의 네트워크 엔드포인트 대부분에서는 적용되었지만, 일부에서는 조용히 무시되었습니다. 이들은 네임스페이스가 다른 경로로 TLS 설정을 불러오는 엔드포인트였습니다. 그래서 인증서가 없는 클라이언트가 영향받는 엔드포인트에서 TLS 핸드셰이크를 마치고 세션을 열 수 있었습니다. 무시된 설정에 대해서는 아무 로그도 남지 않았습니다. 옵션이 적용되는지는 관리자가 설정에서 어떤 네임스페이스를 썼느냐에 달려 있었습니다. 설정 이름의 한 형태는 모든 엔드포인트에서 작동하지만, 대부분의 예제가 쓰는 짧은 형태는 영향받는 엔드포인트에서 무시되었습니다.
놀랍게도 전체 코드 줄 수 대비 비율로 보면 Claude가 공격 표면을 가장 크게 줄인 프로젝트는 OpenSSH였습니다. OpenSSH는 안전한 원격 접속을 제공하는 품질 높은 C 코드베이스입니다. Claude는 10.4 릴리스에서 SSH 데몬의 입력 경로를 추적했고, 포스트 퀀텀 서명 알고리즘인 ML-DSA-44용으로 새로 추가된 실험적 코드 약 4,500줄을 인증되지 않은 클라이언트가 기본 설정에서 도달할 수 있다는 사실을 발견했습니다. ML-DSA는 기본적으로 활성화되어 있지 않지만, 관리자가 이 알고리즘을 명시적으로 제외해도 코드에는 어쨌든 도달할 수 있었습니다. 데몬은 클라이언트가 보낸 키 유형을 가리지 않고 파싱했고(인증서라면 내장된 CA 서명도 검증했습니다), 그런 다음에야 해당 키 유형이 허용되는지 확인했습니다. 설정에서 활성화하지 않았는데도 이처럼 복잡하고 메모리 안전하지 않은 코드에 도달할 수 있어야 할 이유는 없습니다. ML-DSA-44의 이 4,500줄은 기본 sshd에서 OpenSSH 자체 인증 전 코드의 약 16%에 해당합니다. 우리는 이를 패치와 함께 OpenSSH 개발자들에게 보고했고, 10.5 릴리스에서 검사가 파싱보다 앞으로 옮겨졌습니다.
이후 Claude는 10.5의 검사에서 두 가지 빈틈을 짚어냈습니다. 첫 번째는 클라이언트가 주장하는 내용과 실제로 보내는 내용의 불일치였습니다. 검사는 클라이언트가 알린 알고리즘 이름을 보았지만, 파서는 키 블롭 안에 내장된 타입 문자열로 선택되었습니다. 그래서 클라이언트가 ssh-ed25519를 알려 검사를 통과한 뒤 ML-DSA 키를 보내도 그 키가 파싱될 수 있었습니다.
두 번째 빈틈은 10.5 검사의 인증서 처리에 있었습니다. 인증서 키 유형은 기본적으로 활성화되어 있었습니다. 공격자가 제공한 인증서가 들어오면 sshd는 설정을 확인하기 전에 내장된 CA 공개 키를 역직렬화하고, 지원되는 모든 알고리즘에 대해 전체 서명 검증을 수행했습니다. 사용자 키 서명 경로는 서버에 접속이 허용된 공개 키 목록 뒤에 제대로 막혀 있었지만, CA 서명 경로는 그렇지 않았습니다. 그 결과 키 교환을 마칠 수 있는 모든 클라이언트가 바이너리 안의 모든 공개 키 서명 검증기에 도달할 수 있었습니다. 우리는 개념 증명을 만들고 메인테이너들과 여러 차례 수정안을 다듬었습니다. 최종 변경은 검사를 파서 내부에 두었습니다. 이제 인코딩된 키 유형과 인증서의 CA 서명 알고리즘이 해당 역직렬화나 서명 검증 이전에 설정된 허용 목록과 대조됩니다.
이 수정으로 인증되지 않은 클라이언트가 기본 빌드에서 도달할 수 있던 OpenSSH 코드의 약 5분의 1이 사라졌습니다. 그 대부분은 새로 추가된 ML-DSA 코드로, 이제는 클라이언트가 키 유형을 속이거나 허용 가능한 인증서 안에 ML-DSA CA 키를 숨기더라도 도달할 수 없습니다. 허용된 CA 알고리즘으로 서명된 인증서는 여전히 파싱되고 검증되지만, 이제 검사가 파싱 이전에 실행됩니다. 메인테이너들은 여기서 설명한 변경 사항을 버전 10.6⟧에 반영해 배포했습니다.
OpenSSH는 세계에서 가장 철저히 감사된 코드 중 하나입니다. 이런 곳에서도 불필요한 도달 가능성이 남아 있었다면 어디에든 남아 있을 수 있습니다. 코드베이스의 공격 표면을 줄이는 작업이 중요한 많은 코드베이스에서 성과를 낼 가능성이 크다는 뜻입니다.
어떤 코드베이스에 알려지지 않은 취약점이 얼마나 있는지는 측정하기 어렵거나 불가능할지도 모릅니다. 그리고 공격자가 충분한 시간만 있다면(특히 최첨단 AI 모델을 갖췄다면) 그런 취약점을 찾아낼 수 있고 실제로 찾아낼 것임을 보여 주는 데이터는 많습니다. 하지만 코드의 불필요한 공격 표면을 줄이는 방어자는, 이미 은밀히 악용되고 있을 수 있는 취약점까지 포함해 그 코드에 있는 모든 미지의 취약점이 주는 위험을 한꺼번에 줄입니다. OpenSSH는 세계에서 가장 많이 감사된 C 코드 중 하나입니다. 그럼에도 설정 검사보다 앞서 공격자가 도달할 수 있는 실험적 서명 파싱 코드 약 4,500줄이 그대로 배포되었습니다.
우리는 앞서 설명한 공격 표면 축소 기법을 하나의 재사용 가능한 Claude 스킬로 합쳤고, 이를 OSS-Scanner에 통합하는 작업을 진행하고 있습니다. 이 스킬은 코드베이스에 세 차례 패스를 수행합니다. 매번 이전 패스에서 넘겨받는 짧은 컨텍스트를 제외하면 빈 컨텍스트 윈도우에서 새로 시작합니다.
첫 번째 패스에서는 기본 설정에서 도달할 수 있지만 의도된 어떤 기능에서도 쓰이지 않는 코드가 있는지 살펴봅니다. 두 번째 패스에서는 어떤 설정에서든 인증 같은 확실한 관문이나 프로토콜 상태 머신 진입점보다 앞서 실행되는 코드를 살펴봅니다. 마지막 세 번째 패스에서는 어떤 설정 조합이 보안 통제를 조용히 끄거나 효력을 떨어뜨리는지 평가합니다. 모든 패스에서 Claude는 코드에서 진입점 목록을 만들고, 각 발견 사항을 프로그램의 동적 추적으로 확인합니다. 기본 설정 관련 발견에 대해서는 관문을 설계해 구현하고, 이것이 프로그램의 기본 동작을 바꾸지 않는지 확인합니다. 이를 위해 프로젝트 자체 테스트 스위트(와 블랙박스 회귀 테스트 스위트)를 기준선에 대해 실행하고, 수정된 빌드가 동일한 결과를 내도록 요구합니다. 기본이 아닌 설정의 동작을 바꾸게 되는 관문은 사람이 결정하도록 따로 남겨 둡니다. 나머지 두 패스에서는 설정 검사의 순서 재배치, 상태 순서를 강제하는 가드 추가, 설정 로드 시점의 조합 거부 같은 변경을 제안합니다. 이 스킬을 쓰지 않더라도, 여기에 소개한 전반적인 방법론은 효과가 있으리라 생각합니다.
이 글에서 소개한 기법은 공격 표면 축소로 가능한 일의 아주 일부만 보여 줍니다. 예를 들어 Claude는 런타임 로그를 설정과 비교 분석해 꼭 필요한 기능만 활성화되어 있는지 확인할 수도 있습니다. 쓰지 않는 파서, 레거시 프로토콜, 대시보드를 비활성화하면 적은 비용으로 보안 위험을 줄일 수 있습니다.
조직들이 Claude로 자신이 의존하는 소프트웨어의 공격 표면을 분석하고 줄여 보시길 권합니다. 이것이 개발자가 이미 세상에서 돌아가고 있는 코드의 보안을 눈에 띄게 개선할 수 있는 가장 현실적인 방법 중 하나가 되리라 기대합니다.