AI가 라이브러리를 발명할 때: 환각된 임포트(Hallucinated Imports) 탐지하기
요약
AI 코딩 어시스턴트가 생성하는 존재하지 않는 패키지 임포트(환각된 임포트)의 위험성과 이를 탐지하는 BrassCoders의 기술을 소개합니다. 환각된 이름이 악성 패키지 등록(타이포스쿼팅)으로 이어져 공급망 공격의 통로가 될 수 있음을 경고합니다.
핵심 포인트
- AI가 패턴 매칭을 통해 존재하지 않는 패키지 이름을 생성하는 환각 현상 발생
- 환각된 임포트가 타이포스쿼팅 공격과 결합될 경우 심각한 공급망 공격 위험 초래
- 기존 정적 분석기(Pylint 등)는 네트워크 검증 부재로 인해 이를 탐지하지 못함
- BrassCoders는 전용 플래그를 통해 환각된 패키지를 보안 취약점으로 탐지 및 분류
BrassCoders의 OSS 코어는 단 하나의 플래그인 brasscoders scan --check-package-hallucination를 통해 환각된 패키지 임포트(package imports)를 스캔합니다. 이 플래그는 선택 사항(opt-in)인데, 해당 체크가 OSS 코어에서 외부 네트워크 호출을 수행하는 유일한 경로이기 때문입니다. 이 패턴이 중요한 이유는 AI 코딩 어시스턴트(AI coding assistants)가 존재하지 않는 패키지의 임포트를 생성하는 비율이 발표된 연구를 통해 측정될 정도로 빈번하며, 타이포스쿼터(typosquatter)가 이러한 환각된 이름을 악성 코드가 담긴 패키지로 등록할 경우, 환각이 공급망 공격(supply-chain attack)으로 변질되기 때문입니다.
이 포스트에서는 환각된 임포트가 왜 다른 AI 실수와는 다른 종류의 실패인지, 발표된 연구들이 이러한 현상이 얼마나 자주 발생하는지에 대해 무엇이라 말하는지, 타이포스쿼팅(typosquat) 공격 체인이 실제로 어떻게 작동하는지, 그리고 BrassCoders의 환각 체크가 CI 워크플로우(CI workflow)에 어떻게 통합되는지를 살펴봅니다.
환각된 임포트(Hallucinated Import)란 무엇인가
BrassCoders는 환각된 임포트를 자체적인 탐지기가 필요한 발견 유형(finding type)으로 취급합니다. 즉, 관련 레지스트리(registry)에서 확인되지 않는 패키지 이름을 참조하는 import 또는 require 문을 의미합니다. 예를 들어, PyPI에 fastapi-users만 게시되어 있음에도 AI가 from fastapi_users_pydantic import UserManager라고 작성하는 경우입니다. 이 구문은 문법적으로 유효하며, IDE(통합 개발 환경)는 경고를 보내지 않습니다. 이러한 실패는 설치 시점에 드러나거나, 훨씬 더 나쁜 상황으로는 타이포스쿼터가 환각된 이름을 악성 코드를 포함한 패키지로 등록했을 때 발생합니다.
환각이 발생하는 이유: LLM(대규모 언어 모델)은 학습 과정에서 본 수백만 개의 임포트 데이터를 패턴 매칭(pattern-matching)하여 그럴듯하게 들리는 패키지 이름을 생성합니다. fastapi-users를 50,000번 보고 pydantic을 200,000번 본 모델은 구조적 패턴이 올바르게 보이기 때문에 fastapi_users_pydantic을 "결합된" 패키지로 자신 있게 생성할 수 있습니다. 모델은 결합된 패키지가 실제로 존재하는지에 대한 근거(grounding)를 가지고 있지 않으며, 단지 프롬프트(prompt)가 주어졌을 때 가장 가능성 높은 다음 토큰(next token)을 생성할 뿐입니다.
정적 분석기(static analyzers)가 이를 놓치는 이유: 전통적인 Python 린터(linters)는 구문(syntax), 타입 어노테이션(type annotations), 그리고 코드 스타일을 확인합니다. 이 중 어떤 것도 임포트된 이름이 PyPI에 게시된 패키지와 일치하는지 검증하지 않습니다. Pylint도 하지 않습니다. Bandit도 하지 않습니다. Pyflakes도 하지 않습니다. 검증에는 네트워크 호출(network call)이 필요하며, 대부분의 린터는 명시적으로 오프라인 도구로 설계되었기 때문입니다.
BrassCoders는 환각된 임포트(hallucinated imports)를 보안 취약점(security finding)으로 노출하며, 심각도(severity)를 '높음(high)'으로 분류하고 패키지 이름과 소스 라인을 함께 제공합니다. 이후 하위 단계의 AI 소비자(또는 사람 검토자)는 해당 임포트를 실제 패키지로 교체할지, 코드를 완전히 제거할지, 아니면 위험을 수용할지를 결정합니다.
모델이 패키지를 환각하는 빈도
환각된 패키지 발생률에 대한 BrassCoders의 입장: 절대적인 백분율은 모델과 벤치마크(benchmark)에 따라 다르지만, 발표된 연구 전반에 걸쳐 그 비율이 충분히 높기 때문에, 존재 확인(existence-verification) 단계 없이 AI 생성 코드에 의존하는 모든 팀은 실질적인 위험을 배포하고 있는 셈입니다. 모델이 개선되고 벤치마크가 변함에 따라 구체적인 수치는 달라지지만, 이러한 패턴은 지속적입니다.
가장 많이 인용되는 연구는 Lasso Security
의 2024년 LLM 생성 패키지 제안 분석입니다. 그들의 방법론은 다음과 같습니다: 현실적인 리팩터링(refactoring) 작업으로 상용 코드 완성(code-completion) 모델에 프롬프트를 입력하고, 모든 임포트된 패키지를 캡처한 다음, 각 패키지를 관련 레지스트리(registry)와 대조하여 확인합니다. 연구 결과는 환각된 패키지 생성이 주요 모델 전반에 걸쳐 일관된 실패 모드(failure mode)임을 기록하고 있으며, 그 비율은 단순한 일시적 버그가 아니라 하나의 위험 범주를 구성할 수 있을 만큼 측정 가능한 수준입니다.
이 비율은 맥락에 따라 차이가 있습니다. 긴 컨텍스트 리팩토링 (Long-context refactoring, 모델이 학습 메모리로부터 그럴듯하지만 실제로는 존재하지 않는 라이브러리를 채워 넣는 경우)은 짧은 단일 파일 완성 (short single-file completions)보다 더 많은 환각 (hallucinations)을 생성하는 경향이 있습니다. 비주류 언어와 프레임워크는 주류 언어보다 더 높은 비율을 보이는데, 이는 모델이 가진 정답 데이터 (ground-truth)의 커버리지가 더 적기 때문입니다. 또한, 최신 라이브러리는 오래된 라이브러리보다 더 위험한데, 이는 모델의 학습 데이터가 현실을 따라가지 못하기 때문입니다.
이 연구가 알려주지 않는 사실은 다음과 같습니다: 귀하의 특정 회사에서, 귀하의 특정 코드베이스를 대상으로, 귀하의 특정 프롬프트 관행을 사용할 때의 실제 비율입니다. 이를 알 수 있는 유일한 방법은 AI 보조 PR 파이프라인 (AI-augmented PR pipeline)에 환각 체크 기능을 구축하고 측정하는 것입니다.
환각된 임포트가 공급망 리스크인 이유
BrassCoders는 환각된 임포트를 단순한 스타일 오류가 아닌 공급망 취약점 (supply-chain vulnerabilities)으로 취급합니다. 공격 체인은 매우 단순합니다: 악의적인 공격자가 AI가 생성한 코드(공개된 GitHub 저장소, AI 도구 사용 연구 또는 직접적인 모델 동작 테스트를 통해)를 모니터링하여, 자주 환각되는 패키지 이름을 식별합니다. 그런 다음 해당 이름을 악성 코드가 포함된 타이포스쿼팅 (typosquatting) 패키지로 PyPI 또는 npm에 등록하고 기다립니다. AI가 생성한 코드가 개발자의 환경이나 pip install 또는 npm install을 수행하는 CI 러너 (CI runner)에 도달하면, 타이포스쿼트 패키지가 가져와지고 설치 시점의 훅 (install-time hooks)이 실행됩니다.
이는 이론적인 이야기가 아닙니다. PyPI의 보안 권고 피드와 npm 보안 권고 데이터베이스 모두 타이포스쿼팅을 생태계 내에서 가장 큰 공격 표면 (attack surfaces) 중 하나로 추적하고 있으며, AI 도구가 등장하기 전부터 매년 수백 건의 확인된 악성 패키지 사건이 발생해 왔습니다. AI 환각은 타이포스쿼터에게 타겟 이름을 제공함으로써 공격 표면을 가속화합니다. 즉, 공격자가 개발자들이 흔히 저지르는 오타를 추측하는 대신, LLM 자체를 관찰하고 LLM이 만들어낸 이름을 등록하는 것입니다.
공격자가 패키지를 성공적으로 등록했다는 점을 전제로 하면, 악성코드 전달 방식은 설치 시점에 실행되는 무엇이든 될 수 있습니다. ~/.aws/credentials 또는 ~/.ssh/로부터의 자격 증명 유출 (credential exfiltration), 지속적인 백도어 (persistent backdoors), 암호화폐 채굴기 (cryptominers), 랜섬웨어 시드 (ransomware seeds) 등이 그 예입니다. Python 생태계의 setup.py와 JavaScript 생태계의 preinstall/install 훅 (hooks)은 모두 설계상 설치 시점에 임의의 코드를 실행하며, 샌드박싱 (sandboxing) 처리가 되어 있지 않습니다. Snyk의 Vulnerability Database는 확인된 악성 패키지의 카탈로그를 지속적으로 업데이트하여 관리하고 있으며, 이는 아직 공급망 스캐닝 (supply-chain scanning)을 구축하지 않은 팀에게 유용한 현실 점검 도구가 됩니다.
완화 (Mitigation)를 위해서는 설치 전에 환각 (hallucination)을 포착해야 합니다. pip install이 실행되는 시점에는 이미 공격자가 승리한 상태입니다. 코드 리뷰 시점(또는 pre-commit hook으로서)에 환각 체크를 실행하는 것이 유일하고 신뢰할 수 있는 방어책입니다.
BrassCoders의 체크 방식 작동 원리
BrassCoders의 패키지 환각 체크는 스캔된 파일 내의 모든 임포트 문 (import statement)을 파싱하고, 패키지 이름을 추출한 뒤, 대상 레지스트리(target registry) — PyPI의 JSON API인 pypi.org/pypi//json, npm의 레지스트리인 registry.npmjs.org/, 또는 Go의 pkg.go.dev/ — 에 쿼리하여 패키지의 존재 여부를 확인합니다. 200이 아닌 응답은 환각 신호이며, 200 응답은 해당 패키지가 레지스트리에 등록되어 있음을 의미합니다.
이 체크는 --check-package-hallucination CLI 플래그를 통해 선택적으로 활성화(opt-in)할 수 있습니다. 왜냐하면 이는 OSS 코어에서 외부 네트워크 호출을 수행하는 유일한 경로이기 때문입니다. BrassCoders의 기본 방침은 오프라인 우선(offline-first)입니다. 즉, 이 특정 체크를 명시적으로 켜지 않는 한 네트워크 트래픽 없이 스캔이 완료됩니다. 해당 플래그는 --offline 옵션을 준수합니다. --offline을 전달하면 선택적 활성화 상태가 다시 꺼짐(off) 상태로 덮어씌워집니다.
네트워크를 통해 전송되는 데이터는 순수한 패키지 이름(fastapi-users, lodash, github.com/spf13/cobra)뿐입니다. 소스 코드, 주변 문맥, 프로젝트 이름, 텔레메트리 (telemetry) 등은 전혀 전송되지 않습니다. PyPI 입장에서는 일반적인 pip search 쿼리와 아무런 차이가 없습니다.
반환되는 결과: 패키지가 존재하면 PyPI 또는 npm으로부터 JSON 객체가 반환되며, 존재하지 않으면 404 오류가 반환됩니다. BrassCoders는 JSON 콘텐츠를 폐기하고 존재 여부 신호(existence signal)만을 유지합니다. 패키지 이름과 존재 여부 불리언(boolean) 값이 탐지 기록(finding record)에 저장됩니다.
프라이빗 패키지(private packages)의 경우: 만약 귀하의 코드가 공개 레지스트리(public registry)에 게시되지 않은 내부 패키지(예: mycompany_auth)를 임포트(import)한다면, 검사 결과가 이를 환각(hallucinated)된 것으로 표시할 것입니다. 이는 알려진 오탐(false-positive) 패턴입니다. 완화 방법: --internal-packages mycompany_auth,mycompany_billing 옵션을 전달하여 검사에서 제외할 이름을 화이트리스트(whitelist)로 지정하십시오. 또는 프라이빗 패키지를 많이 임포트하는 저장소(repo)에서는 단순히 이 검사를 활성화하지 마십시오.
CI에서 사용하는 방법
BrassCoders의 환각 검사는 머지 전 게이트(pre-merge gate)로서 CI 파이프라인(pipeline)에 통합됩니다. 귀하의 브랜치에 대해 brasscoders scan --check-package-hallucination을 실행하고, 탐지 유형이 HALLUCINATED_IMPORT와 일치하는 경우 빌드를 실패 처리하십시오. 추가되는 지연 시간(latency)은 레지스트리 네트워크 지연 시간에 따라 임포트된 패키지당 100-500ms이며, 이는 일반적으로 일반적인 스캔에 5-30초를 추가합니다.
최소한의 GitHub Actions 워크플로(workflow):
name: BrassCoders scan
on: [pull_request]
jobs:
...
동일한 형태가 GitLab CI, CircleCI, Jenkins 또는 기타 러너(runner)에서도 작동합니다. 유일한 요구 사항은 Python 3.10+ 환경과 PyPI/npm에 대한 네트워크 액세스입니다. 이 검사는 에어갭(air-gapped) 환경 내부에서 실행되어서는 안 됩니다. 그러한 경우에는 (플래그 없는) 일반적인 brasscoders scan과 함께 사용하십시오.
PR(pull request)에 도달하기 전, 코드 생성 시점에 환각을 잡아내는 방법은 어떨까요? 그것이 궁극적인 지향점(end-state)입니다. 귀하가 선택한 AI 어시스턴트가 자신의 출력물에 대해 스스로 환각 검증을 수행하기 전까지는(아직 대부분의 도구가 그러하지 않습니다), 머지 전 스캔(pre-merge scan)이 핵심적인 방어 수단(load-bearing defense)입니다.
환각된 임포트가 가능하게 하는 공급망 공격 체인(supply-chain attack chain)은 보안 조직이 밤 11시에 전화를 걸게 만드는 바로 그런 종류의 일입니다. 완화 방법은 단 한 줄의 CI 단계입니다. 트레이드오프(trade-off)를 따져볼 필요조차 없습니다.
pipx install brasscoders를 통해 BrassCoders를 설치하고, 해당 체크를 PR 파이프라인(PR pipeline)에 추가하세요. AI 코드 리뷰의 실패 모드(failure modes)에 대한 더 넓은 맥락은 AI Code Review: The Practical Guide for 2026를 참조하세요.
AI 자동 생성 콘텐츠
본 콘텐츠는 Dev.to AI tag의 원문을 AI가 자동으로 요약·번역·분석한 것입니다. 원 저작권은 원저작자에게 있으며, 정확한 내용은 반드시 원문을 확인해 주세요.
원문 바로가기