Verwendung von Web-Links zu nicht vertrauenswürdigem Ziel mit window.opener-Zugriff

Beschreibung

Verwendung von Web-Links zu nicht vertrauenswürdigem Ziel mit window.opener-Zugriff tritt auf, wenn eine Webanwendung Links zu externen Websites erstellt, ohne zu verhindern, dass die Zielseite über die JavaScript-Eigenschaft window.opener auf die Ursprungsseite zugreift. Wenn ein Benutzer auf einen Link klickt, der in einem neuen Tab oder Fenster geöffnet wird (mit target="_blank"), erhält die neue Seite Zugriff auf das Window-Objekt des Öffners, was potenziell bösartigen externen Seiten ermöglicht, die ursprüngliche Seite auf eine Phishing-Seite umzuleiten oder deren Inhalt zu manipulieren. Diese Schwachstelle wird allgemein als "Tabnabbing" oder "Reverse Tabnabbing" bezeichnet.

Risiko

Diese Schwachstelle ermöglicht ausgeklügelte Phishing-Angriffe. Die externe bösartige Seite kann den ursprünglichen Tab still auf eine gefälschte Anmeldeseite umleiten, während der Benutzer auf den neuen Tab fokussiert ist. Wenn der Benutzer zum ursprünglichen Tab zurückkehrt, sieht er eine scheinbar legitime Anmeldeseite, die nach Anmeldedaten fragt. Der Angriff ist besonders effektiv, weil Benutzer darauf vertrauen, dass sich der ursprüngliche Tab nicht geändert hat. Angreifer können auch die Opener-Referenz verwenden, um die URL der ursprünglichen Seite zu erkennen und ihre Phishing-Seite entsprechend anzupassen. Das Risiko erhöht sich bei Links zu benutzergenerierten Inhalten oder Drittanbieterseiten.

Lösung

Fügen Sie rel="noopener noreferrer" zu allen Anchor-Tags hinzu, die target="_blank" verwenden. Das "noopener"-Attribut verhindert, dass die neue Seite auf window.opener zugreift. Das "noreferrer"-Attribut verhindert zusätzlich das Senden des Referrer-Headers an die externe Seite. Moderne Browser haben begonnen, noopener implizit für target="_blank"-Links hinzuzufügen, aber explizite Deklaration stellt den Schutz über alle Browser-Versionen sicher. Für programmatisches Fensteröffnen mit window.open() setzen Sie den Opener sofort auf null. Erwägen Sie die Verwendung von Content Security Policy-Headern zur Einschränkung der Navigation. Implementieren Sie Link-Validierung für benutzereingereichte URLs.

Häufige Auswirkungen

AuswirkungDetails
VertraulichkeitBereich: Vertraulichkeit

Anwendungsdaten lesen - Phishing-Angriffe durch Tabnabbing können Benutzeranmeldedaten und sensible Informationen stehlen.
IntegritätBereich: Integrität

Anwendungsdaten ändern - Die Location des Opener-Fensters kann geändert werden, was potenziell zur Injektion bösartiger Inhalte führt.
ZugriffskontrolleBereich: Zugriffskontrolle

Schutzmechanismus umgehen - Das Benutzervertrauen in den ursprünglichen Tab wird ausgenutzt, um das normale Phishing-Bewusstsein zu umgehen.

Beispielcode und Lösung

Verwundbarer Code

<!-- Verwundbar: Links ohne noopener ermöglichen Tabnabbing -->
<!DOCTYPE html>
<html>
<head>
    <title>Mein Banking-Portal</title>
</head>
<body>
    <h1>Willkommen bei Sicher Bank</h1>

    <!-- Verwundbar: Externe Seite kann auf window.opener zugreifen -->
    <a href="https://externer-partner.com" target="_blank">
        Besuchen Sie unsere Partner-Website
    </a>

    <!-- Verwundbar: Benutzergenerierter Link -->
    <a href="https://benutzer-url.com" target="_blank">
        Profil-Link des Benutzers
    </a>

    <!-- Verwundbar: Social-Media-Teilen -->
    <a href="https://twitter.com/share?url=..." target="_blank">
        Auf Twitter teilen
    </a>
</body>
</html>

<!--
Bösartige Seite des Angreifers bei externer-partner.com:
<script>
if (window.opener) {
    // Bank-Tab auf Phishing-Seite umleiten
    window.opener.location = "https://angreifer.com/fake-bank-login";
}
</script>
-->
// Verwundbar: Programmatisches Fensteröffnen ohne Schutz
function verwundbarLinkÖffnen(url) {
    // Verwundbar: Neues Fenster hat Zugriff auf Opener
    window.open(url, '_blank');

    // Die geöffnete Seite kann jetzt:
    // window.opener.location = "https://phishing-seite.com"
}

// Verwundbar: Dynamische Link-Erstellung
function verwundbarTeilenLinkErstellen(url) {
    const link = document.createElement('a');
    link.href = url;
    link.target = '_blank';
    // Fehlendes rel="noopener noreferrer"
    link.textContent = 'Teilen';
    document.body.appendChild(link);
}
// Verwundbar: React-Komponente mit unsicheren externen Links
function VerwundbareExterneLinks({ links }) {
    return (
        <div>
            {links.map(link => (
                // Verwundbar: Fehlendes rel-Attribut
                <a key={link.id} href={link.url} target="_blank">
                    {link.title}
                </a>
            ))}
        </div>
    );
}

// Verwundbar: URLs aus Benutzereingabe öffnen
function VerwundbaresBenutzerprofil({ website }) {
    return (
        <a href={website} target="_blank">
            Website besuchen
        </a>
    );
}

Sichere Lösung

<!-- Sicher: Links mit noopener noreferrer -->
<!DOCTYPE html>
<html>
<head>
    <title>Mein Banking-Portal</title>
</head>
<body>
    <h1>Willkommen bei Sicher Bank</h1>

    <!-- Sicher: rel="noopener noreferrer" verhindert Tabnabbing -->
    <a href="https://externer-partner.com" target="_blank" rel="noopener noreferrer">
        Besuchen Sie unsere Partner-Website
    </a>

    <!-- Sicher: Benutzergenerierte Links geschützt -->
    <a href="https://benutzer-url.com" target="_blank" rel="noopener noreferrer">
        Profil-Link des Benutzers
    </a>

    <!-- Sicher: Social-Sharing geschützt -->
    <a href="https://twitter.com/share?url=..." target="_blank" rel="noopener noreferrer">
        Auf Twitter teilen
    </a>
</body>
</html>
// Sicher: Programmatisches Fensteröffnen mit Schutz
function sicherLinkÖffnen(url) {
    // Sicher: Fenster öffnen und Opener sofort nullen
    const neuesFenster = window.open(url, '_blank');
    if (neuesFenster) {
        neuesFenster.opener = null;
    }
}

// Alternative: noopener-Feature verwenden
function sicherLinkÖffnenAlternativ(url) {
    // Sicher: noopener in Fenster-Features verwenden
    window.open(url, '_blank', 'noopener,noreferrer');
}

// Sicher: Dynamische Link-Erstellung mit Schutz
function sicherTeilenLinkErstellen(url) {
    const link = document.createElement('a');
    link.href = url;
    link.target = '_blank';
    link.rel = 'noopener noreferrer';  // Sicher: rel-Attribut hinzufügen
    link.textContent = 'Teilen';
    document.body.appendChild(link);
}

// Sicher: Hilfsfunktion für sichere externe Links
class SichereExterneLinks {
    static öffnen(url, optionen = {}) {
        // URL validieren
        if (!this.istGültigeUrl(url)) {
            console.error('Ungültige URL');
            return null;
        }

        // Prüfen, ob URL auf Blockliste
        if (this.istBlockierteDomain(url)) {
            console.error('Blockierte Domain');
            return null;
        }

        // Sicher öffnen
        const features = optionen.features || 'noopener,noreferrer';
        return window.open(url, '_blank', features);
    }

    static istGültigeUrl(url) {
        try {
            new URL(url);
            return true;
        } catch {
            return false;
        }
    }

    static istBlockierteDomain(url) {
        const blockliste = ['bösartige-seite.com', 'phishing.example'];
        const domain = new URL(url).hostname;
        return blockliste.some(blockiert => domain.includes(blockiert));
    }
}
// Sicher: React-Komponente mit sicheren externen Links
function SichereExterneLinks({ links }) {
    return (
        <div>
            {links.map(link => (
                // Sicher: Immer rel="noopener noreferrer" einschließen
                <a
                    key={link.id}
                    href={link.url}
                    target="_blank"
                    rel="noopener noreferrer"
                >
                    {link.title}
                </a>
            ))}
        </div>
    );
}

// Sicher: Wiederverwendbare sichere externe Link-Komponente
function SichererExternerLink({ href, children, ...props }) {
    // Sicher: Schutz für externe Links immer hinzufügen
    const istExtern = href && (
        href.startsWith('http://') ||
        href.startsWith('https://') ||
        href.startsWith('//')
    );

    const relWert = istExtern ? 'noopener noreferrer' : undefined;
    const targetWert = istExtern ? '_blank' : undefined;

    return (
        <a
            href={href}
            target={targetWert}
            rel={relWert}
            {...props}
        >
            {children}
        </a>
    );
}

// Verwendung
function Benutzerprofil({ website }) {
    return (
        <SichererExternerLink href={website}>
            Website besuchen
        </SichererExternerLink>
    );
}
<!-- Sicher: PHP generiert geschützte Links -->
<?php
function sichereLinkAusgabe($links) {
    foreach ($links as $link) {
        // Sicher: rel="noopener noreferrer" einschließen
        echo '<a href="' . htmlspecialchars($link['url']) . '" ';
        echo 'target="_blank" rel="noopener noreferrer">';
        echo htmlspecialchars($link['title']);
        echo '</a>';
    }
}

// Sicher: Sichere externe Link-Hilfsfunktion
function sichererExternerLink($url, $text, $cssKlasse = '') {
    // URL validieren
    if (!filter_var($url, FILTER_VALIDATE_URL)) {
        return htmlspecialchars($text) . ' (ungültiger Link)';
    }

    // Protokoll prüfen
    $parsed = parse_url($url);
    if (!in_array($parsed['scheme'], ['http', 'https'])) {
        return htmlspecialchars($text) . ' (ungültiges Protokoll)';
    }

    // Sicheren Link erstellen
    $klasse = $cssKlasse ? ' class="' . htmlspecialchars($cssKlasse) . '"' : '';
    return sprintf(
        '<a href="%s" target="_blank" rel="noopener noreferrer"%s>%s</a>',
        htmlspecialchars($url),
        $klasse,
        htmlspecialchars($text)
    );
}

// Verwendung
echo sichererExternerLink($benutzerUrl, 'Benutzer-Website besuchen', 'externer-link');
?>

Ausgenutzt in der Praxis

Tabnabbing-Angriffe auf E-Mail-Dienste (2016)

Sicherheitsforscher demonstrierten, wie Tabnabbing bei großen E-Mail-Anbietern verwendet werden könnte, um Benutzer auf gefälschte Anmeldeseiten umzuleiten und Anmeldedaten zu stehlen.

WordPress-Plugin-Schwachstellen (2017)

Mehrere populäre WordPress-Plugins hatten Links ohne noopener, die Millionen von Websites für Tabnabbing-Angriffe anfällig machten.

Social-Media-Phishing über Reverse Tabnabbing (2019)

Angreifer nutzten Tabnabbing auf Social-Media-Plattformen, um Benutzer auf Phishing-Seiten umzuleiten, nachdem sie auf scheinbar harmlose externe Links geklickt hatten.


Tools zum Testen und Ausnutzen


CVE-Beispiele

  • CVE-2016-5765 - Anwendung erlaubte Tabnabbing durch Links ohne rel="noopener".

  • CVE-2019-11730 - Browser-Schwachstelle im Zusammenhang mit Cross-Origin window.opener-Zugriff.


Referenzen

  1. MITRE Corporation. "CWE-1022: Use of Web Link to Untrusted Target with window.opener Access." https://cwe.mitre.org/data/definitions/1022.html

  2. OWASP. "Reverse Tabnabbing." https://owasp.org/www-community/attacks/Reverse_Tabnabbing

  3. MDN Web Docs. "Link types: noopener." https://developer.mozilla.org/en-US/docs/Web/HTML/Link_types/noopener