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

ImpactDetails
ConfidentialityScope: Confidentiality

Read Application Data - Public declaration allows any code with object access to read sensitive values directly.
IntegrityScope: Integrity

Modify Application Data - Public declaration allows any code to alter critical values, potentially bypassing security checks.
OtherScope: 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

  1. MITRE Corporation. "CWE-766: Critical Data Element Declared Public." https://cwe.mitre.org/data/definitions/766.html
  2. CERT C++ Coding Standard. "OOP03-CPP. Use accessor and mutator methods for data encapsulation."
  3. Oracle Java Secure Coding Guidelines. "SECCODE-4: Restrict privileges and expose minimal data."