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
| Auswirkung | Details |
|---|---|
| Sonstige | Bereich: Sonstige Qualitätsverschlechterung - Anwendungen funktionieren nicht korrekt, wenn GUI-Operationen in Server-Umgebungen nicht durchgeführt werden können. |
| Verfügbarkeit | Bereich: 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
- MITRE Corporation. "CWE-575: EJB Bad Practices: Use of AWT Swing." https://cwe.mitre.org/data/definitions/575.html
- Oracle. "Enterprise JavaBeans Specification."
- Oracle. "Java Headless Mode Documentation."