EJB Bad Practices: Verwendung von AWT/Swing

Beschreibung

EJB Bad Practices: Verwendung von AWT/Swing ist eine Schwachstelle, bei der ein Enterprise JavaBean die EJB-Spezifikation verletzt, indem es AWT (Abstract Window Toolkit) oder Swing-GUI-Funktionalität einbezieht. Die EJB-Spezifikation verbietet Bean-Anbietern ausdrücklich, "AWT-Funktionalität zu verwenden, um Informationen auf einem Display auszugeben oder Informationen von einer Tastatur einzugeben." EJBs sind darauf ausgelegt, Geschäftslogik in serverseitigen Umgebungen zu verarbeiten, in denen keine Display- oder Tastaturinteraktion verfügbar ist. Der Versuch, GUI-Komponenten in diesem Kontext zu verwenden, führt zu Fehlern, da Serversysteme typischerweise headless ohne Zugriff auf Anzeigegeräte laufen.

Risiko

Die Verwendung von AWT/Swing in EJBs erzeugt sofortige Betriebsausfälle und Architekturverletzungen. In Headless-Serverumgebungen werfen GUI-Operationen HeadlessException und verursachen Laufzeitfehler. Selbst wenn ein Server Display-Kapazität hat, blockieren GUI-Operationen Threads, die auf Benutzereingaben warten, die nie kommen, und verursachen Anwendungshänger. Das Design verletzt das Prinzip der Trennung von Verantwortlichkeiten - das Mischen von Präsentationslogik mit Geschäftslogik macht Code nicht wiederverwendbar und schwer wartbar. Anwendungen werden nicht portabel über verschiedene Deployment-Umgebungen. Container-Management des Bean-Lebenszyklus wird problematisch, wenn Beans GUI-Ressourcen halten.

Lösung

Verwenden Sie niemals AWT- oder Swing-Komponenten in EJB-Implementierungen. Folgen Sie dem Model-View-Controller (MVC)-Muster und halten Sie EJBs strikt als Model-Layer mit Geschäftslogik. Verwenden Sie Web-Tier-Technologien (JSP, JSF, Servlets) oder dedizierte Client-Anwendungen für die Präsentation. Wenn EJBs Daten zur Anzeige bereitstellen müssen, geben Sie Data Transfer Objects (DTOs) zurück, die die Präsentationsschicht rendern kann. Für Reporting oder Dokumentengenerierung verwenden Sie serverseitige Bibliotheken, die kein Display benötigen (wie Apache POI für Excel, iText für PDF). Wenn absolut notwendig, grafische Operationen durchzuführen, verwenden Sie den Headless-Modus mit BufferedImage.

Häufige Auswirkungen

AuswirkungDetails
SonstigeBereich: Sonstige

Qualitätsverschlechterung - Anwendungen funktionieren nicht korrekt, wenn GUI-Operationen in Server-Umgebungen nicht durchgeführt werden können.
VerfügbarkeitBereich: Verfügbarkeit

DoS: Absturz, Beenden oder Neustart - GUI-Operationen in Headless-Umgebungen werfen Exceptions und können die Anwendung potenziell zum Absturz bringen.

Beispielcode

Verwundbarer Code

// Verwundbar: EJB verwendet AWT/Swing-Komponenten
import javax.ejb.Stateless;
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;

@Stateless
public class VulnerableReportBean implements ReportService {

    // Verwundbar: Erstellen eines AWT-Frames in EJB
    public void displayReport(ReportData data) {
        // Dies schlägt in Headless-Serverumgebung fehl
        Frame frame = new Frame("Bericht");  // HeadlessException!
        frame.setSize(800, 600);

        TextArea textArea = new TextArea();
        textArea.setText(data.toString());
        frame.add(textArea);

        frame.setVisible(true);  // Kein Display auf Server verfügbar!
    }
}

// Verwundbar: Stateless-Bean erweitert Component
@Stateless
public class VulnerableInputBean extends Component implements KeyListener {

    // Verwundbar: KeyListener in EJB implementieren
    @Override
    public void keyPressed(KeyEvent e) {
        // Keine Tastatureingabe auf Server verfügbar
        processKey(e.getKeyCode());
    }

    @Override
    public void keyReleased(KeyEvent e) { }

    @Override
    public void keyTyped(KeyEvent e) { }

    // Verwundbar: Versuch, Tastatureingabe zu erfassen
    public String getUserInput() {
        // Dies macht im Server-Kontext keinen Sinn
        TextField input = new TextField();  // HeadlessException!
        input.addKeyListener(this);
        // Wird nie Eingabe empfangen
        return input.getText();
    }
}

// Verwundbar: Swing in EJB verwenden
@Stateless
public class VulnerableDialogBean implements DialogService {

    // Verwundbar: Swing-Dialog in EJB erstellen
    public boolean confirmAction(String message) {
        // Dies schlägt auf Headless-Server fehl
        int result = JOptionPane.showConfirmDialog(
            null,
            message,
            "Bestätigen",
            JOptionPane.YES_NO_OPTION
        );  // HeadlessException in Server-Umgebung!

        return result == JOptionPane.YES_OPTION;
    }

    // Verwundbar: Swing-Frame erstellen
    public void showStatus(String status) {
        JFrame frame = new JFrame("Status");  // HeadlessException!
        JLabel label = new JLabel(status);
        frame.add(label);
        frame.pack();
        frame.setVisible(true);  // Kein Display!
    }
}

// Verwundbar: Graphics für Bildverarbeitung falsch verwenden
@Stateless
public class VulnerableImageBean implements ImageService {

    // Verwundbar: Fenster für Grafiken erstellen
    public byte[] createChart(ChartData data) {
        // Verwundbar: Erfordert Display-Kontext
        Frame frame = new Frame();  // HeadlessException!
        frame.addNotify();

        Image image = frame.createImage(400, 300);
        Graphics g = image.getGraphics();

        // Diagramm zeichnen
        g.drawRect(0, 0, 399, 299);
        // ... mehr Zeichnungen

        g.dispose();
        return convertToBytes(image);
    }
}

Lösungscode

// Behoben: EJB gibt Daten zurück, Präsentationsschicht behandelt Anzeige
import javax.ejb.Stateless;
import java.util.List;

@Stateless
public class SecureReportBean implements ReportService {

    @PersistenceContext
    private EntityManager em;

    // Behoben: Daten für Präsentationsschicht zur Anzeige zurückgeben
    public ReportDTO getReport(Long reportId) {
        // Nur Geschäftslogik - keine GUI
        Report report = em.find(Report.class, reportId);

        // DTO für Präsentationsschicht zurückgeben
        ReportDTO dto = new ReportDTO();
        dto.setTitle(report.getTitle());
        dto.setData(report.getData());
        dto.setGeneratedDate(new Date());

        return dto;
    }

    // Behoben: Bericht als Bytes zum Download generieren
    public byte[] generatePdfReport(Long reportId) {
        Report report = em.find(Report.class, reportId);

        // Serverseitige PDF-Bibliothek verwenden (keine GUI erforderlich)
        return PdfGenerator.createReport(report);
    }
}

// Behoben: JSP/JSF für Präsentation, EJB für Logik verwenden
// JSF Managed Bean (Präsentationsschicht)
@Named
@ViewScoped
public class ReportController implements Serializable {

    @Inject
    private ReportService reportService;  // EJB

    private ReportDTO currentReport;

    public void loadReport(Long reportId) {
        // Daten von EJB holen
        currentReport = reportService.getReport(reportId);
    }

    public ReportDTO getCurrentReport() {
        return currentReport;
    }
}

// Behoben: Präsentationslogik von Geschäftslogik trennen
// EJB behandelt nur Geschäftslogik
@Stateless
public class SecureInputBean implements InputService {

    // Behoben: Keine Tastatureingabe - Daten als Parameter empfangen
    public ProcessResult processUserInput(UserInputDTO input) {
        // Eingabe aus Präsentationsschicht validieren
        validateInput(input);

        // Geschäftslogik verarbeiten
        ProcessResult result = new ProcessResult();
        result.setProcessedValue(calculate(input.getValue()));
        result.setTimestamp(new Date());

        return result;
    }

    private void validateInput(UserInputDTO input) {
        if (input == null || input.getValue() == null) {
            throw new ValidationException("Eingabe erforderlich");
        }
    }
}

// Behoben: Headless-Modus für serverseitige Bildgenerierung verwenden
import java.awt.image.BufferedImage;
import java.awt.Graphics2D;
import java.awt.Color;
import javax.imageio.ImageIO;

@Stateless
public class SecureImageBean implements ImageService {

    // Behoben: BufferedImage verwenden - funktioniert im Headless-Modus
    public byte[] createChart(ChartData data) {
        // Headless-Modus sicherstellen (normalerweise beim JVM-Start gesetzt)
        // -Djava.awt.headless=true

        // Bild ohne Display erstellen
        BufferedImage image = new BufferedImage(
            400, 300, BufferedImage.TYPE_INT_RGB);

        Graphics2D g = image.createGraphics();

        // Diagramm zeichnen
        g.setColor(Color.WHITE);
        g.fillRect(0, 0, 400, 300);
        g.setColor(Color.BLACK);
        g.drawRect(0, 0, 399, 299);

        // Daten zeichnen
        drawChartData(g, data);

        g.dispose();

        // In Bytes konvertieren
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        try {
            ImageIO.write(image, "PNG", baos);
        } catch (IOException e) {
            throw new RuntimeException("Bildgenerierung fehlgeschlagen", e);
        }

        return baos.toByteArray();
    }

    private void drawChartData(Graphics2D g, ChartData data) {
        // Serverseitige Diagramm-Rendering-Logik
    }
}

// Behoben: Serverseitige Diagrammbibliothek verwenden
import org.jfree.chart.JFreeChart;
import org.jfree.chart.ChartFactory;
import org.jfree.chart.ChartUtilities;

@Stateless
public class SecureChartBean implements ChartService {

    // Behoben: JFreeChart verwenden (funktioniert headless)
    public byte[] createBarChart(ChartDataDTO data) {
        // JFreeChart funktioniert im Headless-Modus
        JFreeChart chart = ChartFactory.createBarChart(
            data.getTitle(),
            data.getCategoryLabel(),
            data.getValueLabel(),
            createDataset(data),
            PlotOrientation.VERTICAL,
            true,  // Legende
            true,  // Tooltips
            false  // URLs
        );

        // Als PNG-Bytes exportieren
        try {
            return ChartUtilities.encodeAsPNG(
                chart.createBufferedImage(600, 400));
        } catch (IOException e) {
            throw new RuntimeException("Diagrammgenerierung fehlgeschlagen", e);
        }
    }
}

// Behoben: Webservice-Endpunkt für entfernte Clients
@Stateless
@WebService
public class SecureReportWebService {

    @Inject
    private ReportService reportService;

    // Behoben: SOAP/REST-Service gibt Daten zurück, Client rendert
    @WebMethod
    public ReportDTO getReport(@WebParam(name="reportId") Long reportId) {
        return reportService.getReport(reportId);
    }

    @WebMethod
    public byte[] getReportPdf(@WebParam(name="reportId") Long reportId) {
        return reportService.generatePdfReport(reportId);
    }
}

CVE-Beispiele

Keine spezifischen CVEs werden dieser CWE üblicherweise zugeordnet, da sie primär die Anwendungsfunktionalität betrifft statt Sicherheitsschwachstellen.


Referenzen

  1. MITRE Corporation. "CWE-575: EJB Bad Practices: Use of AWT Swing." https://cwe.mitre.org/data/definitions/575.html
  2. Oracle. "Enterprise JavaBeans Specification."
  3. Oracle. "Java Headless Mode Documentation."