EJB Bad Practices: Verwendung von Synchronisationsprimitiven

Beschreibung

EJB Bad Practices: Verwendung von Synchronisationsprimitiven ist eine Schwachstelle, bei der ein Enterprise JavaBean (EJB) die EJB-Spezifikation verletzt, indem es Thread-Synchronisationsprimitive wie synchronized-Blöcke, -Methoden oder explizite Lock-Objekte verwendet. Die EJB-Spezifikation verbietet diese Praxis ausdrücklich: "Eine Enterprise Bean darf keine Thread-Synchronisationsprimitive verwenden, um die Ausführung mehrerer Instanzen zu synchronisieren." Diese Anforderung existiert, weil EJB-Container die volle Kontrolle über die Thread-Verwaltung haben und Bean-Instanzen in einer einzelnen JVM oder verteilt über mehrere JVMs ausführen können, was das Verhalten der Thread-Synchronisation unvorhersehbar macht.

Risiko

Die Verwendung von Synchronisationsprimitiven in EJBs erzeugt mehrere Risiken. Das Verhalten wird containerabhängig und unvorhersehbar - Code, der in einem EJB-Container funktioniert, kann in einem anderen fehlschlagen. Wenn Beans über mehrere JVMs verteilt sind, gilt die Synchronisation nur innerhalb jeder JVM, was falsche Sicherheit und inkonsistenten Zustand über den Cluster hinweg bietet. Leistungsverschlechterung tritt auf, da Synchronisation Engpässe in einer eigentlich skalierbaren Architektur erzeugt. Der Container kann das Bean-Instanz-Management nicht optimieren, wenn Synchronisationsbeschränkungen existieren. Zusätzlich kann unsachgemäße Synchronisation zu Deadlocks führen, die die Server-Stabilität beeinträchtigen.

Lösung

Verwenden Sie keine Java-Synchronisationsprimitive in EJB-Code. Für thread-sicheren Zugriff auf gemeinsame Ressourcen nutzen Sie EJB-Container-verwaltete Dienste wie Singleton-Beans mit Container-verwalteter Nebenläufigkeit, JPA für Datenbankzugriff mit Transaktionsisolation, JMS für nachrichtenbasierte Koordination oder verteilte Cache-Lösungen. Wenn veränderbarer gemeinsamer Zustand absolut erforderlich ist, verwenden Sie Singleton-Session-Beans mit @Lock-Annotationen, die der Container ordnungsgemäß verwalten kann. Entwerfen Sie zustandslose Beans wo möglich, um Bedenken bezüglich gemeinsamen Zustands gänzlich zu vermeiden. Verwenden Sie Container-Transaktionen, um Datenkonsistenz statt manueller Synchronisation sicherzustellen.

Häufige Auswirkungen

AuswirkungDetails
SonstigeBereich: Sonstige

Qualitätsverschlechterung - Anwendungsportabilität und Zuverlässigkeit werden beeinträchtigt, wenn die EJB-Spezifikation verletzt wird.
VerfügbarkeitBereich: Verfügbarkeit

DoS: Ressourcenverbrauch - Unsachgemäße Synchronisation kann Deadlocks oder Leistungsverschlechterung verursachen, die die Server-Verfügbarkeit beeinträchtigen.

Beispielcode

Verwundbarer Code

// Verwundbar: EJB mit synchronized-Methoden
import javax.ejb.Stateless;

@Stateless
public class VulnerableCounterBean implements CounterService {

    private static int counter = 0;  // Gemeinsamer Zustand

    // Verwundbar: Verwendung von synchronized-Methode in EJB
    public synchronized int incrementCounter() {
        return ++counter;
    }

    // Verwundbar: Synchronized-Methode verletzt EJB-Spezifikation
    public synchronized int getCounter() {
        return counter;
    }
}

// Verwundbar: EJB mit synchronized-Blöcken
@Stateless
public class VulnerableCacheBean implements CacheService {

    private static final Map<String, Object> cache = new HashMap<>();
    private static final Object lock = new Object();

    // Verwundbar: Verwendung von synchronized-Block in EJB
    public Object getFromCache(String key) {
        synchronized (lock) {  // Verletzt EJB-Spezifikation
            return cache.get(key);
        }
    }

    public void putInCache(String key, Object value) {
        synchronized (lock) {  // Verletzt EJB-Spezifikation
            cache.put(key, value);
        }
    }
}

// Verwundbar: Entity-EJB mit Synchronisation
@Entity
public class VulnerableCustomerEntity {

    private String customerId;
    private String firstName;
    private String lastName;

    // Verwundbar: Synchronized-Setter in Entity-Bean
    public synchronized void setCustomerId(String id) {
        this.customerId = id;
    }

    public synchronized void setFirstName(String name) {
        this.firstName = name;
    }

    public synchronized void setLastName(String name) {
        this.lastName = name;
    }

    // Synchronized-Getter - verletzt ebenfalls Spezifikation
    public synchronized String getCustomerId() {
        return customerId;
    }
}

// Verwundbar: Verwendung von ReentrantLock in EJB
import java.util.concurrent.locks.ReentrantLock;

@Stateless
public class VulnerableResourceBean implements ResourceService {

    private static final ReentrantLock lock = new ReentrantLock();
    private static Resource sharedResource;

    // Verwundbar: Verwendung expliziter Locks in EJB
    public void useResource() {
        lock.lock();  // Verletzt EJB-Spezifikation
        try {
            if (sharedResource == null) {
                sharedResource = createResource();
            }
            sharedResource.performOperation();
        } finally {
            lock.unlock();
        }
    }
}

// Verwundbar: Verwendung von wait/notify in EJB
@Stateless
public class VulnerableQueueBean implements QueueService {

    private static final Queue<Task> taskQueue = new LinkedList<>();
    private static final Object monitor = new Object();

    // Verwundbar: Verwendung von wait() in EJB
    public Task getTask() throws InterruptedException {
        synchronized (monitor) {
            while (taskQueue.isEmpty()) {
                monitor.wait();  // Verletzt EJB-Spezifikation
            }
            return taskQueue.poll();
        }
    }

    // Verwundbar: Verwendung von notify() in EJB
    public void addTask(Task task) {
        synchronized (monitor) {
            taskQueue.offer(task);
            monitor.notifyAll();  // Verletzt EJB-Spezifikation
        }
    }
}

Lösungscode

// Behoben: Singleton-Bean mit Container-verwalteter Nebenläufigkeit verwenden
import javax.ejb.Singleton;
import javax.ejb.Lock;
import javax.ejb.LockType;
import javax.ejb.ConcurrencyManagement;
import javax.ejb.ConcurrencyManagementType;

@Singleton
@ConcurrencyManagement(ConcurrencyManagementType.CONTAINER)
public class SecureCounterBean implements CounterService {

    private int counter = 0;

    // Behoben: Container verwaltet Schreibsperre
    @Lock(LockType.WRITE)
    public int incrementCounter() {
        return ++counter;
    }

    // Behoben: Container verwaltet Lesesperre
    @Lock(LockType.READ)
    public int getCounter() {
        return counter;
    }
}

// Behoben: Verteilten Cache statt manueller Synchronisation verwenden
import javax.annotation.Resource;
import javax.ejb.Stateless;
import javax.cache.Cache;

@Stateless
public class SecureCacheBean implements CacheService {

    @Resource
    private Cache<String, Object> distributedCache;  // Container-verwaltet

    // Behoben: Container-verwalteten verteilten Cache verwenden
    public Object getFromCache(String key) {
        return distributedCache.get(key);
    }

    public void putInCache(String key, Object value) {
        distributedCache.put(key, value);
    }
}

// Behoben: Entity ohne Synchronisation - JPA behandelt Nebenläufigkeit
import javax.persistence.*;

@Entity
public class SecureCustomerEntity {

    @Id
    private String customerId;

    @Column
    private String firstName;

    @Column
    private String lastName;

    @Version  // Behoben: Optimistisches Locking über JPA verwenden
    private Long version;

    // Behoben: Keine Synchronisation - JPA/Container verwaltet Nebenläufigkeit
    public void setCustomerId(String id) {
        this.customerId = id;
    }

    public void setFirstName(String name) {
        this.firstName = name;
    }

    public void setLastName(String name) {
        this.lastName = name;
    }

    public String getCustomerId() {
        return customerId;
    }
}

// Behoben: EJB-Timer oder Async für Hintergrundverarbeitung verwenden
import javax.ejb.*;

@Singleton
@Startup
public class SecureResourceBean implements ResourceService {

    private Resource resource;

    @PostConstruct
    public void initialize() {
        // Behoben: Einmal beim Start initialisieren, Container stellt einzelne Ausführung sicher
        resource = createResource();
    }

    @Lock(LockType.READ)
    public void useResource() {
        // Behoben: Container verwaltet gleichzeitigen Zugriff
        resource.performOperation();
    }

    @PreDestroy
    public void cleanup() {
        if (resource != null) {
            resource.close();
        }
    }
}

// Behoben: JMS für warteschlangenbasierte Verarbeitung verwenden
import javax.ejb.ActivationConfigProperty;
import javax.ejb.MessageDriven;
import javax.jms.*;

@MessageDriven(activationConfig = {
    @ActivationConfigProperty(
        propertyName = "destinationType",
        propertyValue = "javax.jms.Queue"),
    @ActivationConfigProperty(
        propertyName = "destination",
        propertyValue = "java:/jms/queue/TaskQueue")
})
public class SecureTaskProcessor implements MessageListener {

    @Override
    public void onMessage(Message message) {
        // Behoben: JMS behandelt Warteschlange und Nebenläufigkeit
        try {
            if (message instanceof ObjectMessage) {
                Task task = (Task) ((ObjectMessage) message).getObject();
                processTask(task);
            }
        } catch (JMSException e) {
            // Fehler behandeln
        }
    }

    private void processTask(Task task) {
        // Task verarbeiten - keine Synchronisation nötig
        task.execute();
    }
}

// Behoben: Zustandsloses Design ohne gemeinsamen Zustand
@Stateless
public class SecureStatelessBean implements ProcessingService {

    @PersistenceContext
    private EntityManager em;

    // Behoben: Zustandsloses Bean ohne gemeinsamen Zustand
    public void processItem(Long itemId) {
        // Jeder Aufruf arbeitet mit frischen Daten aus der Datenbank
        Item item = em.find(Item.class, itemId);
        if (item != null) {
            item.process();
            em.merge(item);
        }
        // Transaktionsisolation bietet Konsistenz
    }
}

// Behoben: CDI für Request-Scoped-Daten verwenden
import javax.enterprise.context.RequestScoped;
import javax.inject.Named;

@Named
@RequestScoped
public class SecureRequestBean {

    private String currentUser;
    private List<String> permissions;

    // Behoben: Request-Scoped-Bean - keine Synchronisation nötig
    // Jede Anfrage erhält ihre eigene Instanz
    public void setCurrentUser(String user) {
        this.currentUser = user;
    }

    public String getCurrentUser() {
        return currentUser;
    }
}

CVE-Beispiele

Keine spezifischen CVEs werden dieser CWE üblicherweise zugeordnet, da sie primär die Anwendungszuverlässigkeit und Portabilität betrifft statt direkter Sicherheitsschwachstellen.


Referenzen

  1. MITRE Corporation. "CWE-574: EJB Bad Practices: Use of Synchronization Primitives." https://cwe.mitre.org/data/definitions/574.html
  2. Oracle. "Enterprise JavaBeans Specification."
  3. Jakarta EE. "Jakarta Enterprise Beans Specification."