Web Security für alle: X-Content-Type-Options Web Security für alle: HSTS

Web Security für alle: X-Frame-Options

Published on Friday, September 23, 2016 4:00:00 AM UTC in Programming & Security

Ich erinnere mich an eine Zeit, in der es ein Katz- und Maus-Spiel gab zwischen Seitenbetreibern und fragwürdigen "Diensten" im Internet, die fremde Seiten in iFrames eingebettet angezeigt haben - man sprach von "frame busting"-Techniken, um dieses ungebetene Einbetten der eigenen Inhalte loszuwerden. Irgendwann wurde erkannt, dass dieses Einbetten nicht nur inhaltliche Aspekte hat, sondern auch ein ernsthaftes Sicherheitsproblem darstellt: Clickjacking kann dazu genutzt werden, Mausklicks und Tastatureingaben unbemerkt und ungewollt umzuleiten, etwa um Passwörter und ähnliche sensible Daten zu stehlen. Vorhandene Cheat-Sheets zu dem Thema zeigen schon, wie komplex dieses scheinbar einfache Thema ist, und wie wirkungslos viele Versuche, sich dagegen zu wehren.

Die heutzutage empfohlene Variante ist das Senden eines http-Headers namens X-Frame-Options, idealerweise mit dem Hinweis, dass das Einbetten der Seite nicht gestattet ist. Dann übernimmt der Browser die Verantwortung, dass die Seite nicht in Frames oder als Object eingebunden wird, und man muss nicht fehleranfällige eigene Wege per Skripts o.ä. wählen. Die Implementierung ist denkbar einfach.

Umsetzung des X-Frame-Options-Headers in ASP.NET Core

Als Grundlage dient die Basisimplementierung vom letzten Mal. Zunächst einmal gilt es, die Konfiguration und die zugehörigen Klassen zu erweitern:

"SecurityHeadersOptions": {  
  "XFrameOptions": {
    "IsEnabled": true,
    "Directive": "DENY"
  } 
} 
public class SecurityHeadersOptions
{
    public XFrameOptions XFrame { get; set; }

    public class XFrameOptions
    {
        public bool IsEnabled { get; set; }
        public XFrameOptionsDirective Directive { get; set; }

        public enum XFrameOptionsDirective
        {
            DENY,
            SAMEORIGIN
        }
    }
    
    // ...
}

Da der dritte mögliche Wert für die Direktive (ALLOW-FROM) deprecated ist und künftig falls gewünscht durch den Content-Security-Policy-Header abgebildet werden soll (zu dem Header später mehr), habe ich diese Option gar nicht erst abgebildet. Im Code-Beispiel sieht man übrigens auch, dass .NET Core die Abbildung von Strings (in der appsettings.json) auf die zugehörigen Enum-Werte problemlos meistert, wenn die Konfiguration geladen wird.

Der eigentliche Header wird dann wieder in der Middleware geschrieben, etwa so:

private Task ApplyHeaders(object state)
{
    var context = (HttpContext)state;

    ApplyHstsHeader(context);
    ApplyXFrameOptionsHeader(context); // <= new and shiny header :)
            
    return Task.CompletedTask;
}

private void ApplyXFrameOptionsHeader(HttpContext context)
{
    var xFrameOptions = _options.XFrame;

    if (!xFrameOptions.IsEnabled)
    {
        return;
    }

    context.Response.Headers["X-Frame-Options"] = xFrameOptions.Directive.ToString();
}

// ...

Und das war's in diesem Fall schon. Mit dem Wert DENY wird das Einbetten der eigenen Seite untersagt, mit SAMEORIGIN könnte man zumindest noch das Einbetten in andere eigene Seiten erlauben. Mehr Details zur Bedeutung und Handhabung des Headers gibt es wie immer bei Mozilla.

Resultat

Auch diese Verbesserung lässt die eigene Seite wieder in der Bewertungsleiter klettern:

grade-with-x-frame-options-header.png

Es wird.

Tags: ASP.NET Core · Clickjacking