Unzureichende Beschränkung gerenderter UI-Schichten oder Frames

Beschreibung

Unzureichende Beschränkung gerenderter UI-Schichten oder Frames tritt auf, wenn eine Webanwendung Frame-Objekte oder UI-Schichten, die zu einer anderen Anwendung oder Domain gehören, nicht einschränkt oder falsch einschränkt. Dies ermöglicht "Clickjacking"-Angriffe, bei denen ein Angreifer transparente oder undurchsichtige Frames über legitime UI-Elemente legt und Benutzer dazu verleitet, auf bösartige Inhalte zu klicken, während sie glauben, mit der legitimen Website zu interagieren. Angreifer können dies nutzen, um Anmeldedaten zu stehlen, nicht autorisierte Aktionen durchzuführen oder Benutzer dazu zu bringen, gefährliche Funktionen zu aktivieren.

Risiko

Clickjacking-Angriffe können schwerwiegende Folgen haben, darunter nicht autorisierte Überweisungen, Kompromittierung von Social-Media-Konten, Aktivierung von Webcam/Mikrofon und Malware-Installation durch Drive-by-Downloads. Bemerkenswerte Angriffe zielten auf Facebooks "Gefällt mir"-Button (Likejacking), Twitters "Folgen"-Button und Banking-Anwendungen. Ohne ordnungsgemäße Frame-Einschränkungen kann potenziell jede authentifizierte Aktion, die ein Benutzer durchführen kann, gekapert werden. Moderne Browser haben einige Schutzmaßnahmen implementiert, aber Anwendungen müssen sich dennoch explizit für diese Schutzmaßnahmen entscheiden.

Lösung

Implementieren Sie den X-Frame-Options-Header mit DENY oder SAMEORIGIN. Verwenden Sie die Content-Security-Policy (CSP) frame-ancestors-Direktive für detailliertere Kontrolle. Implementieren Sie Frame-Busting-JavaScript als Verteidigung in der Tiefe (obwohl es umgangen werden kann). Verwenden Sie das SameSite-Cookie-Attribut, um zu verhindern, dass Cookies in gerahmten Kontexten gesendet werden. Implementieren Sie für sensible Operationen zusätzliche Bestätigungsschritte, die nicht durch Clickjacking umgangen werden können (z.B. erneute Authentifizierung, CAPTCHA).

Häufige Auswirkungen

AuswirkungDetails
IntegritätBereich: Nicht autorisierte Aktionen

Benutzer führen unwissentlich Aktionen wie Einstellungsänderungen, Käufe oder Berechtigungserteilungen durch.
VertraulichkeitBereich: Anmeldedaten-Diebstahl

Anmeldeformulare können überlagert werden, um Anmeldedaten zu stehlen.
ZugriffskontrolleBereich: Berechtigungs-Hijacking

Angreifer können Benutzer dazu verleiten, Berechtigungen für Kamera, Mikrofon oder Standort zu erteilen.

Beispielcode und Lösung

Verwundbarer Code

<!-- VERWUNDBAR: Kein Clickjacking-Schutz -->
<!-- Diese Seite kann in jedem iframe eingebettet werden -->
<!DOCTYPE html>
<html>
<head>
    <title>Banking-Überweisung</title>
</head>
<body>
    <form action="/transfer" method="POST">
        <input type="hidden" name="to" value="angreifer-konto">
        <input type="hidden" name="amount" value="10000">
        <button type="submit">Hier klicken für einen kostenlosen Preis!</button>
    </form>
</body>
</html>
# VERWUNDBAR: Kein X-Frame-Options-Header
from flask import Flask, render_template

@app.route('/settings')
def settings():
    # Kein Frame-Schutz - kann eingebettet und per Clickjacking angegriffen werden
    return render_template('settings.html')
// VERWUNDBAR: Unwirksames Frame-Busting (kann umgangen werden)
if (top != self) {
    top.location = self.location;  // Kann durch sandbox-Attribut blockiert werden
}

Sichere Lösung

# SICHER: X-Frame-Options und CSP-Header
from flask import Flask, make_response

@app.after_request
def sicherheits_header_hinzufügen(response):
    # Framing komplett verhindern
    response.headers['X-Frame-Options'] = 'DENY'

    # Oder nur gleiche Herkunft erlauben
    # response.headers['X-Frame-Options'] = 'SAMEORIGIN'

    # CSP frame-ancestors (flexibler)
    response.headers['Content-Security-Policy'] = "frame-ancestors 'none';"

    return response

# Für spezifische vertrauenswürdige Domains:
# response.headers['Content-Security-Policy'] = "frame-ancestors 'self' https://trusted.example.com;"
// SICHER: Robustes Frame-Busting mit Sandbox-Erkennung
(function() {
    // Prüfen, ob wir in einem Frame sind
    if (self !== top) {
        // Prüfen, ob wir ausbrechen können
        try {
            // Versuch, auf Parent zuzugreifen - wirft Fehler bei Cross-Origin
            if (top.location.hostname !== self.location.hostname) {
                // Wir sind von einer anderen Herkunft gerahmt - umleiten
                top.location = self.location;
            }
        } catch (e) {
            // Cross-Origin-Frame erkannt
            // Inhalt verstecken und Warnung anzeigen
            document.body.innerHTML = '<h1>Dieser Inhalt kann nicht in einem Frame angezeigt werden</h1>';
        }
    }
})();

// SICHER: Zusätzlicher Schutz für sensible Aktionen
function sensibleAktionAusführen() {
    // Erneute Bestätigung erfordern, die nicht durch Clickjacking umgangen werden kann
    const bestätigt = prompt('Geben Sie "BESTÄTIGEN" ein, um mit dieser Aktion fortzufahren:');
    if (bestätigt !== 'BESTÄTIGEN') {
        return false;
    }
    // Mit Aktion fortfahren
}
<!-- SICHER: Serverseitige Header plus clientseitiger Schutz -->
<!DOCTYPE html>
<html>
<head>
    <title>Sichere Seite</title>
    <style>
        /* Inhalt verstecken bis JS bestätigt, dass wir nicht gerahmt sind */
        html { display: none; }
    </style>
    <script>
        if (self === top) {
            document.documentElement.style.display = 'block';
        } else {
            // Gerahmt - Inhalt nicht anzeigen
            top.location = self.location;
        }
    </script>
</head>
<body>
    <!-- Inhalt wird nur angezeigt, wenn nicht gerahmt -->
</body>
</html>
// SICHER: Spring Security-Konfiguration
@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http
            .headers()
                .frameOptions()
                    .deny()  // X-Frame-Options: DENY
                .and()
                .contentSecurityPolicy("frame-ancestors 'none';");
    }
}

// Oder mit Filter für Nicht-Spring-Anwendungen
@WebFilter("/*")
public class ClickjackingFilter implements Filter {
    @Override
    public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain)
            throws IOException, ServletException {
        HttpServletResponse response = (HttpServletResponse) res;
        response.setHeader("X-Frame-Options", "DENY");
        response.setHeader("Content-Security-Policy", "frame-ancestors 'none';");
        chain.doFilter(req, res);
    }
}

Ausgenutzt in der Praxis

Facebook-Likejacking (2010-heute)

Angreifer legen unsichtbare Facebook-"Gefällt mir"-Buttons über verlockende Inhalte und veranlassen Benutzer, unwissentlich Seiten zu liken oder Inhalte zu teilen, die zur Verbreitung von Malware und Betrug verwendet werden.

Twitter-Clickjacking (2009)

Angreifer nutzten Clickjacking, um Twitter-Benutzer dazu zu bringen, Tweets zu posten oder Konten zu folgen, ohne ihr Wissen, wodurch bösartige Links verbreitet wurden.

Adobe Flash Settings Hijacking

Clickjacking-Angriffe zielten auf Adobes Flash-Einstellungsmanager, um Webcam- und Mikrofonzugriff ohne Benutzerkenntnis zu aktivieren.


Tools zum Testen und Ausnutzen


CVE-Beispiele


Referenzen

  1. MITRE Corporation. "CWE-1021: Improper Restriction of Rendered UI Layers or Frames." https://cwe.mitre.org/data/definitions/1021.html

  2. OWASP. "Clickjacking Defense Cheat Sheet." https://cheatsheetseries.owasp.org/cheatsheets/Clickjacking_Defense_Cheat_Sheet.html

  3. MDN Web Docs. "X-Frame-Options." https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/X-Frame-Options