pull_request_target 안전하게 사용하기
pull_request_target 안전하게 사용하기
pull_request_target 이벤트의 보안 위험에 대해 배워볼게요. 이 가이드를 통해 워크플로가 이 이벤트를 써야 하는지 판단하고, GitHub가 기본적으로 적용하는 보호와 그 보호에서 제외하는 방법까지 이해할 수 있어요.
출처: 문서
본문
pull_request_target 이벤트의 보안 위험에 대해 알아봅니다.
이 가이드는 워크플로가 pull_request_target 이벤트를 사용해야 하는지 평가하고 관련된 보안 위험을 이해하는 데 도움을 줍니다. 또한 GitHub가 이러한 위험에 기본적으로 적용하는 보호와, 필요하다면 그러한 보호에서 제외하는 방법도 설명합니다.
pull_request_target의 위험
pull_request_target으로 트리거된 워크플로는 높아진 신뢰로 실행됩니다: 잡은 베이스 저장소의 GITHUB_TOKEN과 저장소 및 조직 시크릿에 대한 접근을 받습니다. 이는 push처럼 협업자만 트리거할 수 있는 이벤트에 주어지는 것과 같은 신뢰이며, 라벨링, 트라이지, 인증된 상태 검사 게시처럼 포크의 풀 리퀘스트에 응답하는 자동화에 pull_request_target을 유용하게 만드는 이유입니다.
왜 기본적으로 안전한지, 그리고 그 안전성이 일반적으로 어떻게 깨지는지 이해하려면 pull_request_target을 pull_request와 비교해서 살펴보세요.
pull_request 이벤트(pull_request_review와 pull_request_review_comment 포함)는 특이합니다: 워크플로 파일을 풀 리퀘스트의 머지 커밋에서 실행합니다. 포크에서 연 풀 리퀘스트의 경우 그 커밋은 베이스 저장소에 write 접근 권한이 없는 사람이 제어합니다. 신뢰할 수 없는 워크플로 코드를 안전하게 실행하기 위해 GitHub는 이러한 이벤트를 읽기 전용 GITHUB_TOKEN으로 제한하고, 다른 시크릿 접근을 보류하며, 컴퓨팅 남용을 방지하기 위해 포크 승인 정책을 적용합니다. 기본적으로 pull_request 워크플로의 actions/checkout도 풀 리퀘스트의 머지 커밋을 체크아웃하므로, 체크아웃된 코드와 실행되는 워크플로가 일관됩니다.
pull_request_target은 결정적이고 미묘한 변화 하나를 만듭니다: 워크플로와, ref를 지정하지 않는 이후의 actions/checkout 호출은 풀 리퀘스트가 아니라 베이스 저장소의 기본 브랜치에서 가져옵니다. 기본 브랜치의 신뢰할 수 있는 코드만 실행되므로 시크릿과 읽기/쓰기 토큰을 부여하는 것이 안전합니다. 기본적으로 포크의 코드는 실행되지 않습니다.
워크플로 작성자가 이 기본값을 재정의해서 포크의 코드를 실행하면 위험이 발생합니다. 개발자들은 CI를 통해 포크의 풀 리퀘스트를 실행하고 또한 시크릿에 접근하고 싶기 때문에(예: 비공개 레지스트리가 필요한 테스트 실행) 자주 pull_request_target을 선택합니다. 이를 위해 actions/checkout을 기본 브랜치 대신 풀 리퀘스트 head를 가리키게 하는데, 이는 안전하지 않습니다:
# INSECURE. Provided as an example only.
on:
pull_request_target:
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
with:
ref: ${{ github.event.pull_request.head.sha }}
- name: Test
run: make test
체크아웃 스텝 자체는 신뢰할 수 없는 코드를 실행하지 않습니다. 워크플로 파일 자체는 여전히 기본 브랜치에서 옵니다. 취약점은 현재 작업 디렉터리로 체크아웃된 코드를 실행하는 다음 스텝에서 완성됩니다. 여기서 make test는 풀 리퀘스트 head에서 가져온 Makefile을 실행합니다. 공격자는 Makefile(또는 빌드 스크립트, 테스트 명령, 의존성, 구성 파일)에 악성 명령이 포함된 포크에서 풀 리퀘스트를 열기만 하면 됩니다. 그러면 그 명령들이 베이스 저장소의 시크릿과 토큰으로 실행됩니다.
이 패턴은 "pwn request"로 알려져 있으며 여러 공급망 침해의 근본 원인이었습니다. 자세한 내용은 GitHub Security Lab의 Preventing pwn requests를 참고하세요. 일반적인 취약한 형태는 다음과 같습니다:
actions/checkout에서 풀 리퀘스트의 head나 머지 커밋(ref: ${{ github.event.pull_request.head.sha }},ref: refs/pull/${{ github.event.pull_request.number }}/merge)을 체크아웃한 다음 결과를 빌드, 테스트, 또는 다른 방식으로 실행하는 것.- 포크의 브랜치를 직접 가져오기 위해
repository:를 포크(repository: ${{ github.event.pull_request.head.repo.full_name }})로 설정하는 것. actions/checkout밖에서 풀 리퀘스트 코드를 가져오는 것(예:git fetch,gh pr checkout, 또는 포크의pull_request실행에서 아티팩트 다운로드) 그런 다음 실행하는 것.
Pwn request는 pull_request_target에만 고유하지 않습니다. 시크릿으로 실행되는 모든 이벤트는 신뢰할 수 없는 코드를 체크아웃하거나 다운로드해서 실행하면 pwn request를 도입할 수 있습니다. 예를 들어 포크의 풀 리퀘스트 코드를 가져와 실행하는 issue_comment나 workflow_run 워크플로도 같은 방식으로 취약합니다. workflow_run 워크플로는 다른 워크플로가 업로드한 아티팩트를 신뢰할 수 없는 데이터로 취급해야 합니다. 그 내용이 포크에서 올 수 있기 때문입니다.
pull_request_target의 기본 정책
워크플로를 신뢰할 수 없는 풀 리퀘스트로부터 보호하기 위해 GitHub는 공개 저장소에서 pull_request_target 이벤트를 차단하는 기본 이벤트 정책을 제공합니다.
기본 정책이 작동하는 방식
아직 적용 가능한 Actions 이벤트 정책이 없는 공개 저장소의 경우 GitHub는 pull_request_target으로 트리거된 워크플로를 차단하는 기본 정책을 추가합니다. 정책에 대한 자세한 내용은 About Actions policies를 참고하세요.
기본 정책:
- 비공개 또는 내부 저장소에는 적용되지 않습니다.
- 이미 구성한 적용 가능한 이벤트 정책을 대체하지 않습니다.
- 현재 평가(evaluate) 모드로 실행됩니다. 이 모드에서는 워크플로 실행이 계속되지만, 정책 인사이트를 사용해서 적용 후 차단될 실행을 식별할 수 있습니다.
2026년 11월 2일에 GitHub는 일반 공급 전에 기본 pull_request_target 정책을 사용 중이던 영향을 받는 저장소에 대해 기본 정책을 적용할 것입니다.
적용 전에 영향 검토하기
정책이 평가 모드에 있는 동안 정책 인사이트를 검토해서 pull_request_target을 사용하고 정책 적용 시 차단될 워크플로를 식별하세요.
영향을 받는 각 워크플로에 대해 pull_request_target이 여전히 필요한지 결정하세요:
- 워크플로가
pull_request_target을 필요로 하지 않는다면 적절한 경우pull_request같은 더 안전한 이벤트를 사용하도록 업데이트하세요. - 워크플로가 계속
pull_request_target을 사용해야 한다면pull_request_target을 명시적으로 허용하는 적용 가능한 Actions 이벤트 정책을 만들거나 업데이트하세요. pull_request_target을 허용하고 싶지 않다면 기본 정책을 그대로 두세요. 적용 후 GitHub는 해당 이벤트로 트리거된 워크플로를 차단합니다.
[!WARNING]
pull_request_target은 필요할 때만 허용하세요. 이 이벤트로 트리거된 워크플로는 저장소 시크릿이나 권한 있는GITHUB_TOKEN에 접근하면서 신뢰할 수 없는 풀 리퀘스트의 코드를 체크아웃하거나, 빌드하거나, 실행해서는 안 됩니다.
이벤트 정책 구성 및 인사이트 보기에 대한 자세한 내용은 Controlling who can execute GitHub Actions workflows를 참고하세요. 정책을 프로그래밍 방식으로 관리하려면 REST API endpoints for GitHub Actions policies를 참고하세요.
pull_request_target을 사용할지 결정하기
일부 워크플로는 높아진 신뢰로 포크 풀 리퀘스트 코드를 체크아웃해야 하며, 이것이 pull_request_target이 원래 만들어진 이유입니다. 예를 들어 비공개 아티팩트 레지스트리가 필요한 커버리지 보고서 생성이나, 풀 리퀘스트에서 도입된 변경 사항에 대해 인증된 검사를 생성·실행하는 경우입니다. pull_request_target을 사용하거나 actions/checkout의 allow-unsafe-pr-checkout 플래그를 선택하기 전에 아래 질문을 고려하세요.
-
대신
pull_request를 사용할 수 있나요?pull_request는pull_request_target과 같은 이벤트에서 트리거되고pull_request머지 브랜치에서 워크플로 코드를 실행합니다. 위에서 자세히 설명한 보호를 통해 포크의 풀 리퀘스트에서 안전하게 실행됩니다. 추가 시크릿 접근이 필요하지 않다면pull_request를 사용하세요. 더 복잡한 워크플로는 잠재적으로 위험한 풀 리퀘스트 코드 처리를 시크릿 접근과 분리하도록 재구성할 수 있습니다. 자세한 내용은 GitHub Security Lab의 Preventing pwn requests를 참고하세요. -
체크아웃된 코드가 실행되나요? 이것이 pwn request 취약점을 도입하는 결함입니다. 가장 흔히
actions/checkout으로 풀 리퀘스트 head를 작업 디렉터리로 체크아웃한 다음 실행함으로써 도입됩니다.path입력이 설정되어 있지 않으면actions/checkout은 코드를$GITHUB_WORKSPACE디렉터리에 씁니다.$GITHUB_WORKSPACE는 일반적으로 이후 명령이 실행되는 작업 디렉터리입니다. 실행은 자신의 스텝에만 국한되지 않습니다:npm install과npm run build같은 빌드·테스트 명령과 코드가 가져오는 구성 파일, 의존성 모두 공격자가 제어하는 코드를 실행할 수 있습니다. 실행에 명확한 빌드 스텝이 필요하지 않습니다.pull_request_target이벤트를 사용하기 전에 체크아웃된 코드가 데이터로만 검사되고 절대 실행되지 않도록 해야 합니다.
pull_request_target 워크플로 강화하기
pull_request_target이 필요하다는 것을 확인했다면 이 고위험 이벤트의 영향을 제한하기 위해 다음 제어를 적용하세요. 이는 워크플로가 풀 리퀘스트 코드를 체크아웃하는지 여부와 관계없이 적용됩니다.
-
시크릿 제한하기.
GITHUB_TOKEN에 설정된 권한이 최소 권한을 가지는지, 워크플로에 필요한 저장소 및 조직 시크릿만 사용되는지 확인하세요. 자세한 내용은 Use GITHUB_TOKEN for authentication in workflows을 참고하세요. -
캐싱에 대한 영향 이해하기. 캐시 오염(cache poisoning) 위험을 줄이기 위해
pull_request_target으로 트리거된 워크플로는 기본 브랜치 스코프의 캐시에 대해 읽기 전용 접근을 가집니다. 이러한 워크플로는 기존 캐시 항목을 복원할 수 있지만 생성하거나 덮어쓸 수는 없으므로 공유 캐시를 통해 다른 무관한 워크플로의 실행에 영향을 줄 수 없습니다. 그러한 워크플로가 캐시를 저장하려고 하면 저장은 실패하지만 스텝과 잡은 계속되고, 실패는 워크플로 로그에서 경고로 보고됩니다. 워크플로가 캐시를 채워야 한다면push같은 신뢰할 수 있는 트리거에서 실행되는 워크플로로부터 저장하세요. 워크플로나 잡은 write 가능한cache-mode를 명시적으로 선언해서 이 읽기 전용 제한에서 제외될 수 있지만,pull_request_target워크플로에서 그렇게 하면 이 제한이 방지하려고 설계된 캐시 오염 위험이 다시 도입됩니다. 자세한 내용은 Dependency caching reference를 참고하세요. -
기본 컴퓨팅이 격리되고 임시적인지 확인하기. 셀프 호스팅 러너를 사용한다면 러너 환경이 내부 리소스로부터 제대로 제한되고 GitHub Actions 실행 간에 재사용되지 않는지 확인해야 합니다. 자세한 내용은 Secure use reference를 참고하세요.
-
GitHub Actions 보안 모범 사례 적용하기. pwn request의 특정 위험 외에도 커맨드 인젝션 같은 다른 일반적인 취약점이 존재할 수 있고 이 권한 있는 이벤트에서 실행되는 코드에 영향을 줄 수 있습니다. 자세한 내용은 GitHub Security Lab의 Keeping your GitHub Actions and workflows secure: Untrusted input을 참고하세요. 일반적인 GitHub Actions 취약점을 식별하고 선제적으로 보호하려면 GitHub Actions용 CodeQL을 활성화하세요. 자세한 내용은 Configuring default setup for code scanning을 참고하세요.
내장 보호에서 제외하기
위 질문들을 검토하고 워크플로가 pull_request_target을 요구하며 안전하게 사용한다는 것을 확인했다면 기본 이벤트 정책과 actions/checkout 보호에서 제외할 수 있습니다.
actions/checkout 입력으로 allow-unsafe-pr-checkout: true를 설정하면 포크의 풀 리퀘스트 head ref를 체크아웃할 수 있습니다. 체크아웃된 코드가 절대 실행되지 않음을 확인한 후에만 이 작업을 수행하세요. 이 입력은 코드 리뷰와 정적 분석에서 쉽게 발견되도록 의도적으로 이름이 지어졌습니다.
이 보호는 포크 풀 리퀘스트 ref만 다룹니다. 무관한 제3자 저장소 같은 다른 신뢰할 수 없는 코드를 체크아웃하거나, git fetch나 gh pr checkout으로 코드를 가져오거나, 다운로드한 아티팩트를 실행하는 것은 actions/checkout 검사에 포함되지 않습니다.