पाठ 17 / 31
Hardening the CI System Itself
यह पाठ अभी हिंदी में अनुवादित नहीं है, इसलिए अंग्रेज़ी में दिखाया जा रहा है।
Lock down CI tokens, pin third-party actions to commit SHAs, and avoid dangerous triggers such as pull_request_target with untrusted code.
लेखक: Rahul Agarwal · 15 साल से वेब डेवलपर
Your pipeline is production
A CI system holds the keys to everything: source code, signing keys, cloud deploy rights. Attackers know this, so the pipeline needs its own hardening. First, least-privilege tokens: in GitHub Actions, set a top-level permissions: block (for example contents: read) and grant extra scopes only to the jobs that need them. Second, pin third-party actions to a full commit SHA rather than a tag; tags can be moved to point at malicious code, as happened in real compromises of popular actions. Third, be careful with triggers: pull_request_target runs with the base repository's secrets and write token, so checking out and running code from a fork's pull request in that context hands an attacker your secrets. Fourth, avoid script injection: never interpolate untrusted values like ${{ github.event.pull_request.title }} directly inside a run: script; pass them through an environment variable. Finally, protect branches and require reviews for workflow file changes.
Finding unpinned actions
A small checker that flags uses: lines not pinned to a 40-character commit SHA. Local actions (./) are skipped. It also flags actions/checkout@v4, the tag style earlier examples use for readability; in a real repository pin those to SHAs too and let Dependabot or Renovate bump them.
import re
workflow = '''
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@39370e3970a6d050c480ffad4ff0ed4d3fdee5af # full SHA of a release
- uses: some-org/deploy-action@main
- uses: ./.github/actions/local
'''
USES = re.compile(r"uses:\s*([^\s#]+)")
SHA = re.compile(r"@[0-9a-f]{40}$")
for ref in USES.findall(workflow):
if ref.startswith("./"):
continue
status = "pinned" if SHA.search(ref) else "NOT PINNED"
print(f"{status:<10} {ref}")
Output:
NOT PINNED actions/checkout@v4 pinned actions/setup-node@39370e3970a6d050c480ffad4ff0ed4d3fdee5af NOT PINNED some-org/deploy-action@main
Pass untrusted input through env
Instead of run: echo "${{ github.event.issue.title }}", set env: TITLE: ${{ github.event.issue.title }} and use "$TITLE" in the script. The shell then treats it as data, not as part of the script text.
A master key on a hook
Leaving deploy credentials in a CI system anyone can trigger is like hanging the hostel's master key on a hook by the entrance with a sign saying 'staff only'. The sign is not a lock.
त्वरित जाँच: Why pin a third-party GitHub Action to a full commit SHA instead of a tag like @v3?
- SHAs make workflows run faster
- Tags are not allowed in workflow files
- A tag can be moved to different code, while a commit SHA always refers to the same content
- SHAs automatically update to the latest version
Answer
A tag can be moved to different code, while a commit SHA always refers to the same content — Git tags are mutable references; a compromised maintainer account can repoint them. A full SHA is immutable.