GitHub Actions 보안 대격변: actions.lock 정식 출시(GA)와 11월 9일 pull_request_target 기본 차단 브라운아웃 대응 가이드
10월 28일 GA된 actions.lock의 3계층 의존성 봉인 원리와 오늘(11월 9일) 시작되는 공개 저장소 pull_request_target 기본 차단 브라운아웃 대응책을 정리한다. 가변 태그 위험, Pwn Request 시크릿 탈취 구조, 로컬 Python 검증과 실무 체크리스트를 살펴본다.
GitHub Actions 공급망 보안 격변을 형상화한 추상 기하학 아트워크다. @v4 태그 가독성을 지키면서 암호학적 커밋 SHA를 강제하고, 저장소 identity 위변조와 위험한 pull_request_target 실행을 차단하는 다층 보안 쉴드 개념을 표현했다.
GitHub Actions 환경에 공급망 보안과 권한 통제를 근본적으로 강화하는 두 가지 거대한 변화가 2026년 11월 동시에 도래했다. 첫째는 2026년 10월 28일 정식 출시(GA)된 **워크플로 의존성 락파일(Workflow Dependency Lockfiles, actions.lock)**이며, 둘째는 12월 2일 전면 시행을 앞두고 2026년 11월 9일 16:00~18:00 UTC에 첫 번째 강제 중단(Brownout) 테스트가 진행되는 pull_request_target 기본 차단 정책이다. GitHub Actions Workflow Lockfile GA 공식 공지, pull_request_target 기본 차단 및 브라운아웃 일정 안내
결론부터 말하면 워크플로 파일에 단순히 @v4 태그를 적어두던 관행은 이제 업스트림 침해나 태그 이동 공격(Moving Tag Attack)에 취약한 레거시로 분류되며, 공개 저장소에서 외부 포크의 PR 코드를 pull_request_target 하에서 체크아웃해 빌드·테스트하던 파이프라인은 오늘 브라운아웃 및 12월 2일부터 즉시 실패하게 된다. 팀은 워크플로 YAML의 가독성을 해치지 않으면서 전이적(Transitive) 의존성까지 암호학적 커밋 SHA로 봉인하는 actions.lock을 도입하고, pull_request_target을 라벨러 등 읽기 전용 메타데이터 처리에만 제한하거나 2단계 분리 파이프라인(pull_request 빌드 후 workflow_run 승격)으로 전환해야 한다.
이 글은 GitHub 공식 문서와 보안 가이드를 바탕으로 actions.lock의 3계층 검증 메커니즘과 pull_request_target 기본 차단 대응 방안을 정리한 실무 엔지니어링 가이드다. 실제 파이프라인 중단 사고를 예방하기 위해 작성한 로컬 Python 정적 분석 스크립트로 락파일의 커밋 SHA 검증, 저장소 탈취(Repo Hijack) 차단, 그리고 유해한 PR 체크아웃 패턴 탐지 동작을 직접 확인했다.
1. 왜 @v4 태그는 위험하며 actions.lock은 이를 어떻게 해결하는가
지금까지 GitHub Actions 워크플로에서는 uses: actions/checkout@v4와 같이 주 버전(major version) Git 태그를 참조하는 방식이 가장 널리 쓰였다. 읽기 쉽고 마이너 업데이트를 자동으로 받을 수 있어 편리하지만, Git 태그는 언제든 삭제되거나 다른 커밋을 가리키도록 변경(force push)될 수 있는 **가변 참조(Mutable Reference)**다.
만약 신뢰하던 외부 오픈소스 액션의 메인테이너 계정이 탈취되거나 악의적인 서플라이 체인 공격으로 태그가 악성 코드가 포함된 커밋으로 변경되면, 해당 액션을 사용하는 모든 CI/CD 파이프라인이 즉시 침해되어 배포 시크릿이나 내부 소스코드가 유출될 수 있다.
이를 막기 위해 그동안 보안 팀은 긴 40자리 커밋 해시(uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683)를 직접 기재하는 방식을 권고해왔다. 하지만 이 방식은 다음과 같은 치명적인 한계가 있었다.
- 가독성 및 버전 추적의 어려움: YAML 파일만 보고는 이것이 v4인지 v3인지 한눈에 알기 어렵고 주석 관리가 번거롭다.
- 전이적(Transitive) 의존성 사각지대: 호출한 액션이 복합 액션(Composite Action)일 경우, 그 내부에서 호출하는 하위 서브 액션들은 여전히 가변 태그로 실행되어 우회 침해 위험이 남는다.
- 저장소 가로채기(Repo Hijacking) 무방비: 액션 저장소가 삭제된 후 동일한 이름으로 공격자가 재생성하거나 다른 소유자로 이전될 경우 단순 커밋 SHA 확인만으로는 탈취 여부를 판별할 수 없다.
GitHub Actions Workflow Lockfile(actions.lock)의 3단계 검증 아키텍처 다이어그램이다. 개발자 YAML의 가독성 유지, 락파일의 전이적 의존성 및 repo_id 봉인, 러너의 런타임 SHA 강제 실행 메커니즘을 도식화했다.
2026년 10월 28일 정식 출시된 **워크플로 의존성 락파일(actions.lock)**은 package-lock.json이나 Cargo.lock과 동일한 원리로 이 문제를 해결한다.
- 인간을 위한 가독성 유지: 개발자는 워크플로 YAML에서 익숙하고 깔끔한
@v4태그를 계속 사용한다.
- 머신을 위한 암호학적 고정: 공식 CLI인
gh actions-lock이.github/workflows/actions.lock파일을 생성하여 액션의 원격 태그를 검증된 고유 커밋 SHA로 매핑한다. 액션 러너(Runner)는 워크플로 실행 시 락파일에 기록된 불변 커밋 SHA를 강제로 체크아웃한다.
- 전이적 의존성의 완전한 봉인: 액션이 내부적으로 포함하는 중첩 하위 composite actions까지 재귀적으로 탐색하여 단일 락파일에 모두 고정한다.
- 저장소 신원 보증(
repo_id/owner_id): 락파일에는 커밋 해시뿐 아니라 GitHub 플랫폼 내부의 불변 고유 번호인repo_id와owner_id가 함께 기록된다. 저장소가 탈취·이전되거나 악의적으로 재등록되더라도 ID 불일치로 즉시 실행이 거부된다.
- 도달 가능성(Branch Reachability) 검사: 락파일에 지정된 커밋이 원본 저장소의 실제 유효한 브랜치나 태그로부터 도달 가능한지 검증하여, 외부 포크에서 조작된 고아 커밋(Orphan commit) 주입을 원천 방지한다.
2. 11월 9일 브라운아웃 발효: pull_request_target 기본 차단과 "Pwn Request" 위험
actions.lock이 외부 액션 공급망을 방어한다면, 11월 9일 발효되는 pull_request_target 기본 차단 정책은 외부 기여자의 악성 Pull Request로부터 원본 저장소의 권한과 시크릿을 보호하기 위한 조치다.
왜 pull_request_target이 치명적인 취약점이 되었는가
일반적인 pull_request 이벤트는 포크(Fork) 저장소에서 들어온 PR에 대해 읽기 전용(Read-only) 권한의 GITHUB_TOKEN만을 부여하며, 원본 저장소에 등록된 시크릿(Secrets)에 일절 접근할 수 없도록 격리된다.
반면 pull_request_target은 포크에서 들어온 PR임에도 불구하고 원본 저장소(Base repository)의 컨텍스트에서 실행되며, 저장소 시크릿과 쓰기 권한이 부여된 토큰을 사용할 수 있다. 원래 목적은 외부 기여자의 PR에 자동으로 라벨을 달거나, 리뷰 요청 코멘트를 남기는 등 안전한 메타데이터 자동화를 지원하기 위함이었다.
그러나 수많은 개발팀과 오픈소스 프로젝트가 빌드 및 통합 테스트 편의를 위해 다음과 같은 치명적인 실수를 저질렀다.
이 패턴은 보안 업계에서 흔히 **"Pwn Request"**라 불린다. 공격자는 단순히 악성 셸 스크립트를 포함한 PR을 제출하는 것만으로 CI 러너에서 임의 코드를 실행(RCE)하고, 메모리나 환경변수에 로드된 원본 저장소의 모든 시크릿을 탈취할 수 있었다.
pull_request_target 기본 차단 정책과 안전한 2단계 분리 파이프라인 비교 다이어그램이다. 포크 PR 코드 실행에 따른 시크릿 탈취 위험(Pwn Request)과 권한 없는 pull_request 빌드 후 workflow_run으로 연결하는 방어 아키텍처를 보여준다.
GitHub의 기본 차단 정책 및 브라운아웃 일정
이러한 침해 사고가 빈번해지자 GitHub은 모든 공개 저장소(Public Repositories)를 대상으로 정책을 강화했다.
- 적용 대상: GitHub Enterprise Cloud, Team, Free를 포함한 모든 공개 저장소(Public Repositories). (비공개/내부 저장소는 제외)
- 차단 내용: 저장소 또는 조직 설정에서 명시적인 Actions 이벤트 정책(Actions Event Policy)을 통해 허용하지 않는 한,
pull_request_target이벤트 트리거가 기본적으로 차단(Block)된다.
- 브라운아웃(Brownout) 일정:
- 1차 브라운아웃: 2026년 11월 9일 16:00 ~ 18:00 UTC (한국 시각 11월 10일 01:00 ~ 03:00)
- 2차 브라운아웃: 2026년 11월 16일 16:00 ~ 18:00 UTC
- 전면 차단 시행일: 2026년 12월 2일
브라운아웃 기간 동안 미승인 pull_request_target 워크플로는 오류 어노테이션과 함께 강제 실패 처리된다.
3. 기존 워크플로 방식과 보안 아키텍처 비교
새로운 보안 정책과 권장 아키텍처의 차이점을 기존 방식들과 비교하면 다음과 같다.
| 구분 | 1. 가변 태그 참조 ( @v4) | 2. 커밋 SHA 직접 기재 | 3. actions.lock (GA) | 4. 안전한 2단계 분리 파이프라인 |
| 가독성 및 코드 유지보수성 | 높음 (간결한 @v4) | 매우 낮음 (긴 40자리 해시 난무, 버전 파악 불가) | 최상 (YAML은 @v4 유지, lockfile에만 해시 기록) | 높음 (책임과 권한이 명확히 분리된 YAML) |
| 공급망 무결성 (Tag Hijack 방어) | 취약 (태그 강제 변경 시 즉시 침해) | 직접 의존성만 보호됨 | 완전 방어 (직접 및 하위 전이적 의존성 모두 SHA 고정) | actions.lock과 병행 적용 |
| 저장소 Identity 위변조 방어 | 방어 불가 | 방어 불가 (동일 이름 재생성 시 수용) | 완전 방어 ( repo_id, owner_id 일치 검증) | actions.lock과 병행 적용 |
| 외부 포크 PR 코드 실행 격리 | 설정에 따라 위험 | 설정에 따라 위험 | 의존성만 고정, PR 트리거 격리는 별개 | 완벽 격리 ( pull_request에서 무권한 빌드 후 workflow_run으로 승격) |
| 11월 9일 브라운아웃 영향 여부 | 영향 없음 | 영향 없음 | 영향 없음 | 필수 점검 (미허용 pull_request_target 차단 대응 완료) |
4. 로컬 Python으로 검증하는 락파일 변조 검출 및 워크플로 정적 보안 감사
실제 CI/CD 환경에서 actions.lock의 커밋 SHA 유효성 검사, 저장소 identity 불일치 차단, 그리고 pull_request_target의 유해 패턴 탐지가 어떻게 동작하는지 재현 가능한 로컬 Python 스크립트로 작성해 검증했다.
직접 실행해 확인한 5가지 검증 시나리오
로컬 터미널 환경에서 위 검증 스크립트(verify_actions_lock_and_pr_target.py)를 실행하여 5가지 핵심 케이스가 모두 정상 통과(All 5 security verification checks passed successfully!)함을 확인했다.
- 정상 락파일 해결 (Case 1): 워크플로의
@v4태그가 락파일을 통해11bd71901bbe5b1630ceea73d27597364c9af683등의 정확한 40자리 커밋 SHA로 매핑되며 유효성 검사를 통과한다.
- 락파일 누락/임의 액션 탐지 (Case 2): 락파일에 서명되지 않은 악의적 액션 참조가 삽입되었을 때 누락 오류(
Missing lockfile entry)를 발생시키며 즉시 차단한다.
- 저장소 가로채기 차단 (Case 3): 액션 저장소의
repo_id가 과거 승인된 식별자와 불일치할 때(저장소 이전 또는 공격자 재생성 시도)Repository identity mismatch로 감지하여 실행을 중단한다.
- 치명적 Pwn Request 패턴 탐지 (Case 4):
pull_request_target에서 포크의head.sha를 체크아웃하고npm install && npm test를 실행하는 워크플로를 검사하여 기본 차단 정책 위반과 원격 코드 실행 취약점(is_pwn_request_vulnerable: True)을 정확히 적발한다.
- 안전한 메타데이터 전용 패턴 허용 (Case 5): 명시적 이벤트 정책이 부여된 상태에서 코드 체크아웃 없이
actions/labeler@v5만 단독 수행하는 정상 워크플로는 차단 없이 승인된다.
5. CI/CD 운영자를 위한 실무 점검 체크리스트
gh actions-lock확장 설치 및 전수 락파일 커밋:
- CI 검증 단계에
--verify옵션 추가:
개발자가 워크플로 YAML에 새 액션을 추가하면서 락파일을 동기화하지 않고 PR을 올리는 일을 방지하기 위해 PR 검증 CI에 다음 단계를 추가한다.
- Dependabot 버전 업데이트 연동 확인:
저장소의.github/dependabot.yml에package-ecosystem: "github-actions"가 설정되어 있다면, Dependabot이 새 액션 버전을 제안할 때actions.lock파일도 동일한 PR 내에서 자동으로 갱신해 주므로 추가 작업 없이 최신 상태를 유지할 수 있다.
- 조직 및 엔터프라이즈의 "Require lockfile" 정책 검토:
조직 관리자는 Repository Settings의 Actions 정책에서 락파일이 없는 워크플로의 실행을 원천 차단하는 Require lockfile 강제 정책 적용을 단계적으로 검토한다.
- 공개 저장소
pull_request_target감사 및 11월 9일 브라운아웃 모니터링: - 공개 저장소 코드베이스에서
pull_request_target을 검색하여 불필요한 사용이 있는지 전수 조사한다. - 외부 기여 코드의 빌드·테스트가 필요하다면 반드시
on: pull_request로 전환하여 권한 없는 격리 샌드박스에서 빌드 후 아티팩트(Artifact)를 업로드하게 한다. - 배포나 승인 토큰이 필요한 후속 작업은 해당 워크플로가 정상 완료된 후 트리거되는
on: workflow_run이벤트로 분리하여 아티팩트만 내려받아 처리한다. - 오늘(11월 9일) 16:00~18:00 UTC 브라운아웃 시간대에 예기치 않게 실패하는 워크플로가 있는지 Actions 탭의 실행 로그를 집중 모니터링한다.
참고 자료
확인 기준일: November 9, 2026