supply-chain-security

5 개의 포스트

github

멀웨어 보안 권고를 npm을 넘어 확장한 방법 (새 탭에서 열림)

Ankit은 GitHub에서 Dependabot 팀을 이끄는 시니어 엔지니어링 매니저입니다. Dependabot은 34개 이상의 패키지 생태계에 걸쳐 3천만 개가 넘는 저장소를 감시하며, 소프트웨어 공급망 공격에 대응하는 역할을 합니다. 이러한 광범위한 책임 때문에 Ankit은 공급망 보안 위협에 대해 높은 경계심을 유지하고 있습니다. ### Ankit의 역할 - GitHub의 Supply Chain Security 조직에서 Dependabot 팀을 이끕니다. - 엔지니어링 관리자로서 소프트웨어 의존성 및 공급망 보안과 관련된 업무를 담당합니다. ### Dependabot의 감시 범위 - 3천만 개 이상의 저장소를 모니터링합니다. - 34개 이상의 패키지 생태계를 지원합니다. - 다양한 패키지 관리자와 의존성을 다뤄야 하므로 매우 광범위한 보안 대응이 필요합니다. ### 소프트웨어 공급망 보안 - 패키지와 의존성은 애플리케이션 보안에 직접적인 영향을 줍니다. - 의존성의 취약점이나 악성 패키지는 다수의 저장소에 빠르게 확산될 수 있습니다. - Dependabot의 대규모 감시 범위는 공급망 공격을 조기에 탐지하고 대응하는 데 중요한 역할을 합니다. 이 글의 제공된 내용은 Ankit과 Dependabot의 역할 및 규모를 소개하는 부분에 해당하며, 구체적인 기술적 주장이나 구현 세부사항은 포함되어 있지 않습니다.

github

Dependabot 길들이기: 업데이트를 그룹화하고, 주기를 늦추되, 보안 업데이트는 신속하게 (새 탭에서 열림)

활성 저장소에서는 Dependabot이 의존성마다 개별 PR을 생성해 알림, 리뷰, CI 실행을 과도하게 늘릴 수 있다. 글은 Microsoft GCToolkit 사례를 통해 의존성 업데이트를 생태계별로 그룹화하고 주기를 월간으로 늦추면 유지보수 소음을 크게 줄일 수 있다고 설명한다. 다만 일반 버전 업데이트와 달리 보안 업데이트는 신속하게 처리되므로, 업데이트 빈도를 낮춰도 보안 대응 속도는 유지할 수 있다. ## Dependabot이 만드는 유지보수 소음 - GCToolkit의 578개 커밋 중 92개가 Dependabot 버전 업데이트였으며, 최근 12개월에만 61개가 발생했다. - 기존 설정은 다음과 같았다. - GitHub Actions만 감시 - 매일 업데이트 확인 - 최대 10개의 PR 허용 - 의존성 10개가 업데이트되면 PR 10개, CI 실행 10회, 리뷰 알림 10건이 발생한다. - `open-pull-requests-limit: 10`은 PR 수를 제한할 뿐, 업데이트가 쏟아지는 원인을 해결하지 못한다. ## 여러 업데이트를 하나의 PR로 그룹화 - `groups`와 `patterns: ["*"]`를 사용하면 모든 의존성 업데이트를 하나의 PR로 묶을 수 있다. - 예를 들어 10개의 업데이트가 발생해도 다음처럼 처리된다. - 브랜치 1개 - PR 1개 - CI 실행 1회 - 리뷰 및 병합 1회 - 그룹 이름은 자유롭게 정할 수 있으며 PR 제목과 브랜치 이름에 표시된다. - 규모가 큰 프로젝트에서는 테스트 라이브러리, 운영 의존성 등 목적별로 여러 그룹을 만들 수 있다. - 모노레포에서는 `directories`와 `group-by: dependency-name`을 사용해 여러 디렉터리에 같은 의존성이 있을 때 하나의 PR로 통합할 수 있다. ```yaml - package-ecosystem: "npm" directories: - "/apps/*" schedule: interval: "monthly" groups: monthly-batch: group-by: dependency-name patterns: - "*" ``` ## 업데이트 주기를 일간에서 월간으로 변경 - `interval: daily`는 평일마다 업데이트를 확인하므로 지속적인 PR 생성으로 이어질 수 있다. - `interval: monthly`로 바꾸면 계획 가능한 월간 배치 방식으로 전환된다. - 그룹화와 결합하면 생태계별로 한 달에 하나의 일괄 PR을 받게 된다. - 프로젝트 특성에 따라 `weekly`를 선택할 수도 있고, `schedule.day`와 `schedule.time`으로 실행 시점을 지정할 수 있다. - 의존성이 안정적인 성숙한 라이브러리에는 월간 주기가 적합하다. ## 실제 사용하는 생태계를 모두 설정 - 기존 설정은 GitHub Actions만 대상으로 했지만, GCToolkit은 Maven 기반 Java 프로젝트이므로 Maven 의존성도 별도로 등록해야 했다. - 생태계마다 `updates` 항목을 추가해야 한다. ```yaml - package-ecosystem: "github-actions" directory: "/" schedule: interval: "monthly" groups: monthly-batch: patterns: - "*" - package-ecosystem: "maven" directory: "/" schedule: interval: "monthly" groups: monthly-batch: patterns: - "*" ``` - GitHub Actions와 Maven 업데이트는 각각 독립된 그룹과 일정으로 관리된다. - 소음을 줄이는 것뿐 아니라, 실제 애플리케이션 의존성까지 Dependabot이 감시하도록 만드는 것이 중요하다. ## 보안 업데이트는 빠르게 유지 - 월간 일정과 그룹 설정은 일반적인 버전 업데이트의 처리 방식을 조정하는 설정이다. - Dependabot 보안 업데이트는 별도의 메커니즘으로 동작하므로, 일반 업데이트 주기를 늦춰도 보안 취약점 대응까지 월간으로 미뤄지지 않는다. - 따라서 평상시에는 일괄 업데이트로 리뷰 부담을 줄이고, 보안 수정은 기존처럼 신속하게 처리하는 균형을 유지할 수 있다. ## 실용적인 적용 방법 - 안정적인 저장소라면 생태계별로 `monthly` 일정과 전체 의존성 그룹화를 적용한다. - 업데이트가 자주 필요하거나 변경 영향이 큰 프로젝트는 `weekly` 또는 목적별 다중 그룹을 사용한다. - GitHub Actions뿐 아니라 Maven, npm 등 실제 사용하는 모든 패키지 생태계를 `dependabot.yml`에 등록한다. - 보안 업데이트 설정은 별도로 확인해 취약점 수정이 지연되지 않는지 점검한다.

github

npm 및 GitHub Actions의 공급망 공격 교란 (새 탭에서 열림)

npm과 GitHub Actions를 겨냥한 공급망 공격은 계정 탈취, CI/CD 취약점 악용, 자격 증명 탈취, 악성 패키지 확산을 연쇄적으로 결합한다. GitHub는 단일 방어 기능보다 공격 단계별로 핵심 연결고리를 끊는 다층 방어가 필요하다고 보고, npm과 GitHub Actions에 계정 보호·워크플로 권한 제한·캐시 보호·무자격 증명 배포 등의 기능을 도입했다. 목표는 공격을 완전히 제거하기보다 초기 침해와 권한 상승, 자격 증명 유출, 대규모 확산을 차단하고 탐지·대응 시간을 확보하는 것이다. ## 공급망 공격의 구조 - 공격은 대개 다음 단계로 진행된다. - 유지보수자 계정이나 프로젝트의 GitHub Actions 워크플로를 침해한다. - CI/CD 환경에서 더 높은 권한과 자격 증명을 찾는다. - 토큰과 계정 정보를 외부로 유출한다. - 탈취한 자격 증명으로 다른 패키지와 프로젝트를 연쇄적으로 감염시킨다. - 공격 방식은 다양하지만, 초기 침해·권한 상승·확산이라는 공통된 흐름을 가진다. - 따라서 하나의 보안 기능보다 공격 사슬의 영향력이 큰 지점을 여러 단계에서 차단하는 접근이 필요하다. ## 초기 침해 차단 - **고영향 npm 계정 보호** - 이메일 주소를 변경하거나 2FA 복구 코드를 사용한 고영향 npm 계정은 72시간 동안 읽기 전용 상태가 된다. - 피싱으로 계정을 탈취당하더라도 공격자가 즉시 악성 버전을 배포하지 못하게 한다. - 해당 기간 동안 유지보수자가 계정을 복구하거나 이상 행위를 확인할 수 있다. - **`pull_request_target`의 안전한 checkout 기본값** - 포크에서 제출된 풀 리퀘스트의 신뢰할 수 없는 코드를 워크플로가 실행하는 “pwn request” 취약점을 완화한다. - `actions/checkout`은 일반적으로 악용되는 트리거에서 포크의 비신뢰 코드를 checkout하지 않도록 변경됐다. - 관리자가 위험을 검토한 뒤 명시적으로 예외를 선택해야 하며, 기존 버전에도 변경 사항이 백포트됐다. - **워크플로 실행 주체와 트리거 제한** - 엔터프라이즈·조직·저장소 수준에서 누가 워크플로를 실행할 수 있는지 설정할 수 있다. - 허용할 워크플로 트리거 유형도 제한할 수 있다. - 이를 통해 Actions 인프라에 최소 권한 원칙을 적용하고 공격 표면을 줄인다. - **비신뢰 트리거의 Actions 캐시 읽기 전용화** - 공격자가 워크플로 실행 권한을 얻은 뒤 공유 캐시를 오염시켜 더 높은 권한의 워크플로를 공격하는 경로를 차단한다. - 신뢰도가 낮은 워크플로는 다른 워크플로와 공유되는 캐시를 수정할 수 없게 된다. - 결과적으로 릴리스·패키지 배포 워크플로의 고권한 자격 증명 탈취로 이어지는 권한 상승을 어렵게 만든다. ## 자격 증명 탈취와 유출 방지 - **장기 자격 증명 제거** - CI/CD 파이프라인에 저장된 장기 토큰은 공격자가 가장 먼저 노리는 대상이다. - npm Trusted Publishing은 장기 자격 증명 없이 패키지를 배포하도록 해 탈취 가능한 비밀 정보를 줄인다. - CircleCI도 Trusted Publishing 공급자로 추가되어, CircleCI 기반 배포에서도 장기 자격 증명 제거가 가능해졌다. - **Actions 네트워크 방화벽** - 기술 프리뷰 단계의 기능으로, Actions 실행 중 발생하는 모든 외부 네트워크 트래픽을 기록한다. - 악성 코드 다운로드나 새 도메인으로의 자격 증명 유출처럼 비정상적인 통신을 탐지하는 데 활용할 수 있다. - 향후에는 네트워크 외부 연결 제한과 정책 기반 차단을 지원해 유출이 발생하기 전에 공격을 막는 방향으로 확장될 예정이다. ## 악성 패키지 확산 억제 - **npm 단계적 게시(Staged Publishing)** - 패키지 배포 자격 증명만 가지고는 새 버전을 즉시 공개할 수 없도록 한다. - 배포된 패키지는 추가 승인과 npm CLI 또는 npmjs.com에서의 2FA 인증이 완료될 때까지 보류된다. - 선택적으로 활성화할 수 있으며, 자동화·CI/CD 자격 증명과 실제 배포 승인 절차를 분리한다. - 탈취된 배포 자격 증명으로 악성 버전을 대규모 배포하는 공격을 줄인다. ## 실용적인 권장 사항 npm 유지보수자는 고영향 계정 보호, Trusted Publishing, 단계적 게시를 검토하고, GitHub Actions 사용자는 포크 PR 처리 방식과 캐시 공유 구조를 점검하는 것이 좋다. 또한 워크플로 실행 주체를 최소화하고, Actions 네트워크 트래픽을 모니터링하면 자격 증명 유출과 연쇄 확산을 조기에 발견할 가능성을 높일 수 있다.

gitlab

샤이훌루드 모방 캠페인, PyPI 타이포스쿼팅으로 파이썬 개발자 노려 (새 탭에서 열림)

2026년 6월, GitLab은 PyPI를 대상으로 한 Shai-Hulud 악성코드 모방 공급망 공격을 발견했다. 공격자는 Flask·Requests·NumPy의 오타 패키지와 정상 프로젝트를 변조한 패키지에 설치 시 자동 실행되는 자격 증명 탈취 웜을 삽입했다. 이 웜은 CI/CD와 클라우드 자격 증명을 훔칠 뿐 아니라 GitHub 저장소와 패키지 레지스트리에 악성 코드를 퍼뜨려 추가 감염을 시도한다. ## 공격 대상 패키지와 위장 방식 - 공격 패키지 5개는 모두 PyPI 계정 `elitexp`에서 배포됐다. - 오타 패키지: - `rlask`, `tlask`: Flask 사칭 - `rsquests`: Requests 사칭 - `nhmpy`: NumPy 사칭 - `mflux-streamlit`은 원래 정상적으로 사용되던 프로젝트였지만, 공격자가 버전 `0.0.3`, `0.0.4`를 악성 버전으로 변조했다. - 공격자는 먼저 실제 최신 버전과 동일한 “탐색용” 정상 패키지를 등록한 뒤, 악성 페이로드가 포함된 후속 버전을 배포했다. - 따라서 단순 오타 입력뿐 아니라, 기존 프로젝트의 정상적인 의존성 업데이트도 감염 경로가 될 수 있다. ## `.pth` 파일을 이용한 설치 시 자동 실행 - npm 기반 Shai-Hulud 변종이 `preinstall` 스크립트를 사용한 것과 달리, 이번 공격은 Python의 `.pth` 메커니즘을 악용했다. - Python은 시작 시 패키지에 포함된 `.pth` 파일을 자동 처리하므로, 사용자가 악성 모듈을 직접 import하거나 함수를 호출하지 않아도 코드가 실행될 수 있다. - 예시 파일은 `rlask-setup.pth`이며, 임시 디렉터리의 `.bun_ran` 마커 파일을 확인한 뒤 다음 작업을 수행한다. - GitHub에서 Bun JavaScript 런타임 다운로드 - 패키지에 포함된 약 5MB 크기의 난독화 JavaScript 실행 - 마커 파일을 이용해 반복 실행 방지 - 초기 `rlask` 버전에는 Python 시작 시 자동 import되는 `sitecustomize.py`도 포함되어 `_index.js`를 실행하는 보조 경로가 있었다. - 이후 버전에서는 `.pth` 실행 방식만 남겨 공격 구조를 단순화했다. ## 다층 난독화와 페이로드 구성 - JavaScript 페이로드는 세 단계로 난독화됐다. - 정수 배열에 ROT-N 문자 치환 적용 - AES-128-GCM으로 두 개의 데이터 블록 암호화 - `_0x` 접두사의 변수명 난독화 - 패키지마다 ROT 값이 달랐다. - `rlask`: ROT-13 - `rsquests`: ROT-17 - `tlask`: ROT-25 - 분석 결과: - 첫 번째 블록 약 907바이트: Bun 런타임 다운로드 코드 - 두 번째 블록 약 772KB: Shai-Hulud 자격 증명 탈취 웜 전체 - 두 번째 페이로드에는 2,538개의 하드코딩된 문자열이 포함돼 있었다. - GitLab 연구팀은 실제 실행 없이 정적 분석만으로 페이로드를 복호화했다. ## 광범위한 자격 증명 탈취 웜은 주요 클라우드, CI/CD, 패키지 저장소와 개발 환경을 폭넓게 탐색한다. - GitHub: - `GITHUB_TOKEN`, 개인·Fine-grained 토큰 - OIDC 토큰, 조직·저장소 시크릿 - Actions 아티팩트와 러너 프로세스 메모리 - AWS: - IAM 액세스 키와 시크릿 키 - 세션 토큰, STS 토큰 - IMDS 주소 `169.254.169.254`의 인스턴스 자격 증명 - Secrets Manager와 SSM 파라미터 - Azure 및 GCP: - 클라이언트 시크릿, 관리형 ID 토큰, Key Vault 시크릿 - 서비스 계정 키와 Application Default Credentials - HashiCorp Vault: - `/var/run/secrets/vault-token`, `/etc/vault/token`, `/root/.vault-token` 등 알려진 경로 - API 및 Kubernetes 인증 정보 - 패키지 저장소: - npm, JFrog/Artifactory, PyPI, RubyGems 토큰 - OIDC 토큰 교환 정보 - 기타: - SSH 개인 키 - Kubernetes 서비스 계정 토큰과 kubeconfig - Sigstore OIDC 토큰 및 Fulcio 서명 인증서 - MongoDB, MySQL, PostgreSQL, Redis 접속 문자열과 비밀번호 ## 자격 증명 탈취를 넘어선 자기 전파 - 이 악성코드는 단순한 정보 탈취기가 아니라 훔친 권한을 이용해 다른 환경으로 확산하는 웜이다. - 접근 가능한 GitHub 저장소에 다음 파일을 커밋한다. - `.github/setup.js` - GitHub Actions 워크플로 파일 - 이를 통해 다른 CI 파이프라인에서 악성 코드가 다시 실행되도록 만든다. - `.github/copilot-instructions.md`를 삽입해 AI 코딩 도구의 동작을 오염시키려 한다. - 훔친 레지스트리 토큰으로 PyPI, npm, RubyGems에 추가 악성 패키지를 배포한다. - 자체 호스팅 CI 러너에서는 `sudoers` 규칙을 삽입해 권한 상승을 시도한다. - StepSecurity의 `harden-runner`가 존재하는지 확인하고 탐지되면 동작을 조정한다. ## 공격자와 정상 프로젝트 변조 - PyPI 계정 `elitexp`는 2024년 11월 생성됐으며, 원래 정상 패키지 `mflux-streamlit`을 보유하고 있었다. - 연결된 GitHub 계정은 13년 이상 된 계정으로, 대학 과제와 Laravel 프로젝트 등 다수의 공개 저장소가 있어 신뢰성을 높이는 데 활용됐을 가능성이 있다. - 모든 패키지의 업로드에는 `Bun/1.3.14` 사용자 에이전트가 사용됐다. - 공격자는 악성코드 실행 과정에서도 동일한 Bun 런타임을 다운로드한다. - `mflux-streamlit`의 `0.0.1`, `0.0.2`는 정상 버전이지만, 이후 `0.0.3`, `0.0.4`에는 동일한 `.pth` 드로퍼와 난독화 페이로드가 포함됐다. - 정상 프로젝트의 기존 사용자까지 일반적인 업데이트 과정에서 감염될 수 있다는 점이 전형적인 typosquatting보다 위험하다. ## 실용적인 대응 권고 - `rlask`, `tlask`, `rsquests`, `nhmpy`, `mflux-streamlit`의 악성 버전 설치 여부를 확인하고 즉시 제거한다. - 해당 패키지가 설치된 환경에서는 CI/CD, 클라우드, 패키지 저장소, SSH 관련 자격 증명을 모두 폐기하고 재발급한다. - GitHub 저장소의 워크플로와 `.github/setup.js`, `.github/copilot-instructions.md` 변경 이력을 점검한다. - Python 패키지 설치 시 패키지명뿐 아니라 유지보수자, 배포 이력, 버전 변화, 해시를 검증한다. - CI 환경에서는 최소 권한 토큰, 짧은 수명의 자격 증명, 네트워크 제한, 패키지 허용 목록을 적용하는 것이 안전하다.

github

GitHub 전반의 오픈 소스 공급망 보안 강화 (새 탭에서 열림)

공격자들은 GitHub Actions 워크플로를 침해해 API 키 같은 비밀을 탈취한 뒤, 악성 패키지를 배포하고 더 많은 프로젝트로 공격을 확산시키고 있다. 이에 대응하려면 Actions 워크플로를 CodeQL로 점검하고, 서드파티 액션을 커밋 SHA로 고정하며, 장기 비밀 대신 OIDC 기반 인증과 trusted publishing을 사용해야 한다. GitHub는 npm 악성코드 탐지와 Actions·npm 보안 로드맵을 강화하고 있지만, 오픈소스 생태계 전반의 지속적인 협력이 필요하다고 강조한다. ## GitHub Actions 워크플로가 공격의 출발점 - 최근 오픈소스 공급망 공격은 GitHub Actions 워크플로의 취약점을 찾는 방식으로 시작되는 경우가 많다. - 공격자는 워크플로에서 API 키, 토큰, 배포 자격 증명 등의 비밀을 탈취한다. - 탈취한 자격 증명으로: - 공격자가 통제하는 환경에서 악성 패키지를 배포하고 - 해당 패키지를 이용해 다른 프로젝트와 계정으로 공격을 확산한다. ## 지금 적용할 수 있는 Actions 보안 조치 - 공개 저장소에서 무료로 사용할 수 있는 **CodeQL의 GitHub Actions 분석 기능**을 활성화한다. - 워크플로 구현상의 보안 취약점과 보안 모범 사례 위반을 점검할 수 있다. - `pull_request_target`을 사용하지 않는다. - 외부 기여자의 코드가 높은 권한의 워크플로 컨텍스트에서 실행될 위험이 있다. - 서드파티 GitHub Actions를 전체 길이의 커밋 SHA로 고정한다. - 태그나 브랜치는 이후 다른 코드로 변경될 수 있지만, SHA 고정은 특정 코드 버전을 보장한다. - 이 변경은 저장소 관리자나 Dependabot이 수행해야 하며, 외부 Pull Request가 액션 버전 고정을 바꾸려 하면 주의해야 한다. - 사용자 입력을 셸 명령이나 스크립트에 직접 삽입하지 않는다. - 이 방식은 스크립트 인젝션으로 이어질 수 있다. - 침해된 의존성은 GitHub Advisory Database에서 확인한다. - Dependabot을 사용해 악성 또는 취약한 의존성에 대한 알림을 받는다. ## 비밀 대신 OIDC와 trusted publishing 사용 - 워크플로에 장기 보관되는 비밀을 넣는 대신, **OpenID Connect(OIDC) 토큰**을 사용할 수 있다. - OIDC 토큰에는 실행 중인 워크로드의 신원이 포함되므로, 클라우드 제공업체·패키지 저장소·호스팅 서비스가 해당 워크플로를 검증하고 권한을 부여할 수 있다. - GitHub와 OpenSSF는 이를 패키지 저장소의 **trusted publishing**으로 확산하고 있다. - 현재 npm, PyPI, NuGet, RubyGems, Crates 등 여러 저장소에서 지원된다. - trusted publishing의 장점: - 빌드 파이프라인에서 장기 비밀을 제거한다. - 패키지가 어떤 신뢰된 워크플로에서 배포됐는지 확인할 수 있다. - 패키지가 갑자기 trusted publishing을 중단하면, 탈취된 자격 증명을 이용한 공격 가능성을 조사하는 신호가 된다. ## npm의 악성 패키지 탐지 - npm에서는 매일 3만 개가 넘는 패키지가 배포된다. - GitHub는 모든 npm 패키지 버전을 악성코드 관점에서 검사한다. - 탐지 규칙은 공격 방식의 변화에 맞춰 지속적으로 개선된다. - 매일 수백 개의 신규 배포 패키지에서 악성 코드가 발견될 수 있으며, 실제 조치 전에는 사람이 양성 여부를 검토한다. - 오탐률이 1%만 되어도 매일 수백 개의 정상 패키지 배포가 중단될 수 있으므로, 자동 탐지와 사람의 검증 사이 균형이 중요하다. ## Shai-Hulud 이후의 보안 로드맵 - 2025년 말 발생한 Shai-Hulud 공격은 npm 보안 로드맵을 재정비하는 계기가 됐다. - GitHub는 다음 작업을 가속했다. - npm trusted publishing 확대 - 악성코드 탐지 및 제거 기능 강화 - 오픈소스 유지관리자와의 보안 요구사항 논의 - 보안 변경은 기존 워크플로를 수정하게 만들거나 하위 호환성을 깨뜨릴 수 있으므로, GitHub는 생태계가 원활하게 전환할 수 있도록 지원할 계획이다. - 최근 공격을 계기로 GitHub Actions 보안 로드맵도 재검토하고, 이미 진행 중인 보안 기능의 출시를 앞당기고 있다. ## 오픈소스 생태계의 공동 대응 - 오픈소스는 전 세계가 공유하는 공공재인 만큼 공격이 앞으로도 계속될 가능성이 높다. - GitHub는 npm과 GitHub Actions를 비롯해 향후 등장할 새로운 공격 경로까지 방어 범위를 확대하겠다고 밝혔다. - 효과적인 보안 기능을 만들기 위해 유지관리자와 커뮤니티의 피드백이 중요하다. 실무적으로는 먼저 CodeQL로 Actions를 검사하고, `pull_request_target` 제거·커밋 SHA 고정·스크립트 인젝션 점검을 진행하는 것이 좋다. 이후 가능하면 OIDC와 trusted publishing으로 배포 인증을 전환해, 탈취 가능한 장기 비밀 자체를 줄여야 한다.