CWE-20: Improper Input Validation
Overview
Improper Input Validation occurs when an application fails to enforce required constraints on externally supplied data before it is used. Validation alone does not prevent many classes of vulnerability, but its absence lets unexpected or malformed values reach security-sensitive logic.
Static analysis tools commonly flag CWE-20 when code accepts external input without clear type, range, format, or semantic constraints before use. This finding indicates a lack of defensive boundaries, not necessarily an immediately exploitable condition.
Treat a CWE-20 finding as a pointer to the weakness that actually applies rather than as the finding itself.
Relationship to Other CWEs
A CWE-20 finding is usually the wrong number rather than a wrong answer. MITRE marks it DISCOURAGED for mapping, because CWE-20 "is commonly misused in low-information vulnerability reports when lower-level CWEs could be used instead, or when more details about the vulnerability are available", and adds that it "is used more often than preferred, and it is a source of frequent confusion". The routing table under Remediation Steps below is the main way to find the number to report instead: it maps the common sinks to the weakness that names them. MITRE also draws a narrower line around the term than most tools do - within CWE, "input validation" means checking whether a value is already safe, which is not the same as filtering dangerous elements out of it (CWE-790) or encoding it for its destination (CWE-116). Neither of those has a page here, but if the code transforms the value rather than accepting or rejecting it, CWE-20 is the wrong family and not merely the wrong level.
The neighbours below are the ones that table does not reach, and they differ by which question the missing check should have asked:
- CWE-20 (this page) - nothing checked that the value was of the expected type, shape, or range before it was used
- CWE-183 (Permissive List of Allowed Inputs) - a check exists and runs, but accepts more than it should. CWE-20 is the case where no comparison happens at all; where there is a rule and it is too loose, CWE-183 is the accurate report
- CWE-345 (Insufficient Verification of Data Authenticity) - the question is provenance rather than shape. A value can satisfy every type, range, and format rule on this page and still have been forged or altered in transit, which no validation rule detects
- CWE-74 (Injection) - MITRE records that CWE-20 can precede it: missing validation is one way untrusted data reaches an interpreter. Its page is a router covering more of the injection family than the table below, so it is worth a look when a finding is injection-shaped but the sink is not yet pinned down
- CWE-119 (Improper Restriction of Operations within the Bounds of a Memory Buffer) - the unvalidated value is a length, size, or offset reaching a memory operation in C or C++. MITRE marks CWE-119 Discouraged as well, so once the direction of the access is known, report the read, CWE-125, or the write, CWE-787
Where the finding really is about validation, with no interpreter or memory operation downstream, MITRE's preferred targets are the low-level children it added for that purpose: CWE-1284 for a quantity, CWE-1285 for an index or offset, and CWE-1286 for syntactic correctness. None of the three has a page here; the two cases this site covers directly are CWE-129, an index under CWE-1285, and CWE-112, XML structure under CWE-1286.
OWASP Classification
A05:2025 - Injection
Note: OWASP maps CWE-20 under Injection because unconstrained input frequently enables injection-style flaws when it reaches interpreters or security-sensitive operations.
Risk
Medium: Improper input validation rarely results in direct exploitation on its own, but it frequently enables other vulnerabilities by allowing unexpected data to influence application behavior, control flow, or downstream components. Impact depends entirely on how and where the input is used.
CWE-20 is usually a supporting failure or a bypass primitive rather than a standalone vulnerability.
Remediation Steps
Core Principle: CWE-20 is an abstract weakness - the correct fix depends on identifying the specific weakness that matches how the unvalidated input is actually used, then applying the appropriate defense for that vulnerability.
A CWE-20 finding reports missing input constraints, but the fix has to address the risk that the unconstrained input actually creates. The finding does not point at a single vulnerable endpoint or function - it points at a trust boundary, so tracing the data flow is the first step rather than an optional one.
Trace the data flow to identify the specific vulnerability
Follow the unvalidated input from entry point to where it's used:
- Where does the input originate? (HTTP parameter, header, file upload, API request, database query result)
- What assumptions are made about it? (shape, type, range, or implicit conversions/defaults applied before it's used)
- Where is it used? (HTML output, SQL query, shell command, file path, LDAP query, XML parser, eval statement)
- What security-sensitive operations does it influence? (authentication, authorization, file access, code execution)
Validation gaps are most significant where input crosses from untrusted to trusted contexts.
Map to the specific CWE
Identify which specific vulnerability applies based on the data flow:
| Input Usage Context | Specific CWE | Primary Defense |
|---|---|---|
| HTML output (templates, responses) | CWE-79 (XSS) | Context-correct output encoding |
| SQL query (WHERE, ORDER BY, etc.) | CWE-89 (SQL Injection) | Parameterized queries |
| Shell command (system(), exec()) | CWE-78 (OS Command Injection) | Avoid shell execution, use APIs |
| File path (open(), include()) | CWE-22 (Path Traversal) | Canonicalize, then enforce containment in an allowlisted base directory |
| LDAP query | CWE-90 (LDAP Injection) | Structured filter builders, context-correct escaping |
| XML parser | CWE-611 (XXE) | Disable external entities and DTD processing |
| eval/exec (dynamic code) | CWE-94 (Code Injection), CWE-95 (Eval Injection) | Never build code from user input |
| HTTP redirect | CWE-601 (Open Redirect) | Allowlist validation |
| SSRF targets | CWE-918 (SSRF) | URL validation, network restrictions |
| Deserialization | CWE-502 (Deserialization of Untrusted Data) | Use safe serialization formats |
| Log entry (message text, log fields) | CWE-117 (Log Injection) | Structured logging that keeps input in fields |
| Configuration setting (env vars, system properties, feature flags) | CWE-15 (External Control of Configuration Setting) | Allowlist permitted keys and values |
| Array or collection index | CWE-129 (Improper Validation of Array Index) | Bounds validation against the actual length |
| Integer operations | CWE-190 (Integer Overflow) | Range validation, safe math |
Not every CWE-20 finding has an interpreter downstream. Where the value is used as an index, a quantity, a loop bound, a state selector, or a business-logic amount, there is nothing to parameterize or encode, and constraint checking is the fix rather than a supplementary layer - the array-index and integer-operation rows above are that case. The distinction matters because it decides what "fixed" means: for the interpreter rows, a validated value that is still concatenated is not fixed.
Apply the specific defense for the CWE you identified
- Open the CWE page linked in the table above
- Apply its primary defense (parameterized queries, output encoding, and so on) - this is the actual fix
- Where the input reaches an interpreter, do not rely on input validation alone - it constrains what an attacker can send, but the encoding or parameterization is what removes the weakness
Add input validation as defense-in-depth
After applying the primary defense, add validation as an additional layer:
- Check the value is the type you expect: integer, email, UUID
- Enforce its structure by parsing it, or with a regex: a date format, a phone number pattern
- Check bounds: numeric range, string length, date range
- Compare against an allowlist of known-good values: an enum or a predefined list
- Check the business-logic constraints: age > 0, future dates only
Two rules apply whichever of these you use. Validate the value in the form the sink will see it: decode and canonicalize first, then check, or the check inspects a string the sink never receives. And find out what the framework has already decoded - query and form parameters arrive URL-decoded from request.getParameter, @RequestParam, ASP.NET model binding and Flask's request.args alike, so a rule hunting for %2e%2e in those never matches while the decoded ../ passes it unnoticed.
Example:
// Defense-in-depth: input validation runs before the value is used
if not is_integer(userId) or userId < 1:
reject("Invalid user ID")
// Primary defense: parameterized query (prevents SQL injection)
query = prepare("SELECT * FROM users WHERE id = ?")
query.bind(1, userId) // type-safe parameter binding
execute(query)
Verify the fix addresses the root vulnerability
- Confirm the specific CWE you identified is remediated (XSS prevented, SQL injection blocked, etc.)
- Where an interpreter is involved, confirm input validation is supplementary, not the sole defense
- Test with malicious payloads specific to the identified vulnerability type, including unexpected-but-syntactically-valid values, boundary conditions and default behaviors, implicit type coercion or fallback logic, and alternate execution paths that bypass validation
- Assert that every legitimate value is still accepted, including the awkward ones - unicode names, addresses with apostrophes, the maximum permitted length, the boundary values of a numeric range. A validation rule that rejects everything passes every malicious-payload test above, and only the accept-side assertions tell the two apart
- Review related CWEs where the unconstrained input could have a greater impact than initially scoped
- Re-run static analysis to confirm the missing constraints are no longer reported
Additional Resources
- CWE-20: Improper Input Validation
- OWASP Input Validation Cheat Sheet
- OWASP Testing Guide: Injection - the chapter WSTG's input-validation tests moved into
- OWASP Top 10 2025: A05 Injection