User Interface (UI) Misrepresentation of Critical Information
Description
User Interface Misrepresentation of Critical Information is a vulnerability where the user interface fails to accurately represent critical information to the user, allowing data or its source to be obscured or falsified. This weakness manifests in several ways: incorrect indicators displaying false information about system state, overlays where one UI element obscures another enabling clickjacking, icon manipulation making dangerous files appear harmless, timing issues causing incorrect state indicators, visual truncation hiding critical parts of information like file extensions, visual distinction problems making it difficult to identify critical information, and homograph attacks using similar-looking characters to deceive users.
Risk
UI misrepresentation enables a wide range of attacks, particularly phishing and social engineering. Attackers can make malicious websites appear legitimate by spoofing URLs or security indicators. Executable files can be disguised as harmless documents through icon or extension manipulation. Clickjacking attacks overlay invisible elements to capture unintended clicks. Unicode homograph attacks use look-alike characters to create convincing fake URLs. The risk is severe because users rely on visual cues to make security decisions, and manipulated interfaces subvert this trust. These attacks are particularly effective because they exploit human perception rather than technical vulnerabilities.
Solution
During implementation, validate all input syntax, length, and format before processing and display. During architecture and design, develop an information presentation strategy that accounts for unusual characters, homoglyphs, and potential truncation. Ensure critical information like URLs, file extensions, and security indicators cannot be obscured or manipulated. Use visual design that clearly distinguishes between trusted and untrusted content. Implement protections against frame-based attacks (X-Frame-Options, CSP frame-ancestors). Display full URLs without truncation in security-critical contexts. Use robust homograph detection for domain names and other critical identifiers.
Common Consequences
| Impact | Details |
|---|---|
| Non-Repudiation | Scope: Non-Repudiation Hide Activities - Attackers can obscure malicious activity by manipulating how information is displayed to users. |
| Access Control | Scope: Access Control Bypass Protection Mechanism - Users may inadvertently grant access or permissions when the UI misrepresents what action is being taken. |
Example Code
Vulnerable Code
<!-- Vulnerable: URL display that can be truncated or spoofed -->
<div class="url-bar">
<!-- Vulnerable: Long URLs get truncated, hiding real domain -->
<input type="text" class="url-display"
value="https://[email protected]/login"
style="width: 200px; overflow: hidden;">
<!-- User sees "https://www.secure-bank.com" but actual domain is evil.com -->
</div>
<!-- Vulnerable: Lock icon that doesn't reflect actual security -->
<div class="security-indicator">
<!-- Vulnerable: Shows lock even for mixed content pages -->
<img src="lock-icon.png" class="always-show-lock">
<!-- User trusts page is secure when insecure content is loaded -->
</div>
<!-- Vulnerable: Clickjacking via transparent overlay -->
<style>
.visible-content {
position: relative;
z-index: 1;
}
.hidden-action {
/* Vulnerable: Invisible iframe captures clicks */
position: absolute;
opacity: 0;
z-index: 2;
width: 100%;
height: 100%;
}
</style>
<div class="visible-content">
<button>Click for Free Prize!</button>
<iframe class="hidden-action" src="https://bank.com/transfer?to=attacker&amount=1000"></iframe>
<!-- User clicks "Free Prize" but actually initiates bank transfer -->
</div>
# Vulnerable: File display with extension hiding
class VulnerableFileDisplay:
def display_filename(self, filename, max_length=20):
# Vulnerable: Truncates filename, may hide dangerous extension
if len(filename) > max_length:
return filename[:max_length] + "..."
# "important_document.txt.exe" becomes "important_document...."
# User doesn't see .exe extension
return filename
def get_file_icon(self, filename):
# Vulnerable: Uses embedded icon from file
# Malware can embed document icon in .exe file
try:
return extract_embedded_icon(filename) # From the file itself
except:
return self.get_default_icon(filename)
# User sees Word document icon for malware.exe
// Vulnerable: Dialog box with misleading origin
class VulnerableDialogManager {
showAlert(message, title) {
// Vulnerable: Dialog doesn't show which webpage triggered it
const dialog = document.createElement('div');
dialog.className = 'system-dialog'; // Looks like OS dialog
dialog.innerHTML = `
<h2>${title}</h2>
<p>${message}</p>
<button onclick="handleOK()">OK</button>
`;
// User can't tell this is from a webpage, not the system
// Attacker shows: "Your computer is infected! Click OK to clean"
document.body.appendChild(dialog);
}
// Vulnerable: Password prompt that hides real destination
showPasswordPrompt() {
// Dialog shows "Enter password for secure-site.com"
// But actually sends to attacker.com
return prompt("Enter your password for secure-site.com");
// Submitted to current page's domain (attacker-controlled)
}
}
// Vulnerable: Homograph attack in URL display
public class VulnerableURLDisplay {
public String displayURL(String url) {
// Vulnerable: Doesn't detect Unicode homoglyphs
// "аpple.com" (Cyrillic 'a') looks like "apple.com" (Latin 'a')
return url; // Displayed as-is, user can't see difference
}
// Vulnerable: Certificate display without proper verification
public void displayCertificateInfo(X509Certificate cert) {
// Vulnerable: Shows subject without validating
String subject = cert.getSubjectDN().getName();
// Attacker uses certificate for "secure-bаnk.com" (Cyrillic 'a')
// Display shows what looks like "secure-bank.com"
System.out.println("Certificate for: " + subject);
}
}
Fixed Code
<!-- Fixed: URL display with security protections -->
<style>
.url-bar {
display: flex;
align-items: center;
}
.url-display {
/* Fixed: Show full URL, allow horizontal scroll */
width: 100%;
overflow-x: auto;
white-space: nowrap;
font-family: monospace; /* Fixed-width for better readability */
}
.domain-highlight {
/* Fixed: Highlight actual domain */
font-weight: bold;
color: #000;
}
.path-portion {
color: #666;
}
</style>
<div class="url-bar">
<!-- Fixed: Parse and display URL components clearly -->
<span class="protocol">https://</span>
<span class="domain-highlight" id="actual-domain">evil.com</span>
<span class="path-portion">/www.secure-bank.com/login</span>
</div>
<script>
// Fixed: Parse URL and highlight actual domain
function displayURL(url) {
try {
const parsed = new URL(url);
document.querySelector('.protocol').textContent = parsed.protocol + '//';
document.querySelector('.domain-highlight').textContent = parsed.hostname;
document.querySelector('.path-portion').textContent = parsed.pathname + parsed.search;
} catch (e) {
// Fixed: Show warning for invalid URLs
document.querySelector('.url-bar').classList.add('invalid-url');
}
}
</script>
<!-- Fixed: Clickjacking protection -->
<head>
<!-- Fixed: Prevent framing -->
<meta http-equiv="Content-Security-Policy" content="frame-ancestors 'self'">
</head>
<!-- Fixed: Server-side header -->
<!-- X-Frame-Options: DENY -->
<!-- Content-Security-Policy: frame-ancestors 'self' -->
# Fixed: File display with extension protection
import os
from pathlib import Path
class SecureFileDisplay:
DANGEROUS_EXTENSIONS = {'.exe', '.bat', '.cmd', '.ps1', '.vbs', '.js', '.msi', '.scr'}
def display_filename(self, filename, max_length=50):
# Fixed: Never hide the extension
name = Path(filename).stem
ext = Path(filename).suffix
if len(filename) <= max_length:
return filename
# Fixed: Truncate name portion only, always show extension
available_for_name = max_length - len(ext) - 3 # "..." takes 3
if available_for_name < 10:
available_for_name = 10 # Minimum name length
return f"{name[:available_for_name]}...{ext}"
def get_file_icon(self, filename):
# Fixed: Use extension-based icon, never trust embedded icons
ext = Path(filename).suffix.lower()
# Fixed: Map extensions to known-safe icons
icon_map = {
'.txt': 'text-icon.png',
'.pdf': 'pdf-icon.png',
'.doc': 'doc-icon.png',
'.docx': 'doc-icon.png',
'.exe': 'executable-warning-icon.png', # Warning icon for executables
}
return icon_map.get(ext, 'unknown-file-icon.png')
def analyze_filename_risks(self, filename):
"""Detect potentially deceptive filenames."""
risks = []
# Fixed: Check for double extensions
parts = filename.split('.')
if len(parts) > 2:
for ext in parts[1:-1]:
if f'.{ext}' in self.DANGEROUS_EXTENSIONS:
risks.append(f"Hidden dangerous extension: .{ext}")
# Fixed: Check for unicode tricks
if any(ord(c) > 127 for c in filename):
risks.append("Filename contains non-ASCII characters")
# Fixed: Check for RTL override
if '\u202e' in filename:
risks.append("Filename contains RTL override character")
return risks
// Fixed: Dialog box with clear origin indication
class SecureDialogManager {
showAlert(message, title) {
// Fixed: Clearly indicate webpage origin
const origin = window.location.hostname;
const dialog = document.createElement('div');
dialog.className = 'webpage-dialog'; // Distinct from system dialogs
dialog.innerHTML = `
<div class="dialog-origin">
⚠️ This message is from: ${this.escapeHtml(origin)}
</div>
<h2>${this.escapeHtml(title)}</h2>
<p>${this.escapeHtml(message)}</p>
<button onclick="this.closest('.webpage-dialog').remove()">OK</button>
`;
// Fixed: Style makes it clear this is from webpage
dialog.style.cssText = `
border: 3px solid orange;
background: #fff3cd;
`;
document.body.appendChild(dialog);
}
escapeHtml(text) {
const div = document.createElement('div');
div.textContent = text;
return div.innerHTML;
}
// Fixed: Credential prompts must show real destination
showCredentialPrompt(action, destination) {
// Fixed: Use browser's built-in credential management
// Or show clear, verified destination
if (destination !== window.location.hostname) {
alert(`Warning: Credentials will be sent to ${destination}, not this website.`);
return null;
}
// Fixed: Use credential management API
return navigator.credentials.get({
password: true,
mediation: 'required'
});
}
}
// Fixed: Homograph detection in URL display
import java.text.Normalizer;
import java.util.regex.Pattern;
public class SecureURLDisplay {
// Unicode block ranges for confusable scripts
private static final Pattern CYRILLIC = Pattern.compile("[\\u0400-\\u04FF]");
private static final Pattern GREEK = Pattern.compile("[\\u0370-\\u03FF]");
public DisplayResult displayURL(String url) {
DisplayResult result = new DisplayResult();
// Fixed: Check for mixed scripts (potential homograph)
String domain = extractDomain(url);
if (containsMixedScripts(domain)) {
result.warning = "Warning: Domain contains characters from multiple scripts (possible spoofing)";
result.displayDomain = convertToAscii(domain); // Show punycode
} else {
result.displayDomain = domain;
}
// Fixed: Highlight confusable characters
result.highlightedDomain = highlightConfusables(domain);
return result;
}
private boolean containsMixedScripts(String text) {
boolean hasLatin = text.matches(".*[a-zA-Z].*");
boolean hasCyrillic = CYRILLIC.matcher(text).find();
boolean hasGreek = GREEK.matcher(text).find();
// Mixed scripts are suspicious
int scriptCount = 0;
if (hasLatin) scriptCount++;
if (hasCyrillic) scriptCount++;
if (hasGreek) scriptCount++;
return scriptCount > 1;
}
private String convertToAscii(String domain) {
// Fixed: Convert IDN to ASCII (punycode)
try {
return java.net.IDN.toASCII(domain, java.net.IDN.ALLOW_UNASSIGNED);
} catch (IllegalArgumentException e) {
return "[Invalid domain]";
}
}
public CertificateDisplayResult displayCertificateInfo(X509Certificate cert) {
CertificateDisplayResult result = new CertificateDisplayResult();
String subject = cert.getSubjectX500Principal().getName();
// Fixed: Extract and validate CN
String cn = extractCN(subject);
// Fixed: Check for homograph in certificate CN
if (containsMixedScripts(cn)) {
result.warning = "Certificate contains mixed-script characters";
result.asciiCN = convertToAscii(cn);
}
// Fixed: Show full certificate details including issuer
result.subject = subject;
result.issuer = cert.getIssuerX500Principal().getName();
result.validFrom = cert.getNotBefore();
result.validTo = cert.getNotAfter();
return result;
}
}
CVE Examples
- CVE-2004-2227 - File dialog truncated long filenames, hiding executable extensions from users.
- CVE-2004-1104 - Browser tricked into displaying incorrect URL in address bar.
- CVE-2005-0143 - Lock icon displayed despite page loading insecure content (mixed content vulnerability).
- CVE-2004-0537 - Wide icon overlayed and obscured address bar, enabling URL spoofing.
- CVE-2005-2271 - JavaScript dialogs didn't clearly indicate which webpage triggered them.
- CVE-2003-1025 - Special URL character truncated user portion, hiding real domain from users.
- CVE-2004-1451 - Null character in URL prevented full URL display in browser.
References
- MITRE Corporation. "CWE-451: User Interface (UI) Misrepresentation of Critical Information." https://cwe.mitre.org/data/definitions/451.html
- OWASP. "Clickjacking Defense Cheat Sheet." https://cheatsheetseries.owasp.org/cheatsheets/Clickjacking_Defense_Cheat_Sheet.html
- The Unicode Consortium. "Unicode Security Mechanisms." https://www.unicode.org/reports/tr39/