본문으로 건너뛰기
개발 머꼬
개발 노트GitHub Actions
hohyeon.dev18

GitHub Actions 시크릿 마스킹을 믿었다가 로그에 노출된 사고

  • #CI/CD
  • #Engineering Note
  • #GitHub Actions
  • #Security

문제 발생

워크플로우 로그에서 시크릿 값이 그대로 노출된 것을 발견했습니다. secrets.API_KEY를 썼는데도 GitHub Actions의 자동 마스킹이 작동하지 않은 것처럼 보였습니다.

원인 분석

GitHub Actions는 워크플로우에 등록된 시크릿 값이 로그에 그대로 출력되면 ***로 치환합니다. 하지만 이 마스킹은 정확히 일치하는 문자열을 찾아 치환하는 방식이라 몇 가지 조건에서 우회됩니다.

  1. 가공된 값: 시크릿을 Base64로 인코딩하거나, 일부만 잘라 쓰거나, 다른 문자열과 합쳐서 출력하면 원본 문자열과 정확히 일치하지 않아 마스킹되지 않습니다.
  2. 여러 줄로 나뉜 값: 시크릿 값 자체가 개행을 포함하면 각 줄이 따로 출력될 때 마스킹이 깨질 수 있습니다.
  3. 환경변수로 명시적으로 등록하지 않은 경우: ${{ secrets.API_KEY }}를 워크플로우 YAML의 run: 블록에서 직접 문자열 보간으로 쓰면, 셸에 그대로 전개되어 ps 같은 프로세스 목록에 노출될 수 있고 마스킹도 100% 보장되지 않습니다.

해결 방안

  1. 시크릿은 환경변수를 통해서만 셸 스크립트에 전달합니다 — YAML 표현식으로 명령어 문자열에 직접 삽입하지 않습니다.
# 위험: 명령어 문자열에 직접 삽입
- run: curl -H "Authorization: ${{ secrets.API_KEY }}" https://api.example.com

# 안전: 환경변수로 전달
- run: curl -H "Authorization: $API_KEY" https://api.example.com
  env:
    API_KEY: ${{ secrets.API_KEY }}
  1. 시크릿 값을 가공(인코딩, 일부 추출 등)해서 사용해야 한다면, 그 가공된 값도 ::add-mask:: 워크플로우 명령으로 직접 마스킹 대상에 추가할 수 있습니다.
echo "::add-mask::$PROCESSED_VALUE"
  1. 시크릿을 디버깅 목적으로 echo하지 않습니다 — 마스킹을 우회하는 경로가 있다는 걸 알았다면 애초에 로그에 남길 수 있는 경로 자체를 만들지 않는 게 가장 안전합니다.
  2. 시크릿이 노출된 적이 있다면 마스킹 여부와 무관하게 **그 값은 유출된 것으로 간주하고 즉시 회전(rotate)**합니다 — 로그는 리포지토리 접근 권한이 있는 사람이면 과거 실행 기록까지 볼 수 있다는 점도 함께 고려합니다.
  3. Pull Request가 fork에서 온 경우, pull_request 이벤트는 기본적으로 시크릿에 접근할 수 없도록 되어 있습니다 — pull_request_target처럼 시크릿 접근이 가능한 이벤트를 fork PR에 쓸 때는 특히 주의가 필요합니다.

공식 문서

마지막 수정

좋아요북마크

댓글0

아직 댓글이 없어요. 첫 의견을 편하게 남겨 보세요.