Preventing leakage of sensitive information
Design pipelines to prevent leakage of sensitive information
Masking is a safety net, not a control
Azure Pipelines masks known secret values in logs. It cannot mask what it does not recognise — a secret that has been transformed, base64-encoded, split across variables or embedded in a URL will print in clear text. Treat masking as the last line of defence, not the design.
Where leaks actually come from
| Source | Mitigation |
|---|---|
echo / set -x / verbose task logging | Never print secrets; avoid debug logging in jobs that hold them |
| Secrets passed as command-line arguments | Use environment variables — arguments can appear in process listings and logs |
| Fork pull requests | Secrets are withheld from fork PR validations by default — keep it that way |
| Non-secret variables | Only variables marked secret are protected; non-secret values are not masked, and approvals do not restrict them |
| Committed configuration | A secret in git history survives rotation |
| Error messages and stack traces | A connection string in an exception is a leak |
Explicit mapping
Secret variables are not automatically available as environment variables to scripts — they must be mapped in deliberately:
- script: ./deploy.sh
env:
API_KEY: $(apiKey)That friction is intentional. It makes the set of steps that can see a secret explicit and reviewable.
Least exposure
Scope secrets to the stage or job that needs them, prefer secretless authentication where it exists, and remember that the strongest control is not having a secret at all — which is why OIDC and managed identities belong in this conversation as much as masking does.
Primary sources