Critical Data Element Declared Public
Description
Critical Data Element Declared Public is an encapsulation vulnerability where software declares a critical variable, field, or member to be public when the intended security policy requires it to be private. This typically occurs in object-oriented languages where access modifiers control visibility of class members. When security-sensitive data like passwords, cryptographic keys, or internal state variables are declared public, any code with access to the object can read or modify these values, bypassing intended access controls and security boundaries.
Risk
Declaring critical data elements as public creates significant security and maintainability risks. Attackers or malicious code with access to the object can directly read sensitive information (passwords, tokens, keys) or modify critical state variables to bypass security checks. This violates the principle of information hiding and breaks encapsulation, making it difficult to enforce security invariants. The issue also impacts maintainability by creating tight coupling between components, making security fixes harder to implement. Even if exploitation isn't immediately obvious, the exposed data surface increases attack opportunities.
Solution
Always declare security-critical variables as private (or protected if inheritance requires it). Use accessor (getter) and mutator (setter) methods that can enforce validation and access control. Mark fields as final/const when the value shouldn't change after initialization. Consider making sensitive data static and final where appropriate. Apply the principle of least privilege to data access—only expose what's absolutely necessary through controlled interfaces. Use automated static analysis tools to detect public declarations of sensitive data types. Follow secure coding standards specific to your language.
Common Consequences
| Impact | Details |
|---|---|
| Confidentiality | Scope: Confidentiality Read Application Data - Public declaration allows any code with object access to read sensitive values directly. |
| Integrity | Scope: Integrity Modify Application Data - Public declaration allows any code to alter critical values, potentially bypassing security checks. |
| Other | Scope: Other Reduce Maintainability - Makes it difficult to change internal implementation without affecting dependent code. |
Example Code
Vulnerable Code
// Vulnerable: Password declared public
class UserAccount {
public:
char* username;
char* password; // Vulnerable: Should be private
int accountBalance; // Vulnerable: Should be private
bool isAdmin; // Vulnerable: Critical security flag public
void authenticate(const char* inputPassword) {
if (strcmp(password, inputPassword) == 0) {
// Authenticated
}
}
};
void exploit() {
UserAccount user;
// Attacker can directly access sensitive data
printf("Password: %s\n", user.password); // Direct read
user.isAdmin = true; // Privilege escalation
user.accountBalance = 999999; // Data manipulation
}
// Vulnerable: Java class with public sensitive fields
public class BankAccount {
public String accountNumber;
public double balance; // Vulnerable: Should be private
public String pin; // Vulnerable: Critical credential public
public String[] transactionHistory; // Vulnerable: Sensitive data
public BankAccount(String accountNumber, String pin) {
this.accountNumber = accountNumber;
this.pin = pin;
this.balance = 0;
}
// Vulnerable: No access control on critical data
}
// Exploitation
class AttackerCode {
public void steal(BankAccount victim) {
System.out.println("PIN: " + victim.pin); // Direct credential access
victim.balance = 0; // Direct manipulation
}
}
// Vulnerable: C# class with public cryptographic material
public class SecureSession
{
public byte[] encryptionKey; // Vulnerable: Crypto key public
public byte[] sessionToken; // Vulnerable: Auth token public
public string userId;
public DateTime expiration;
public SecureSession(string userId)
{
this.userId = userId;
this.encryptionKey = GenerateKey();
this.sessionToken = GenerateToken();
}
}
// Attacker can read keys and tokens directly
var session = GetUserSession();
byte[] stolenKey = session.encryptionKey;
byte[] stolenToken = session.sessionToken;
Fixed Code
// Fixed: Proper encapsulation with private members
class UserAccount {
private:
std::string username;
std::string password; // Private: Protected from direct access
int accountBalance; // Private: Controlled access
bool isAdmin; // Private: Security flag protected
public:
UserAccount(const std::string& user, const std::string& pass)
: username(user), password(pass), accountBalance(0), isAdmin(false) {}
// Controlled accessors with validation
std::string getUsername() const { return username; }
bool authenticate(const std::string& inputPassword) const {
// Use constant-time comparison for security
return password == inputPassword; // Simplified; use secure comparison
}
int getBalance() const { return accountBalance; }
bool deposit(int amount) {
if (amount > 0) {
accountBalance += amount;
return true;
}
return false;
}
// No public setter for isAdmin - controlled by system only
bool checkAdmin() const { return isAdmin; }
};
// Fixed: Proper encapsulation in Java
public class BankAccount {
private final String accountNumber; // Immutable identifier
private double balance;
private String pin;
private final List<String> transactionHistory;
public BankAccount(String accountNumber, String pin) {
this.accountNumber = accountNumber;
this.pin = pin;
this.balance = 0;
this.transactionHistory = new ArrayList<>();
}
// Controlled accessor - returns copy for sensitive collection
public String getAccountNumber() {
return accountNumber;
}
public double getBalance() {
return balance;
}
// No getter for PIN - only validation method
public boolean validatePin(String inputPin) {
// Use constant-time comparison
return MessageDigest.isEqual(
pin.getBytes(StandardCharsets.UTF_8),
inputPin.getBytes(StandardCharsets.UTF_8)
);
}
// Controlled modification with validation
public synchronized void deposit(double amount) {
if (amount > 0) {
balance += amount;
transactionHistory.add("Deposit: " + amount);
}
}
// Return defensive copy of transaction history
public List<String> getTransactionHistory() {
return new ArrayList<>(transactionHistory);
}
}
// Fixed: C# with proper encapsulation and immutability
public class SecureSession
{
private readonly byte[] _encryptionKey;
private readonly byte[] _sessionToken;
private readonly string _userId;
private readonly DateTime _expiration;
public SecureSession(string userId)
{
_userId = userId;
_encryptionKey = GenerateKey();
_sessionToken = GenerateToken();
_expiration = DateTime.UtcNow.AddHours(1);
}
public string UserId => _userId;
public DateTime Expiration => _expiration;
// No direct access to keys - provide operations instead
public byte[] Encrypt(byte[] data)
{
// Use internal key for encryption
using var aes = Aes.Create();
aes.Key = _encryptionKey;
// ... perform encryption
return encryptedData;
}
public bool ValidateToken(byte[] providedToken)
{
return CryptographicOperations.FixedTimeEquals(_sessionToken, providedToken);
}
// Secure disposal of sensitive data
public void Dispose()
{
CryptographicOperations.ZeroMemory(_encryptionKey);
CryptographicOperations.ZeroMemory(_sessionToken);
}
}
CVE Examples
- CVE-2010-3860: JBoss MicroContainer allowed remote attackers to read system properties like user name and home directory via variables declared public in class definitions.
References
- MITRE Corporation. "CWE-766: Critical Data Element Declared Public." https://cwe.mitre.org/data/definitions/766.html
- CERT C++ Coding Standard. "OOP03-CPP. Use accessor and mutator methods for data encapsulation."
- Oracle Java Secure Coding Guidelines. "SECCODE-4: Restrict privileges and expose minimal data."