의존성 취약점 스캔: pip-audit로 안전한 패키지 관리
우리가 직접 쓰지 않는 간접 의존성에도 취약점이 숨습니다. pip-audit로 알려진 CVE를 점검하고, 버전 고정과 자동화로 공급망을 지키는 방법을 정리합니다.
지난 글에서 내가 짠 코드가 위험을 부르는 경우를 봤다면, 이번에는 내가 짜지 않은 코드 — 즉 외부 패키지 — 가 문제가 되는 상황을 다룬다. 현대의 파이썬 프로젝트는 수십, 수백 개의 서드파티 패키지에 기대어 돌아간다. 그중 하나에 알려진 보안 취약점(CVE)이 있다면, 그건 곧 내 애플리케이션의 취약점이 된다.
진짜 위험은 “간접 의존성”
requirements.txt나 pyproject.toml에 내가 직접 적은 패키지는 직접 의존성이다. 하지만 그 패키지들이 다시 끌고 오는 패키지 — 간접(전이) 의존성 — 가 훨씬 많고, 바로 여기에 위험이 숨는다. 내가 requests만 적었더라도, 그 뒤로 urllib3, charset-normalizer, certifi 같은 패키지들이 함께 설치된다.
문제는 우리가 이 간접 의존성을 의식하지 않는다는 점이다. urllib3의 특정 버전에 취약점이 공개됐다 해도, “나는 urllib3를 쓴 적 없는데?”라고 생각하기 쉽다. 그러나 그 코드는 분명히 내 환경 안에서 돌고 있다.
pip-audit로 점검하기
pip-audit는 PyPA(파이썬 패키징 공식 기관)가 관리하는 도구로, 현재 설치된(또는 lock 파일에 적힌) 패키지들을 알려진 취약점 데이터베이스와 대조한다. 직접·간접을 가리지 않고 전부 검사한다.
# 설치
pip install pip-audit
# 현재 환경 전체 스캔
pip-audit
# 특정 requirements 파일 기준으로 스캔
pip-audit -r requirements.txt
# 가능한 경우 안전한 버전으로 자동 수정 제안
pip-audit --fix
실행하면 취약한 패키지, 해당 CVE 번호, 그리고 취약점이 고쳐진 버전을 함께 알려준다. 출력에서 “fixed in 1.26.18” 같은 안내를 보고 그 버전 이상으로 올리면 된다. --fix 옵션은 이 갱신을 자동으로 시도해 준다.
버전 고정이 먼저다
취약점을 정확히 추적하려면 무엇이 설치되어 있는지를 정확히 알아야 한다. 그래서 의존성 버전을 고정하는 lock 파일이 보안의 출발점이다. pip-tools의 pip-compile, 또는 Poetry·PDM·uv 같은 도구가 만드는 lock 파일에는 간접 의존성까지 정확한 버전과 해시가 박힌다.
이렇게 고정된 상태에서 정기적으로 스캔하고, 취약점이 나오면 버전을 올린 뒤 다시 lock을 갱신하는 루프를 돈다. 고정 → 검사 → 갱신의 반복이다.
CI에 붙여 자동화하기
가장 효과적인 방법은 이 검사를 사람의 기억에 의존하지 않게 만드는 것이다. CI 파이프라인에 한 단계로 넣어 두면, 코드를 푸시할 때마다 — 그리고 새 취약점이 공개됐을 때마다 — 자동으로 알려준다.
# GitHub Actions 예시
- name: Audit dependencies
run: |
pip install pip-audit
pip-audit -r requirements.txt --strict
--strict 옵션을 주면 취약점이 하나라도 발견될 때 빌드를 실패시킨다. 이렇게 하면 취약한 의존성이 배포까지 흘러가는 걸 막을 수 있다. 여기에 GitHub의 Dependabot이나 Renovate 같은 봇을 함께 쓰면, 취약 패키지를 올리는 풀 리퀘스트까지 자동으로 만들어 준다.
정리
내가 쓰는 코드의 대부분은 사실 남이 쓴 코드다. 그 코드의 보안은 곧 내 애플리케이션의 보안이며, 위험은 눈에 보이는 직접 의존성보다 그 아래 깔린 간접 의존성에 숨어 있다. 버전을 lock으로 고정하고, pip-audit로 정기적으로 검사하고, 이 과정을 CI에 자동화하자. 의존성 관리는 한 번 하고 끝나는 일이 아니라 계속 돌려야 하는 루프다. 다음 글부터는 보안에서 잠시 벗어나, 파이썬 개발에서 흔히 마주치는 버그들 — 그 첫 번째로 순환 임포트 — 를 다룬다.
지난 글: eval과 exec의 위험성
다음 글: 순환 임포트 해결하기
읽어주셔서 감사합니다. 😊