GitHub Actions run 블록 명령 주입

GitHub Actions에서 신뢰할 수 없는 이벤트 값을 셸 코드에 직접 삽입하지 말고 데이터로 전달하세요.

설명

이슈·PR 제목과 본문, 댓글, 커밋 메시지와 작성자 정보는 외부 사용자가 제어할 수 있습니다. 이런 값을 ${{ }} 표현식으로 run의 명령 문자열에 직접 넣으면 셸이 실행하기 전에 코드에 삽입되어 명령 주입으로 이어질 수 있습니다. 따옴표로 감싸는 것만으로 충분하지 않습니다.

잠재적 영향

  • 주입된 명령이 실행되면 실행기의 파일과 해당 작업에 제공된 토큰·비밀정보에 접근할 수 있습니다.
  • 실제 권한에 따라 결과물 변조나 저장소 작업 오용으로 이어질 수 있습니다.

해결 방법

  • 외부 값을 인라인 셸 코드에 직접 삽입하지 마세요. 필요한 값은 단계의 env로 전달하고 셸에서는 "$VALUE"처럼 인용된 변수로 사용하세요. eval 등으로 다시 코드로 평가하지 마세요.
  • 형식 검증과 명령의 인자 구분을 적용하고 GITHUB_TOKEN 및 비밀정보 접근을 필요한 범위로 제한하세요.
  • pull_request_target처럼 권한이 높은 워크플로에서는 신뢰하지 않는 PR 코드를 체크아웃하거나 실행하지 마세요.

예시

첫 예시는 PR 본문을 명령에 삽입합니다. 두 번째는 저장소의 고정된 테스트 명령을 실행하는 별도 예시이며, 신뢰하는 기본 브랜치의 코드와 테스트 스크립트가 있다는 전제입니다.

변경 전

yaml
name: Pull Request Workflow

on:
  pull_request_target:
    types:
      - opened

jobs:
  process_pull_request:
    runs-on: ubuntu-latest
    steps:
      - name: Echo Pull Request Body
        run: |
          echo "Pull Request Body: ${{ github.event.pull_request.body }}"

PR 본문이 실행할 셸 코드에 포함됩니다. 공격자가 제어하는 본문을 단순히 큰따옴표 안에 넣어도 코드 주입 위험은 남습니다.

변경 후

yaml
name: check-go-coverage

on:
  pull_request_target:
    branches: [master]

jobs:
  coverage:
    name: Check Go coverage
    runs-on: ubuntu-latest
    steps:
      - name: Checkout Source
        uses: actions/checkout@v4
        with:
          fetch-depth: 0
      - name: Set up Go 1.22.x
        uses: actions/setup-go@v5
        with:
          go-version: 1.22.x
      - name: Run test metrics script
        id: testcov
        run: |
          make test-coverage-report | tee test-results
          echo "coverage=$(cat test-results | grep "Total coverage: " test-results | cut -d ":" -f 2 | bc)" >> $GITHUB_ENV

이 명령에는 PR 본문을 직접 삽입하지 않습니다. 이후 외부 값을 추가한다면 데이터로 전달하고, 권한이 높은 실행에서 신뢰하지 않는 코드를 실행하지 않는 원칙을 유지하세요.

참조