A password, API key or private key written into source code is copied to every clone, fork, build artefact and backup of that code, and it stays in version control history after someone deletes the line. MITRE D3FEND names the countermeasure Credential Scrubbing (D3-CS) and defines it as "The systematic removal of hard-coded credentials from source code to prevent accidental exposure and unauthorized access." It sits under the Harden tactic as a child of Source Code Hardening, next to techniques such as Pointer Validation and Variable Initialization. This card covers what D3FEND says, how thin its ATT&CK mapping is, how scrubbing is done in practice, how to prove a codebase is clean, and where the control fails. It is written for application security engineers, platform teams and secrets owners; the wider programme is in the Secrets Management Guide.
Key takeaways
- Scrubbing means revoke, replace, then remove. D3FEND's own considerations say credentials found in version control history must be disabled and replaced promptly; deleting the line or rewriting history without revoking leaves the secret valid in every existing copy.
- D3FEND's ATT&CK link is thin. The ontology only says D3-CS hardens a Subroutine, and the D3FEND site infers a single related technique from that, T1505.001 SQL Stored Procedures. The real attacker behaviour it addresses is credential harvesting from code, which ATT&CK covers as T1552.001 Credentials In Files.
- Prevention beats clean-up. Pre-commit hooks and server-side push protection stop new secrets; history scanning finds the old ones. Both are needed.
- The replacement matters as much as the removal. Moving a key from code to a plaintext environment file in the same repository is not scrubbing. The credential has to come from a secrets manager or, better, be replaced by a federated identity with no static secret.
- Verification is a scan result, not a policy. A codebase is scrubbed when a full-history scan with agreed rules returns no live findings, and every finding has a recorded revocation.
D3-CS at a glance
| Attribute | Value |
|---|---|
| D3FEND ID | D3-CS (d3f:CredentialScrubbing) |
| Tactic | Harden |
| Parent technique | Source Code Hardening (siblings: Domain Logic Validation, Integer Range Validation, Pointer Validation, Reference Nullification, Trusted Library, Variable Initialization, Variable Type Validation) |
| Digital artefact | Subroutine (D3FEND relation: hardens Subroutine) |
| Weakness it addresses | CWE-798 Use of Hard-coded Credentials (D3FEND see-also) |
| Attack pattern it addresses | CAPEC-191 Read Sensitive Constants Within an Executable (D3FEND see-also) |
| D3FEND reference | OWASP Secrets Management Cheat Sheet |
| What it changes | Credentials move out of source, configuration in the repository and compiled binaries into a managed store or are replaced by workload identity |
| Main limitation | Does nothing for credentials already copied out of the repository unless they are revoked |
How it works
D3FEND's knowledge base article describes the technique in two sentences: identify and eliminate hard-coded credentials such as usernames, passwords, API keys and tokens from source code repositories, and manage them through environment variables, secret management tools or secure vaults instead. Its considerations add three practical points: audit source code regularly, disable and replace credentials found in version control history promptly, and back the change with role-based access control and rotation.
CWE-798 explains why the problem is wider than leakage. MITRE splits hard-coded credentials into two variants:
- Inbound: the product checks incoming logins against a credential built into the code, often a default administrator account that is identical across every installation and cannot be changed without a patch. Anyone who reads one copy of the code or binary can log in to every deployment.
- Outbound: the product embeds the credential it uses to reach another system, such as a database, a cloud API or a back-end service. Anyone who reads the code inherits that access.
CAPEC-191 describes the attacker side: searching a compiled program for passwords, keys and cryptographic constants with string extraction, disassembly or decompilation. That matters because a secret removed from today's source can still sit in last year's container image, mobile app build or installer.
D3FEND's choice of Subroutine as the artefact reflects that the credential lives inside code units. It also explains the single inferred ATT&CK relation discussed below.
Where identity fits
Almost every credential found in code belongs to a non-human identity: a service account password, a cloud access key, a database user, a webhook token or an application client secret. Scrubbing is therefore an NHI lifecycle event. Each finding should be traced to the identity that owns it, so the owner can be asked to rotate, the identity's permissions can be reviewed, and the identity can be retired if the code no longer needs it. The hardest finding to close is an orphaned key with no owner, which is why scrubbing works best alongside an NHI inventory.
ATT&CK techniques and D3-CS
Relationship type: inferred only. The D3FEND ontology gives D3-CS no direct ATT&CK mapping. The D3FEND site (knowledge graph 1.6.0, checked November 2026) infers one related technique through the shared Subroutine artefact and labels such relations "inferred and experimental".
| ATT&CK ID | Technique | Source of the relation | How it relates to scrubbing |
|---|---|---|---|
| T1505.001 | SQL Stored Procedures | D3FEND inferred, via Subroutine | Weak. A stored procedure is a subroutine an attacker adds for persistence; scrubbing does not stop that. The shared artefact, not shared purpose, creates the link |
| T1552.001 | Credentials In Files | NHI Mgmt Group view, not a D3FEND mapping | The direct target: adversaries search local files and shared file systems, including source and configuration files, for stored credentials |
| T1213.003 | Code Repositories | NHI Mgmt Group view, not a D3FEND mapping | Adversaries collect data from code repositories; hard-coded secrets make that collection an access path |
| T1078 | Valid Accounts | NHI Mgmt Group view, not a D3FEND mapping | The follow-on step when a harvested secret is used to authenticate |
For the attacker view of the harvesting step, see the MITRE ATT&CK T1552 Unsecured Credentials Guide. The ATT&CK mitigation that carries the same developer intent is covered in M1013 Application Developer Guidance; D3FEND notes against M1013 that a future release will define a taxonomy of Source Code Hardening techniques.
Where it is implemented
| Implementation class | What it does | Where it acts | Limits |
|---|---|---|---|
| Pre-commit and IDE scanning | Blocks a commit locally when a secret pattern matches. Gitleaks, for example, supports the pre-commit framework and a gitleaks:allow line comment | Developer workstation | Developers can skip local hooks; it only sees new changes |
| Server-side push protection | Rejects a push that contains a recognised secret. GitHub push protection covers command line pushes, web UI commits, file uploads and REST API requests | Source control platform | Writers can bypass with a reason; coverage depends on the pattern type and the provider |
| History and CI scanning | Scans the full commit history, branches and pull requests; can use a baseline so only new findings fail the build | CI pipeline, scheduled jobs | A baseline hides old findings unless someone works through them |
| Artefact and image scanning | Searches built images, packages and binaries for embedded credentials, the CAPEC-191 path | Registry, release pipeline | Obfuscated or encrypted constants are hard to detect |
| Secrets manager and runtime injection | Supplies the replacement credential at run time through a mounted file, a sidecar or the platform | Runtime | Needs the application changed to read it |
| Workload identity federation | Removes the static credential entirely; the workload proves its identity with a short-lived platform token | Cloud and CI platforms | Not every target system supports it |
The OWASP Secrets Management Cheat Sheet, D3FEND's reference, recommends running more than one detection tool and correlating results, using one standard test secret per type to cut false positives, and building signatures for connection strings, API keys, private keys, session tokens and cloud credentials. On injection it prefers orchestrator-mounted files or a sidecar that fetches from a secrets manager, and advises against environment variables unless nothing else is possible, because they are visible to all processes and appear in logs and dumps. Platform specifics are in the Git Secret Scanning Guide and How to enable GitHub secret scanning push protection.
Implementation steps
- Stop the inflow first. Turn on server-side push protection for all repositories and offer a pre-commit hook. Restrict who may bypass (GitHub's delegated bypass is one way) and route bypass alerts to the security team.
- Baseline the history. Run a full-history scan of every repository, including archived ones and forks you control, with the same rule set. Save the result as the baseline so CI fails only on new findings while the backlog is worked.
- Triage by live access. For each finding, decide whether the credential is still valid and what it can reach. Live production credentials go first, test fixtures last.
- Revoke and replace. Follow the OWASP order: revoke, rotate, then delete. Issue the replacement through the secrets manager or switch the workload to federation, deploy, and confirm the old credential no longer authenticates.
- Remove it from the code. Change the code to read the credential at run time. Rewrite history only when the exposure itself is the risk, such as personal data or a secret that cannot be rotated. GitHub's documentation recommends git-filter-repo 2.47 or later with its --sensitive-data-removal option and warns that existing clones and forks keep the old commits; see How to Remove Secrets From Git History.
- Fix the inbound variant. For products that ship a built-in login, replace it with a first-use setup that forces a unique credential, as CWE-798 recommends.
- Roll back safely. Keep the old credential disabled rather than deleted for a short window (for example seven days, an NHI Mgmt Group suggestion, not a standard) so a missed consumer can be found from its failed authentications and moved before final deletion.
How to verify it works
- Full-history scan: a scheduled scan of all repositories and all branches returns no unresolved findings outside approved test secrets.
- Revocation proof: for every closed finding, the record shows the revocation date and a failed authentication test with the old value.
- Seeded test: push an approved canary secret format to a test repository and confirm push protection blocks it and the bypass path raises an alert.
- Artefact scan: scan current release images and binaries, not only source, to confirm old builds are not still shipping a credential.
- Bypass review: review push protection bypasses monthly; "I'll fix it later" bypasses leave an open alert on GitHub and should be tracked to closure.
| Metric | Example target (NHI Mgmt Group suggestion, not a standard) | Why it matters |
|---|---|---|
| Repositories covered by push protection | 100% of active repositories | Shows the inflow is controlled |
| Median time from finding to revocation for live credentials | Under 24 hours | Exposure time is the risk, not the code change |
| Open baseline findings | Falling every month | Shows the historical backlog is being worked, not hidden |
| Push protection bypasses later confirmed as real secrets | Tracked and trending down | Shows whether bypass rights are used responsibly |
| Findings with an identified owning identity | Close to 100% | Unowned keys cannot be rotated safely |
Limits, bypasses and failure patterns
- Deleting the line and calling it fixed: the secret remains in history, clones, forks and CI logs. The Toyota T-Connect Key Exposure 2022 involved an access key that sat in public source code for about five years; only revocation ends that kind of exposure.
- Private repository complacency: scrubbing is often scoped to public code. The Uber Breach 2016 began with an AWS key in a private repository.
- Moving the secret next door: replacing a literal with an .env file or a CI variable file committed to the same repository changes nothing. See Environment Variables and .env Files.
- Pattern blind spots: scanners find structured keys well and human-chosen passwords poorly. OWASP warns about balancing entropy against known formats; GitHub lists AI-detected generic secrets as a public preview feature.
- Allowlist creep: inline allow comments and ignore files added to unblock a build become permanent exceptions. Review them like any other exception.
- Binaries left behind: CAPEC-191 shows attackers read constants from compiled code. Old mobile builds, installers and images keep the credential after the source is clean.
Framework mapping
| Framework | Reference | Relevance |
|---|---|---|
| NIST SP 800-53 Rev 5 | IA-5(7) No Embedded Unencrypted Static Authenticators | The control that directly prohibits credentials embedded in applications and scripts |
| NIST SP 800-53 Rev 5 | IA-5 Authenticator Management, SA-11 Developer Testing and Evaluation, SA-15 Development Process, Standards, and Tools | Rotation of replaced credentials; secret scanning as a developer test and tooling requirement |
| NIST CSF 2.0 | PR.AA-01, PR.PS-06 | Managing credentials for services; secure software development practice |
| OWASP ASVS 5.0 | V13.3.1, V13.3.2, V13.3.4 | Backend secrets held in a secrets management solution, least privilege access to them, expiry and rotation |
| ISO/IEC 27001:2022 Annex A | A.8.28 Secure coding, A.5.17 Authentication information | Secure coding practice and handling of authentication information |
| MITRE | CWE-798, CAPEC-191, D3-CS | Weakness, attack pattern and countermeasure |
Related countermeasures
- Credential Rotation (D3-CRO): the step that makes a scrubbed secret harmless. See D3-CRO Credential Rotation.
- Credential Revocation (D3-CR): the immediate response to a live finding. See D3-CR Credential Revocation.
- Credential Compromise Scope Analysis (D3-CCSA): works out what a leaked key could reach. See D3-CCSA Credential Compromise Scope Analysis.
- Decoy User Credential (D3-DUC): a planted decoy in a repository turns a harvesting attempt into an alert. See D3-DUC Decoy User Credential.
Practitioner checklist
- Enable server-side push protection on every active repository and limit who can bypass.
- Run a full-history scan of all repositories, including archived ones, and keep the baseline.
- Revoke and replace every live finding before touching history.
- Record the owning identity, revocation date and failed-authentication test for each finding.
- Replace static credentials with workload identity federation where the target supports it.
- Scan release images, packages and binaries, not only source.
- Replace built-in default logins with a forced first-use credential.
- Review allowlist entries and bypasses monthly.
Standards and references
- MITRE D3FEND: Credential Scrubbing (D3-CS)
- MITRE CWE: CWE-798 Use of Hard-coded Credentials
- MITRE CAPEC: CAPEC-191 Read Sensitive Constants Within an Executable
- OWASP: Secrets Management Cheat Sheet
- NIST: SP 800-53 Rev. 5 Security and Privacy Controls (IA-5(7))
- OWASP: Application Security Verification Standard 5.0
- GitHub Docs: About push protection
- GitHub Docs: Removing sensitive data from a repository
- Gitleaks: project documentation
Related NHI Mgmt Group resources: Secrets Management Guide · Git Secret Scanning Guide · How to Remove Secrets From Git History · MITRE ATT&CK T1552 Unsecured Credentials Guide · MITRE D3FEND Guide