When to use this guide
- A token is embedded in
.github/workflows/*.yml. - Credentials appear in a
Jenkinsfile,.gitlab-ci.ymlor deployment shell script. - A build-time token is written to a file instead of supplied through an environment variable.
Recommended approach
- Use the CI/CD platform’s secret storage.
- Keep only references in workflow files.
- Review log masking, least-privilege permissions and rotation policies together.
- For cloud deployments, prefer short-lived credential integration such as OIDC over long-lived static secrets where possible.
Examples
Before
yaml
steps:
- name: Deploy
run: |
curl -H "Authorization: Bearer ghp_xxxxxxxxx" https://example.com/deploy
After
yaml
steps:
- name: Deploy
env:
DEPLOY_TOKEN: ${{ secrets.DEPLOY_TOKEN }}
run: |
curl -H "Authorization: Bearer ${DEPLOY_TOKEN}" https://example.com/deploy
Remediation steps for the owner
- Revoke exposed tokens first and replace them with new values.
- Remove actual secrets from workflows, Jenkinsfiles, CI configuration files and deployment scripts.
- Register the newly issued token or password in the CI/CD secret store.
- Keep only
${{ secrets.NAME }}or the platform’s equivalent reference syntax in files. - For cloud provider access, assess whether OIDC federation can replace the static credential.
- Check recent run logs for exposure of the value.
Example instructions for the owner
- “Remove tokens stored in workflow files and use GitHub Actions Secrets or your CI credential store.”
- “Keep secret references, not literal values, in CI/CD configuration files.”
- “Check recent build runs too, because their logs may retain secret values.”
Additional checks
- Does log masking work?
- Can external triggers, such as pull requests from forks, expose secrets?
- Does the deployment token have excessive permissions?
- Can OIDC or workload identity replace long-lived access keys?