UI Discrepancy for Security Feature
Description
UI Discrepancy for Security Feature is a vulnerability where the user interface does not correctly enable or configure a security feature, but the interface provides feedback that causes the user to believe that the feature is in a secure state. When user interface feedback misaligns with actual system behavior, users develop false confidence in security controls. This can occur when users check an encryption checkbox that doesn't actually activate encryption, or when applying access control rules that appear restrictive but are only partially implemented in the backend.
Risk
UI discrepancies for security features create dangerous false confidence in security controls. Users believe they have enabled encryption, access restrictions, or other protections when in fact these features are misconfigured, disabled, or non-functional. Attackers can exploit this gap between perceived and actual security to access data users believed was protected. The risk is compounded because users have no reason to verify that security controls are actually working as displayed. Enterprise environments are particularly vulnerable when administrators configure security policies through interfaces that don't accurately reflect backend implementation.
Solution
Ensure user interface feedback accurately reflects the actual state of security features. Implement comprehensive testing that verifies UI state matches backend security implementation. Use automated testing to confirm security features are correctly enabled when users activate them through the UI. Provide clear error messages when security features cannot be enabled. Implement backend validation that confirms security controls are active and report status accurately to the UI. Consider adding verification mechanisms that users can use to confirm security features are functioning as expected.
Common Consequences
| Impact | Details |
|---|---|
| Other | Scope: Other Varies by Context - The specific impact depends on the security feature that is incorrectly represented. Users may believe data is encrypted when it is not, access controls are in place when they are missing, or audit logging is active when it has failed. |
Example Code
Vulnerable Code
// Vulnerable: UI shows encryption enabled but backend doesn't implement it
class VulnerableSecuritySettings {
constructor() {
this.uiState = {
encryptionEnabled: false,
accessControlEnabled: false
};
}
// Vulnerable: Updates UI state but doesn't enable actual encryption
toggleEncryption() {
this.uiState.encryptionEnabled = !this.uiState.encryptionEnabled;
// Vulnerable: Only updates UI checkbox, backend encryption not implemented
this.updateUI();
// Missing: actual call to enable encryption
// enableDataEncryption(this.uiState.encryptionEnabled);
// User sees "Encryption: Enabled" but data is stored in cleartext
return { success: true, message: "Encryption settings updated" };
}
// Vulnerable: Access control appears enabled but rule application is partial
setAccessControl(rule) {
// UI accepts "restrict ALL" but backend only implements "restrict SOME"
this.uiState.accessControlEnabled = true;
// Vulnerable: Backend has bugs that don't apply all restrictions
// Backend silently ignores certain resource types
applyPartialAccessControl(rule); // Doesn't actually restrict all access
// User sees "Access Control: ALL resources restricted"
return { success: true, message: "Access control enabled for all resources" };
}
}
# Vulnerable: Security configuration panel with UI/backend mismatch
class VulnerableConfigPanel:
def __init__(self):
self.settings_display = {}
def enable_two_factor_auth(self, user_id):
# Vulnerable: Updates UI but 2FA not actually configured
self.settings_display[user_id] = {'2fa_enabled': True}
# Vulnerable: Backend call fails silently
try:
configure_2fa(user_id) # May throw exception
except Exception:
pass # Error swallowed, UI still shows 2FA enabled
# User sees "Two-Factor Authentication: Enabled"
# But 2FA is not actually protecting their account
return True
def enable_audit_logging(self):
# Vulnerable: Shows logging enabled but log destination not configured
self.settings_display['audit_logging'] = True
# Vulnerable: Logging enabled but logs go to /dev/null
# or log destination is not writable
configure_logging(destination=None) # Logs nowhere
# Admin sees "Audit Logging: Active"
# Security events are not being recorded
return "Audit logging is now active"
// Vulnerable: Browser security settings UI mismatch
public class VulnerableSecurityUI {
private boolean displayedSSLEnabled = false;
private boolean displayedCertValidation = true;
// Vulnerable: SSL appears enabled but implementation is flawed
public void enableSSL() {
displayedSSLEnabled = true;
updateUICheckbox("ssl_enabled", true);
// Vulnerable: SSL/TLS configured but with unsafe defaults
SSLContext ctx = SSLContext.getInstance("SSL");
ctx.init(null, new TrustManager[] {
// Vulnerable: Trust manager accepts all certificates
new X509TrustManager() {
public void checkClientTrusted(X509Certificate[] chain, String auth) {}
public void checkServerTrusted(X509Certificate[] chain, String auth) {}
public X509Certificate[] getAcceptedIssuers() { return null; }
}
}, null);
// User sees "SSL: Enabled" with green checkmark
// But all certificates are accepted, providing no real security
}
// Vulnerable: Certificate validation UI doesn't match behavior
public boolean validateCertificate(Certificate cert) {
// UI shows "Certificate Validation: Strict"
// But validation always returns true
return true; // Vulnerable: No actual validation
}
}
Fixed Code
// Fixed: UI accurately reflects backend security state
class SecureSecuritySettings {
constructor() {
this.uiState = {
encryptionEnabled: false,
accessControlEnabled: false
};
}
async toggleEncryption() {
const previousState = this.uiState.encryptionEnabled;
const newState = !previousState;
try {
// Fixed: Actually enable/disable encryption in backend
const result = await enableDataEncryption(newState);
if (result.success) {
// Fixed: Only update UI after backend confirms success
this.uiState.encryptionEnabled = newState;
this.updateUI();
// Fixed: Verify encryption is actually working
const verification = await verifyEncryptionActive();
if (!verification.active) {
throw new Error('Encryption verification failed');
}
return { success: true, message: "Encryption enabled and verified" };
} else {
throw new Error(result.error);
}
} catch (error) {
// Fixed: Rollback UI on failure
this.uiState.encryptionEnabled = previousState;
this.updateUI();
// Fixed: Show clear error to user
return {
success: false,
message: `Failed to change encryption: ${error.message}`
};
}
}
async setAccessControl(rule) {
try {
// Fixed: Apply access control and verify
const result = await applyAccessControl(rule);
if (!result.fullyApplied) {
// Fixed: Report partial application clearly
const unprotected = result.unprotectedResources.join(', ');
return {
success: false,
message: `Access control could not be applied to: ${unprotected}`
};
}
// Fixed: Verify access control is actually blocking
const verification = await testAccessControl(rule);
if (!verification.allBlocked) {
throw new Error('Access control verification failed');
}
this.uiState.accessControlEnabled = true;
this.updateUI();
return { success: true, message: "Access control enabled and verified" };
} catch (error) {
return {
success: false,
message: `Access control failed: ${error.message}`
};
}
}
}
# Fixed: Security configuration with accurate UI feedback
class SecureConfigPanel:
def __init__(self):
self.settings_display = {}
def enable_two_factor_auth(self, user_id):
try:
# Fixed: Configure 2FA and verify
result = configure_2fa(user_id)
if not result.success:
# Fixed: Report failure clearly
return {
'success': False,
'message': f'2FA configuration failed: {result.error}'
}
# Fixed: Verify 2FA is actually required for login
verification = verify_2fa_required(user_id)
if not verification.enforced:
# Fixed: Rollback on verification failure
remove_2fa(user_id)
return {
'success': False,
'message': '2FA could not be verified as active'
}
# Fixed: Only update UI after verification
self.settings_display[user_id] = {'2fa_enabled': True}
return {
'success': True,
'message': '2FA enabled and verified'
}
except Exception as e:
# Fixed: Clear error reporting
logger.error(f"2FA configuration error: {e}")
return {
'success': False,
'message': 'Failed to enable 2FA. Please contact support.'
}
def enable_audit_logging(self):
try:
# Fixed: Verify log destination is writable
destination = get_log_destination()
if not destination or not is_writable(destination):
return {
'success': False,
'message': 'Audit log destination not configured or not writable'
}
# Fixed: Enable logging and verify
configure_logging(destination=destination)
# Fixed: Write test entry and verify it was logged
test_id = write_audit_test_entry()
if not verify_audit_entry_exists(test_id):
return {
'success': False,
'message': 'Audit logging verification failed'
}
self.settings_display['audit_logging'] = True
return {
'success': True,
'message': f'Audit logging active, writing to {destination}'
}
except Exception as e:
logger.error(f"Audit logging error: {e}")
return {
'success': False,
'message': 'Failed to enable audit logging'
}
// Fixed: Accurate security UI with proper implementation
public class SecureSecurityUI {
private boolean sslEnabled = false;
private boolean certValidationEnabled = true;
public boolean enableSSL() throws SecurityException {
try {
// Fixed: Configure SSL with secure defaults
SSLContext ctx = SSLContext.getInstance("TLSv1.3");
// Fixed: Use proper certificate validation
TrustManagerFactory tmf = TrustManagerFactory.getInstance(
TrustManagerFactory.getDefaultAlgorithm()
);
tmf.init((KeyStore) null); // Use system trust store
ctx.init(null, tmf.getTrustManagers(), new SecureRandom());
// Fixed: Verify SSL is working correctly
HttpsURLConnection.setDefaultSSLSocketFactory(ctx.getSocketFactory());
// Fixed: Test connection to verify SSL works
if (!testSSLConnection()) {
throw new SecurityException("SSL verification failed");
}
sslEnabled = true;
updateUICheckbox("ssl_enabled", true);
return true;
} catch (Exception e) {
// Fixed: Clear error, UI reflects failure
sslEnabled = false;
updateUICheckbox("ssl_enabled", false);
showError("Failed to enable SSL: " + e.getMessage());
return false;
}
}
public ValidationResult validateCertificate(Certificate cert) {
// Fixed: Actual certificate validation
try {
X509Certificate x509 = (X509Certificate) cert;
// Fixed: Check expiration
x509.checkValidity();
// Fixed: Verify against trust store
TrustManagerFactory tmf = TrustManagerFactory.getInstance(
TrustManagerFactory.getDefaultAlgorithm()
);
tmf.init((KeyStore) null);
for (TrustManager tm : tmf.getTrustManagers()) {
if (tm instanceof X509TrustManager) {
((X509TrustManager) tm).checkServerTrusted(
new X509Certificate[]{x509}, "RSA"
);
}
}
return ValidationResult.valid();
} catch (CertificateExpiredException e) {
// Fixed: Report specific failure
return ValidationResult.invalid("Certificate expired");
} catch (CertificateNotYetValidException e) {
return ValidationResult.invalid("Certificate not yet valid");
} catch (CertificateException e) {
return ValidationResult.invalid("Certificate validation failed: " + e.getMessage());
}
}
}
CVE Examples
- CVE-1999-1446 - Browser's 'Clear History' option did not actually clear visited URLs list, leaving browsing history accessible despite UI indicating it was cleared.
References
- MITRE Corporation. "CWE-446: UI Discrepancy for Security Feature." https://cwe.mitre.org/data/definitions/446.html
- OWASP. "Testing for Client-side Security." https://owasp.org/www-project-web-security-testing-guide/