Improper Validation of Specified Type of Input
Description
Improper Validation of Specified Type of Input occurs when a product receives input expected to be a specific type but fails to validate or incorrectly validates that the input actually matches that expected type. When input doesn't conform to the expected type, attackers can trigger unexpected errors, cause incorrect actions, or exploit latent vulnerabilities. This weakness commonly appears in type-unsafe languages or those supporting type casting/conversion.
Risk
Improper type validation has severe security implications. Type confusion attacks possible. Injection vulnerabilities may occur. Integer overflows from type coercion. Buffer overflows from size mismatches. Business logic bypasses enabled. Application crashes may happen. Data corruption possible. Security controls can be circumvented.
Solution
Employ "accept known good" input validation strategy using strict whitelists of acceptable inputs. Validate all relevant properties including length, type, value ranges, syntax, and business rule conformance. Use strongly typed languages or strict type checking. Explicitly validate and convert types before use. While denylists can detect attacks, they're insufficient alone.
Common Consequences
| Impact | Details |
|---|---|
| Other | Scope: Other Impact varies based on how type validation failure is exploited. |
| Integrity | Scope: Integrity Type confusion can lead to data corruption. |
| Confidentiality | Scope: Confidentiality Incorrect types may expose unintended data. |
Example Code
Vulnerable Code
# Vulnerable: No type validation in Python
def vulnerable_process_age(age):
# VULNERABLE: No type check
# Could receive string, list, None, etc.
if age > 18: # TypeError if age is not comparable
return "Adult"
return "Minor"
def vulnerable_calculate_discount(price, quantity):
# VULNERABLE: No type validation
# price could be string "100; DROP TABLE users"
# quantity could be negative or non-numeric
total = price * quantity # Unexpected behavior with wrong types
return total
def vulnerable_process_user_data(data):
# VULNERABLE: Assumes data is dict without checking
name = data['name'] # KeyError if missing, TypeError if not dict
email = data['email'] # Same issues
age = data['age'] # Could be string instead of int
# Process without type validation
return f"{name} ({age}) - {email}"
# VULNERABLE: SQL query with unvalidated numeric input
def vulnerable_get_user(user_id):
# VULNERABLE: user_id might be string with injection
query = f"SELECT * FROM users WHERE id = {user_id}"
return execute_query(query)
# user_id = "1 OR 1=1" would expose all users
// Vulnerable: JavaScript type coercion issues
// VULNERABLE: No type validation
function vulnerableCalculateTotal(price, quantity) {
// JavaScript will coerce types, potentially incorrectly
return price * quantity;
// "10" * "5" = 50 (string to number coercion)
// "10" * "abc" = NaN
// [1,2] * 3 = NaN
}
// VULNERABLE: Assuming array without checking
function vulnerableProcessItems(items) {
// VULNERABLE: If items is not array, forEach throws
items.forEach(item => {
console.log(item.name); // Also assumes item has name property
});
}
// VULNERABLE: Object property access without type check
function vulnerableGetUserAge(user) {
// VULNERABLE: No validation of user object
return user.profile.age; // Throws if user/profile is null/undefined
}
// VULNERABLE: parseInt behavior
function vulnerableParseUserId(idString) {
// VULNERABLE: parseInt has surprising behavior
return parseInt(idString);
// parseInt("42abc") = 42 (ignores trailing chars)
// parseInt("0x10") = 16 (hex parsing)
// parseInt("08") might = 0 in older JS (octal)
}
// Vulnerable: C type handling issues
#include <stdio.h>
#include <stdlib.h>
// VULNERABLE: No validation of numeric string
int vulnerable_parse_int(const char* str) {
// VULNERABLE: atoi returns 0 for invalid input
// Cannot distinguish "0" from "invalid"
return atoi(str);
}
// VULNERABLE: Type confusion with void pointer
void vulnerable_process_data(void* data, int type) {
// VULNERABLE: Trust caller's type indicator
if (type == 1) {
int* int_data = (int*)data;
printf("Integer: %d\n", *int_data);
} else if (type == 2) {
char* str_data = (char*)data;
printf("String: %s\n", str_data);
}
// If type doesn't match actual data, undefined behavior
// Attacker controlling 'type' can cause type confusion
}
// VULNERABLE: Size type mismatch
void vulnerable_copy(char* dest, const char* src, int size) {
// VULNERABLE: 'size' is signed int
// Negative size could bypass checks or cause large copy
if (size < sizeof(dest)) { // Comparison issues with negative
memcpy(dest, src, size); // size cast to size_t (unsigned)
}
}
Fixed Code
# Fixed: Proper type validation in Python
from typing import Dict, Any, Union
import numbers
def secure_process_age(age: Any) -> str:
"""Process age with type validation."""
# FIXED: Validate type
if not isinstance(age, (int, float)):
raise TypeError(f"Age must be numeric, got {type(age).__name__}")
# FIXED: Validate it's a reasonable number
if not isinstance(age, numbers.Real):
raise TypeError(f"Age must be a real number")
age_float = float(age)
# FIXED: Validate range
if age_float < 0 or age_float > 150:
raise ValueError(f"Age must be between 0 and 150, got {age}")
if age_float >= 18:
return "Adult"
return "Minor"
def secure_calculate_discount(price: Any, quantity: Any) -> float:
"""Calculate discount with validated types."""
# FIXED: Validate price type
if not isinstance(price, (int, float)):
raise TypeError(f"Price must be numeric, got {type(price).__name__}")
# FIXED: Validate quantity type
if not isinstance(quantity, int):
raise TypeError(f"Quantity must be integer, got {type(quantity).__name__}")
# FIXED: Validate ranges
if price < 0:
raise ValueError("Price cannot be negative")
if quantity < 0:
raise ValueError("Quantity cannot be negative")
return float(price) * quantity
def secure_process_user_data(data: Any) -> str:
"""Process user data with type validation."""
# FIXED: Validate data is dict
if not isinstance(data, dict):
raise TypeError(f"Data must be dict, got {type(data).__name__}")
# FIXED: Validate required keys exist
required_keys = ['name', 'email', 'age']
for key in required_keys:
if key not in data:
raise ValueError(f"Missing required key: {key}")
# FIXED: Validate types of values
if not isinstance(data['name'], str):
raise TypeError("Name must be string")
if not isinstance(data['email'], str):
raise TypeError("Email must be string")
if not isinstance(data['age'], int):
raise TypeError("Age must be integer")
return f"{data['name']} ({data['age']}) - {data['email']}"
# FIXED: Parameterized query with validated input
def secure_get_user(user_id: Any):
"""Get user with validated ID."""
# FIXED: Validate type
if not isinstance(user_id, int):
raise TypeError(f"User ID must be integer, got {type(user_id).__name__}")
# FIXED: Validate range
if user_id < 1:
raise ValueError("User ID must be positive")
# FIXED: Use parameterized query
query = "SELECT * FROM users WHERE id = %s"
return execute_query(query, (user_id,))
// Fixed: JavaScript with type validation
// FIXED: Type validation with explicit checks
function secureCalculateTotal(price, quantity) {
// FIXED: Validate price type
if (typeof price !== 'number' || Number.isNaN(price)) {
throw new TypeError(`Price must be number, got ${typeof price}`);
}
// FIXED: Validate quantity type
if (!Number.isInteger(quantity)) {
throw new TypeError(`Quantity must be integer, got ${typeof quantity}`);
}
// FIXED: Validate ranges
if (price < 0) {
throw new RangeError('Price cannot be negative');
}
if (quantity < 0) {
throw new RangeError('Quantity cannot be negative');
}
return price * quantity;
}
// FIXED: Array validation before iteration
function secureProcessItems(items) {
// FIXED: Validate items is array
if (!Array.isArray(items)) {
throw new TypeError(`Items must be array, got ${typeof items}`);
}
items.forEach(item => {
// FIXED: Validate each item
if (typeof item !== 'object' || item === null) {
throw new TypeError('Each item must be an object');
}
if (typeof item.name !== 'string') {
throw new TypeError('Item name must be string');
}
console.log(item.name);
});
}
// FIXED: Safe property access with validation
function secureGetUserAge(user) {
// FIXED: Validate user object
if (typeof user !== 'object' || user === null) {
throw new TypeError('User must be an object');
}
if (typeof user.profile !== 'object' || user.profile === null) {
throw new TypeError('User profile must be an object');
}
if (typeof user.profile.age !== 'number') {
throw new TypeError('User age must be a number');
}
return user.profile.age;
}
// FIXED: Strict integer parsing
function secureParseUserId(idString) {
// FIXED: Validate input type
if (typeof idString !== 'string') {
throw new TypeError('ID must be string');
}
// FIXED: Validate format (digits only)
if (!/^\d+$/.test(idString)) {
throw new Error('Invalid user ID format');
}
const id = parseInt(idString, 10); // FIXED: Explicit radix
// FIXED: Validate result
if (Number.isNaN(id) || id < 1) {
throw new Error('Invalid user ID');
}
return id;
}
// Fixed: C with proper type validation
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <limits.h>
#include <stdbool.h>
// FIXED: Validated integer parsing
bool secure_parse_int(const char* str, int* result) {
if (str == NULL || result == NULL) {
return false;
}
// FIXED: Check for empty string
if (*str == '\0') {
return false;
}
char* endptr;
errno = 0;
// FIXED: Use strtol for proper error detection
long val = strtol(str, &endptr, 10);
// FIXED: Check for conversion errors
if (errno == ERANGE) {
return false; // Overflow/underflow
}
if (endptr == str) {
return false; // No digits found
}
if (*endptr != '\0') {
return false; // Trailing characters
}
// FIXED: Check int range
if (val < INT_MIN || val > INT_MAX) {
return false;
}
*result = (int)val;
return true;
}
// FIXED: Type-safe data processing
typedef enum {
DATA_TYPE_INT = 1,
DATA_TYPE_STRING = 2
} data_type_t;
typedef struct {
data_type_t type;
union {
int int_value;
char str_value[256];
} data;
} typed_data_t;
void secure_process_data(const typed_data_t* data) {
if (data == NULL) {
return;
}
// FIXED: Type is part of data structure, not separate parameter
switch (data->type) {
case DATA_TYPE_INT:
printf("Integer: %d\n", data->data.int_value);
break;
case DATA_TYPE_STRING:
printf("String: %s\n", data->data.str_value);
break;
default:
fprintf(stderr, "Unknown data type: %d\n", data->type);
break;
}
}
// FIXED: Proper size type handling
bool secure_copy(char* dest, size_t dest_size, const char* src, size_t copy_size) {
// FIXED: All parameters are validated
if (dest == NULL || src == NULL) {
return false;
}
// FIXED: Use size_t (unsigned) for sizes
if (copy_size == 0) {
return true; // Nothing to copy
}
// FIXED: Bounds check
if (copy_size > dest_size) {
return false; // Would overflow destination
}
memcpy(dest, src, copy_size);
return true;
}
CVE Examples
- CVE-2024-37032: LLM tool failed to validate digest format, enabling path traversal through type confusion.
- CVE-2008-2223: SQL injection via improperly validated numeric ID - string input was used where integer was expected.
Related CWEs
- CWE-20: Improper Input Validation (parent)
- CWE-843: Access of Resource Using Incompatible Type (Type Confusion) (related)
- CWE-136: Type Errors (category)
- CWE-1215: Data Validation Issues (category)
References
- MITRE Corporation. "CWE-1287: Improper Validation of Specified Type of Input." https://cwe.mitre.org/data/definitions/1287.html
- OWASP. "Input Validation Cheat Sheet"
- CERT. "Type Safety Guidelines"