Array als public, final und static deklariert
Beschreibung
Array als public, final und static deklariert ist eine Sicherheitsschwachstelle in Java, bei der eine Klasse ein Array mit den Modifikatoren public, static und final deklariert. Während das final-Schlüsselwort die Neuzuweisung der Array-Referenz selbst verhindert, schützt es nicht die Inhalte des Arrays vor Modifikation. Da das Array öffentlich zugänglich ist, kann jeder Code - einschließlich bösartigen Codes - die im Array gespeicherten Werte modifizieren. Dies ist fast immer ein Sicherheitsfehler, da der final-Modifikator ein falsches Gefühl der Unveränderlichkeit erzeugt, während die Array-Inhalte vollständig veränderbar bleiben.
Risiko
Öffentliche statische finale Arrays exponieren veränderbaren Zustand für allen Code in der Anwendung und potenziell für externe Angreifer. In Applet- oder Mobile-Code-Umgebungen kann bösartiger Code die Array-Inhalte modifizieren, um das Anwendungsverhalten zu ändern. Konfigurations-Arrays können geändert werden, um Sicherheitseinstellungen zu umgehen. URL-Listen, Berechtigungs-Arrays oder erlaubte Werte können manipuliert werden. Konstanten-Arrays, die feste Werte definieren sollen, werden unzuverlässig, wenn externer Code sie modifiziert. Die irreführende Verwendung von final erzeugt eine falsche Annahme der Unveränderlichkeit und führt Entwickler dazu, Array-Inhalten zu vertrauen, die möglicherweise manipuliert wurden.
Lösung
Machen Sie das Array privat und stellen Sie eine öffentliche Methode bereit, die eine Kopie des Arrays oder eine unveränderliche Ansicht zurückgibt. Alternativ verwenden Sie eine unveränderliche Collection wie Collections.unmodifiableList() oder List.of() (Java 9+). Wenn das Array zugänglich sein muss, stellen Sie nur einen Getter bereit, der eine defensive Kopie zurückgibt. Exponieren Sie niemals veränderbaren internen Zustand durch öffentliche statische Felder. Erwägen Sie die Verwendung von Enums für feste Wertemengen. Dokumentieren Sie, dass zurückgegebene Arrays Kopien sind, wenn dieses Design gewählt wird.
Häufige Auswirkungen
| Auswirkung | Details |
|---|---|
| Integrität | Bereich: Integrität Anwendungsdaten modifizieren - Externer Code kann Array-Inhalte modifizieren und angeblich konstante Werte ändern sowie das Anwendungsverhalten verändern. |
| Zugriffskontrolle | Bereich: Zugriffskontrolle Schutzmechanismus umgehen - Sicherheitsrelevante Arrays wie erlaubte URLs, Berechtigungen oder vertrauenswürdige Hosts können modifiziert werden, um Zugriffskontrollen zu umgehen. |
Beispielcode
Verwundbarer Code
// Verwundbar: Öffentliches statisches finales Array
public final class VulnerableConfig extends Applet {
// Verwundbar: Array-Inhalte können trotz 'final' modifiziert werden
public static final String[] ALLOWED_HOSTS = {
"trusted.example.com",
"secure.example.org"
};
// Verwundbar: URL-Array öffentlich exponiert
public static final URL[] RESOURCE_URLS;
static {
try {
RESOURCE_URLS = new URL[] {
new URL("https://api.example.com"),
new URL("https://data.example.com")
};
} catch (MalformedURLException e) {
throw new RuntimeException(e);
}
}
// Verwundbar: Berechtigungs-Array
public static final Permission[] REQUIRED_PERMISSIONS = {
new FilePermission("/safe/path", "read"),
new SocketPermission("localhost:8080", "connect")
};
}
// Bösartiger Code kann die Arrays modifizieren:
public class Attacker {
public static void exploit() {
// Erlaubte Hosts auf Angreifer-Server ändern
VulnerableConfig.ALLOWED_HOSTS[0] = "evil.attacker.com";
// Ressourcen-URLs umleiten
try {
VulnerableConfig.RESOURCE_URLS[0] =
new URL("https://malicious.attacker.com/steal-data");
} catch (MalformedURLException e) {}
// Berechtigungen abschwächen
VulnerableConfig.REQUIRED_PERMISSIONS[0] =
new FilePermission("/-", "read,write,delete");
}
}
// Verwundbar: Konstanten die nicht wirklich konstant sind
public class VulnerableConstants {
// Verwundbar: Sieht unveränderlich aus, ist es aber nicht
public static final int[] MAGIC_NUMBERS = {42, 17, 256};
// Verwundbar: Konfigurationswerte
public static final String[] VALID_ROLES = {
"user", "admin", "moderator"
};
// Verwundbar: Sensible Daten
public static final byte[] ENCRYPTION_IV = {
0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F
};
}
// Angriffsvektor
public class ConstantAttacker {
public static void corrupt() {
// "Konstanten" modifizieren
VulnerableConstants.MAGIC_NUMBERS[0] = 0;
// Nicht autorisierte Rolle hinzufügen
VulnerableConstants.VALID_ROLES[1] = "superadmin";
// Verschlüsselung abschwächen
Arrays.fill(VulnerableConstants.ENCRYPTION_IV, (byte) 0);
}
}
// Verwundbar: Öffentliches veränderbares Feld in Sicherheitsklasse
public class VulnerableSecurityManager {
// Verwundbar: Vertrauenswürdige Ursprünge können modifiziert werden
public static final String[] TRUSTED_ORIGINS = {
"https://app.example.com",
"https://admin.example.com"
};
public boolean isOriginTrusted(String origin) {
for (String trusted : TRUSTED_ORIGINS) {
if (trusted.equals(origin)) {
return true;
}
}
return false;
}
}
Lösungscode
// Behoben: Privates Array mit defensivem Kopier-Getter
public final class SecureConfig {
// Behoben: Array privat machen
private static final String[] ALLOWED_HOSTS = {
"trusted.example.com",
"secure.example.org"
};
// Behoben: Defensive Kopie zurückgeben
public static String[] getAllowedHosts() {
return ALLOWED_HOSTS.clone();
}
// Behoben: Oder als unveränderliche Liste zurückgeben
public static List<String> getAllowedHostsList() {
return Collections.unmodifiableList(Arrays.asList(ALLOWED_HOSTS));
}
// Behoben: Privates URL-Array
private static final URL[] RESOURCE_URLS;
static {
try {
RESOURCE_URLS = new URL[] {
new URL("https://api.example.com"),
new URL("https://data.example.com")
};
} catch (MalformedURLException e) {
throw new RuntimeException(e);
}
}
public static URL[] getResourceUrls() {
return RESOURCE_URLS.clone();
}
// Behoben: Spezifischen Index prüfen ohne Array zu exponieren
public static URL getResourceUrl(int index) {
if (index < 0 || index >= RESOURCE_URLS.length) {
throw new IndexOutOfBoundsException();
}
return RESOURCE_URLS[index];
}
}
// Behoben: Unveränderliche Collections verwenden (Java 9+)
public final class SecureConstants {
// Behoben: Unveränderliche Liste
public static final List<String> VALID_ROLES =
List.of("user", "admin", "moderator");
// Behoben: Unveränderliches Set
public static final Set<String> ALLOWED_EXTENSIONS =
Set.of("txt", "pdf", "jpg", "png");
// Behoben: Für primitive Arrays privat + Getter verwenden
private static final int[] MAGIC_NUMBERS = {42, 17, 256};
public static int[] getMagicNumbers() {
return MAGIC_NUMBERS.clone();
}
// Behoben: Für sensible Byte-Arrays
private static final byte[] ENCRYPTION_IV = {
0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F
};
public static byte[] getEncryptionIv() {
return ENCRYPTION_IV.clone();
}
}
// Behoben: Enum für feste Wertemengen verwenden
public enum Role {
USER("user"),
ADMIN("admin"),
MODERATOR("moderator");
private final String name;
Role(String name) {
this.name = name;
}
public String getName() {
return name;
}
public static Role fromName(String name) {
for (Role role : values()) {
if (role.name.equals(name)) {
return role;
}
}
throw new IllegalArgumentException("Unbekannte Rolle: " + name);
}
}
// Behoben: Security-Manager mit unveränderlichen vertrauenswürdigen Ursprüngen
public class SecureSecurityManager {
// Behoben: Unveränderliches Set
private static final Set<String> TRUSTED_ORIGINS =
Collections.unmodifiableSet(new HashSet<>(Arrays.asList(
"https://app.example.com",
"https://admin.example.com"
)));
// Java 9+ Version
// private static final Set<String> TRUSTED_ORIGINS =
// Set.of("https://app.example.com", "https://admin.example.com");
public boolean isOriginTrusted(String origin) {
return TRUSTED_ORIGINS.contains(origin);
}
// Gibt unveränderliche Ansicht zurück
public Set<String> getTrustedOrigins() {
return TRUSTED_ORIGINS;
}
}
// Behoben: Konfigurationsklasse mit defensivem Kopieren
public class SecureConfiguration {
private final List<String> allowedHosts;
private final Map<String, String> settings;
public SecureConfiguration(List<String> hosts, Map<String, String> settings) {
// Defensive Kopie bei Konstruktion
this.allowedHosts = new ArrayList<>(hosts);
this.settings = new HashMap<>(settings);
}
public List<String> getAllowedHosts() {
// Defensive Kopie bei Abruf
return new ArrayList<>(allowedHosts);
}
// Oder unveränderliche Ansicht zurückgeben
public List<String> getAllowedHostsView() {
return Collections.unmodifiableList(allowedHosts);
}
public Map<String, String> getSettings() {
return Collections.unmodifiableMap(settings);
}
}
// Behoben: Wrapper-Klasse für Byte-Arrays verwenden
public final class ImmutableByteArray {
private final byte[] data;
public ImmutableByteArray(byte[] source) {
this.data = source.clone(); // Defensive Kopie
}
public byte[] toByteArray() {
return data.clone(); // Kopie zurückgeben
}
public int length() {
return data.length;
}
public byte get(int index) {
return data[index];
}
// Keine Setter-Methoden - wirklich unveränderlich
}
// Verwendung
public class CryptoConfig {
public static final ImmutableByteArray DEFAULT_IV =
new ImmutableByteArray(new byte[] {
0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F
});
}
CVE-Beispiele
Keine spezifischen CVEs werden dieser CWE direkt zugeordnet, obwohl das Schwachstellenmuster in verschiedenen Java-Anwendungen und Applets identifiziert wurde.
Referenzen
- MITRE Corporation. "CWE-582: Array Declared Public, Final, and Static." https://cwe.mitre.org/data/definitions/582.html
- Joshua Bloch. "Effective Java" - Item 15: Minimize the accessibility of classes and members.
- CERT. "OBJ01-J. Limit accessibility of fields."