CI/CD 용어 사전
GitHub ActionsSHA 고정 · 액션 고정

액션 SHA 고정

태그는 옮길 수 있다. 전체 커밋 SHA만이 불변 참조다.

- uses: actions/checkout@v4                                     # 태그 — 옮길 수 있다
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683  # SHA — 불변

공식 문서가 못 박는다 — 전체 길이 커밋 SHA 로 고정하는 것이 액션을 불변 릴리스로 쓰는 유일한 방법이다.

태그가 왜 약속이 아닌가

Git 태그는 특정 커밋을 가리키는 이름표일 뿐이고 언제든 다른 커밋으로 옮길 수 있다. v4 는 관례상 v4 계열의 최신을 가리키도록 관리자가 계속 옮긴다 — 즉 옮기는 것이 정상 동작이다.

그래서 액션 저장소가 침해되면 공격자는 v4 를 자기 커밋으로 옮기기만 하면 된다. 그 순간 그 액션을 쓰는 모든 저장소가 다음 실행부터 공격자 코드를 돌린다. 우리 쪽은 아무것도 바꾸지 않았는데 말이다.

이것이 공급망 공격이 무서운 이유다 — 한 곳을 뚫으면 그것을 신뢰하는 전부에 닿는다. 실제로 널리 쓰이던 액션이 침해돼 시크릿이 대량 유출된 사건이 있었다.

그럼 업데이트는 어떻게 하나

- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2

SHA 뒤에 버전을 주석으로 남긴다. Dependabot 이 이 주석을 읽어 새 버전의 SHA 로 갱신하는 PR 을 만들어 준다. 고정이 곧 방치가 아니게 되고, 갱신이 리뷰를 거치는 변경으로 바뀐다.

사고는 이렇게 번진다

① 인기 액션의 관리자 계정·토큰이 침해된다
② 공격자가 악성 커밋을 밀고 v3 태그를 그리로 옮긴다
③ 그 액션을 쓰는 저장소들이 다음 실행부터 그 코드를 돌린다
④ 워크플로 메모리에서 시크릿을 긁어 로그에 흘린다
⑤ 로그가 공개된 저장소에서는 누구나 읽을 수 있다

우리 저장소는 아무것도 바뀌지 않았는데 시크릿이 나간다. 코드 리뷰로도, 우리 쪽 테스트로도 걸리지 않는다. 신뢰의 방향이 바깥을 향하고 있기 때문이다.

SHA 로 고정돼 있었다면 ②가 우리에게 닿지 않는다. 태그가 어디를 가리키든 우리는 그 커밋만 실행한다.

조직 차원에서 조이기

  • 허용 액션 목록 — 검증된 게시자와 지정한 액션만 쓰게 한다
  • 재사용 워크플로 — 표준 파이프라인을 한곳에서 관리하면 고정과 권한 설정을 한 번에 강제할 수 있다. 저장소마다 확인할 필요가 없어진다
  • 로그 감사 — 시크릿을 쓴 뒤의 로그를 주기적으로 본다

고정의 한계도 안다

SHA 고정은 그 액션이 다시 실행하는 것까지 막아 주지 않는다. 액션 내부가 설치 시점에 패키지를 받아 온다면 그 경로는 여전히 열려 있다. 그래서 중요한 파이프라인에서는 의존성 자체도 잠금 파일과 무결성 해시로 고정한다.

어디까지 고정하나

  • 서드파티 액션 — 반드시 고정한다
  • actions/ 공식 액션 — 조직 정책에 따르되, 엄격한 곳은 함께 고정한다
  • 컨테이너 이미지 — 같은 논리가 그대로 적용된다. latest 대신 다이제스트로 고정한다

조직 설정에서 허용 액션 목록을 제한하면 한 겹 더 좁힐 수 있다.

면접 함정

  • "버전 태그를 쓰면 고정된 것" → 태그는 옮겨진다. SHA 만 불변이다.
  • "짧은 SHA 로도 충분" → 전체 길이를 쓰라는 것이 공식 권장이다.
  • "고정하면 보안 패치를 못 받는다" → Dependabot 이 갱신 PR 을 만든다. 받되 검토하고 받는 것이다.

함께 보면 좋은 용어

노트에서 맥락과 함께 보기 — GitHub Actions — 문법과 실행 모델