Clickjacking ist seit 2008 dokumentiert und gehört zu den ältesten noch aktiven Web-Angriffs-Klassen. Das Prinzip ist einfach: ein:e Angreifer:in lädt deine Webseite in einem transparenten iframe auf ihrer eigenen Seite, überlagert sie mit eigenen Elementen — und ein Klick, den die Nutzer:in für ihre eigene Aktion hält, löst tatsächlich eine Aktion auf deiner Seite aus. Dieser Artikel zeigt die Varianten, die realen Vorfälle und die zwei Header-Mechanismen, die das Problem strukturell lösen.

Wie Clickjacking technisch funktioniert

Der klassische Aufbau:

  1. Angreifer:in baut eine eigene Seite (evil.example) mit harmlosem Inhalt — z. B. einem Spiel oder einem Gewinnspiel.
  2. In diese Seite wird die echte Ziel-Seite (z. B. das Banking-Portal bank.example) als iframe geladen.
  3. Mit CSS wird der iframe transparent gemacht (opacity: 0) oder mit eigenen Elementen überlagert, sodass die echte Seite unsichtbar ist, aber klickbar bleibt.
  4. Die Position des iframes wird so gewählt, dass kritische Buttons der Ziel-Seite direkt unter den sichtbaren Buttons der Angreifer-Seite liegen.
  5. Wer auf den sichtbaren Button klickt, klickt tatsächlich in die unsichtbare iframe-Schicht — und löst die Aktion auf der echten Seite aus.

Voraussetzung: Die Nutzer:in ist auf der Ziel-Seite bereits eingeloggt — sonst hätte der Klick keinen Effekt. Genau das macht Clickjacking gefährlich: es funktioniert genau in den Szenarien, in denen User-Aktionen Wirkung haben.

Beispiel-Demonstration (vereinfacht):

HTML clickjacking-demo.html
<!-- Angreifer-Seite -->
<html>
<head><style>
  .lure-button { position: absolute; top: 200px; left: 100px; }
  iframe { position: absolute; top: 0; left: 0; opacity: 0; width: 100%; height: 100%; }
</style></head>
<body>
  <h1>Klicke hier, um zu gewinnen!</h1>
  <button class="lure-button">JETZT GEWINNEN</button>
  <iframe src="https://bank.example/transfer?to=attacker&amount=1000&confirm=yes"></iframe>
</body>
</html>

Der „GEWINNEN"-Button überlagert exakt den „Bestätigen"-Button im iframe der Bank-Seite. Klick → Überweisung läuft.

(Hinweis: heute scheitert dieser Angriff bei jeder seriösen Bank an X-Frame-Options oder frame-ancestors. Aber bei kleineren Web-Apps, die diese Header nicht setzen, ist es weiter möglich.)

Die Varianten

Clickjacking ist eine Familie verwandter Angriffe — gemeinsamer Begriff oft UI-Redress.

  • Klassisches Clickjacking — wie oben, mit überlagertem Klick auf Buttons.
  • Likejacking — verbreitet 2010–2014, Nutzer:innen klicken Facebook-„Like"-Buttons unsichtbar, ohne es zu wollen. Viele Like-Spam-Kampagnen waren so aufgebaut.
  • Cursorjacking — Cursor wird visuell verschoben, sodass Klicks woanders landen als die Nutzer:in glaubt.
  • Filejacking / Drag-and-Drop-Hijacking — Nutzer:in zieht eine Datei aus ihrem Dateimanager in den sichtbaren Bereich; in Wahrheit landet die Datei in einem Upload-Feld des iframes.
  • Keystroke-Hijacking / Strokejacking — Tasten-Eingaben werden in unsichtbare iframes umgeleitet.
  • Tap-Jacking auf Mobile — Touch-Events statt Klicks; auf Mobile-Browsern besonders effektiv.
  • Cookie-Jacking — eine sehr alte Variante, die ausnutzt, dass Drag-and-Drop in manchen Browsern Cookie-Inhalte transportierbar machen konnte.

Alle haben den gleichen Kern: die Nutzer:in tut etwas anderes als sie glaubt. Der Angriffsweg ist sozial-psychologisch (Visual-Deception), die technische Mechanik ist Browser-Eigenschaft.

Reale Vorfälle

Facebook Likejacking (2010–2014). Massen-Welle: gefälschte „Wow, das musst du sehen"-Posts mit Bild-Link. Klick führte auf eine Seite mit unsichtbarem Facebook-Like-Button. Eine Klick auf irgendwo auf der Seite war ein „Like" — der Post wurde im Profil des/der Nutzer:in geteilt, Welle pflanzte sich fort.

Twitter Worm (2010). Tweet mit unsichtbarem „Retweet"-Button — wer den scheinbar harmlosen Link klickte, retweetete den Wurm-Tweet selbst.

Adobe Flash Webcam-Aktivierung (2008). Sehr früher Vorfall: Flash-Plugin-Einstellungs-Dialog (für Webcam-Zustimmung) wurde in unsichtbaren iframes geladen. Klick auf einer Spiele-Seite aktivierte Webcam.

LinkedIn Email-Erfassung (2013–2014). „CSV importieren"-Funktion mit ungeschütztem iframe — Drag-and-Drop-Hijacking-Variante.

Bug Bounties bis heute. Auch 2024/25 finden Bug-Bounty-Forschende regelmäßig Clickjacking-Funde — meist bei kleineren Apps, gelegentlich auch bei großen, wenn ein neuer Endpunkt vergessen wurde. Der Schaden reicht von Konto-Aktionen (Profil-Änderungen, Friend-Adds) bis zu finanziellen Aktionen (Geldtransfer, Kauf-Bestätigungen).

Schutz 1 — X-Frame-Options

Der klassische Schutz, eingeführt von Microsoft 2009 (IE8), seit Jahren von allen großen Browsern unterstützt.

Drei Werte:

  • DENY — die Seite darf in keinem iframe geladen werden, egal woher.
  • SAMEORIGIN — die Seite darf nur in iframes der eigenen Origin geladen werden.
  • ALLOW-FROM <uri> — die Seite darf in iframes der angegebenen Origin geladen werden. Veraltet, nicht mehr in modernen Browsern unterstützt — durch CSP frame-ancestors ersetzt.

Setting in Nginx:

Nginx nginx-xfo.conf
add_header X-Frame-Options "SAMEORIGIN" always;

Setting in Express:

JavaScript express-xfo.js
app.use((req, res, next) => {
  res.setHeader('X-Frame-Options', 'SAMEORIGIN');
  next();
});

Setting in Spring (Java):

Java spring-xfo.java
http.headers(headers -> headers
  .frameOptions(frame -> frame.sameOrigin())
);

X-Frame-Options ist breit unterstützt (alle Browser seit ca. 2010), funktioniert zuverlässig. Aber:

  • ALLOW-FROM für mehrere Domains funktioniert nicht — nur eine Origin pro Header möglich, und das ist veraltet.
  • Granularität fehlt — entweder alles oder bestimmte Origin. Komplexere Embed-Szenarien sind nicht abbildbar.

Schutz 2 — CSP frame-ancestors

Die moderne Alternative ist die frame-ancestors-Direktive in der Content Security Policy. Sie bietet mehr Flexibilität und ersetzt X-Frame-Options für moderne Browser.

Beispiel-CSP-Header:

HTTP csp-frame-ancestors.http
Content-Security-Policy: frame-ancestors 'self';

# oder
Content-Security-Policy: frame-ancestors 'none';

# oder mit erlaubten externen Domains
Content-Security-Policy: frame-ancestors 'self' https://partner.example;

# mit Wildcard
Content-Security-Policy: frame-ancestors 'self' *.partner.example;

Werte:

  • 'none' — Seite darf in keinen iframe geladen werden.
  • 'self' — nur eigene Origin.
  • Eine oder mehrere Domain-Ausdrücke — bestimmte Origins erlaubt.

Vorteile gegenüber X-Frame-Options:

  • Mehrere erlaubte Origins in einem Header.
  • Wildcards unterstützt (vorsichtig nutzen).
  • Konsistent mit dem Rest der CSP (siehe content-security-policy — Kap 11).
  • ZukunftsfähigX-Frame-Options wird langsam zur Legacy.

Praxis-Empfehlung: Beide Header setzenX-Frame-Options für ältere Browser (auch wenn das immer weniger nötig ist), frame-ancestors für moderne. Wenn beide gesetzt sind und sich widersprechen, gewinnt in modernen Browsern frame-ancestors.

Setting in Nginx:

Nginx nginx-csp-fa.conf
add_header Content-Security-Policy "frame-ancestors 'self'; default-src 'self'; ..." always;
add_header X-Frame-Options "SAMEORIGIN" always;

SameSite-Cookies als Sekundär-Schutz

SameSite-Cookies sind primär eine CSRF-Verteidigung — aber sie wirken auch sekundär gegen Clickjacking-Konsequenzen.

Wenn ein Cookie als SameSite=Lax oder SameSite=Strict markiert ist, wird es bei Cross-Site-Requests in iframes nicht mitgesendet. Selbst wenn ein:e Angreifer:in deine Seite in ein iframe einbettet — der iframe hat keine Session, weil die Cookies fehlen.

Das ist keine vollständige Lösung:

  • Wenn der iframe als SameSite=None (mit Secure) markiert ist, läuft das Cookie weiter mit.
  • Wenn Aktionen über GET (statt POST) möglich sind, kann Lax umgangen werden (Browser sendet Cookies bei Top-Level-GET).
  • Manche Anwendungs-Flows brauchen Cross-Site-Cookies legitim.

Aber: in Kombination mit frame-ancestors ist SameSite=Lax (Browser-Default seit 2020) ein zusätzlicher Schutz-Layer.

JavaScript-basierte Frame-Buster (alte Variante, nicht empfohlen)

Vor dem Aufkommen von X-Frame-Options nutzten Web-Apps JavaScript-Tricks, um aus iframes herauszuspringen:

JavaScript frame-buster-alt.js
// Klassischer Frame-Buster (deprecated)
if (top !== self) {
  top.location = self.location;
}

Diese Ansätze waren über Jahre vielfach umgehbar:

  • sandbox-Attribut auf iframes deaktiviert das Top-Frame-Schreiben.
  • onbeforeunload-Handler kann Navigations-Versuche abfangen.
  • Verschachtelte iframes machen die Top-Window-Detection schwer.

Heute ist die einzige zuverlässige Verteidigung HTTP-Header-basiertX-Frame-Options oder frame-ancestors. Frame-Buster im Code sind nicht mehr empfehlenswert und sollten durch Header ersetzt werden.

Wann darf eine Seite eingebettet sein?

Es gibt legitime Embed-Szenarien:

  • YouTube-Videos, die du in deine Seite einbettest — YouTube setzt für die Embed-Variante (youtube.com/embed/...) X-Frame-Options: ALLOWALL.
  • Zahlungs-Widgets (Stripe Checkout, PayPal-Buttons) — Anbieter setzen Whitelists.
  • Karten-Embeds (Google Maps, OpenStreetMap).
  • Marketing-Widgets (Trustpilot, Bewertungs-Plattformen).
  • OAuth-Login-Popups — heute primär als Top-Level-Redirects, nicht iframes.

Wann nicht einbettbar:

  • Sensible Aktionen — Banking, E-Mail, Cloud-Datei-Verwaltung. Alles, wo ein:e angemeldete:r Nutzer:in eine impactvolle Aktion auslösen könnte.
  • Login-Seiten — sollten nie in iframes laden. Stoppt nicht nur Clickjacking, sondern auch transparente Phishing-Overlays.
  • Account-Einstellungen, Zahlungs-Methoden.

Default-Empfehlung: Auf einer normalen Web-App ohne spezifischen Embed-Bedarf gehört frame-ancestors 'self' oder frame-ancestors 'none' global gesetzt — dann erst pro-Seite oder pro-Pfad lockern, wo es nötig ist.

Testen

Sehr einfach zu testen, ob deine Seite verwundbar ist:

Manueller Test: Erstelle eine HTML-Datei mit:

HTML clickjacking-test.html
<html><body>
  <h1>Test</h1>
  <iframe src="https://deine-seite.example" width="800" height="600"></iframe>
</body></html>

Öffne die Datei lokal im Browser. Wenn deine Seite im iframe sichtbar wird, ist sie verwundbar. Wenn statt dessen eine Fehlermeldung kommt (refused to display ... in a frame because it set "X-Frame-Options" to ...), ist sie geschützt.

Automatisiert:

  • securityheaders.com prüft Header inklusive X-Frame-Options und frame-ancestors.
  • OWASP ZAP hat einen passiven Scanner für fehlende Frame-Schutz-Header.
  • Lighthouse-Audit zeigt Hinweise.

Interessantes

Clickjacking war 2008 ein Sensations-Thema

Robert Hansen (RSnake) und Jeremiah Grossman haben den Begriff 2008 geprägt und Adobe-Flash-Webcam-Aktivierung als Demonstration vorgeführt. Eine geplante DEF-CON-Präsentation wurde damals auf Adobes Bitte zurückgehalten — das Thema kam erst nach koordinierter Disclosure heraus.

X-Frame-Options ist offiziell deprecated

Im W3C-Standard ist X-Frame-Options seit Jahren als „obsolete" markiert — frame-ancestors ist die zukünftige Standardlösung. In der Praxis senden viele Sites beide Header, weil ältere Browser-Versionen frame-ancestors nicht kennen. Stand 2026 sind diese Browser sehr selten.

Mobile Tap-Jacking ist weniger erforscht

Auf Smartphones lassen sich Touch-Events ähnlich umleiten — die Forschung dazu ist weniger weit als bei Desktop-Clickjacking. Apple und Google haben WebView-Härtungen, die das meiste verhindern, aber Custom-WebViews in Apps haben gelegentlich Lücken.

OAuth-Flows niemals in iframes

Eine wichtige Konsequenz: OAuth-2.0-Authorization-Endpunkte dürfen nicht in iframes laden. Genau deshalb wird der „Implicit Flow" als unsicher eingestuft — er ermöglicht Embedding-Szenarien. Moderne OAuth 2.1 verlangt Authorization-Code-Flow mit PKCE in Top-Level-Redirects.

frame-ancestors überschreibt X-Frame-Options

Wenn beide Header gesetzt sind und sich widersprechen, gewinnt in modernen Browsern frame-ancestors aus der CSP. Falls deine Site beide setzt, sollten sie konsistent sein — sonst gibt es überraschende Browser-Unterschiede.

Manche WAFs setzen Header automatisch

Cloudflare, AWS CloudFront, Fastly bieten Optionen, fehlende Sicherheits-Header automatisch zu setzen. Praktisch, wenn der Origin-Server sie nicht selbst setzt — aber gefährlich, wenn man darauf vertraut, ohne es zu prüfen.

Bug-Bounty-Reports für Clickjacking sind oft niedrig bewertet

Im Bug-Bounty-Bereich werden reine Clickjacking-Funde meist als Low-Severity bewertet, weil sie Nutzer-Interaktion verlangen und meist nur einzelne Aktionen ermöglichen. Höhere Bewertung gibt es, wenn der Angriff schwere Konsequenzen hat (Konto-Übernahme, finanzielle Aktion) oder mit anderen Schwachstellen verkettet wird.

Weiterführende Ressourcen

Externe Quellen

/ Weiter

Zurück zu OWASP Top 10

Zur Übersicht