Mise en place des headers de sécurité avec NWebSec pour une Azure WebApp

Dans cet article, nous allons prendre une application ASP.Net Core 2 très simple qui sera hébergée dans une Azure WebApp et nous allons configurer les headers de sécurité avec NWebSec.

Le sujet de la sécurité étant très vaste, nous allons avoir une approche assez pragmatique. Il vous faudra prendre du temps pour creuser et étudier toutes les options qui sont mises à votre disposition pour ces headers.

Nous allons partir du template Asp.Net Core 2 Web Application MVC dans Visual Studio 2017.

Je vous laisse créer une petite webapp de test sur Azure et déployer l'application. Dans cet article, mon application a cette url: http://securityheaderstest.azurewebsites.net/.

Vous allez vous rendre sur le site suivant: https://securityheaders.io et entrer l'adresse de votre WebApp. Si tout se passe normalement, vous devriez avoir ce résultat :

Ce qu'il faut retenir de ce rapport est qu'aucun des 5 headers de sécurité attendus n'est présent et que le site fonctionne en HTTP.

Les headers qui vont nous intéresser sont donc :

  • Content-Security-Policy
  • X-Frame-Options
  • X-XSS-Protection
  • X-Content-Type-Options
  • Referrer-Policy

Vous pouvez maintenant refaire le scan en cliquant sur le lien (Scan again over https), on obtient toujours un F et un sixième header est apparu :

  • Strict-Transport-Security

Revenons dans notre solution dans Visual Studio et installons le package NWebSec :

Install-Package NWebsec.AspNetCore.Middleware

Nous sommes prêts maintenant à corriger tous les headers manquants.

X-Frame-Options

Utilité : Sert à bloquer les iframes et prévenir le clickjacking.

Placez la ligne qui suit après app.UseStaticFiles(); dans votre fichier Startup.cs :

app.UseXfo(options => options.SameOrigin());

Attention, si vous utilisez l'AntiforgeryToken, vous devez changer l'enregistrement comme suit, sinon l'option choisie avec NWebsec sera écrasée par celle envoyée par l'AntiforgeryToken.

services.AddAntiforgery(opt => opt.SuppressXFrameOptionsHeader = true);

Pour aller plus loin :

X-XSS-Protection

Utilité : Sert à prévenir d'une attaque de type cross site scripting (XSS).

Placez la ligne qui suit après app.UseStaticFiles(); dans votre fichier Startup.cs :

app.UseXXssProtection(options => options.EnabledWithBlockMode());

Pour aller plus loin :

X-Content-Type-Options

Utilité : Sert à bloquer les attaques de type MIME type sniffing.

Placez la ligne qui suit avant app.UseStaticFiles(); dans votre fichier Startup.cs :

app.UseXContentTypeOptions();

Pour aller plus loin :

Referrer-Policy

Utilité : Sert à contrôler la quantité d'informations envoyées par le navigateur lorsque l'on navigue vers un autre site.

Placez la ligne qui suit avant app.UseStaticFiles(); dans votre fichier Startup.cs :

app.UseReferrerPolicy(opts => opts.NoReferrer());

Pour aller plus loin :

Strict-Transport-Security

Utilité : Sert à forcer la communication en HTTPS. Vous devez donc avoir un certificat valide avant de commencer cette section.

Ce header demande des précautions supplémentaires lors de sa mise en place car vous pourriez rendre votre site inaccessible très longtemps.

Vous devez augmenter la variable max-age progressivement afin de vous assurer que votre site est toujours accessible et qu'en cas de problème votre site ne sera pas indisponible trop longtemps.

Je vous conseille de commencer par 30 s et de tester l'impact sur votre site.

Placez la ligne qui suit avant app.UseStaticFiles(); dans votre fichier Startup.cs :

app.UseHsts(opt => opt.MaxAge(seconds: 30));

Si tout se passe bien et que vous voulez protéger tous les sous-domaines, vous pouvez inclure la directive includeSubDomains, toujours avec max-age à 30 s :

app.UseHsts(opt => opt.MaxAge(seconds: 30).IncludeSubdomains());

Si tout va bien, vous pouvez maintenant faire progresser la valeur de max-age à 5 min par exemple et ainsi de suite jusqu'à atteindre 1 an.

La dernière étape est d'indiquer que le site n'autorise plus que le HTTPS avec la directive preload. Attention, vous devez être sûr que tout fonctionne sans aucun problème avant d'appliquer cette directive. Le HSTS preloading est un point de non-retour, votre site sera inscrit sur les listes mondiales n'acceptant plus que le HTTPS. (Vous pouvez demander à sortir de cette liste, mais cela demandera du temps et il ne sera pas garanti que toutes les listes seront nettoyées).

Si vous êtes prêts et que vous avez bien compris les impacts de la directive preload :

app.UseHsts(opt => opt.MaxAge(days: 365).IncludeSubdomains().Preload());

Enfin, pensez bien à mettre une redirection vers HTTPS tant que vous n'utilisez pas la directive preload.

Pour aller plus loin :

Content-Security-Policy

Utilité : Sert à définir les sources de confiance qui distribuent vos contenus (JS, CSS, images, fonts).

Ce header existe en deux variantes :

  • Content-Security-Policy : bloque réellement les contenus dont les origines ne sont pas autorisées
  • Content-Security-Policy-Report-Only : ne bloque rien, mais vous prévient des actions prises par le navigateur pour bloquer certains contenus

Avant de commencer, remplacez dans le fichier _Layout.cshtml les directives de chargements des CSS et du JS.

Remplacer le code de chargement CSS par celui-ci :

<link rel="stylesheet" href="https://ajax.aspnetcdn.com/ajax/bootstrap/3.3.7/css/bootstrap.min.css" />
<link rel="stylesheet" href="~/css/site.css" />

Remplacer le code de chargement JS par celui-ci:

<script src="https://ajax.aspnetcdn.com/ajax/jquery/jquery-2.2.0.min.js"
        crossorigin="anonymous"
        integrity="sha384-K+ctZQ+LL8q6tP7I94W+qzQsfRV2a+AfHIi9k8z8l9ggpc8X+Ytst4yBo/hH+8Fk">
</script>
<script src="https://ajax.aspnetcdn.com/ajax/bootstrap/3.3.7/bootstrap.min.js"
        crossorigin="anonymous"
        integrity="sha384-Tc5IQib027qvyjSMfHjOMaLkfuWVxZxUPnCJA7l2mCWNIpG9mGCD8wGNIcPD7Txa">
</script>
<script src="~/js/site.min.js" asp-append-version="true"></script>

Retournez dans le Startup.cs et placez la ligne qui suit après app.UseStaticFiles(); :

app.UseCsp(opt => opt.DefaultSources(s => s.Self()));

Si vous lancez votre site en local, vous verrez qu'il s'affiche mal et que les animations ne fonctionnent plus.

Ouvrez la console de votre browser et faites un refresh, vous verrez que le navigateur a bloqué le chargement de Bootstrap et jQuery.

Je vous conseille d'ajouter le code qui suit ligne par ligne et de faire un refresh pour voir les erreurs dans la console :

app.UseCsp(opt => opt
    .DefaultSources(s => s.Self())
    .StyleSources(s => s.Self()
                        .CustomSources("ajax.aspnetcdn.com"))
    .ScriptSources(s => s.Self()
                         .CustomSources("ajax.aspnetcdn.com"))
    .FontSources(s => s.CustomSources("ajax.aspnetcdn.com")));

Je vous conseille aussi de mettre en place les rapports en utilisant la fonction ReportUris après FontSources. Pour les rapports, vous pouvez utiliser le service Report-Uri par exemple.

Pour aller plus loin :

Si vous republiez votre application sur Azure et que vous scannez en HTTPS, vous devriez obtenir un A+ :

Le post étant un peu long, je vous conseille d'y aller tranquillement header par header. Prenez bien le temps de lire la documentation de NWebsec et de Mozilla, comprenez bien à quoi servent chaque en-tête et ses options.

Vous pouvez refaire un scan de votre site après chaque publication et voir votre note évoluer.