Multiple Interpretations of UI Input
Description
Multiple Interpretations of UI Input is a vulnerability where the UI has multiple interpretations of user input but does not prompt the user when it selects the less secure interpretation. When an application accepts user input that can be understood in multiple ways, it may proceed with a less secure interpretation without alerting the user to this choice. This can lead to security vulnerabilities when the system's interpretation differs from the user's intent, potentially allowing unintended actions or security bypasses.
Risk
Multiple interpretations of UI input create security risks when the system silently chooses a less secure option. Users may intend restrictive settings but the system interprets their input permissively. Date formats interpreted differently may grant access during wrong time periods. File path interpretations may allow access to unintended directories. Character encoding ambiguities may bypass input validation. The risk is amplified because users are not informed when the less secure interpretation is chosen, leading to false confidence in the security of their configuration.
Solution
Employ an "accept known good" input validation approach using allowlists of acceptable inputs that strictly conform to specifications. Reject any input that does not meet strict validation criteria or could have multiple interpretations. When multiple valid interpretations exist, prompt the user to clarify their intent rather than silently choosing one. Account for length, type, acceptable value ranges, missing or extra inputs, syntax, consistency across fields, and business logic conformance. Decode and canonicalize all inputs to the application's internal representation before validation. Avoid double-decoding to prevent bypass attacks.
Common Consequences
| Impact | Details |
|---|---|
| Other | Scope: Other Varies by Context - Impact depends on the specific interpretation chosen and the security implications of that choice compared to the user's intended interpretation. |
Example Code
Vulnerable Code
# Vulnerable: Date input with multiple interpretations
class VulnerableDateParser:
def parse_access_expiry(self, date_string):
# Vulnerable: Multiple date formats accepted without clarification
# "01/02/2024" - Is this January 2 or February 1?
# Silently chooses MM/DD/YYYY (American) format
parts = date_string.split('/')
month = int(parts[0])
day = int(parts[1])
year = int(parts[2])
# User in Europe intended DD/MM/YYYY
# They set expiry for Feb 1 (01/02/2024)
# System interprets as Jan 2 - access expires month early
# Or worse: User sets Jan 2 expiry, system interprets as Feb 1
# Access continues for extra month
return datetime(year, month, day)
def parse_time_restriction(self, time_input):
# Vulnerable: "9:00" - AM or PM?
# System defaults to 24-hour interpretation
# "9:00" becomes 09:00 (morning)
# User intended 9 PM restriction
# System allows access at 9 AM instead
hours, minutes = map(int, time_input.split(':'))
return time(hours, minutes)
// Vulnerable: Permission input with ambiguous interpretation
class VulnerablePermissionParser {
parsePermissionLevel(input) {
// Vulnerable: "admin" could mean different things
// - Full administrator
// - Admin of a specific section
// - Read-only admin access
const levels = {
'admin': 'FULL_ADMIN', // System assumes most permissive
'user': 'STANDARD_USER',
'guest': 'GUEST'
};
// User types "admin" meaning section admin
// System grants full administrator privileges
return levels[input.toLowerCase()] || 'GUEST';
}
parseIPRange(input) {
// Vulnerable: "192.168.1.0" - Is this a single IP or a /24 network?
// System silently interprets as /24 network
if (!input.includes('/')) {
// Vulnerable: Assumes network without confirming
if (input.endsWith('.0')) {
return input + '/24'; // Silently adds subnet
}
}
// User intended single IP 192.168.1.0
// System allows entire 192.168.1.0/24 range (256 IPs)
return input;
}
}
// Vulnerable: File path with multiple interpretations
public class VulnerablePathParser {
public File resolvePath(String userPath) {
// Vulnerable: Relative vs absolute interpretation
// "config/settings.json" - relative to what?
// System silently uses current working directory
// User might have intended application root
File file = new File(userPath);
// Vulnerable: ".." interpretation varies
// On some systems "../secret" accesses parent
// User might not realize path traversal is possible
return file.getCanonicalFile();
}
public String parseFileType(String filename) {
// Vulnerable: "document.txt.exe" - what is the type?
// System uses last extension (.exe)
// User sees .txt and thinks it's safe
int lastDot = filename.lastIndexOf('.');
if (lastDot > 0) {
return filename.substring(lastDot + 1);
}
return "";
// No warning that file is actually executable
}
}
// Vulnerable: Numeric input with multiple interpretations
#include <stdlib.h>
#include <string.h>
int parse_port_number(const char *input) {
// Vulnerable: "010" - decimal 10 or octal 8?
// strtol with base 0 interprets leading zero as octal
long port = strtol(input, NULL, 0);
// User types "010" meaning port 10
// System interprets as octal 8
// Wrong port is opened
return (int)port;
}
int parse_permission_mask(const char *input) {
// Vulnerable: "755" - string or octal?
// User types 755 meaning rwxr-xr-x
int mask;
// Vulnerable: Different interpretation based on context
if (input[0] == '0') {
// Treats as octal
mask = strtol(input, NULL, 8);
} else {
// Treats as decimal - wrong!
mask = strtol(input, NULL, 10); // 755 decimal != 0755 octal
}
// "755" becomes 755 decimal = 01363 octal
// Completely wrong permissions set
return mask;
}
Fixed Code
# Fixed: Date input with explicit format selection
from datetime import datetime, time
class SecureDateParser:
SUPPORTED_FORMATS = {
'ISO': '%Y-%m-%d',
'US': '%m/%d/%Y',
'EU': '%d/%m/%Y',
'UK': '%d-%m-%Y'
}
def parse_access_expiry(self, date_string, format_hint=None):
# Fixed: Require explicit format or detect and confirm
if format_hint and format_hint in self.SUPPORTED_FORMATS:
return self._parse_with_format(date_string, self.SUPPORTED_FORMATS[format_hint])
# Fixed: Detect possible formats
possible_formats = self._detect_possible_formats(date_string)
if len(possible_formats) > 1:
# Fixed: Raise error requiring clarification
raise AmbiguousInputError(
f"Date '{date_string}' has multiple interpretations: {possible_formats}. "
"Please specify format explicitly."
)
return self._parse_with_format(date_string, possible_formats[0])
def _detect_possible_formats(self, date_string):
possible = []
for name, fmt in self.SUPPORTED_FORMATS.items():
try:
datetime.strptime(date_string, fmt)
possible.append((name, fmt))
except ValueError:
pass
return possible
def parse_time_restriction(self, time_input):
# Fixed: Require explicit AM/PM or 24-hour format
if 'AM' in time_input.upper() or 'PM' in time_input.upper():
# 12-hour format with explicit indicator
return datetime.strptime(time_input.upper(), '%I:%M %p').time()
elif len(time_input.split(':')[0]) == 2:
# 24-hour format (must have leading zero: "09:00")
return datetime.strptime(time_input, '%H:%M').time()
else:
raise AmbiguousInputError(
f"Time '{time_input}' is ambiguous. Use '09:00' for 24-hour "
"or '9:00 AM/PM' for 12-hour format."
)
// Fixed: Permission input with explicit clarification
class SecurePermissionParser {
parsePermissionLevel(input) {
// Fixed: Require specific, unambiguous permission names
const levels = {
'full_administrator': 'FULL_ADMIN',
'section_administrator': 'SECTION_ADMIN',
'read_only_admin': 'READONLY_ADMIN',
'standard_user': 'STANDARD_USER',
'guest': 'GUEST'
};
const normalized = input.toLowerCase().replace(/\s+/g, '_');
if (!levels[normalized]) {
throw new AmbiguousInputError(
`Unknown permission level: '${input}'. ` +
`Valid options: ${Object.keys(levels).join(', ')}`
);
}
return levels[normalized];
}
parseIPRange(input) {
// Fixed: Require explicit CIDR notation
if (!input.includes('/')) {
// Fixed: Prompt for clarification
throw new AmbiguousInputError(
`IP '${input}' is ambiguous. ` +
`Use '${input}/32' for single IP or '${input}/24' for network.`
);
}
// Fixed: Validate CIDR notation
const [ip, mask] = input.split('/');
const maskNum = parseInt(mask);
if (isNaN(maskNum) || maskNum < 0 || maskNum > 32) {
throw new InvalidInputError(`Invalid CIDR mask: ${mask}`);
}
return input;
}
}
// Fixed: File path with explicit interpretation
public class SecurePathParser {
private final Path applicationRoot;
public SecurePathParser(Path applicationRoot) {
this.applicationRoot = applicationRoot.toAbsolutePath();
}
public Path resolvePath(String userPath, PathContext context) throws AmbiguousInputException {
// Fixed: Require explicit context for path resolution
Path resolved;
switch (context) {
case ABSOLUTE:
resolved = Paths.get(userPath).toAbsolutePath();
break;
case RELATIVE_TO_APP_ROOT:
resolved = applicationRoot.resolve(userPath).normalize();
break;
case RELATIVE_TO_WORKING_DIR:
resolved = Paths.get(userPath).toAbsolutePath().normalize();
break;
default:
throw new AmbiguousInputException(
"Path context must be explicitly specified"
);
}
// Fixed: Check for path traversal
if (context == PathContext.RELATIVE_TO_APP_ROOT) {
if (!resolved.startsWith(applicationRoot)) {
throw new SecurityException("Path traversal detected");
}
}
return resolved;
}
public FileTypeResult parseFileType(String filename) {
// Fixed: Detect and warn about multiple extensions
String[] parts = filename.split("\\.");
if (parts.length > 2) {
// Fixed: Multiple extensions detected
String visibleExt = parts[parts.length - 2];
String actualExt = parts[parts.length - 1];
if (isDangerousExtension(actualExt) && !isDangerousExtension(visibleExt)) {
throw new AmbiguousInputException(
String.format(
"File '%s' has multiple extensions. " +
"Appears to be '.%s' but is actually '.%s' (potentially dangerous)",
filename, visibleExt, actualExt
)
);
}
}
return new FileTypeResult(parts[parts.length - 1], parts.length > 2);
}
private boolean isDangerousExtension(String ext) {
return Set.of("exe", "bat", "cmd", "ps1", "vbs", "js", "msi")
.contains(ext.toLowerCase());
}
}
// Fixed: Numeric input with explicit base specification
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <errno.h>
typedef struct {
int value;
int base_used;
char error[256];
} ParseResult;
ParseResult parse_port_number(const char *input, int explicit_base) {
ParseResult result = {0, 10, ""};
char *endptr;
// Fixed: Require explicit base or reject ambiguous input
if (explicit_base == 0) {
// Fixed: Check for ambiguous leading zero
if (input[0] == '0' && input[1] != '\0' && input[1] != 'x') {
snprintf(result.error, sizeof(result.error),
"Ambiguous input '%s'. Use '0o' prefix for octal, "
"or remove leading zero for decimal.", input);
return result;
}
}
errno = 0;
long port = strtol(input, &endptr, explicit_base);
if (errno != 0 || *endptr != '\0') {
snprintf(result.error, sizeof(result.error),
"Invalid port number: %s", input);
return result;
}
if (port < 1 || port > 65535) {
snprintf(result.error, sizeof(result.error),
"Port must be between 1 and 65535");
return result;
}
result.value = (int)port;
result.base_used = explicit_base ? explicit_base : 10;
return result;
}
ParseResult parse_permission_mask(const char *input) {
ParseResult result = {0, 8, ""};
char *endptr;
// Fixed: Always require octal prefix for permission masks
if (input[0] != '0' || (input[1] != '\0' && input[1] != 'o' &&
!('0' <= input[1] && input[1] <= '7'))) {
snprintf(result.error, sizeof(result.error),
"Permission mask must be in octal format (e.g., '0755' or '0o755')");
return result;
}
// Fixed: Parse as explicit octal
const char *parse_start = input;
if (input[0] == '0' && input[1] == 'o') {
parse_start = input + 2; // Skip "0o" prefix
}
errno = 0;
long mask = strtol(parse_start, &endptr, 8);
if (errno != 0 || *endptr != '\0' || mask < 0 || mask > 0777) {
snprintf(result.error, sizeof(result.error),
"Invalid permission mask: %s", input);
return result;
}
result.value = (int)mask;
return result;
}
CVE Examples
No specific CVEs are listed in the MITRE database for this CWE. However, the pattern is common in:
- Date/time parsing vulnerabilities leading to access control bypasses
- File type detection bypasses using ambiguous extensions
- Numeric interpretation issues causing configuration errors
References
- MITRE Corporation. "CWE-450: Multiple Interpretations of UI Input." https://cwe.mitre.org/data/definitions/450.html
- OWASP. "Input Validation Cheat Sheet." https://cheatsheetseries.owasp.org/cheatsheets/Input_Validation_Cheat_Sheet.html