필요한 정보 모아보기
테크

GitHub Actions pull_request_target, 11월 2일부터 공개 저장소 기본 차단 — 워크플로 실행 보호 정리

#GitHub Actions#pull_request_target#보안

GitHub Actions 워크플로 실행 보호(workflow execution protections)는 누가, 어떤 이벤트로 워크플로를 실행할 수 있는지 허용목록으로 정하는 정책 기능이다. 6월 18일 공개 프리뷰를 거쳐 9월 17일 정식 출시(GA)됐다. 가장 큰 변화는 날짜가 박힌 쪽이다. 2026년 11월 2일부터 공개 저장소의 pull_request_target 이벤트가 기본으로 차단된다.

GitHub 문서는 출발점을 이렇게 짚는다. "By default, every user with write access to a repository can trigger workflows."(기본적으로 저장소에 쓰기 권한이 있는 사용자는 누구나 워크플로를 실행할 수 있다.) 코드를 올릴 수 있는 사람과 CI를 돌릴 수 있는 사람이 지금까지는 같았다는 얘기다. 이번 기능은 이 둘을 떼어 놓는다.

규칙 두 개, 그리고 평가 모드

GA 공지의 요약이 가장 짧고 정확하다. "actor rules cover the who, event rules cover the what, and actions evaluate both before a run."(액터 규칙은 '누가', 이벤트 규칙은 '무엇이'를 다루고, 실행 전에 둘 다 평가한다.)

구분정하는 것예시
액터 규칙워크플로를 실행할 수 있는 사람·봇·팀·앱배포는 release 팀만
이벤트 규칙워크플로를 시작할 수 있는 이벤트pull_request_target 금지, pull_request만 허용
대상 범위정책을 걸 워크플로 파일.github/workflows/deploy.yml만 (GA에서 추가)
적용 방식active(차단) / evaluate(기록만) / disabled먼저 evaluate로 돌려 본다

설정 화면은 저장소·조직의 Settings → Actions → Policies에 있다. 엔터프라이즈는 Policies 탭 아래 Actions에서 연다. 규칙에 걸린 실행은 같은 메뉴의 Policy insights에 모인다. 평가 모드로 걸어 둔 규칙이 "이대로 켰다면 막혔을 실행"도 여기서 보인다.

한 가지 조건이 붙는다. 문서상 사용자가 직접 만드는 정책의 평가 모드는 GitHub Enterprise Cloud 전용이다. Dependabot처럼 봇이 돌리는 워크플로가 있다면 액터 규칙에 dependabot[bot]을 따로 넣어야 한다는 점도 놓치기 쉽다. 규칙에 막히면 실행 로그에 Event 'workflow_dispatch' is not allowed to trigger Actions workflows. 같은 메시지가 남는다.

REST API로도 같은 일을 한다. 조직은 /orgs/{org}/actions/policies, 저장소는 /repos/{owner}/{repo}/actions/policies다. GitHub 문서의 예시를 옮기면 이렇다.

gh api -X POST /orgs/ORG/actions/policies \
  -H "X-GitHub-Api-Version: 2026-03-10" \
  --input - <<'EOF'
{
  "name": "Require approved actors",
  "enforcement": "evaluate",
  "rules": [
    {
      "type": "restrict_actions_actors",
      "parameters": { "allowed_actors": [ { "id": 1234, "type": "Team" } ] }
    }
  ]
}
EOF

원래 예시의 enforcement는 active다. 여기서는 처음 거는 규칙이라는 가정으로 evaluate로 바꿨다. id는 허용할 팀의 숫자 ID다. 이벤트를 제한하려면 규칙 타입을 restrict_action_events로 두고 allowed_events 배열을 넘긴다.

우리 저장소에도 불러 봤다. 9월 30일 gh api repos/<비공개 저장소>/actions/policies를 GitHub Free 계정으로 호출하자 403과 함께 "Upgrade to GitHub Pro or make this repository public to enable this feature."가 돌아왔다. 문서에 플랜 조건이 명시돼 있진 않다. 다만 실제 응답으로 보면 무료 계정의 비공개 저장소에서는 쓸 수 없다.

pull_request_target은 왜 표적이 됐나

pull_request_target은 포크에서 온 PR에 반응하는 이벤트다. 비슷한 pull_request와 결정적으로 다른 점은 권한이다. GitHub 문서의 설명은 이렇다. "Workflows triggered by pull_request_target run with elevated trust: the job receives the base repository's GITHUB_TOKEN and access to repository and organization secrets."(높은 신뢰로 실행되며, 원본 저장소의 GITHUB_TOKEN과 저장소·조직 시크릿에 접근한다.)

라벨 붙이기나 PR 트리아지처럼 PR 코드를 실행하지 않는 작업에는 편한 기능이다. 문제는 이 권한을 쥔 채 PR 코드를 체크아웃해 npm install이나 테스트를 돌릴 때다. 외부인이 보낸 코드가 시크릿 옆에서 실행된다. 문서는 이렇게 경고한다. "This pattern is known as a "pwn request" and has been the root cause of multiple supply-chain compromises."(이 패턴은 'pwn request'라 불리며 여러 공급망 침해 사고의 근본 원인이었다.)

말뿐인 경고가 아니다. 굵직한 사례만 추려도 이 정도다.

시기사건무슨 일이 있었나
2024년 11~12월SpotBugspull_request_target 워크플로에서 관리자 토큰이 털렸고, 이것이 이듬해 tj-actions 사고의 출발점이 됨(Palo Alto Unit 42 분석)
2024년 12월Ultralytics같은 이벤트의 워크플로에 브랜치 이름으로 명령을 주입, PyPI 배포판에 채굴 코드가 들어감(Wiz 분석)
2025년 3월tj-actions/changed-files널리 쓰이는 액션의 태그가 악성 커밋으로 바뀜(CVE-2025-30066), 2만 3천여 저장소가 영향
2026년 2~3월hackerbot-clawAI 봇이 공개 저장소 7곳을 공격, 그중 3곳이 pull_request_target 경로(StepSecurity 보고)
2026년 3~4월prt-scan계정 6개가 500건 넘는 악성 PR을 뿌림. 4월 2일 한 계정은 "over 475 malicious PRs in 26 hours"(Wiz Research)

prt-scan에 대해 Wiz는 분석한 공격 450여 건 중 성공률이 10% 미만이었다고 밝혔다. 대부분 막혔다는 뜻이지만, 뚫린 곳에선 npm 패키지 배포 권한까지 넘어갔다. 자동화된 공격은 실패율이 높아도 문제없다. 시도 횟수로 밀어붙이기 때문이다.

CI 러너가 노려지는 이유는 숫자로도 보인다. GitGuardian의 「State of Secrets Sprawl 2026」(3월 17일 발표)에 따르면 2025년 공개 GitHub에서 새로 노출된 하드코딩 시크릿은 2,865만 건으로 전년보다 34% 늘었다. 같은 보고서는 Shai-Hulud 2 공급망 공격을 분석하면서 감염된 머신 6,943대 가운데 59%가 개발자 PC가 아니라 CI/CD 러너였다고 적었다. 시크릿이 모여 있는 곳이 결국 러너라는 얘기다.

11월 2일, 정확히 무엇이 바뀌나

적용 대상은 생각보다 좁다. 공개 저장소이면서, 이벤트 정책을 따로 걸어 두지 않은 곳이다. 비공개(private)·내부(internal) 저장소는 대상이 아니다. 지금은 이 기본 규칙이 평가 모드로 돌면서 기록만 남기고, 11월 2일부터 실제로 차단한다.

pull_request_target을 쓰는 공개 워크플로가 얼마나 될까. 9월 30일 GitHub 코드 검색 API로 pull_request_target path:.github/workflows를 찾아보니 약 8만 건(80,128)이 나왔다. 검색 인덱스 기준의 근사치라 정확한 저장소 수는 아니다. 그래도 적은 숫자는 아니다. GitHub CLI 저장소(cli/cli)도 PR 분류용 워크플로에 이 이벤트를 쓰고 있었다. 악의 없는 트리아지 워크플로도 그대로 두면 멈출 수 있다는 뜻이다.

GitHub은 이 방향을 3월 26일 발표한 「2026 Actions 보안 로드맵」에서 예고했다. 로드맵에는 조직이 "prohibit pull_request_target events entirely and only allow pull_request"(pull_request_target을 완전히 금지하고 pull_request만 허용)할 수 있게 하겠다는 구절이 있다. 6월 18일에는 actions/checkout v7이 나와 pull_request_target·workflow_run 워크플로에서 포크 코드 체크아웃을 기본으로 거부하게 됐다. 실행 보호는 그 위에 덧댄 두 번째 잠금장치다. checkout을 거치지 않고 git이나 gh로 PR 코드를 받아 오는 경우까지는 v7이 못 막기 때문이다.

공개 저장소 관리자라면, 지금 할 일

개인적으로는 11월 2일을 "기능 추가"가 아니라 "기본값 변경" 일정으로 보는 게 맞다고 본다. 설정을 아무것도 안 해도 동작이 바뀐다. 순서는 이렇다.

  1. 어디서 쓰는지부터 찾는다. 로컬에선 grep -rln "pull_request_target" .github/workflows/, 조직 전체라면 gh search code "pull_request_target" --owner <조직명> --extension yml 한 줄이면 된다. (--filename "*.yml"은 와일드카드가 먹지 않아 0건이 나온다.)
  2. 찾은 워크플로가 PR 코드를 실행하는지 본다. 라벨링·댓글처럼 코드를 안 돌리면 이벤트 정책으로 명시적으로 허용하는 쪽이다. 테스트·빌드를 돌린다면 pull_request로 옮기는 게 맞다.
  3. Settings → Actions → Policy insights에서 평가 모드 기록을 확인한다. 11월 2일 전에 "막혔을 실행"이 뜨는지 보면 된다.
  4. 배포처럼 민감한 워크플로에는 액터 규칙을 따로 건다. deploy.yml만 대상으로 잡고 허용 팀을 지정하면, 나머지 CI는 기존처럼 모든 기여자에게 열어 둘 수 있다.

비공개 저장소만 운영한다면 11월 2일에 당장 달라지는 건 없다. 그래도 러너가 시크릿을 쥐고 있는 한 "누가 돌릴 수 있나"는 한 번쯤 정리해 둘 질문이다. 위 표의 사고들도 결국 시크릿을 쥔 워크플로가 남의 코드를 실행한 데서 시작됐다.

인용한 GitHub 문서(docs.github.com) 문장은 CC BY 4.0 라이선스이며, 체인지로그·블로그 문장은 GitHub 원문을 짧게 인용했다.

출처 github.blog/…/2026-09-17-workflow-execution-protections-in-github-actions-generally-availabledocs.github.com/…/about-actions-policiesdocs.github.com/…/securely-using-pull_request_targetgithub.blog/…/whats-coming-to-our-github-actions-2026-security-roadmapgithub.blog/…/2026-06-18-safer-pull_request_target-defaults-for-github-actions-checkoutwiz.ioblog.gitguardian.com

내용이 틀렸나요? 정정 요청

관련 글