Public Data Assigned to Private Array-Typed Field

Description

Public Data Assigned to Private Array-Typed Field is a vulnerability where a class assigns publicly provided data directly to a private array field without making a defensive copy. This provides the caller equivalent access to that array as if it were public, since they retain a reference to the same array object. Any modifications the caller makes to their original array will be reflected in the private field, and vice versa, completely undermining the intended encapsulation and data protection.

Risk

Direct assignment of external arrays to private fields creates serious integrity and security vulnerabilities. Callers retain the ability to modify supposedly private data at any time after the assignment. In security contexts, this allows attackers to modify permission arrays, role lists, or configuration data after passing validation. The timing aspect makes this particularly dangerous - data may be valid at assignment time but modified later to bypass security checks. Collections of sensitive data like allowed operations, trusted hosts, or user permissions become vulnerable to unauthorized modification.

Solution

Never assign external array references directly to private fields. Always create defensive copies of arrays received from external sources. Use clone() or Arrays.copyOf() to create independent copies. For collections, use copy constructors or Collections.copy(). Validate the array contents before storing. Consider using immutable data structures when modification is not needed. Apply the same defensive copying when returning private arrays (related to CWE-495). Document the copying behavior in API documentation.

Common Consequences

ImpactDetails
IntegrityScope: Integrity

Modify Application Data - The contents of the private array can be modified from outside the intended scope, compromising data integrity since external code retains a reference to the internal data.
Access ControlScope: Access Control

Bypass Protection Mechanism - Security checks performed at assignment time can be bypassed by modifying the array contents after assignment.

Example Code

Vulnerable Code

// Vulnerable: Direct assignment of external array to private field
public class VulnerableUserProfile {
    private String[] permissions;
    private String[] trustedDomains;
    private byte[] encryptionKey;

    // Vulnerable: Direct assignment
    public void setPermissions(String[] permissions) {
        this.permissions = permissions;  // Caller retains reference!
    }

    // Vulnerable: Direct assignment
    public void setTrustedDomains(String[] domains) {
        this.trustedDomains = domains;  // External reference stored!
    }

    // Vulnerable: Direct assignment of sensitive data
    public void setEncryptionKey(byte[] key) {
        this.encryptionKey = key;  // Key can be modified externally!
    }

    public boolean hasPermission(String perm) {
        for (String p : permissions) {
            if (p.equals(perm)) {
                return true;
            }
        }
        return false;
    }
}

// Attacker exploits the vulnerability
public class PermissionExploit {
    public void exploit() {
        VulnerableUserProfile profile = new VulnerableUserProfile();

        // Pass array and keep reference
        String[] perms = {"READ"};
        profile.setPermissions(perms);

        // Validation passes - user only has READ
        System.out.println(profile.hasPermission("ADMIN")); // false

        // Later: modify the array we still hold
        perms[0] = "ADMIN";

        // Now the private field is corrupted!
        System.out.println(profile.hasPermission("ADMIN")); // true!
    }

    public void exploitEncryptionKey() {
        VulnerableUserProfile profile = new VulnerableUserProfile();

        byte[] key = generateValidKey();
        profile.setEncryptionKey(key);

        // Later: corrupt the key
        Arrays.fill(key, (byte) 0);  // Key is now all zeros!
        // Profile's encryption is broken
    }
}

// Vulnerable: Constructor accepting arrays
public class VulnerableConfig {
    private String[] allowedHosts;
    private int[] portNumbers;
    private Object[] handlers;

    // Vulnerable: Constructor stores references directly
    public VulnerableConfig(String[] hosts, int[] ports, Object[] handlers) {
        this.allowedHosts = hosts;      // Vulnerable
        this.portNumbers = ports;        // Vulnerable
        this.handlers = handlers;        // Vulnerable
    }

    public boolean isAllowedHost(String host) {
        return Arrays.asList(allowedHosts).contains(host);
    }
}

// Exploit through constructor
public class ConfigExploit {
    public void exploit() {
        String[] hosts = {"trusted.com"};
        int[] ports = {443};
        Object[] handlers = {new SafeHandler()};

        VulnerableConfig config = new VulnerableConfig(hosts, ports, handlers);

        // Modify after construction
        hosts[0] = "evil.com";           // Add malicious host
        ports[0] = 22;                   // Change to SSH port
        handlers[0] = new MaliciousHandler();  // Replace handler

        // Config is now compromised
        config.isAllowedHost("evil.com"); // true!
    }
}
// Vulnerable: C++ storing external array reference
class VulnerableSettings {
private:
    int* allowedPorts;
    size_t portCount;
    char* secretKey;

public:
    // Vulnerable: Stores pointer to external array
    void setAllowedPorts(int* ports, size_t count) {
        this->allowedPorts = ports;  // External pointer stored!
        this->portCount = count;
    }

    // Vulnerable: Stores pointer to external data
    void setSecretKey(char* key) {
        this->secretKey = key;  // External pointer stored!
    }

    bool isPortAllowed(int port) {
        for (size_t i = 0; i < portCount; i++) {
            if (allowedPorts[i] == port) {
                return true;
            }
        }
        return false;
    }
};

// Exploit
void exploit() {
    VulnerableSettings settings;

    int ports[] = {80, 443};
    settings.setAllowedPorts(ports, 2);

    // Modify the external array
    ports[0] = 22;  // Now SSH is allowed!

    char key[] = "secret123";
    settings.setSecretKey(key);

    // Corrupt the key
    memset(key, 0, sizeof(key));  // Key is now empty!
}

Fixed Code

// Fixed: Defensive copying on assignment
public class SecureUserProfile {
    private String[] permissions;
    private String[] trustedDomains;
    private byte[] encryptionKey;

    // Fixed: Create defensive copy
    public void setPermissions(String[] permissions) {
        if (permissions == null) {
            this.permissions = new String[0];
        } else {
            // Fixed: Clone the array
            this.permissions = permissions.clone();
        }
    }

    // Fixed: Defensive copy with validation
    public void setTrustedDomains(String[] domains) {
        if (domains == null) {
            this.trustedDomains = new String[0];
            return;
        }

        // Fixed: Copy and validate each element
        this.trustedDomains = new String[domains.length];
        for (int i = 0; i < domains.length; i++) {
            if (!isValidDomain(domains[i])) {
                throw new IllegalArgumentException("Invalid domain: " + domains[i]);
            }
            this.trustedDomains[i] = domains[i];
        }
    }

    // Fixed: Defensive copy and clear source for sensitive data
    public void setEncryptionKey(byte[] key) {
        if (key == null || key.length < 16) {
            throw new IllegalArgumentException("Invalid key");
        }
        // Fixed: Copy the key
        this.encryptionKey = key.clone();
        // Clear the source array to prevent external access
        Arrays.fill(key, (byte) 0);
    }

    public boolean hasPermission(String perm) {
        for (String p : permissions) {
            if (p.equals(perm)) {
                return true;
            }
        }
        return false;
    }

    private boolean isValidDomain(String domain) {
        return domain != null &&
               domain.matches("^[a-zA-Z0-9][a-zA-Z0-9.-]*[a-zA-Z0-9]$");
    }
}

// Fixed: Constructor with defensive copying
public class SecureConfig {
    private final String[] allowedHosts;
    private final int[] portNumbers;
    private final Object[] handlers;

    // Fixed: Constructor creates defensive copies
    public SecureConfig(String[] hosts, int[] ports, Object[] handlers) {
        // Fixed: Clone arrays
        this.allowedHosts = hosts != null ? hosts.clone() : new String[0];
        this.portNumbers = ports != null ? ports.clone() : new int[0];

        // Fixed: Deep copy for mutable objects
        if (handlers != null) {
            this.handlers = new Object[handlers.length];
            for (int i = 0; i < handlers.length; i++) {
                // Validate handler type before storing
                if (handlers[i] instanceof Handler) {
                    this.handlers[i] = handlers[i];  // Immutable handler reference
                } else {
                    throw new IllegalArgumentException("Invalid handler type");
                }
            }
        } else {
            this.handlers = new Object[0];
        }
    }

    public boolean isAllowedHost(String host) {
        return Arrays.asList(allowedHosts).contains(host);
    }

    // Fixed: Also return defensive copy
    public String[] getAllowedHosts() {
        return allowedHosts.clone();
    }
}

// Fixed: Using Collections instead of arrays
public class SecureConfigWithCollections {
    private final List<String> allowedHosts;
    private final Set<Integer> allowedPorts;

    public SecureConfigWithCollections(List<String> hosts, Set<Integer> ports) {
        // Fixed: Create unmodifiable copies
        this.allowedHosts = hosts != null
            ? Collections.unmodifiableList(new ArrayList<>(hosts))
            : Collections.emptyList();

        this.allowedPorts = ports != null
            ? Collections.unmodifiableSet(new HashSet<>(ports))
            : Collections.emptySet();
    }

    public boolean isAllowedHost(String host) {
        return allowedHosts.contains(host);
    }

    public boolean isAllowedPort(int port) {
        return allowedPorts.contains(port);
    }
}
// Fixed: C++ with defensive copying
class SecureSettings {
private:
    std::vector<int> allowedPorts;
    std::string secretKey;

public:
    // Fixed: Copy data into internal storage
    void setAllowedPorts(const int* ports, size_t count) {
        allowedPorts.clear();
        if (ports != nullptr && count > 0) {
            // Fixed: Copy elements into vector
            allowedPorts.assign(ports, ports + count);
        }
    }

    // Fixed: Accept by const reference, store copy
    void setAllowedPorts(const std::vector<int>& ports) {
        allowedPorts = ports;  // Copy assignment
    }

    // Fixed: Copy string data
    void setSecretKey(const char* key) {
        if (key != nullptr) {
            secretKey = std::string(key);  // Creates copy
        } else {
            secretKey.clear();
        }
    }

    // Fixed: Accept string by value (caller can move if they want)
    void setSecretKey(std::string key) {
        secretKey = std::move(key);  // Take ownership
    }

    bool isPortAllowed(int port) const {
        return std::find(allowedPorts.begin(), allowedPorts.end(), port)
               != allowedPorts.end();
    }
};

// Fixed: Using smart pointers and RAII
class SecureConfig {
private:
    std::unique_ptr<std::vector<std::string>> hosts;

public:
    // Fixed: Create owned copy
    void setHosts(const std::vector<std::string>& newHosts) {
        hosts = std::make_unique<std::vector<std::string>>(newHosts);
    }

    // Fixed: Const access only
    const std::vector<std::string>& getHosts() const {
        static const std::vector<std::string> empty;
        return hosts ? *hosts : empty;
    }
};

CVE Examples

No specific CVEs are listed in the MITRE database for this CWE. However, the vulnerability pattern is documented in:

  • CERT Secure Coding Standards
  • Java security guidelines on encapsulation

References

  1. MITRE Corporation. "CWE-496: Public Data Assigned to Private Array-Typed Field." https://cwe.mitre.org/data/definitions/496.html
  2. CERT Oracle Secure Coding Standard for Java. "OBJ06-J. Defensively copy mutable inputs and mutable internal components."
  3. Bloch, Joshua. "Effective Java" - Item 50: Make defensive copies when needed.