A leaked API key, token or connection string lets anyone who finds it authenticate as the workload it belongs to, and nothing in the request looks unusual. OWASP ranks this as NHI2:2025 Secret Leakage, the second risk in the OWASP Non-Human Identities Top 10, and rates it easy to exploit, common and hard to detect. This guide explains what OWASP actually says about NHI2, how to turn its five prevention points into controls you can test, what evidence an assessor should ask for, and how NHI2 maps to NIST SP 800-53, ASVS 5.0, ATT&CK and CWE. It is written for AppSec, platform, cloud security and IAM teams who own the secrets that machines use, and for auditors who need to assess them.
Key takeaways
- NHI2 is about where secrets end up, not how they are stored by design: hard-coded in source, in plaintext config files, scripts on shared drives, logs, chat tools and developer laptops.
- OWASP's first prevention point is to remove the secret by moving to short-lived, on-demand credentials (managed identities, cloud token services, OAuth tokens). Scanning only reduces the leakage of what remains.
- Revocation, not deletion, ends the exposure. Removing a key from a repository leaves it in Git history and in every clone; the credential must be rotated or disabled at the issuer.
- Block at the point of commit with push protection and pre-commit hooks, and treat every bypass as a reviewed exception, because detection after the fact races attackers who scan public sources continuously.
- Measure exposure time, from leak to revocation, as the primary outcome metric; the Microsoft SAS token and Toyota key incidents both stayed live for years.
What it is and who it applies to
The OWASP Non-Human Identities Top 10 (2025 edition) ranks Secret Leakage second, after NHI1 Improper Offboarding. It is an awareness document, not a certification standard, so nobody is audited "against NHI2" in a formal sense. In practice it is used as a shared vocabulary in risk registers, AppSec programmes, vendor questionnaires and board reporting.
OWASP defines NHI2 as sensitive NHI credentials (API keys, tokens, encryption keys and certificates) ending up in unsanctioned places during the software development lifecycle. The examples it names are secrets hard-coded in source code, stored in plaintext configuration files and pasted into public chat applications. Once exposed, the secret lets an attacker access systems, exfiltrate data or escalate privileges.
- Who it applies to: any organisation that builds or runs software which authenticates to other systems with a secret. That includes SaaS integrations, CI/CD pipelines, infrastructure as code, mobile apps, data pipelines and AI agents calling model and tool APIs.
- Who owns it: usually shared between AppSec (scanning and developer workflow), platform engineering (secret delivery and workload identity) and IAM or NHI governance (inventory, ownership and revocation). Name one accountable owner, or leaks fall between teams.
- How it relates to NHI7: NHI2 is the exposure; NHI7 Long-Lived Secrets decides how long the exposure stays useful. A leaked secret that expires in an hour is an incident; one that never expires is a standing backdoor. See the OWASP NHI Top 10 NHI7 Long-Lived Secrets Guide.
Structure and key requirements
OWASP's NHI2 page, element by element:
| Element | What OWASP states for NHI2 | Practical reading |
|---|---|---|
| Threat agents | External attackers finding secrets in public repositories, logs, config files, SaaS tools or cloud platforms, or via malware on developer machines; insiders with limited access reaching secrets in shared locations | Assume both opportunistic internet-wide scanning and targeted insiders |
| Attack vectors | Public code repositories; scripts and config files on shared file systems; developer endpoints, application logs and malware | Scope scanning beyond Git: file shares, logs, tickets, chat, container images |
| Exploitability | Easy: the secret authenticates the attacker as the legitimate application | No exploit or phishing step is needed |
| Prevalence | Common | Expect findings on first scan of any mature estate |
| Detectability | Hard, because secrets can sit in many data stores | Coverage of locations matters more than detector count |
| Technical impact | Severe: secrets often grant access to high-value resources such as API keys and database connection strings | Prioritise by what the secret can reach, not by where it was found |
| Business impact | Specific: data breach, service disruption, loss of trust | Depends on the system the identity can access |
OWASP's five prevention points are the requirements to implement:
- Use short-lived credentials: replace static secrets with on-demand credentials such as cloud token services, managed identities or OAuth tokens.
- Use secret management tools: keep secrets out of source code, configuration files and scripts, and store them in a dedicated secrets manager.
- Scan in CI/CD: integrate automated secret scanning so secrets are blocked before they are committed.
- Least privilege and RBAC: only the services that need a secret can read it.
- Rotate automatically: rotate on a schedule with versioning so dependent services pick up new values without outage.
The page cites two data points: a Cloud Security Alliance NHI report attributing 31% of NHI-related incidents to poor secrets management and finding that 37% of organisations store secrets in environment variables or hard-code them, and the Verizon DBIR figure that stolen credentials were the initial action in 21% of breaches. Both are cited by OWASP; check the original reports before reusing the numbers.
OWASP's two scenarios are worth reading closely. In the first, an Azure SAS token committed to a public GitHub repository exposed a storage account; this is the Microsoft SAS Token Exposure 2023, where an account-level, full-control token was public from July 2020 until June 2023. In the second, a privileged access management admin API key stored in a script on an employee-accessible file share let a low-privileged insider retrieve an administrator credential. Note that this leak had nothing to do with Git.
Identity and NHI requirements
Secret leakage is an identity problem before it is a scanning problem. Every secret is the authenticator of some non-human identity, and the questions that decide the response are identity questions: who owns it, what can it reach, who else uses it, and can it be replaced.
| Secret type | Typical leak locations | Leak-resistant replacement | Revocation lever |
|---|---|---|---|
| Cloud access keys (AWS access key ID and secret) | .env files, IaC, CI variables, laptops | IAM roles on AWS compute; IAM Roles Anywhere with X.509; AssumeRoleWithWebIdentity from CI OIDC | Set key Inactive, then delete once last-used data confirms no dependency |
| Google Cloud service account keys (JSON) | Repositories, container images, notebooks | Attached service accounts, workload identity federation | Delete key in IAM; org policy to disable key creation and upload |
| Azure Storage SAS and account keys | Repositories, documentation, scripts | User delegation SAS signed with Microsoft Entra credentials; managed identities | Ad hoc SAS cannot be revoked directly: regenerate the signing account key, or use stored access policies for service SAS |
| SaaS API keys and personal access tokens | Scripts, tickets, chat, browser extensions | OAuth apps with scoped, expiring tokens; fine-grained tokens with lifetime limits | Revoke in the issuing platform; partner programmes may auto-revoke public leaks |
| Database connection strings | Config files, logs, error pages | Dynamic database credentials with leases from a secrets manager; cloud IAM database authentication | Rotate password, kill sessions, revoke lease or lease prefix |
| Private keys and certificates | Repositories, images, backups | Keys generated in HSM or KMS and never exported; short-lived certificates | Revoke certificate, replace key pair |
- Inventory with ownership: every detected secret must resolve to an NHI record with an owner, otherwise nobody can safely rotate it. See the NHI Ownership Guide.
- Blast radius first: a leaked read-only token for a test bucket and a leaked org-admin token are not the same finding. Join scanner output to the permissions of the identity behind each secret. See the OWASP NHI Top 10 NHI5 Overprivileged NHI Guide.
- One secret, one consumer: a key shared by five services cannot be rotated quickly after a leak, which turns NHI2 into an outage decision. See the OWASP NHI Top 10 NHI9 NHI Reuse Guide.
- Human copies: secrets pasted into chat, tickets or personal scripts are frequently a symptom of people using machine credentials. See the OWASP NHI Top 10 NHI10 Human Use of NHI Guide.
- AI agents: agents that read repositories, tickets or files can surface secrets in prompts, outputs and logs. Keep agent credentials in a broker or vault rather than in agent configuration. See the Credential Vaulting for AI Agents Guide.
Implementation steps
The order matters: stop new leaks first, then find old ones, then remove the class of secret that leaks. Doing it the other way round produces a backlog that refills as fast as it is cleared.
- Turn on the free controls (week 1): on GitHub, enable secret scanning and push protection on public repositories (user push protection is on by default for public pushes) and repository push protection for private ones where licensed. Push protection applies to command-line pushes, web UI commits, file uploads and REST API requests. On Google Cloud, confirm the organisation policy constraints that disable service account key creation and key upload, which are enforced by default for organisations created on or after 3 May 2024, and set the service account key exposure response to
DISABLE_KEY. - Decide bypass rules: by default anyone with write access can bypass push protection by choosing "used in tests", "false positive" or "I'll fix it later", and each bypass raises an alert and an audit log entry. For repositories holding production code, use delegated bypass so only named reviewers approve. Review "false positive" closures: in the Microsoft SAS incident a scanner flagged the URL and it was marked a false positive.
- Add a developer-side gate: run a pre-commit hook with the same rules as CI so secrets are caught before they enter local history, and a CI job that fails the build on new findings. Keep the rule set (including custom patterns for internal token formats) in one place. See the Git Secret Scanning Guide.
- Baseline the estate (weeks 2 to 6): scan full Git history of all repositories, container registries, CI variable stores, wikis, ticketing systems, chat exports, shared drives and log indexes. Use validity checks where available to separate live from dead secrets. Record each finding with location, secret type, owning identity and first-seen date. See the Secret Sprawl Measurement Guide.
- Triage by reach and liveness: live secrets in public locations first, then live secrets with production or admin scope in internal locations, then everything else. Dead secrets still need closing so they are not re-used later.
- Revoke and rotate: rotate at the issuer, confirm the old value fails, then clean the location. GitHub's own guidance is that rewriting history is time-consuming and often unnecessary once the credential is revoked; Google's guidance is that removing a key from a repository is not enough because history persists. For AWS keys, set the key Inactive first so the change can be rolled back, check last-used data, then delete. See the Leaked Credential Response Playbook and the Cloud Access Key Leak Playbook.
- Move secrets into a manager: replace values in code and config with references resolved at runtime from a secrets manager, with access granted per workload. Pilot on one service, verify start-up and failover when the manager is unavailable, then roll out by team. See the Secrets Management Guide.
- Remove the secret class: replace CI-stored cloud keys with OIDC federation (the cloud issues a short-lived token valid for a single job), static database passwords with dynamic leased credentials, and account-key SAS with user delegation SAS. Where a long-lived key cannot yet go, document the exception, scope it narrowly and rotate it. See the Workload Identity Federation Guide.
- Close the non-Git channels: mask secrets in logs, apply DLP to chat and ticketing, and scan images before publication.
Decision guidance: small teams on one Git platform get most of the value from native secret scanning, push protection and cloud-native workload identity, without buying a separate scanner. Larger estates with several code hosts, self-managed CI and many SaaS tools usually need a scanner that covers non-Git sources and centralises findings, and an NHI inventory to resolve ownership. Choose blocking over alerting wherever developer friction is acceptable; choose alerting only where blocking would push people to work around the control.
Evidence and assessment
An assessor should be able to see that leaks are prevented, found and closed, not only that a tool is licensed.
| Control | Evidence to request | Test |
|---|---|---|
| Push protection | Coverage report by repository; bypass audit log for the last 90 days | Attempt to push a vendor test token to a sample repository and confirm it is blocked |
| History scanning | Scan scope (repositories, registries, wikis, tickets, chat, shares) and last full-scan date | Pick three repositories at random and confirm they appear in results |
| Response | Finding records with first-seen, revoked and closed timestamps | Sample five closed findings and verify the credential was rotated at the issuer, not only deleted from the file |
| Secret storage | Proportion of services reading secrets from a manager; access policies per secret path | Check one service's config and runtime environment for literal secrets |
| Static secret removal | Count of cloud keys, service account keys and account-key SAS in use, with owner and exception | Compare against cloud inventory (for example AWS credential report, GCP key list) |
Metrics that show it is working:
- Mean time to revoke: hours from first detection to issuer-side revocation, reported separately for public and internal leaks.
- Live secrets open: count of validated, still-active secrets in any scanned location, trending to zero.
- Push protection block rate and bypass rate: blocks per month and the share bypassed; a rising "false positive" bypass share needs review.
- Coverage: percentage of repositories, registries and non-Git sources under scanning.
- Static secret count: long-lived cloud and SaaS keys remaining, which should fall as federation and dynamic credentials replace them.
What to log and monitor: push protection bypasses, alert state changes, cloud provider leaked-key notifications, and any use of known-leaked or deactivated credentials.
Mapping to other frameworks
| Framework | Reference | Relevance to NHI2 |
|---|---|---|
| NIST SP 800-53 Rev 5 | IA-5 Authenticator Management; IA-5(7) No Embedded Unencrypted Static Authenticators | IA-5(7) directly prohibits secrets embedded in code and scripts |
| NIST SP 800-53 Rev 5 | SC-28 Protection of Information at Rest; AC-6 Least Privilege; SA-11 Developer Testing and Evaluation; SI-4 System Monitoring | Storage, scope, pipeline scanning and monitoring for misuse |
| NIST CSF 2.0 | PR.AA-01; PR.PS-06; DE.CM-09 | Credentials for services managed; secure development practices; monitoring runtime environments |
| OWASP ASVS 5.0 | V13.3.1, V13.3.2 | Secrets management solution for backend secrets; least-privilege access to secrets |
| CWE | CWE-798 Use of Hard-coded Credentials (children CWE-259, CWE-321) | The code weakness behind most NHI2 findings |
| MITRE ATT&CK v20 | T1552.001 Credentials In Files; T1552.004 Private Keys; T1552.008 Chat Messages; T1213.003 Code Repositories; T1078.004 Cloud Accounts | How attackers find leaked secrets and then use them |
| MITRE D3FEND | D3-CR Credential Revocation; D3-CRO Credential Rotation | The defensive techniques that end an exposure |
| ISO/IEC 27001:2022 Annex A | A.5.17 Authentication information; A.8.4 Access to source code; A.8.28 Secure coding | Policy and secure development controls |
| CIS Controls v8.1 | Control 3 Data Protection; Control 6 Access Control Management; Control 16 Application Software Security | Programme-level home for scanning and secret handling |
For the wider context see the OWASP ASVS Guide and the OWASP Top 10 for Agentic Applications Guide.
Common pitfalls
- Deleting the file instead of revoking the key: the commit history and every fork and clone keep the value. The Toyota T-Connect Key Exposure 2022 key sat in a subcontractor's public repository from December 2017 until September 2022; the access key had to be changed after the repository was made private.
- Closing alerts as false positives without validation: the Microsoft SAS Token Exposure 2023 URL was flagged by scanning and dismissed. Require a validity check or second reviewer before dismissal of high-entropy or cloud-format findings.
- Scanning only the main branch of private repositories: leaks also come from repositories the organisation does not own. In the CISA Private-CISA GitHub Leak 2026, a contractor's public repository exposed AWS GovCloud admin credentials for about six months, with GitHub's default secret-publishing protection disabled. Enterprise-level public monitoring and partner programme notifications cover part of this gap.
- Ignoring non-Git locations: the OWASP insider scenario was a script on a file share. Container images, CI logs and chat matter too: in the Docker Hub Secrets Leak 2025, researchers found keys in 10,456 public images, and most secrets that developers removed were never revoked.
- Environment variables treated as safe storage: they are readable by anything in the process, end up in crash dumps and CI logs, and OWASP cites them alongside hard-coding as a common bad practice. Resolve secrets from a manager at runtime and keep them out of logged environments.
- Rotating without an owner: nobody rotates a key they cannot attribute, for fear of an outage, so the finding ages. Ownership must be resolved during triage, not after.
Practitioner checklist
- Enable secret scanning and push protection on every repository, with delegated bypass for production code.
- Run the same detection rules in pre-commit hooks and CI, including custom patterns for internal token formats.
- Baseline full history across repositories, registries, CI variables, wikis, tickets, chat and file shares.
- Resolve every finding to an owning NHI and its permissions before prioritising.
- Revoke at the issuer first, confirm failure of the old value, then clean the location.
- Enforce GCP key creation and upload constraints and the
DISABLE_KEYexposure response. - Replace CI-stored cloud keys with OIDC federation and account-key SAS with user delegation SAS.
- Store remaining secrets in a manager with per-workload access and automated rotation.
- Report mean time to revoke and live secrets open monthly.
Standards and references
- OWASP: NHI2:2025 Secret Leakage
- OWASP: Top 10 Non-Human Identities Risks 2025
- MITRE: CWE-798 Use of Hard-coded Credentials
- OWASP: Application Security Verification Standard 5.0
- GitHub Docs: About push protection
- GitHub Docs: About secret scanning
- Google Cloud: Best practices for managing service account keys
- Microsoft Learn: Grant limited access to Azure Storage resources using shared access signatures
Related NHI Mgmt Group resources: Git Secret Scanning Guide · Secrets Management Guide · Leaked Credential Response Playbook · OWASP NHI Top 10 NHI7 Long-Lived Secrets Guide · Non-Human Identity (NHI) Guide