Abhängigkeit von Paket-Ebenen-Geltungsbereich

Beschreibung

Abhängigkeit von Paket-Ebenen-Geltungsbereich ist eine Schwachstelle, bei der Code Java-Paketzugriffskontrollen fälschlicherweise als Sicherheitsgrenzen behandelt. Java-Pakete sind nicht inhärent geschlossen; sie existieren primär als Komfortfunktion für Entwickler zur Codeorganisation und Vermeidung versehentlichen Zugriffs, nicht als Sicherheitsmechanismus. Jeder Code kann sich als Teil jedes Pakets deklarieren, und da verteilter Java-Code durch Klassen in anderen JAR-Dateien, die dasselbe Paket beanspruchen, erweitert werden kann, bietet das Verlassen auf Paket-Geltungsbereich für Sicherheit keinen echten Schutz.

Risiko

Paket-Ebenen-Geltungsbereich bietet keine Sicherheitsgarantien in Java-Anwendungen. Angreifer können eigene Klassen erstellen, die sich als Teil eines Zielpakets deklarieren, und erhalten Zugriff auf alle paket-privaten Mitglieder. Dies ist besonders gefährlich für sensible Daten oder Methoden, die als geschützt angenommen wurden. In verteilten Umgebungen können mehrere JAR-Dateien Klassen zum selben Paket beitragen, was bösartigem Code den Zugriff auf vermeintlich geschützte Mitglieder ermöglicht. Die Schwachstelle ist besonders schwerwiegend, wenn Paket-Geltungsbereich zum Schutz sicherheitskritischer Daten wie Anmeldedaten, kryptographischer Schlüssel oder Autorisierungstoken verwendet wird.

Lösung

Verlassen Sie sich niemals auf Paket-Ebenen-Geltungsbereich für Sicherheit. Machen Sie sensible Daten wann immer möglich private und final. Verwenden Sie ordnungsgemäße Kapselung mit privaten Feldern und kontrollierten Zugriffsmethoden. Für sicherheitskritischen Code verwenden Sie Javas SecurityManager und Zugriffskontrollmechanismen. Erwägen Sie versiegelte Pakete (Java 15+), um externen Code vom Beitritt zum Paket zu verhindern. Implementieren Sie ordnungsgemäße Authentifizierung und Autorisierung auf API-Ebene anstatt sich auf Sprachebenen-Zugriffskontrollen zu verlassen. Verwenden Sie Verschlüsselung für sensible Daten anstatt sich auf Sichtbarkeitsmodifikatoren zu verlassen.

Häufige Auswirkungen

AuswirkungDetails
VertraulichkeitUmfang: Vertraulichkeit

Anwendungsdaten lesen - Da Java-Pakete durch externen Code erweitert werden können, können paket-private Daten von bösartigen Klassen abgerufen werden, die sich als Teil des Pakets deklarieren.
IntegritätUmfang: Integrität

Anwendungsdaten modifizieren - Paket-private Felder und Methoden können von unbefugtem Code, der dem Paket beitritt, abgerufen und modifiziert werden.

Beispielcode

Anfälliger Code

// Anfällig: Verlassen auf Paket-Geltungsbereich für Sicherheit
package com.banking.security;

// Anfällig: Paket-private Klasse als geschützt angenommen
class InternalSecurityManager {
    // Anfällig: Paket-privates Feld als sicher angenommen
    static String masterKey = "super-geheimer-schlüssel-12345";

    // Anfällig: Paket-private Methode als geschützt angenommen
    static boolean validateAdminAccess(String token) {
        return token.equals(masterKey);
    }

    // Paket-privates Hilfsmittel - Angreifer kann dies aufrufen
    static void resetAllSessions() {
        // Gefährliche Operation als durch Paket-Geltungsbereich geschützt angenommen
        SessionStore.invalidateAll();
    }
}

// Anfällig: Sensible Daten mit Paket-Geltungsbereich
package com.banking.data;

class CustomerDatabase {
    // Anfällig: Paket-Geltungsbereich für sensible Daten
    String[] customerSSNs = new String[1000];
    String[] accountNumbers = new String[1000];

    // Anfällig: Paket-private Methode legt Daten offen
    String getSSN(int customerId) {
        return customerSSNs[customerId];
    }
}
// Angreifercode - kann sich als Teil desselben Pakets deklarieren
package com.banking.security;  // Dasselbe Paket wie Ziel!

public class MaliciousAccessor {

    public static void exploit() {
        // Angreifer kann auf paket-private Mitglieder zugreifen
        System.out.println("Master-Schlüssel: " + InternalSecurityManager.masterKey);

        // Angreifer kann paket-private Methoden aufrufen
        InternalSecurityManager.resetAllSessions();

        // Angreifer kann Admin-Zugriff fälschen
        boolean hasAccess = InternalSecurityManager.validateAdminAccess(
            InternalSecurityManager.masterKey
        );
    }
}
// Anfällig: Konfiguration mit paket-privaten Feldern
package com.app.config;

public class AppConfiguration {
    // Anfällig: Paket-Geltungsbereich für sensible Konfiguration
    String databasePassword = "db_passwort_123";
    String apiSecret = "api_geheimer_schlüssel";

    // Öffentliche Methode aber verlässt sich darauf, dass Aufrufer im selben Paket sind
    // um auf interne Felder zuzugreifen
    public void initialize() {
        // Nimmt an, nur vertrauenswürdiger Code kann Felder sehen
        connectToDatabase(databasePassword);
        initializeApi(apiSecret);
    }
}

// Anfällig: Sicherheitsprüfung verlässt sich auf Paket-Geltungsbereich
class SecurityValidator {
    // Paket-privat - angenommen nur interne Klassen können aufrufen
    boolean bypassSecurityCheck = false;

    public boolean isSecure(Request request) {
        // Anfällig: bypassSecurityCheck kann vom Angreifer gesetzt werden
        if (bypassSecurityCheck) {
            return true;  // Angreifer kann alle Prüfungen umgehen
        }
        return performActualValidation(request);
    }
}

Korrigierter Code

// Korrigiert: Ordnungsgemäße Kapselung ohne Verlassen auf Paket-Geltungsbereich
package com.banking.security;

public final class SecureSecurityManager {
    // Korrigiert: Privates, finales Feld - kann nicht abgerufen oder modifiziert werden
    private static final String masterKey;

    static {
        // Korrigiert: Aus sicherer Quelle laden, nicht hartcodiert
        masterKey = loadKeyFromSecureStore();
    }

    // Korrigiert: Kein direkter Zugriff auf Master-Schlüssel
    // Stattdessen sichere Vergleichsmethode verwenden
    public static boolean validateAdminAccess(String token) {
        // Korrigiert: Konstantzeit-Vergleich um Timing-Angriffe zu verhindern
        return MessageDigest.isEqual(
            token.getBytes(StandardCharsets.UTF_8),
            masterKey.getBytes(StandardCharsets.UTF_8)
        );
    }

    // Korrigiert: Ordnungsgemäße Autorisierung für sensible Operationen erfordern
    public static void resetAllSessions(AdminCredentials creds) {
        if (!validateAdminCredentials(creds)) {
            throw new SecurityException("Unbefugt");
        }
        // Audit-Log vor gefährlicher Operation
        auditLog("Session-Reset durch: " + creds.getAdminId());
        SessionStore.invalidateAll();
    }

    private static String loadKeyFromSecureStore() {
        // Aus HSM, verschlüsselter Konfiguration oder sicherem Tresor laden
        return SecureKeyVault.getKey("master-key");
    }

    private static boolean validateAdminCredentials(AdminCredentials creds) {
        // Ordnungsgemäße Authentifizierungsprüfung
        return AuthenticationService.validate(creds);
    }
}
// Korrigiert: Ordnungsgemäße Datenkapselung
package com.banking.data;

public final class SecureCustomerDatabase {
    // Korrigiert: Private Felder ohne direkten Zugriff
    private final String[] customerSSNs;
    private final String[] accountNumbers;

    public SecureCustomerDatabase(int capacity) {
        this.customerSSNs = new String[capacity];
        this.accountNumbers = new String[capacity];
    }

    // Korrigiert: Kontrollierter Zugriff mit Autorisierungsprüfung
    public String getSSN(int customerId, UserContext context) {
        // Korrigiert: Autorisierung vor Rückgabe sensibler Daten verifizieren
        if (!AuthorizationService.canAccessSSN(context, customerId)) {
            throw new SecurityException("Nicht autorisiert für SSN-Zugriff");
        }

        // Korrigiert: Zugriff auf sensible Daten protokollieren
        AuditLog.logDataAccess(context.getUserId(), "SSN", customerId);

        // Korrigiert: Maskierte Daten zurückgeben außer voller Zugriff gewährt
        if (context.hasFullSSNAccess()) {
            return customerSSNs[customerId];
        } else {
            return maskSSN(customerSSNs[customerId]);
        }
    }

    private String maskSSN(String ssn) {
        return "XXX-XX-" + ssn.substring(ssn.length() - 4);
    }
}
// Korrigiert: Sichere Konfigurationsverwaltung
package com.app.config;

public final class SecureAppConfiguration {
    // Korrigiert: Privat, final und sicher geladen
    private final String databasePassword;
    private final String apiSecret;

    // Korrigiert: Sicheres Laden der Konfiguration verwenden
    private SecureAppConfiguration() {
        // Aus verschlüsselter Konfiguration laden
        EncryptedConfig config = EncryptedConfig.load();
        this.databasePassword = config.getDecrypted("db.password");
        this.apiSecret = config.getDecrypted("api.secret");
    }

    // Korrigiert: Singleton mit ordnungsgemäßer Synchronisation
    private static volatile SecureAppConfiguration instance;

    public static SecureAppConfiguration getInstance() {
        if (instance == null) {
            synchronized (SecureAppConfiguration.class) {
                if (instance == null) {
                    instance = new SecureAppConfiguration();
                }
            }
        }
        return instance;
    }

    // Korrigiert: Keine Getter für sensible Daten
    // Stattdessen funktionale Methoden bereitstellen, die Daten intern verwenden
    public Connection getDatabaseConnection() {
        return DatabasePool.getConnection(databasePassword);
    }

    public ApiClient getApiClient() {
        return new ApiClient(apiSecret);
    }
}

// Korrigiert: Unveränderlicher Sicherheitsvalidator
public final class SecureSecurityValidator {
    // Korrigiert: Kein veränderbares Umgehungs-Flag

    public boolean isSecure(Request request) {
        // Korrigiert: Immer Validierung durchführen, kein Umgehungsmechanismus
        return performActualValidation(request);
    }

    private boolean performActualValidation(Request request) {
        return checkAuthentication(request) &&
               checkAuthorization(request) &&
               checkInputValidation(request) &&
               checkRateLimiting(request);
    }
}
// Korrigiert: Verwendung von Java 15+ versiegelten Klassen für stärkere Kapselung
package com.banking.security;

// Korrigiert: Versiegelte Klassenhierarchie - nur erlaubte Unterklassen
public sealed class SecurityToken
    permits AdminToken, UserToken, ServiceToken {

    private final String tokenValue;
    private final Instant expiration;

    protected SecurityToken(String tokenValue, Instant expiration) {
        this.tokenValue = tokenValue;
        this.expiration = expiration;
    }

    public boolean isValid() {
        return Instant.now().isBefore(expiration);
    }
}

// Nur diese Klassen können SecurityToken erweitern
public final class AdminToken extends SecurityToken {
    private final Set<Permission> permissions;

    public AdminToken(String token, Instant exp, Set<Permission> perms) {
        super(token, exp);
        this.permissions = Set.copyOf(perms);  // Unveränderliche Kopie
    }
}

CVE-Beispiele

Keine spezifischen CVEs sind in der MITRE-Datenbank für dieses CWE aufgelistet. Das Schwachstellenmuster ist jedoch dokumentiert in:

  • Java-Sicherheits-Best-Practices-Dokumentation
  • Seven Pernicious Kingdoms Taxonomie

Referenzen

  1. MITRE Corporation. "CWE-487: Reliance on Package-level Scope." https://cwe.mitre.org/data/definitions/487.html
  2. Oracle. "Secure Coding Guidelines for Java SE."
  3. OWASP. "Java Security Cheat Sheet."