EJB Bad Practices: Use of Synchronization Primitives

Description

EJB Bad Practices: Use of Synchronization Primitives is a vulnerability where an Enterprise JavaBean (EJB) violates the EJB specification by employing thread synchronization primitives such as synchronized blocks, methods, or explicit lock objects. The EJB specification explicitly forbids this practice: "An enterprise bean must not use thread synchronization primitives to synchronize execution of multiple instances." This requirement exists because EJB containers have full control over thread management and may execute bean instances in a single JVM or distribute them across multiple JVMs, making thread synchronization behavior unpredictable.

Risk

Using synchronization primitives in EJBs creates several risks. The behavior becomes container-dependent and unpredictable—code that works in one EJB container may fail in another. When beans are distributed across multiple JVMs, synchronization only applies within each JVM, providing false security and inconsistent state across the cluster. Performance degradation occurs as synchronization creates bottlenecks in what should be a scalable architecture. The container cannot optimize bean instance management when synchronization constraints exist. Additionally, improper synchronization can lead to deadlocks that affect server stability.

Solution

Do not use Java synchronization primitives in EJB code. For thread-safe access to shared resources, use EJB container-managed services such as singleton beans with container-managed concurrency, JPA for database access with transaction isolation, JMS for message-based coordination, or distributed cache solutions. If mutable shared state is absolutely required, use singleton session beans with @Lock annotations that the container can manage properly. Design stateless beans where possible to avoid shared state concerns entirely. Use container transactions to ensure data consistency instead of manual synchronization.

Common Consequences

ImpactDetails
OtherScope: Other

Quality Degradation - Application portability and reliability are compromised when EJB specification is violated.
AvailabilityScope: Availability

DoS: Resource Consumption - Improper synchronization can cause deadlocks or performance degradation affecting server availability.

Example Code

Vulnerable Code

// Vulnerable: EJB with synchronized methods
import javax.ejb.Stateless;

@Stateless
public class VulnerableCounterBean implements CounterService {

    private static int counter = 0;  // Shared state

    // Vulnerable: Using synchronized method in EJB
    public synchronized int incrementCounter() {
        return ++counter;
    }

    // Vulnerable: Synchronized method violates EJB spec
    public synchronized int getCounter() {
        return counter;
    }
}

// Vulnerable: EJB with synchronized blocks
@Stateless
public class VulnerableCacheBean implements CacheService {

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

    // Vulnerable: Using synchronized block in EJB
    public Object getFromCache(String key) {
        synchronized (lock) {  // Violates EJB specification
            return cache.get(key);
        }
    }

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

// Vulnerable: Entity EJB with synchronization
@Entity
public class VulnerableCustomerEntity {

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

    // Vulnerable: Synchronized setters 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 getters - also violates spec
    public synchronized String getCustomerId() {
        return customerId;
    }
}

// Vulnerable: Using 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;

    // Vulnerable: Using explicit locks in EJB
    public void useResource() {
        lock.lock();  // Violates EJB specification
        try {
            if (sharedResource == null) {
                sharedResource = createResource();
            }
            sharedResource.performOperation();
        } finally {
            lock.unlock();
        }
    }
}

// Vulnerable: Using 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();

    // Vulnerable: Using wait() in EJB
    public Task getTask() throws InterruptedException {
        synchronized (monitor) {
            while (taskQueue.isEmpty()) {
                monitor.wait();  // Violates EJB specification
            }
            return taskQueue.poll();
        }
    }

    // Vulnerable: Using notify() in EJB
    public void addTask(Task task) {
        synchronized (monitor) {
            taskQueue.offer(task);
            monitor.notifyAll();  // Violates EJB specification
        }
    }
}

Fixed Code

// Fixed: Use Singleton bean with container-managed concurrency
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;

    // Fixed: Container manages write lock
    @Lock(LockType.WRITE)
    public int incrementCounter() {
        return ++counter;
    }

    // Fixed: Container manages read lock
    @Lock(LockType.READ)
    public int getCounter() {
        return counter;
    }
}

// Fixed: Use distributed cache instead of manual synchronization
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-managed

    // Fixed: Use container-managed distributed cache
    public Object getFromCache(String key) {
        return distributedCache.get(key);
    }

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

// Fixed: Entity without synchronization - JPA handles concurrency
import javax.persistence.*;

@Entity
public class SecureCustomerEntity {

    @Id
    private String customerId;

    @Column
    private String firstName;

    @Column
    private String lastName;

    @Version  // Fixed: Use optimistic locking via JPA
    private Long version;

    // Fixed: No synchronization - JPA/container manages concurrency
    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;
    }
}

// Fixed: Use EJB timer or async for background processing
import javax.ejb.*;

@Singleton
@Startup
public class SecureResourceBean implements ResourceService {

    private Resource resource;

    @PostConstruct
    public void initialize() {
        // Fixed: Initialize once at startup, container ensures single execution
        resource = createResource();
    }

    @Lock(LockType.READ)
    public void useResource() {
        // Fixed: Container manages concurrent access
        resource.performOperation();
    }

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

// Fixed: Use JMS for queue-based processing
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) {
        // Fixed: JMS handles queuing and concurrency
        try {
            if (message instanceof ObjectMessage) {
                Task task = (Task) ((ObjectMessage) message).getObject();
                processTask(task);
            }
        } catch (JMSException e) {
            // Handle error
        }
    }

    private void processTask(Task task) {
        // Process task - no synchronization needed
        task.execute();
    }
}

// Fixed: Stateless design avoiding shared state
@Stateless
public class SecureStatelessBean implements ProcessingService {

    @PersistenceContext
    private EntityManager em;

    // Fixed: Stateless bean with no shared state
    public void processItem(Long itemId) {
        // Each invocation works with fresh data from database
        Item item = em.find(Item.class, itemId);
        if (item != null) {
            item.process();
            em.merge(item);
        }
        // Transaction isolation provides consistency
    }
}

// Fixed: Using CDI for request-scoped data
import javax.enterprise.context.RequestScoped;
import javax.inject.Named;

@Named
@RequestScoped
public class SecureRequestBean {

    private String currentUser;
    private List<String> permissions;

    // Fixed: Request-scoped bean - no synchronization needed
    // Each request gets its own instance
    public void setCurrentUser(String user) {
        this.currentUser = user;
    }

    public String getCurrentUser() {
        return currentUser;
    }
}

CVE Examples

No specific CVEs are commonly attributed to this CWE, as it primarily affects application reliability and portability rather than direct security vulnerabilities.


References

  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."