오픈소스 의존성 지옥 — AI가 추천하는 패키지, 정말 안전한가? 실전 검증 가이드
이 글을 끝까지 읽으면, AI가 추천한 패키지를 그대로 설치하기 전에 반드시 확인해야 할 3가지 검증 절차와 바로 쓸 수 있는 자동 검증 스크립트까지 손에 넣게 됩니다.
혹시 이런 경험 있으신가요? Copilot이나 ChatGPT에게 "이 기능 구현할 패키지 추천해줘"라고 물으면, 낯선 패키지명이 뚝 떨어집니다. 그리고 별생각 없이 pip install이나 npm install을 바로 실행해 본 적, 한 번쯤 있으실 겁니다.
솔직히 저도 오랜기간 개발·보안 현장에 있으면서 이 부분을 가장 위험하게 봅니다. AI는 패키지가 실제로 존재하는지, 유지보수가 되고 있는지, 악성코드가 심어져 있지는 않은지 확인해주지 않습니다. 그냥 "그럴듯한 이름"을 만들어낼 뿐이죠.
오늘은 AI가 추천하는 오픈소스 패키지의 실제 위험성과, 이를 실전에서 검증하는 자동화 스크립트까지 전부 정리해드리겠습니다.
📌 바로가기 목차
| AI가 제안한 pip install 또는 npm install 명령을 검증 없이 실행하면 패키지 환각, 슬롭스쿼팅, 악성 패키지와 공급망 공격에 노출될 수 있습니다. 실존 여부 확인, OSV 취약점 스캔, CI 화이트리스트 검증이 필요합니다. |
1. AI 코드 어시스턴트가 추천하는 패키지, 왜 위험한가
사실 대부분의 개발자가 모르는 게 있는데요, LLM은 패키지 저장소를 실시간으로 조회하지 않습니다. 학습 데이터에서 "이런 이름이 있었을 법하다"는 통계적 패턴으로 패키지명을 생성할 뿐이죠.
2024년 발표된 학술 연구에서는 대형 언어모델이 생성한 코드 샘플 중 상당수가 존재하지 않는 패키지를 참조했다는 결과가 나왔습니다. 문제는 이 존재하지 않는 이름을, 공격자가 미리 실제 패키지로 등록해두면 그대로 악성코드 설치 통로가 된다는 점입니다.
여러분 팀은 지금 AI가 추천한 패키지를 설치하기 전에 몇 초라도 검증 절차를 거치시나요? 대부분 "그냥 믿고 설치"하는 게 현실입니다. 다음 섹션에서 이 문제가 왜 '환각'이라는 이름까지 붙었는지 구체적으로 살펴보겠습니다.
2. 패키지 환각(Hallucination)과 슬롭스쿼팅의 실체
"슬롭스쿼팅(Slopsquatting)"이라는 용어, 생소하실 텐데요. 기존 타이포스쿼팅이 "requsets" 같은 오타를 노렸다면, 슬롭스쿼팅은 AI가 반복적으로 만들어내는 존재하지 않는 패키지명을 공격자가 선점 등록하는 방식입니다.
공격자 입장에서는 간단합니다. 여러 LLM에 같은 질문을 반복해서 자주 등장하는 가짜 패키지명을 수집하고, 그 이름으로 PyPI나 npm에 악성 패키지를 미리 올려두기만 하면 됩니다. 개발자가 AI 제안을 그대로 복사해서 설치하는 순간 감염이 시작되죠.
| 구분 | 타이포스쿼팅 | 슬롭스쿼팅 |
|---|---|---|
| 공격 대상 | 사람의 타이핑 실수 | AI의 환각 응답 |
| 패키지명 유형 | 기존 패키지와 유사한 오타형 | 그럴듯하지만 실존하지 않던 신규명 |
| 방어 난이도 | 비교적 낮음 (오타 확인) | 높음 (그럴듯해서 의심 안 함) |
| 확산 경로 | 사람의 직접 입력 | AI 코드 제안 복사·붙여넣기 |
표에서 보시듯, 슬롭스쿼팅은 사람이 직접 의심할 계기 자체가 없다는 게 핵심 문제입니다. 다음 섹션에서 어떤 신호가 보이면 즉시 의심해야 하는지 구체적으로 짚어보겠습니다.
3. 이상 신호로 보는 위험 패키지 체크포인트
경험상, 위험한 패키지는 몇 가지 공통된 신호를 남깁니다. 설치 전 30초만 투자해서 아래 항목을 확인하는 습관이 생기면, 대부분의 공급망 공격을 초기에 걸러낼 수 있습니다.
- 등록일이 매우 최근: 생성된 지 몇 주 안 된 패키지는 신뢰도를 별도로 검증해야 합니다.
- 다운로드 수 대비 낮은 스타 수: GitHub 저장소가 없거나 스타·이슈 활동이 거의 없는 경우 위험 신호입니다.
- 유명 패키지와 이름이 미묘하게 다름: 하이픈·언더스코어 위치, 복수형 여부 차이는 슬롭스쿼팅의 전형적 패턴입니다.
- 유지보수자 정보 부재: 작성자 프로필이 비어있거나 이 패키지 하나만 등록한 계정은 의심 대상입니다.
⚠️ 주의: AI가 코드와 함께 설치 명령어(pip install, npm install 등)까지 제안했다고 해서, 그 명령어를 자동 실행 에이전트가 검증 없이 바로 실행하도록 허용하는 것은 매우 위험합니다. 반드시 사람의 승인 단계를 거치세요.
![]() |
| AI가 추천한 패키지를 바로 설치하기 전에 실존 여부, 유지보수 상태, 취약점과 평판을 확인해야 합니다. 패키지 환각과 슬롭스쿼팅 위험부터 PyPI 검사, OSV.dev 취약점 스캔, CI 화이트리스트 게이트까지 실무 검증 절차를 정리했습니다. |
4. 실제 공급망 공격 사례로 보는 교훈
"에이, 설마 우리 회사가 그런 일을 당하겠어?"라고 생각하신다면, 이미 늦었을 수도 있습니다. PyPI와 npm 생태계에서는 매년 수천 건의 악성 패키지가 업로드되고, 그중 상당수가 개발자의 검증 없는 설치 습관을 노립니다.
대표적인 패턴은 이렇습니다. 공격자는 인기 패키지와 유사한 이름의 악성 패키지를 등록하고, 설치 시 자동 실행되는 스크립트(setup.py, postinstall 훅 등)에 자격 증명 탈취 코드나 백도어를 심습니다. 개발자는 정상 설치 로그만 보고 아무 문제 없다고 생각한 채 넘어가죠.
더 심각한 건, AI 코딩 도구의 보급으로 이런 악성 패키지가 '추천'이라는 형태로 유입되는 속도가 훨씬 빨라졌다는 점입니다. 사람이 직접 검색해서 신중하게 고르던 시절과 비교하면, 검증 단계 자체가 생략되는 경우가 늘고 있습니다. 다음 섹션에서 이런 위협을 실제로 걸러내는 검증 방법을 비교해보겠습니다.
5. 검증 방법 비교 — 자동 스캔 vs 수동 검토
검증 방법은 크게 자동화 스캔과 수동 검토로 나뉩니다. 실무에서는 두 방식을 병행하는 것이 가장 현실적입니다. 각각의 장단점을 표로 정리했습니다.
| 검증 방식 | 장점 | 한계 |
|---|---|---|
| 레지스트리 API 존재 확인 | 즉시 자동화 가능, 빠름 | 존재만 확인, 악성 여부는 별도 판단 필요 |
| OSV/CVE 취약점 스캔 | 알려진 취약점 즉시 탐지 | 신규·미공개 악성 패키지는 탐지 불가 |
| CI 파이프라인 화이트리스트 게이트 | 조직 전체에 강제 적용 가능 | 화이트리스트 관리 부담 발생 |
| 사람의 수동 코드 리뷰 | 맥락적 판단 가능 | 시간 소요 크고 규모 확장 어려움 |
결론적으로, 자동 스캔으로 1차 필터링하고 신규·저평판 패키지만 사람이 리뷰하는 구조가 가장 효율적입니다.
6. 팀 차원의 의존성 보안 체크리스트
개인 습관만으로는 조직 전체의 위험을 막을 수 없습니다. 아래 체크리스트를 팀 온보딩 문서나 CI 정책에 반영해보세요.
- 신규 의존성 승인 절차 도입: AI가 제안한 패키지든 아니든, 신규 추가 시 최소 1인 리뷰를 의무화합니다.
- CI에 취약점·존재 여부 게이트 자동화: 사람이 놓쳐도 파이프라인이 걸러내도록 자동화합니다.
- SBOM(소프트웨어 자재 명세) 유지: 현재 사용 중인 전체 의존성 목록을 상시 파악합니다.
- AI 코드 제안 정책 문서화: "AI가 제안한 설치 명령은 반드시 검증 후 실행"을 팀 규칙으로 명문화합니다.
다음 섹션에서는 이 체크리스트를 실제로 자동화하는 코드를 바로 보여드리겠습니다.
![]() |
| 신규 의존성을 추가할 때는 패키지 등록일, 저장소 활동성, 유사 이름, 유지보수자 정보를 점검해야 합니다. 자동 취약점 스캔과 수동 코드 검토를 병행하고, 조직 차원에서는 승인 절차와 CI 보안 게이트, SBOM 관리를 적용하는 것이 안전합니다. |
💻 실전 코드 — 바로 쓰는 AI 추천 패키지 자동 검증 스크립트
아래 세 가지 코드는 AI가 제안한 패키지를 설치하기 전에 존재 여부·등록일·취약점·화이트리스트 여부를 자동으로 확인하는 최소 검증 파이프라인입니다. 순서대로 조합하면 로컬 개발 단계부터 CI까지 전체를 커버할 수 있습니다.
▶ 실전 코드 ① — PyPI 패키지 실존 여부 및 슬롭스쿼팅 위험도 검사 (Python)
PyPI JSON API로 패키지가 실제로 존재하는지, 등록된 지 얼마나 됐는지 확인하고 최근 30일 이내 신규 패키지는 경고를 띄웁니다. AI가 제안한 패키지명을 그대로 넣어 실행하면 됩니다.
# PyPI 패키지 실존 여부 및 신규 등록 위험도 검사
import urllib.request
import json
from datetime import datetime, timezone
def check_package(name: str) -> dict:
url = f"https://pypi.org/pypi/{name}/json"
try:
with urllib.request.urlopen(url, timeout=5) as res:
data = json.loads(res.read())
except Exception:
# 404 등 조회 실패 시 미등록 패키지로 판단 (AI 환각 가능성)
return {"name": name, "exists": False, "risk": "HIGH", "reason": "레지스트리에 존재하지 않음"}
releases = data.get("releases", {})
if not releases:
return {"name": name, "exists": True, "risk": "MEDIUM", "reason": "배포 이력 없음"}
# 가장 오래된 배포일 기준으로 패키지 나이 계산
first_upload = min(
(f["upload_time_iso_8601"] for files in releases.values() for f in files if files),
default=None
)
if first_upload:
age_days = (datetime.now(timezone.utc) - datetime.fromisoformat(first_upload.replace("Z", "+00:00"))).days
risk = "HIGH" if age_days < 30 else "LOW"
return {"name": name, "exists": True, "risk": risk, "age_days": age_days}
return {"name": name, "exists": True, "risk": "MEDIUM", "reason": "배포일 확인 불가"}
if __name__ == "__main__":
candidates = ["requests", "REPLACE_WITH_AI_SUGGESTED_PACKAGE"]
for pkg in candidates:
print(check_package(pkg))
실행 결과에서 exists: False가 나오면 AI가 존재하지 않는 패키지명을 만들어낸 것이니 절대 설치하면 안 됩니다. age_days가 30일 미만이면 슬롭스쿼팅 표적으로 등록됐을 가능성을 의심하고 GitHub 저장소·유지보수자 이력을 추가로 확인하세요.
▶ 실전 코드 ② — OSV.dev API 기반 의존성 취약점 일괄 스캔 (Python)
requirements.txt 전체를 구글의 OSV(Open Source Vulnerabilities) 데이터베이스와 대조해 알려진 취약점(CVE)이 있는지 한 번에 확인하는 스크립트입니다. CI에서도 그대로 재사용할 수 있습니다.
# requirements.txt 전체 의존성을 OSV.dev로 일괄 취약점 스캔
import json
import urllib.request
OSV_API = "https://api.osv.dev/v1/query"
def load_requirements(path: str) -> list:
packages = []
with open(path, "r", encoding="utf-8") as f:
for line in f:
line = line.strip()
if not line or line.startswith("#") or "==" not in line:
continue
name, version = line.split("==")
packages.append((name.strip(), version.strip()))
return packages
def query_osv(name: str, version: str) -> list:
payload = json.dumps({
"package": {"name": name, "ecosystem": "PyPI"},
"version": version
}).encode()
req = urllib.request.Request(OSV_API, data=payload, headers={"Content-Type": "application/json"})
try:
with urllib.request.urlopen(req, timeout=8) as res:
result = json.loads(res.read())
return result.get("vulns", [])
except Exception as e:
print(f" [경고] {name} 조회 실패: {e}")
return []
def scan_all(path: str = "requirements.txt"):
for name, version in load_requirements(path):
vulns = query_osv(name, version)
if vulns:
ids = [v.get("id") for v in vulns]
print(f"🔴 {name}=={version} 취약점 {len(vulns)}건 발견: {ids}")
else:
print(f"🟢 {name}=={version} 알려진 취약점 없음")
if __name__ == "__main__":
scan_all()
🔴 표시가 뜬 패키지는 즉시 버전을 올리거나 대체 패키지를 검토해야 합니다. 이 스크립트는 requirements.txt뿐 아니라 ecosystem 값을 "npm"으로 바꾸면 package-lock.json 계열에도 동일하게 적용할 수 있습니다.
▶ 실전 코드 ③ — CI 파이프라인 신규 의존성 화이트리스트 게이트 (Bash)
PR마다 requirements.txt에 새로 추가된 패키지만 뽑아내, 사전 승인된 화이트리스트에 없으면 빌드를 실패시키는 CI 게이트 스크립트입니다. 사람이 검증을 깜빡해도 파이프라인이 대신 막아줍니다.
#!/bin/bash
# 신규 의존성 화이트리스트 검증 게이트 (CI 파이프라인용)
set -euo pipefail
WHITELIST_FILE="approved-packages.txt"
BASE_BRANCH="origin/main"
# main 브랜치와 비교해 새로 추가된 패키지 라인만 추출
NEW_PACKAGES=$(git diff "$BASE_BRANCH"...HEAD -- requirements.txt \
| grep '^+' \
| grep -v '^+++' \
| sed 's/^+//' \
| cut -d'=' -f1 \
| sed 's/[[:space:]]*$//')
if [ -z "$NEW_PACKAGES" ]; then
echo "신규 의존성 없음 — 통과"
exit 0
fi
FAILED=0
while IFS= read -r pkg; do
[ -z "$pkg" ] && continue
if grep -qx "$pkg" "$WHITELIST_FILE"; then
echo "승인됨: $pkg"
else
echo "❌ 미승인 패키지 발견: $pkg (화이트리스트 등록 및 보안 리뷰 필요)"
FAILED=1
fi
done <<< "$NEW_PACKAGES"
if [ "$FAILED" -eq 1 ]; then
echo "빌드 실패 — 미승인 의존성을 확인하세요."
exit 1
fi
echo "모든 신규 의존성 검증 통과"
approved-packages.txt는 보안 리뷰를 거친 패키지명만 한 줄씩 기록해 저장소에 커밋해두는 방식입니다. GitHub Actions나 GitLab CI 어디서든 이 스크립트를 Job 하나로 등록하면 신규 의존성이 리뷰 없이 병합되는 것을 원천 차단할 수 있습니다.
💡 실전 팁: 세 스크립트를 하나의 pre-commit 훅과 CI Job으로 묶으면, 로컬에서 1차로 걸러지고 CI에서 2차로 강제되는 이중 방어선이 만들어집니다. OSV 스캔 결과는 반드시 Slack이나 이메일로 자동 알림 처리해 담당자가 놓치지 않도록 하세요.
| AI 코딩 도구가 추천한 패키지는 실제 존재 여부와 안전성을 자동으로 보장하지 않습니다. 설치 전 패키지 저장소, 유지보수 이력, 알려진 취약점과 의존성 위험을 반드시 검증해야 합니다. |
7. 자주 묻는 질문 (FAQ)
네, 충분히 가능합니다. 2번 섹션에서 설명했듯 LLM은 통계적으로 그럴듯한 이름을 생성할 뿐, 실시간 레지스트리 조회를 하지 않습니다. 설치 전 반드시 존재 여부를 확인하세요.
실전 코드 ①의 존재 여부 확인 스크립트만이라도 설치 전 습관화하세요. 5초 투자로 가장 치명적인 위험(존재하지 않는 패키지 설치)의 상당 부분을 막을 수 있습니다.
네. 알려진 취약점(CVE)이 없다는 것과 악성코드가 없다는 것은 다른 이야기입니다. 3번 섹션의 등록일·유지보수자 정보 같은 정성적 신호도 함께 확인해야 합니다.
네, 가능합니다. 실전 코드 ②에서 OSV API의 ecosystem 값을 "npm"으로 바꾸고, 코드 ①의 조회 URL을 registry.npmjs.org로 교체하면 동일한 로직을 그대로 적용할 수 있습니다.
아니요, 금지가 답은 아닙니다. AI 코드 제안은 생산성 측면에서 이미 되돌릴 수 없는 흐름입니다. 6번 체크리스트처럼 승인 절차와 자동 검증을 병행하는 것이 현실적인 답입니다. 더 궁금한 점은 댓글로 남겨주세요!
8. 마무리 요약
✅ AI 추천 패키지, 검증 없이는 절대 설치하지 마세요
AI 코드 어시스턴트는 패키지가 실제로 존재하는지 검증해주지 않습니다. 슬롭스쿼팅 공격자는 바로 이 지점을 노려 그럴듯한 이름의 악성 패키지를 미리 등록해둡니다.
존재 여부 확인, OSV 취약점 스캔, CI 화이트리스트 게이트 — 이 세 가지 자동화만 갖춰도 대부분의 공급망 공격을 초기에 걸러낼 수 있습니다. 개인의 주의력에만 의존하지 말고, 파이프라인이 대신 검증하도록 설계하는 것이 진짜 방어입니다.
오늘 이 글을 읽었다면, 지금 당장 실전 코드 ①을 복사해서 최근 AI가 추천했던 패키지 하나에 실행해보세요. 5분 투자가 큰 사고를 막을 수 있습니다.
여러분 팀은 지금 AI가 추천한 패키지를 어떻게 검증하고 계신가요? 댓글로 현황을 공유해 주시면 같이 고민해 드리겠습니다. 다음 포스팅에서는 AI 코드 어시스턴트 도입 시 필요한 사내 보안 가이드라인 작성법을 다룰 예정이니 기대해 주세요!


댓글
댓글 쓰기