GitHub Actions 시크릿 마스킹을 믿었다가 로그에 노출된 사고
문제 발생
워크플로우 로그에서 시크릿 값이 그대로 노출된 것을 발견했습니다. secrets.API_KEY를 썼는데도 GitHub Actions의 자동 마스킹이 작동하지 않은 것처럼 보였습니다.
원인 분석
GitHub Actions는 워크플로우에 등록된 시크릿 값이 로그에 그대로 출력되면 ***로 치환합니다. 하지만 이 마스킹은 정확히 일치하는 문자열을 찾아 치환하는 방식이라 몇 가지 조건에서 우회됩니다.
- 가공된 값: 시크릿을 Base64로 인코딩하거나, 일부만 잘라 쓰거나, 다른 문자열과 합쳐서 출력하면 원본 문자열과 정확히 일치하지 않아 마스킹되지 않습니다.
- 여러 줄로 나뉜 값: 시크릿 값 자체가 개행을 포함하면 각 줄이 따로 출력될 때 마스킹이 깨질 수 있습니다.
- 환경변수로 명시적으로 등록하지 않은 경우:
${{ secrets.API_KEY }}를 워크플로우 YAML의run:블록에서 직접 문자열 보간으로 쓰면, 셸에 그대로 전개되어ps같은 프로세스 목록에 노출될 수 있고 마스킹도 100% 보장되지 않습니다.
해결 방안
- 시크릿은 환경변수를 통해서만 셸 스크립트에 전달합니다 — 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 }}- 시크릿 값을 가공(인코딩, 일부 추출 등)해서 사용해야 한다면, 그 가공된 값도
::add-mask::워크플로우 명령으로 직접 마스킹 대상에 추가할 수 있습니다.
echo "::add-mask::$PROCESSED_VALUE"- 시크릿을 디버깅 목적으로
echo하지 않습니다 — 마스킹을 우회하는 경로가 있다는 걸 알았다면 애초에 로그에 남길 수 있는 경로 자체를 만들지 않는 게 가장 안전합니다. - 시크릿이 노출된 적이 있다면 마스킹 여부와 무관하게 **그 값은 유출된 것으로 간주하고 즉시 회전(rotate)**합니다 — 로그는 리포지토리 접근 권한이 있는 사람이면 과거 실행 기록까지 볼 수 있다는 점도 함께 고려합니다.
- Pull Request가 fork에서 온 경우,
pull_request이벤트는 기본적으로 시크릿에 접근할 수 없도록 되어 있습니다 —pull_request_target처럼 시크릿 접근이 가능한 이벤트를 fork PR에 쓸 때는 특히 주의가 필요합니다.
댓글0
댓글을 남기려면 로그인이 필요해요. 로그인
아직 댓글이 없어요. 첫 의견을 편하게 남겨 보세요.