Improper Validation of Syntactic Correctness of Input

Description

Improper Validation of Syntactic Correctness of Input occurs when a product receives input expected to follow specific syntax but fails to validate or incorrectly validates conformance. Complex inputs often must follow particular syntax requirements—whether for data formats, markup languages, or programming languages. Without proper validation of untrusted input, attackers can trigger parsing failures, unexpected errors, or exploit latent vulnerabilities that wouldn't be accessible with properly formatted data.

Risk

Improper syntactic validation has severe security implications. Parsing errors may occur. Injection attacks become possible. XML/JSON parsing exploits enabled. Configuration file manipulation. Protocol-level attacks possible. Application crashes may happen. Security controls can be bypassed. Data corruption may result.

Solution

Implement "accept known good" validation—maintain strict lists of acceptable inputs conforming to specifications. Reject non-conforming data or transform it appropriately. Verify length, type, acceptable ranges, missing/extra inputs, syntax, and business rules. Use schema validation for structured formats. Employ parser generators for complex syntax.

Common Consequences

ImpactDetails
OtherScope: Other

Impact varies - from crashes to security bypasses.
IntegrityScope: Integrity

Injection attacks may modify data or behavior.
AvailabilityScope: Availability

Malformed input may crash parsers.

Example Code

Vulnerable Code

// Vulnerable: XML parsing without validation

import javax.xml.parsers.*;
import org.w3c.dom.*;
import java.io.*;

public class VulnerableXmlParser {

    // VULNERABLE: No XML schema validation
    public Document parseXml(File xmlFile) throws Exception {
        DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();

        // VULNERABLE: Validation disabled
        factory.setValidating(false);

        // VULNERABLE: No schema set
        // factory.setSchema(...);

        DocumentBuilder builder = factory.newDocumentBuilder();
        Document doc = builder.parse(xmlFile);

        // Parses any well-formed XML, even if it doesn't match expected structure
        // Attacker can inject unexpected elements or attributes
        return doc;
    }

    // VULNERABLE: JSON parsing without schema validation
    public void processJsonConfig(String jsonInput) {
        // VULNERABLE: No validation against expected schema

        JSONObject config = new JSONObject(jsonInput);

        // Assumes specific structure without verification
        String serverUrl = config.getString("serverUrl");  // May not exist
        int port = config.getInt("port");                  // May be string

        // Attacker can provide malformed JSON or unexpected types
    }
}
// Vulnerable: Protocol parsing without syntax validation

#include <stdio.h>
#include <string.h>
#include <stdlib.h>

// VULNERABLE: HTTP request parsing without proper validation
void vulnerable_parse_http_request(const char* request) {
    char method[16];
    char path[256];
    char version[16];

    // VULNERABLE: Simple sscanf without validation
    int parsed = sscanf(request, "%s %s %s", method, path, version);

    // VULNERABLE: No validation of HTTP version format
    // version could be anything, not just "HTTP/1.0" or "HTTP/1.1"

    // VULNERABLE: No validation of method
    // method could contain invalid characters

    // VULNERABLE: No validation of path
    // path could contain malicious sequences

    if (parsed == 3) {
        process_request(method, path, version);
    }
    // Missing proper error handling
}

// VULNERABLE: IP address parsing without proper format validation
int vulnerable_parse_ip(const char* ip_string, uint32_t* ip_out) {
    // VULNERABLE: inet_addr accepts octal/hex notation
    // "0x7f.0.0.1" or "0177.0.0.1" could bypass filters

    *ip_out = inet_addr(ip_string);
    return (*ip_out != INADDR_NONE);

    // Filter checking for "127.0.0.1" would miss "0x7f.0.0.1"
}

// VULNERABLE: Email validation with insufficient syntax check
int vulnerable_validate_email(const char* email) {
    // VULNERABLE: Only checks for @ symbol
    if (strchr(email, '@') != NULL) {
        return 1;  // "Valid"
    }
    return 0;

    // Accepts: "not valid@", "@invalid", "a@b", etc.
    // Many syntactically invalid emails pass this check
}
# Vulnerable: Input parsing without syntax validation

import re
import json

# VULNERABLE: URL parsing without proper validation
def vulnerable_parse_url(url):
    # VULNERABLE: Simple regex doesn't validate full URL syntax
    match = re.match(r'https?://(.+)', url)

    if match:
        return match.group(1)  # Returns everything after protocol
    return None

    # Accepts malformed URLs like "http://invalid url with spaces"
    # Doesn't validate hostname syntax, port numbers, etc.

# VULNERABLE: Date parsing without format validation
def vulnerable_parse_date(date_str):
    # VULNERABLE: No format validation
    parts = date_str.split('-')

    if len(parts) == 3:
        year, month, day = parts
        return {
            'year': int(year),   # Could fail on non-numeric
            'month': int(month),  # Could be 13, -1, etc.
            'day': int(day)       # Could be 32, 0, etc.
        }
    return None

    # Accepts: "2024-13-45", "0-0-0", "-1--2--3"

# VULNERABLE: SQL-like query parsing
def vulnerable_parse_query(query):
    # VULNERABLE: No syntax validation
    # Accepts any string as a "query"

    if 'SELECT' in query.upper():
        # Process as select query
        pass
    elif 'INSERT' in query.upper():
        # Process as insert query
        pass

    # No real SQL syntax validation
    # Attacker can inject malformed queries

Fixed Code

// Fixed: XML parsing with proper validation

import javax.xml.parsers.*;
import javax.xml.validation.*;
import org.w3c.dom.*;
import org.xml.sax.*;
import java.io.*;

public class SecureXmlParser {

    // FIXED: XML parsing with schema validation
    public Document parseXml(File xmlFile, File schemaFile) throws Exception {
        // Load schema
        SchemaFactory schemaFactory = SchemaFactory.newInstance(
            XMLConstants.W3C_XML_SCHEMA_NS_URI
        );
        Schema schema = schemaFactory.newSchema(schemaFile);

        DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();

        // FIXED: Enable validation
        factory.setValidating(true);
        factory.setNamespaceAware(true);
        factory.setSchema(schema);

        // FIXED: Security hardening
        factory.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING, true);
        factory.setFeature(
            "http://apache.org/xml/features/disallow-doctype-decl", true);

        DocumentBuilder builder = factory.newDocumentBuilder();

        // FIXED: Error handler for validation errors
        builder.setErrorHandler(new ErrorHandler() {
            @Override
            public void error(SAXParseException e) throws SAXException {
                throw e;  // Fail on validation error
            }

            @Override
            public void fatalError(SAXParseException e) throws SAXException {
                throw e;
            }

            @Override
            public void warning(SAXParseException e) {
                System.err.println("Warning: " + e.getMessage());
            }
        });

        return builder.parse(xmlFile);
    }

    // FIXED: JSON parsing with schema validation
    public Config parseJsonConfig(String jsonInput) throws ValidationException {
        // FIXED: Use JSON Schema validation
        ObjectMapper mapper = new ObjectMapper();

        // Parse JSON
        JsonNode rootNode;
        try {
            rootNode = mapper.readTree(jsonInput);
        } catch (JsonProcessingException e) {
            throw new ValidationException("Invalid JSON syntax: " + e.getMessage());
        }

        // FIXED: Validate against schema
        JsonSchema schema = loadConfigSchema();
        Set<ValidationMessage> errors = schema.validate(rootNode);

        if (!errors.isEmpty()) {
            throw new ValidationException("JSON validation failed: " + errors);
        }

        // FIXED: Deserialize to typed object
        return mapper.treeToValue(rootNode, Config.class);
    }
}
// Fixed: Protocol parsing with proper syntax validation

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <ctype.h>
#include <stdbool.h>
#include <arpa/inet.h>

// FIXED: HTTP request parsing with validation
bool secure_parse_http_request(const char* request,
                                char* method_out, size_t method_size,
                                char* path_out, size_t path_size,
                                char* version_out, size_t version_size) {
    const char* p = request;

    // FIXED: Parse and validate method
    const char* valid_methods[] = {"GET", "POST", "PUT", "DELETE", "HEAD", "OPTIONS"};
    bool method_valid = false;

    size_t method_len = 0;
    while (*p && *p != ' ' && method_len < method_size - 1) {
        if (!isupper(*p)) {
            return false;  // Method must be uppercase
        }
        method_out[method_len++] = *p++;
    }
    method_out[method_len] = '\0';

    // FIXED: Verify method is in allowed list
    for (int i = 0; i < sizeof(valid_methods)/sizeof(valid_methods[0]); i++) {
        if (strcmp(method_out, valid_methods[i]) == 0) {
            method_valid = true;
            break;
        }
    }
    if (!method_valid) {
        return false;
    }

    // Skip space
    if (*p != ' ') return false;
    p++;

    // FIXED: Parse and validate path
    size_t path_len = 0;
    while (*p && *p != ' ' && path_len < path_size - 1) {
        // FIXED: Validate path characters
        if (!isprint(*p) || *p == '<' || *p == '>') {
            return false;  // Invalid path character
        }
        path_out[path_len++] = *p++;
    }
    path_out[path_len] = '\0';

    // FIXED: Path must start with /
    if (path_out[0] != '/') {
        return false;
    }

    // Skip space
    if (*p != ' ') return false;
    p++;

    // FIXED: Parse and validate HTTP version
    if (strncmp(p, "HTTP/", 5) != 0) {
        return false;
    }
    p += 5;

    // FIXED: Only accept HTTP/1.0 or HTTP/1.1
    if (strcmp(p, "1.0\r\n") != 0 && strcmp(p, "1.1\r\n") != 0) {
        return false;
    }

    strncpy(version_out, p, version_size - 1);
    version_out[version_size - 1] = '\0';

    return true;
}

// FIXED: IP address parsing with strict format validation
bool secure_parse_ip(const char* ip_string, uint32_t* ip_out) {
    // FIXED: Only accept standard decimal dotted notation
    unsigned int octets[4];
    char extra;

    // FIXED: Strict format parsing
    int parsed = sscanf(ip_string, "%u.%u.%u.%u%c",
                        &octets[0], &octets[1], &octets[2], &octets[3], &extra);

    // Must parse exactly 4 octets, no trailing characters
    if (parsed != 4) {
        return false;
    }

    // FIXED: Validate octet ranges
    for (int i = 0; i < 4; i++) {
        if (octets[i] > 255) {
            return false;
        }
    }

    // FIXED: Verify no octal/hex notation (no leading zeros except for 0)
    const char* p = ip_string;
    for (int i = 0; i < 4; i++) {
        if (*p == '0' && isdigit(*(p+1))) {
            // Leading zero - could be octal, reject
            return false;
        }
        while (*p && *p != '.') p++;
        if (*p == '.') p++;
    }

    *ip_out = (octets[0] << 24) | (octets[1] << 16) | (octets[2] << 8) | octets[3];
    return true;
}

// FIXED: Email validation with proper syntax checking
bool secure_validate_email(const char* email) {
    // FIXED: Comprehensive email syntax validation
    const char* at = strchr(email, '@');
    if (at == NULL) return false;

    // Check only one @
    if (strchr(at + 1, '@') != NULL) return false;

    // Local part validation (before @)
    const char* p = email;
    if (p == at) return false;  // Empty local part

    while (p < at) {
        char c = *p++;
        // FIXED: Validate allowed characters in local part
        if (!isalnum(c) && c != '.' && c != '_' && c != '-' && c != '+') {
            return false;
        }
    }

    // Domain part validation (after @)
    p = at + 1;
    if (*p == '\0') return false;  // Empty domain

    int dot_count = 0;
    while (*p) {
        char c = *p++;
        if (c == '.') {
            dot_count++;
            if (*(p-2) == '.' || *(p) == '\0') {
                return false;  // Consecutive dots or trailing dot
            }
        } else if (!isalnum(c) && c != '-') {
            return false;
        }
    }

    // Must have at least one dot in domain
    if (dot_count == 0) return false;

    return true;
}
# Fixed: Input parsing with proper syntax validation

import re
import json
from urllib.parse import urlparse
from datetime import datetime
from jsonschema import validate, ValidationError as JsonValidationError

# FIXED: URL parsing with proper validation
def secure_parse_url(url):
    """Parse and validate URL syntax."""
    # FIXED: Use proper URL parser
    try:
        parsed = urlparse(url)
    except Exception as e:
        raise ValueError(f"Invalid URL: {e}")

    # FIXED: Validate required components
    if not parsed.scheme:
        raise ValueError("URL missing scheme")

    if parsed.scheme not in ('http', 'https'):
        raise ValueError(f"Invalid scheme: {parsed.scheme}")

    if not parsed.netloc:
        raise ValueError("URL missing hostname")

    # FIXED: Validate hostname syntax
    hostname = parsed.hostname
    if hostname is None:
        raise ValueError("Invalid hostname")

    # Check hostname characters
    hostname_pattern = r'^[a-zA-Z0-9]([a-zA-Z0-9-]*[a-zA-Z0-9])?(\.[a-zA-Z0-9]([a-zA-Z0-9-]*[a-zA-Z0-9])?)*$'
    if not re.match(hostname_pattern, hostname):
        raise ValueError(f"Invalid hostname syntax: {hostname}")

    # FIXED: Validate port if present
    if parsed.port is not None:
        if parsed.port < 1 or parsed.port > 65535:
            raise ValueError(f"Invalid port: {parsed.port}")

    return parsed

# FIXED: Date parsing with format validation
def secure_parse_date(date_str, expected_format='%Y-%m-%d'):
    """Parse date with strict format validation."""
    # FIXED: Validate format pattern
    if not isinstance(date_str, str):
        raise TypeError(f"Date must be string, got {type(date_str)}")

    # FIXED: Validate length matches expected format
    if expected_format == '%Y-%m-%d' and len(date_str) != 10:
        raise ValueError(f"Invalid date format: {date_str}")

    # FIXED: Use strict datetime parsing
    try:
        parsed = datetime.strptime(date_str, expected_format)
    except ValueError as e:
        raise ValueError(f"Invalid date: {e}")

    # FIXED: Additional validation
    # Ensure the parsed date matches the input (catch issues like Feb 30)
    if parsed.strftime(expected_format) != date_str:
        raise ValueError(f"Invalid date values in: {date_str}")

    return {
        'year': parsed.year,
        'month': parsed.month,
        'day': parsed.day
    }

# FIXED: JSON config validation with schema
CONFIG_SCHEMA = {
    "type": "object",
    "required": ["serverUrl", "port"],
    "properties": {
        "serverUrl": {
            "type": "string",
            "pattern": "^https?://[a-zA-Z0-9.-]+(/.*)?$"
        },
        "port": {
            "type": "integer",
            "minimum": 1,
            "maximum": 65535
        },
        "timeout": {
            "type": "integer",
            "minimum": 0,
            "maximum": 300000
        }
    },
    "additionalProperties": False
}

def secure_parse_json_config(json_input):
    """Parse JSON config with schema validation."""
    # FIXED: Parse JSON
    try:
        config = json.loads(json_input)
    except json.JSONDecodeError as e:
        raise ValueError(f"Invalid JSON syntax: {e}")

    # FIXED: Validate against schema
    try:
        validate(instance=config, schema=CONFIG_SCHEMA)
    except JsonValidationError as e:
        raise ValueError(f"Config validation failed: {e.message}")

    return config

CVE Examples

  • CVE-2016-4029: Incorrect IP address format validation enabled octal/hex parsing bypass - IP filters could be bypassed using alternative notations.
  • CVE-2007-5893: Missing HTTP protocol version validation caused application crashes.

  • CWE-20: Improper Input Validation (parent)
  • CWE-112: Missing XML Validation (child)
  • CWE-1215: Data Validation Issues (category)
  • CWE-91: XML Injection (related)

References

  1. MITRE Corporation. "CWE-1286: Improper Validation of Syntactic Correctness of Input." https://cwe.mitre.org/data/definitions/1286.html
  2. OWASP. "Input Validation Cheat Sheet"
  3. W3C. "XML Schema Validation"