Most exploitable bugs are written into code long before any runtime control sees them, and the fixes are usually small, local and cheap if a team knows which ones to apply where. MITRE D3FEND groups these coding countermeasures under Source Code Hardening (D3-SCH), a technique in the Harden tactic that D3FEND defines as hardening source code with the intention of making it more difficult to exploit and less error prone. This guide treats D3-SCH as a programme for engineering and application security teams: what each child technique covers, how to choose and sequence them by language and code base, how to prove they are in place, and where the family stops helping. Each child has its own page on this site; this page does not repeat them. For D3FEND itself see the MITRE D3FEND Guide, and for the wider discipline the Secure Coding Guide.
Key takeaways
- D3-SCH is a family of eight direct children and three grandchildren. They cover credentials in code, domain and operational logic checks, integer ranges, pointers, memory references, trusted libraries, initialisation and types.
- Most children target memory-unsafe languages. Pointer, null, memory block, reference and initialisation checks matter most in C and C++; Credential Scrubbing, Trusted Library and Domain Logic Validation apply to every language.
- Compilers and analysers do much of the work. Warnings, sanitizers, static analysis and secret scanning turn these coding rules into checks a pipeline can enforce.
- D3FEND's ATT&CK links for the parent are thin. The D3FEND page lists one inferred relation, SQL Stored Procedures (T1505.001), and labels its mappings inferred and experimental.
- Coverage is measured in code, not in tools. Track rule violations per thousand lines, secrets found before commit and sanitizer findings fixed, not the number of scanners licensed.
What D3FEND means by Source Code Hardening
In the D3FEND ontology, D3-SCH sits directly under the Harden tactic beside Application Hardening, Platform Hardening and similar families. Its children mostly declare a "hardens" relation to a code artefact: a Subroutine, a Mathematical Function, a Memory Free Function or a Pointer Dereferencing Function. That modelling choice is useful in practice: each child is a property of individual functions, so it is enforced in code review, compiler settings and static analysis rather than by an appliance.
D3-SCH is distinct from Application Hardening, which covers build and runtime protections such as stack canaries and control flow integrity applied to a compiled program. A rough rule from NHI Mgmt Group: if the fix is a change a developer makes to source lines, it is Source Code Hardening; if it is a compiler flag, loader feature or runtime mitigation, it is usually Application Hardening. See the D3-SFCV Stack Frame Canary Validation page for an example of the latter.
The child techniques
The table lists every technique under D3-SCH in the D3FEND ontology, with its one-line purpose taken from the D3FEND definition. Grandchildren are shown under their parent.
| D3FEND technique | Purpose | Languages where it matters most |
|---|---|---|
| Credential Scrubbing (D3-CS) | Systematic removal of hard-coded credentials from source code | All |
| Domain Logic Validation (D3-DLV) | Validate variable state in the context of the business domain | All |
| Operational Logic Validation (D3-OLV) | Validate variable state against the control logic of an operational application, such as PLC code | Controller and embedded code |
| Integer Range Validation (D3-IRV) | Ensure an integer is within a valid range | C, C++, and any language with fixed-width integers |
| Pointer Validation (D3-PV) | Ensure a pointer has the required properties for use | C, C++ |
| Memory Block Start Validation (D3-MBSV) | Ensure a pointer references the beginning of a designated memory block | C, C++ |
| Null Pointer Checking (D3-NPC) | Check whether a pointer is NULL before use | C, C++, and languages with nullable references |
| Reference Nullification (D3-RN) | Invalidate all pointers to a freed memory block to prevent use after free | C, C++ |
| Trusted Library (D3-TL) | Use pre-verified, vetted code modules instead of writing functions from scratch | All |
| Variable Initialization (D3-VI) | Set variables to a known value before use | C, C++, and other languages without default initialisation |
| Variable Type Validation (D3-VTV) | Ensure a variable has the correct type before use | All, most in weakly or dynamically typed code |
The "languages" column is NHI Mgmt Group guidance, not part of D3FEND. Several D3FEND entries carry the note that they are not a definitive or exhaustive coding guideline; use a language-specific standard as the rule book and D3-SCH as the map.
How to choose and sequence the children
Start from the code base, not the list
- Every repository: Credential Scrubbing first. A secret in code is exploitable on the day it is pushed and stays in history after it is deleted. OWASP's Secrets Management Cheat Sheet orders the response as revoke, rotate, delete, then log.
- Every service with business rules: Domain Logic Validation and Variable Type Validation at trust boundaries, so values are checked for type, value and range in the context of the business process before they drive decisions.
- C and C++ code: Integer Range Validation, Variable Initialization, Null Pointer Checking, Pointer Validation and Reference Nullification. These five close the classic overflow, uninitialised read, null dereference and use after free classes. See the C and C++ Secure Coding Guide.
- Controller and firmware code: Operational Logic Validation on top of the C and C++ set. See the Embedded and Firmware Secure Coding guide.
- All third-party code: Trusted Library, which in practice means an approved component list, a software inventory and update monitoring. See the SBOM Guide.
Combine with language choice
CISA's publication The Case for Memory Safe Roadmaps argues that software manufacturers should move to memory safe languages and publish roadmaps for doing so. A memory safe language removes most of the need for D3-PV, D3-MBSV, D3-RN and D3-VI in new code, because the compiler and runtime enforce them. The children remain necessary for existing C and C++ code, unsafe blocks and foreign function boundaries. See the Memory-Safe Languages Guide.
Turn rules into automated checks
| Child technique | Automated check that enforces it | Example tooling category |
|---|---|---|
| D3-CS | Pre-commit and server-side secret detection, history scanning | Secret scanners, push protection |
| D3-IRV | Signed overflow and conversion checks; GCC and Clang builtins such as __builtin_sadd_overflow | Clang UBSan signed-integer-overflow and implicit-*-truncation checks |
| D3-NPC, D3-PV | Null and alignment checks in test builds | Clang UBSan null, nonnull-attribute, alignment |
| D3-RN, D3-MBSV | Use after free and invalid free detection in test builds | AddressSanitizer, static analysers |
| D3-VI | Uninitialised variable warnings treated as errors | Compiler warnings, static analysis |
| D3-VTV, D3-DLV | Schema and input validation at boundaries, type checking | Type checkers, validation libraries, SAST rules |
| D3-TL | Component inventory and known vulnerability checks | Software composition analysis, dependency update automation |
The Clang UndefinedBehaviorSanitizer documentation says its full runtime is intended for testing and that security-sensitive production builds should consider the minimal runtime (-fsanitize-minimal-runtime) or trap mode (-fsanitize-trap). Treat sanitizers as a test-stage verification for D3-SCH, not as the control itself. For pipeline rollout see the SAST, SCA and code scanning tool rollout playbook.
Implementation roadmap
- Inventory languages and code ownership. List repositories by language, criticality and owner. The C, C++ and firmware share decides how much of the pointer and memory set you need.
- Adopt a coding standard per language. Map each D3-SCH child to the standard's rules (for C, the SEI CERT C rules such as INT32-C for signed overflow). The standard is what reviewers enforce; D3FEND is the index.
- Switch on Credential Scrubbing everywhere. Enable secret detection in the IDE or pre-commit hook and on the server, scan history once, then revoke and rotate anything found. See the Git Secret Scanning Guide.
- Run new checks in report mode. Add warnings, analyser rules and sanitizer test builds without failing the build. Collect a baseline over at least two release cycles (an example period, not a standard).
- Gate new code first. Fail builds on new violations only, so teams are not blocked by legacy debt. Keep a documented way to revert a rule to report mode if it produces false positives at scale.
- Burn down legacy findings by risk. Start with internet-facing and parsing code, where integer and pointer defects are most reachable.
- Verify with tests. Fuzz parsers with sanitizers enabled; a crash with a sanitizer report is evidence a D3-SCH child is missing at that location.
Small and large organisations
- Small teams: secret scanning with push protection, compiler warnings as errors, one SAST tool and dependency update automation cover D3-CS, D3-VI, much of D3-VTV and D3-TL with little effort.
- Large or regulated estates: add sanitizer test builds and fuzzing for C and C++, per-language rule sets mapped to each child, and exception tracking with expiry.
ATT&CK relations D3FEND infers for the parent
D3FEND links defensive and offensive techniques through shared digital artefacts. For D3-SCH itself, the D3FEND page lists one offensive relation, SQL Stored Procedures (T1505.001) under Persistence, and states that its mappings are inferred, experimental and will improve as the knowledge graph grows. Treat this as a weak signal: stored procedures are code, and hardened code is harder to abuse, but the relation does not mean D3-SCH prevents the technique.
In practice the stronger link is to exploitation techniques that depend on coding defects, such as T1190 Exploit Public-Facing Application Defensive Guide and T1068 Exploitation for Privilege Escalation, and, for Credential Scrubbing, the credential theft covered in the MITRE ATT&CK T1552 Unsecured Credentials Guide. These are NHI Mgmt Group judgements, not D3FEND mappings.
Where identity fits
Credential Scrubbing is the one child that is about identity. The credentials found in code are overwhelmingly non-human: API keys, database connection strings, cloud access keys and service account tokens. NIST SP 800-53 Rev. 5 control IA-5(7) states the requirement directly: unencrypted static authenticators must not be embedded in applications or other forms of static storage. Removing the string is only half the fix; the workload still needs a credential, so pair scrubbing with a secrets manager or workload identity. See the AI Coding Tools and Secrets Sprawl guide for the newer source of hard-coded secrets.
Detection and measurement
Source code hardening is verified in the pipeline, so its signals are build and scan results rather than SOC alerts. Useful signals include secrets blocked at push, sanitizer reports from fuzzing and test runs, new analyser findings per merge request, and suppressed findings without an owner. Metrics we suggest at NHI Mgmt Group:
- Child coverage: percentage of applicable D3-SCH children with an automated check per language.
- Secrets caught before commit versus after: the ratio should rise over time.
- Mean time to revoke a secret found in code, from detection to revocation.
- New memory safety and integer findings per thousand changed lines in C and C++ code.
- Fuzzing hours with sanitizers enabled on internet-facing parsers per release.
- Share of new components drawn from the approved library list (D3-TL).
Framework mapping
| Framework | Reference | Link to D3-SCH |
|---|---|---|
| NIST SP 800-53 Rev. 5 | SA-15 Development Process, Standards, and Tools | Coding standards and tool configuration that encode the children |
| NIST SP 800-53 Rev. 5 | SA-11 Developer Testing and Evaluation | Static analysis, sanitizers and fuzzing as verification |
| NIST SP 800-53 Rev. 5 | SI-16 Memory Protection | Runtime partner to the pointer and memory children |
| NIST SP 800-53 Rev. 5 | IA-5(7) No Embedded Unencrypted Static Authenticators | Credential Scrubbing |
| NIST CSF 2.0 | PR.PS-06 | Secure software development practices integrated and monitored |
| OWASP Proactive Controls 2018 | C2 Leverage Security Frameworks and Libraries | Trusted Library: trusted sources, inventory, updates, minimal exposure |
Common failure patterns
- Deleting a secret without revoking it. The value remains in git history and forks; OWASP's order is revoke and rotate before delete.
- Sanitizers only on a developer's laptop. Without a CI test build and fuzzing, pointer and integer defects reach release unseen.
- Relying on wrapping behaviour. SEI CERT notes that signed overflow is undefined in C, so compilers may optimise on the assumption it never happens; precondition checks are needed, not assumptions.
- Validation at the edge only. Type and domain checks at the API gateway do not protect internal callers or queue consumers.
- "Trusted" libraries never re-checked. A library vetted once and never updated stops being trusted; D3-TL needs inventory and monitoring.
- Blanket suppressions. Rules switched off repository-wide to unblock a release and never switched back on.
Practitioner checklist
- Map each D3-SCH child to a rule in your language coding standard.
- Enable secret detection before commit and on the server, and scan history once.
- Revoke and rotate every secret found in code before deleting it.
- Treat uninitialised variable and conversion warnings as errors in C and C++.
- Run sanitizer test builds and fuzz internet-facing parsers each release.
- Validate type, value and range at every trust boundary, not only the external edge.
- Maintain an approved component list and automated dependency updates.
- Gate new code first, then burn down legacy findings by exposure.
- Publish a memory safety roadmap for C and C++ components.
Standards and references
- MITRE D3FEND: Source Code Hardening
- MITRE D3FEND: Credential Scrubbing
- OWASP: Secrets Management Cheat Sheet
- SEI CERT C: INT32-C Ensure that operations on signed integers do not result in overflow
- LLVM: Clang UndefinedBehaviorSanitizer
- CISA: The Case for Memory Safe Roadmaps
- OWASP: Proactive Controls C2 Leverage Security Frameworks and Libraries
- NIST: SP 800-53 Rev. 5
Related NHI Mgmt Group resources: MITRE D3FEND Guide · Secure Coding Guide · Memory-Safe Languages Guide · D3-CS Credential Scrubbing · SAST Triage Guide