공급망
소프트웨어 공급망 보안 모범 사례
첫 커밋부터 릴리스 이후의 대응까지 다루는 실용적인 체크리스트입니다. 각 섹션은 가장 적은 노력으로 가장 큰 보호 효과를 얻는 단계부터 시작하므로, 어느 지점에서 멈추더라도 이전보다 나은 상태가 됩니다.
업데이트
소스
- 강력한 인증을 요구하세요. 모든 코드 호스팅 계정에 다중 인증을 사용하세요.
- 메인 브랜치를 보호하세요. 병합 전에 검토를 필수로 하고, 누구도 릴리스 브랜치에 직접 푸시하지 못하게 하세요.
- 시크릿을 코드에서 분리하세요. 푸시할 때마다 시크릿을 스캔하고, 노출된 것은 모두 교체하세요.
- 워크플로 파일을 코드처럼 다루세요. 파이프라인 변경은 시크릿에 닿을 수 있으므로 소유자의 검토를 필수로 하세요.
종속성
-
잠금 파일을 커밋하고 그것으로 설치하세요.
npm ci와 그에 상응하는 명령을 사용해 빌드가 실수로 새 버전을 가져오지 않게 하세요. 잠금 파일에는 한계가 있지만, 출발점으로는 적합합니다. - 새 버전에 대기 기간을 두세요. 악성 릴리스는 공개 직후에 발견되는 경우가 많으므로, 게시 당일에 새 버전을 도입하지 않으면 발견될 기회가 생깁니다.
- 설치 스크립트를 제한하세요. 가능한 곳에서는 비활성화하세요. 설치 시 실행되는 스크립트는 npm 웜이 퍼지는 경로입니다.
- 새 종속성을 검증하세요. 이름이 타이포스쿼팅이 아닌지 확인하고, 유지 관리 상태와 인기도를 살펴보세요. OpenSSF Scorecard가 이 작업의 일부를 자동화합니다.
- 내부 패키지 이름을 보호하세요. 스코프와 레지스트리 구성을 사용해 종속성 혼동(dependency confusion)으로 공개 패키지가 내부 패키지를 대신하지 못하게 하세요.
- 필요 없는 것은 덜어 내세요. 종속성 하나하나가 모니터링해야 할 대상입니다.
빌드 및 CI/CD
- GitHub Actions를 전체 커밋 해시로 고정하세요. tj-actions 침해 사고에서 보았듯이 태그는 옮겨질 수 있습니다.
- 각 작업이 할 수 있는 일을 제한하세요. 워크플로 토큰에는 필요한 최소 권한만 부여하고, 수명이 긴 시크릿 대신 수명이 짧은 자격 증명을 사용하세요.
- 아웃바운드 트래픽을 제한하세요. 빌드는 알려진 소스에서 패키지를 받고 알려진 곳에 게시합니다. 그 밖의 모든 것을 차단하면 탈취된 시크릿이 갈 곳이 없어집니다. GitHub Actions 이그레스 정책을 참고하세요.
- 각 작업을 새롭고 격리된 환경에서 실행하세요. 한 작업에서 다음 작업으로 아무것도 이어져서는 안 되며, 외부 기여자의 풀 리퀘스트에서는 더더욱 그렇습니다.
- 캐시를 입력으로 다루세요. 오염된 캐시는 빌드 사이에 살아남으며, 캐시가 복원하는 내용은 간과하기 쉽습니다. GitHub Actions 캐시를 참고하세요.
전체 목록은 CI/CD 파이프라인 보안 체크리스트와 러너 보안 비교를 참고하세요.
릴리스
- 모든 릴리스에 대해 빌드에서 SBOM을 생성하세요. 아티팩트와 함께 보관하세요. SBOM 모범 사례를 참고하세요.
- 아티팩트에 서명하고 출처 증명을 게시하세요. 그러면 사용자가 릴리스의 출처를 확인할 수 있습니다. SLSA가 그 수준을 정의합니다.
- 릴리스는 CI에서만 빌드하세요. 노트북에서 만든 릴리스에는 신뢰할 수 있는 기록이 없습니다.
- 테스트부터 프로덕션까지 하나의 아티팩트를 사용하세요. 다시 빌드하지 말고 테스트한 것을 그대로 승격해야 기록이 사실과 일치합니다.
릴리스 이후
- 출시한 것을 모니터링하세요. 지원 중인 릴리스의 SBOM을 새 취약점에 대해 계속 확인하세요.
- CVSS 외의 기준으로도 우선순위를 정하세요. CISA KEV, EPSS와 맥락을 활용하세요. 취약점 우선순위 지정 방법을 참고하세요.
- 얼마나 오래 노출되었는지 파악하세요. 빌드 이력을 보면 어떤 릴리스에 특정 패키지가 언제부터 포함되었는지 알 수 있습니다.
- 필요해지기 전에 계획을 세우세요. 누가 분류하고, 누가 수정하고, 누가 고객과 소통할지 정하세요. 취약점 관리 라이프사이클를 참고하세요.
- 실제 시나리오로 연습하세요. 특정 패키지가 포함된 모든 릴리스를 찾는 데 얼마나 걸릴지 따져 보세요. 답이 며칠이라면 거기서부터 시작하세요.
먼저 할 일
| 시기 | 할 일 |
|---|---|
| 이번 주 | 다중 인증, CI에서 잠금 파일 사용, GitHub Actions 고정, 최소 권한 워크플로 토큰 |
| 이번 달 | 모든 릴리스 빌드의 SBOM, 담당자가 지정된 종속성 알림, 시크릿 스캔 |
| 이번 분기 | 빌드의 아웃바운드 트래픽 규칙, 출시된 릴리스 모니터링, 검증된 대응 계획 |
| 그다음 | 서명된 출처 증명, SLSA 또는 NIST SSDF 적용 범위에 대한 공식 검토, 공급업체 SBOM |
이 중 CRACI가 다루는 것
CRACI는 빌드와 그 이후의 모든 것을 다룹니다. GitHub Actions 러너로서 각 작업을 격리된 자체 가상 머신에서 실행하고, 작업이 가져오는 모든 패키지를 SBOM에 기록하며, CI 캐시에서 복원된 패키지를 표시하고, 빌드에 필요 없는 연결을 차단하는 이그레스 정책을 적용합니다. 그런 다음 출시된 것을 계속 확인하고, 어떤 빌드에 취약한 패키지가 포함되었는지 보여 주며, 정책 게이트로 빌드를 차단할 수 있습니다.
나머지는 다른 도구의 몫입니다. CRACI는 소스 코드나 시크릿을 스캔하지 않고, 동작을 기준으로 새로운 악성 패키지를 탐지하지 않으며, SBOM이나 출처 증명에 서명하지 않습니다. 각 공백을 무엇이 채우는지는 소프트웨어 공급망 보안 도구 비교를 참고하세요.
소프트웨어 공급망 보안 모범 사례: 자주 묻는 질문
소프트웨어 공급망은 어떻게 보호하나요?
모든 릴리스 빌드의 SBOM으로 무엇을 출시하는지 파악하고, 잠금 파일과 검증으로 무엇이 들어오는지 통제하며, 시크릿과 아웃바운드 트래픽을 제한해 빌드를 보호하고, 릴리스한 것을 계속 모니터링하세요.
작은 팀은 무엇부터 해야 하나요?
다중 인증을 켜고, CI에서 잠금 파일로 설치하고, GitHub Actions를 커밋 해시로 고정하고, 워크플로 토큰에 필요한 최소 권한만 부여하세요. 며칠이면 끝나는 작업으로 흔한 공격 경로를 막을 수 있습니다.
악성 패키지는 어떻게 막나요?
한 가지 조치만으로는 막을 수 없습니다. 새 버전 도입을 늦추고, 설치 스크립트를 제한하고, 새 종속성을 검증하세요. 빌드의 아웃바운드 트래픽을 제한하면 악성 패키지가 통과하더라도 피해를 줄일 수 있습니다.
왜 GitHub Actions를 커밋 해시로 고정해야 하나요?
버전 태그는 다른 코드로 옮겨질 수 있기 때문입니다. 전체 커밋 해시는 항상 같은 코드를 가리키므로, 탈취된 태그가 파이프라인에 도달하지 못합니다.
공급망은 얼마나 자주 검토해야 하나요?
모니터링은 지속적이어야 합니다. 종속성 정책, 워크플로 권한, 대응 계획은 최소 1년에 한 번, 그리고 사용 중인 스택에서 인시던트가 발생할 때마다 검토하세요.