Sicherheit per Header - Kleine Zeile, große Wirkung

7 Minuten zum Lesen
Sicherheit per Header - Kleine Zeile, große Wirkung
Security Headers sind kleine, aber wirkungsvolle Maßnahmen, um Angriffe wie XSS, Clickjacking oder Information Disclosure zu verhindern und können einfach in ASP.NET Core und anderen Frameworks eingebunden werden.

Security Headers sind einfache HTTP-Header mit großer Wirkung. Richtig eingesetzt, blockieren sie ganze Klassen von Angriffen wie XSS, Clickjacking oder Information Disclosure, ohne dass Code geändert werden muss. Trotzdem werden sie oft übersehen oder falsch konfiguriert. Dabei ist ihre Einführung schnell erledigt und kann in modernen Frameworks wie ASP.NET Core einfach umgesetzt werden.

XSS-Absicherung durch Policies statt Vertrauen

Cross-Site Scripting (XSS) ist nach wie vor eine der häufigsten Schwachstellen im Web. Früher setzten Entwickler auf den Browser-Header X-XSS-Protection. Der war zwar gut gemeint, ist aber heute kaum noch relevant. Viel effektiver ist der Einsatz einer modernen Content Security Policy (CSP). Sie definiert präzise, welche Skripte, Styles, Objekte und andere Ressourcen geladen werden dürfen, und verhindert so das Einschleusen unerwünschter Inhalte. Besonders wichtig ist dabei die Kontrolle von Inline-Skripten und -Styles, die oft ein Einfallstor für Angriffe darstellen. Durch die Verwendung von Nonces oder Hashes können selbst diese sicher eingebunden werden.

Die Konfiguration kann anstrengend werden, besonders wenn Drittanbieterdienste wie Analytics oder ähnliches im Spiel sind. Aber der Aufwand lohnt sich, denn eine gut gepflegte CSP schützt zuverlässig.

Content-Security-Policy:
  default-src 'self';
  script-src 'self' 'nonce-abc123';
  object-src 'none';
  frame-ancestors 'none';
  form-action 'self';

In diesem Beispiel erlauben wir Skripte nur von der eigenen Domain und fügen ein Nonce für Inline-Skripte hinzu.

Eine Nonce (Number used once) ist ein einmaliger, zufälliger Wert, der bei jedem Seitenaufruf neu generiert wird. Dieser Wert wird im HTTP-Header übermittelt und muss in Inline-Skripten explizit angegeben werden. Dadurch kann der Browser sicherstellen, dass nur Skripte ausgeführt werden, die vom Backend autorisiert sind. Dies bietet eine effektive Absicherung gegen Cross-Site Scripting (XSS)-Angriffe, da Angreifer keine gültige Nonce für ihre schädlichen Skripte besitzen.

object-src 'none' verhindert das Laden von Objekten, und frame-ancestors 'none' verbietet das Einbetten in Frames. form-action 'self' stellt sicher, dass Daten aus Formularen nur an die eigene Domain gesendet werden.

Mehr als nur XSS: Weitere Schutzschichten per Header

Der Header X-Frame-Options oder alternativ frame-ancestors verhindert Clickjacking, indem er das Einbetten der Seite in iframes unterbindet. Ohne diesen Schutz könnten Angreifer unsichtbare Frames nutzen, um Benutzeraktionen wie Klicks oder Eingaben auf manipulierte Inhalte umzuleiten. Ein Beispiel wäre eine gefälschte Login-Seite, die in einem unsichtbaren Frame über die echte Seite gelegt wird. Der Benutzer glaubt, seine Daten sicher einzugeben, während sie tatsächlich an den Angreifer gesendet werden.

X-Frame-Options: DENY

Mit X-Content-Type-Options wird sichergestellt, dass Browser sich an den deklarierten Content-Typ halten und nicht eigenständig versuchen, den Typ zu erraten. Ohne diese Absicherung könnten Angreifer Dateien mit manipulierten Inhalten bereitstellen, die der Browser falsch interpretiert und ausführt. Ein Beispiel wäre eine als Bild deklarierte Datei, die tatsächlich ein ausführbares Skript enthält. Der Browser könnte versuchen, das Skript auszuführen, was zu einer Sicherheitslücke führt.

X-Content-Type-Options: nosniff

Strict-Transport-Security (HSTS) sorgt dafür, dass die Seite ausschließlich über HTTPS geladen wird. Dies schützt vor Downgrade-Angriffen, bei denen Angreifer versuchen, eine Verbindung auf unsicheres HTTP zurückzusetzen, um sensible Daten abzufangen. Ein konkretes Szenario wäre ein Man-in-the-Middle-Angriff in einem öffentlichen WLAN, bei dem der Angreifer den Benutzer auf eine HTTP-Version der Seite umleitet, um Login-Daten oder andere sensible Informationen abzugreifen. Die maximale Gültigkeitsdauer von 31536000 Sekunden (1 Jahr) sorgt dafür, dass der Browser auch bei zukünftigen Besuchen automatisch HTTPS verwendet. Je länger diese Zeitspanne, desto langfristiger der Schutz.

Strict-Transport-Security: max-age=31536000; includeSubDomains

Die Permissions-Policy erlaubt das gezielte Einschränken von Funktionen wie Kamera, Mikrofon oder Standortfreigabe. Ohne diese Einschränkungen könnten Angreifer ungenutzte oder unnötige Berechtigungen ausnutzen, um auf sensible Daten oder Gerätefunktionen zuzugreifen. Ein Beispiel wäre eine bösartige Webseite, die ohne Wissen des Benutzers die Kamera aktiviert, um Bilder oder Videos aufzunehmen, oder die Standortdaten abruft, um Bewegungsprofile zu erstellen.

Permissions-Policy: camera=(), microphone=(), geolocation=()

CORS: Komplex, aber notwendig

CORS (Cross-Origin Resource Sharing) ist für viele Entwickler ein rotes Tuch, weil es oft “funktionierende” Frontend-Backendanbindungen plötzlich blockiert. Aber genau das ist seine Aufgabe. CORS regelt, welche Domains mit welchen Rechten auf eine API zugreifen dürfen.

Wer einfach Access-Control-Allow-Origin: * setzt, öffnet Tür und Tor für ungewollte Datenabflüsse. Die Konfiguration sollte daher so restriktiv wie möglich sein, idealerweise nur für bekannte und kontrollierte Domains. Diese lassen sich einfach per Konfiguration oder Umgebungsvariablen steuern.

Die einzelnen Header sind:

  • Access-Control-Allow-Origin: Gibt an, welche Frontend Apps auf die Ressource zugreifen dürfen. Hier sollte nur eine Liste vertrauenswürdiger Domains stehen.
  • Access-Control-Allow-Methods: Definiert, welche HTTP-Methoden (GET, POST, PUT, DELETE) erlaubt sind.
  • Access-Control-Allow-Headers: Gibt an, welche Request Header in der Anfrage erlaubt sind.

Wer eine API für Dritte bereitstellt, sollte sich zusätzlich auch Gedanken über die Access-Control-Allow-Credentials-Einstellung machen. Diese regelt, ob Cookies, Client Zertifikate oder HTTP Basic Auth Daten automatisch vom Browser mitgesendet werden dürfen. Damit lassen sich Cross-Site-Request-Forgery (CSRF)-Angriffe verhindern, bei denen Angreifer versuchen, im Namen des Benutzers Aktionen über eine andere Frontend-Anwendung auszuführen.

// {
//   "Cors": {
//     "AllowedOrigins": [
//       "https://example.com",
//       "https://sub.example.com"
//     ]
//   }
// }
var allowedOrigins = Configuration.GetSection("Cors:AllowedOrigins").Get<string[]>();

services.AddCors(options =>
{
  options.AddPolicy("CorsPolicy", builder =>
  {
    builder.WithOrigins(allowedOrigins)
         .WithHeaders(HeaderNames.ContentType, "x-api-version")
         .WithMethods(["GET", "POST", "DELETE", "PUT", "PATCH"])
         .AllowCredentials(); // Nur wenn nötig
  });
});

app.UseCors("CorsPolicy");

Weniger ist mehr: Überflüssige Header vermeiden

Bevor wir neue Header hinzufügen, sollten wir uns um die bestehenden kümmern. Viele Webserver senden Metainformationen mit, etwa den verwendeten Webserver oder die eingesetzte .NET-Version. Header wie Server, X-Powered-By oder X-AspNet-Version verraten unnötige Details über das System. Wer weiß, was im Hintergrund läuft, weiß auch, worauf er zielen kann. Diese Header sollten konsequent entfernt oder neutralisiert werden.

In ASP.NET Core lassen sich diese Header mit wenigen Zeilen Middleware-Code zuverlässig unterdrücken:

app.Use(async (context, next) =>
{
  context.Response.Headers.Remove("X-Powered-By");
  context.Response.Headers.Remove("X-AspNet-Version");
  await next();
});

Andere Header werden im Zweifel vom Webserver gesetzt. Hier ist es wichtig, die Konfiguration des Webservers zu überprüfen und gegebenenfalls anzupassen. Bei IIS kann das über die web.config geschehen, bei Nginx oder Apache über die jeweiligen Konfigurationsdateien:

<configuration>
  <system.webServer>
  <httpProtocol>
    <customHeaders>
    <remove name="X-Powered-By" />
    <remove name="X-AspNet-Version" />
    </customHeaders>
  </httpProtocol>
  </system.webServer>
</configuration>

Die Referrer-Policy ist ein weiterer unterschätzter Punkt. Ohne sie senden Browser bei ausgehenden Links unnötig viel Kontext mit, wie etwa vollständige URLs oder Pfade. Das schafft Einblick in interne Strukturen, den niemand bekommen sollte.

Referer: https://example.com/internal/page?user=12345

Mit der Referrer-Policy können wir steuern, was weitergegeben wird. Eine gute Wahl ist strict-origin-when-cross-origin, die nur den Ursprung (Domain) und nicht den vollständigen Pfad überträgt. So bleibt die interne Struktur verborgen.

Security beginnt damit, unnötige Informationen zu vermeiden. Wer weniger preisgibt, macht es Angreifern schwerer, Schwachstellen zu finden.

Orientierung mit OWASP und Online-Tools

Wer sich unsicher ist, welche Header wichtig sind oder wie die eigene Seite aktuell abschneidet, findet Hilfe bei OWASP. Das HTTP Headers Cheat Sheet gibt eine klare Übersicht mit Empfehlungen.

Für die praktische Prüfung helfen Dienste wie securityheaders.com oder das Mozilla HTTP Observatory. Beide liefern detaillierte Analysen und konkrete Hinweise auf fehlende oder falsch konfigurierte Header.

Die Mozilla-Tools lassen sich sogar über das npm-Paket @mdn/mdn-http-observatory automatisiert in CI/CD-Pipelines einbinden, um Regressionen frühzeitig zu erkennen. Aber auch den OWASP ZAP Scanner, den wir im Artikel Automatisierter Web Security Check mit OWASP ZAP vorgestellt haben, kann hier helfen. Er prüft nicht nur auf Sicherheitslücken, sondern auch auf fehlende Security Header und gibt konkrete Empfehlungen zur Behebung.

Umsetzung in ASP.NET Core

In ASP.NET Core lassen sich Security Headers sehr elegant per Middleware setzen. Das Paket NetEscapades.AspNetCore.SecurityHeaders bietet eine einfache Möglichkeit, sinnvolle Header-Vorgaben per Fluent API zu konfigurieren. Damit lassen sich CSP-Regeln, das Entfernen von Headern oder die Setzung von HSTS (HTTP Strict Transport Security) zentral steuern.

var policyCollection = new HeaderPolicyCollection()
  .AddFrameOptionsDeny() // X-Frame-Options: Deny
  .AddContentTypeOptionsNoSniff() // X-Content-Type-Options: nosniff
  .AddStrictTransportSecurityMaxAgeIncludeSubDomains(maxAgeInSeconds: 60 * 60 * 24 * 365) // HSTS: Gültig für 1 Jahr
  .AddReferrerPolicyStrictOriginWhenCrossOrigin() // Referrer-Policy: strict-origin-when-cross-origin
  .RemoveServerHeader() // Server: ASP.NET Core
  .AddContentSecurityPolicy(builder => // Content-Seucrity-Policy
  {
      builder.AddObjectSrc().None(); // object-src: none
      builder.AddFormAction().Self(); // form-action: self
      builder.AddFrameAncestors().None(); // frame-ancestors: none
  })
  .AddCrossOriginOpenerPolicy(x => x.SameOrigin()); // Cross-Origin-Opener-Policy: same-origin

app.UseSecurityHeaders(policyCollection);

Security Headers sind kein Allheilmittel, aber ein wichtiger Baustein im Gesamtpaket. Ihre Implementierung ist einfach, der Impact jedoch groß. Probleme entstehen meist nicht durch technische Komplexität, sondern durch fehlende Standards, unklare Verantwortlichkeiten oder veraltete Konfigurationen.

Die wichtigsten Learnings aus der Praxis zeigen, dass Präzision bei der Konfiguration einer Content Security Policy (CSP) entscheidend ist, auch wenn dies mit einem gewissen Aufwand verbunden sein kann. Der Nutzen einer gut gepflegten CSP überwiegt jedoch deutlich, da sie eine zuverlässige Absicherung gegen viele Angriffsvektoren bietet.

Ebenso sollte die Konfiguration von Cross-Origin Resource Sharing (CORS) stets so restriktiv wie möglich erfolgen, um ungewollte Datenabflüsse zu verhindern. Bequemlichkeit darf weder hier noch anderswo über Sicherheit stehen. Schließlich sind automatisierte Prüfungen ein unverzichtbares Werkzeug, um Sicherheitsstandards langfristig einzuhalten und Regressionen frühzeitig zu erkennen.

Wer Sicherheit von Anfang an mitdenkt, muss später nicht unter Druck reagieren.

Security Headers sind einfache und wirkungsvolle Schutzmaßnahmen gegen viele Webangriffe. Sie gehören in jede Webanwendung, sollten automatisiert, gepflegt, bewusst eingesetzt und regelmäßig validiert werden.

Nutzt ihr bereits Security Headers, um eure Anwendungen abzusichern? Schau gerne bei uns auf LinkedIn vorbei und diskutiere mit.

Florian Bader

Florian Bader

Florian ist Solution Architect und Microsoft Most Valuable Professional für Azure IoT mit langjähriger Erfahrung im Bereich DevOps, Cloud und Digitalisierung. Er unterstützt Unternehmen dabei, effiziente und effektive Lösungen zu entwickeln, die ihre digitalen Projekte nachhaltig zum Erfolg führen.

Verwandte Beiträge

Entdecken Sie weitere Artikel, die ähnliche Themen behandeln und vertiefen Sie Ihr Wissen. Diese Beiträge wurden basierend auf gemeinsamen Tags und Veröffentlichungsdaten ausgewählt. Viel Spaß beim Weiterlesen!