Azure : configurer CSP et HSTS sur App Service, Front Door, Functions et Static Web Apps (guide complet)

Votre application Azure a échoué à un audit sécurité faute de CSP et HSTS ? Voici, pas à pas, comment ajouter, tester et déployer ces en‑têtes selon l’hébergement : App Service (Windows/Linux), Front Door/CDN, Application Gateway, Azure Functions et Static Web Apps.

Sommaire

Problématique

Une application hébergée dans Azure sur un domaine personnalisé n’a pas passé une analyse de cybersécurité : les en‑têtes Content‑Security‑Policy (CSP) et HTTP Strict Transport Security (HSTS) sont manquants ou trop permissifs. L’auteur n’a pas trouvé où les définir dans Azure.

Réponse initiale obtenue

La discussion d’origine n’apportait pas de solution technique ; la recommandation était de poser la question sur le forum Microsoft Q&A. Ci‑dessous, vous trouverez au contraire un guide complet et opérationnel pour implémenter CSP et HSTS selon votre scénario d’hébergement Azure.

Où configurer CSP et HSTS dans Azure

Azure offre plusieurs couches possibles pour injecter des en‑têtes HTTP : dans l’application (code, serveur web), à l’edge (Front Door/CDN) ou au niveau du proxy/LB (Application Gateway). La règle d’or : placer HSTS sur la première réponse HTTPS que voit le navigateur, et propager CSP au plus près de la page qui l’applique.

Scénario d’hébergementOù ajouter ou modifier les en‑têtes
App Service Windows (IIS intégré)Fichier web.config à la racine du site (IIS) pour définir customHeaders et éventuellement la redirection HTTPS.
App Service Linux / conteneur DockerDans le code (ex. Helmet pour Node.js, middleware ASP.NET), ou côté serveur web du conteneur (Nginx/Apache) via le Dockerfile.
Front Door, Application Gateway, CDNDans une règle de réécriture/ingénierie de règles : action « Ajouter/Modifier un en‑tête de réponse » pour injecter CSP et HSTS à l’edge.
Azure FunctionsMiddleware (runtime .NET isolé, Node.js, Python) ou logique commune de réponse. Remarque : host.json ne sert pas à injecter globalement des en‑têtes de réponse ; préférez middleware ou l’edge.
Static Web AppsFichier staticwebapp.config.json (globalHeaders ou headers par route). Front Door peut aussi ajouter ces en‑têtes.

Bonnes pratiques essentielles

  1. Forcer HTTPS avant d’activer HSTS pour éviter de « bloquer » des clients encore en HTTP.
  2. Pour HSTS, visez max-age31536000 (1 an), ajoutez includeSubDomains et, si vous êtes prêts, preload.
  3. Déployez CSP en mode Report‑Only jusqu’à stabilisation, puis passez en mode bloquant.
  4. Testez systématiquement avec des analyseurs d’en‑têtes (ex. SecurityHeaders, Mozilla Observatory, Qualys SSL Labs).

Mise en œuvre par scénario

App Service Windows (IIS) : via web.config

Placez un fichier web.config à la racine du site. Il ajoute les en‑têtes et force la redirection HTTPS. Exemple minimal :

<configuration>
  <system.webServer>
    <rewrite>
      <rules>
        <rule name="HTTP to HTTPS" enabled="true" stopProcessing="true">
          <match url="(.*)" />
          <conditions>
            <add input="{HTTPS}" pattern="off" ignoreCase="true" />
          </conditions>
          <action type="Redirect" url="https://{HTTP_HOST}/{R:1}" redirectType="Permanent" />
        </rule>
      </rules>
    </rewrite>

```
&lt;httpProtocol&gt;
  &lt;customHeaders&gt;
    &lt;add name="Strict-Transport-Security" 
         value="max-age=31536000; includeSubDomains; preload" /&gt;
    &lt;add name="Content-Security-Policy" 
         value="default-src 'self'; object-src 'none'; base-uri 'self'; frame-ancestors 'none';" /&gt;

    &lt;!-- En‑têtes complémentaires conseillés --&gt;
    &lt;add name="X-Content-Type-Options" value="nosniff" /&gt;
    &lt;add name="Referrer-Policy" value="no-referrer-when-downgrade" /&gt;
    &lt;add name="Permissions-Policy" value="geolocation=(), microphone=()" /&gt;
  &lt;/customHeaders&gt;
&lt;/httpProtocol&gt;
```


 

Astuce : si vous retournez beaucoup de redirections (301/302/307) depuis IIS, ajoutez l’attribut always quand c’est possible côté Nginx/Apache (voir plus bas) ; sur IIS, le bloc customHeaders s’applique aux réponses générées par IIS. HSTS n’est pris en compte par les navigateurs que sur des réponses HTTPS.

Validation rapide

  • Déployez web.config, rechargez le site en HTTPS et inspectez la réponse initiale (F12 → Réseau) : cherchez Strict-Transport-Security et Content-Security-Policy.
  • Corrigez les éventuels blocages CSP en mode Report‑Only avant de basculer en bloquant.

App Service Linux ou conteneur Docker

Node.js avec Helmet

import express from 'express';
import helmet from 'helmet';

const app = express();

// HSTS (1 an + sous-domaines + précharge)
app.use(helmet.hsts({
maxAge: 31536000,
includeSubDomains: true,
preload: true
}));

// CSP stricte (adaptez les directives à votre app)
app.use(helmet.contentSecurityPolicy({
useDefaults: false,
directives: {
defaultSrc: ["'self'"],
scriptSrc: ["'self'"],          // ajoutez des nonces/hachages si nécessaire
styleSrc: ["'self'"],
imgSrc: ["'self'","data:"],
connectSrc: ["'self'"],
fontSrc: ["'self'","data:"],
objectSrc: ["'none'"],
baseUri: ["'self'"],
frameAncestors: ["'none'"]
}
}));

// redirection HTTPS si vous terminez TLS ailleurs (Front Door/AppGW)
app.use((req, res, next) => {
const proto = req.headers['x-forwarded-proto'];
if (proto && proto !== 'https') {
return res.redirect(308, 'https://' + req.headers.host + req.originalUrl);
}
next();
});

app.get('/', (req, res) => res.send('OK'));
app.listen(process.env.PORT || 3000); 

ASP.NET Core (hébergement Linux)

// Program.cs (.NET 7/8)
var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();

if (!app.Environment.IsDevelopment())
{
app.UseHsts(); // Configurez si besoin: app.UseHsts(new HstsOptions{ MaxAge = TimeSpan.FromDays(365), IncludeSubDomains = true, Preload = true });
}

// Redirection HTTPS si nécessaire
app.UseHttpsRedirection();

// CSP via middleware simple (ou via librairie spécialisée)
app.Use(async (ctx, next) =>
{
ctx.Response.Headers["Content-Security-Policy"] =
"default-src 'self'; object-src 'none'; base-uri 'self'; frame-ancestors 'none'";
// HSTS est déjà géré par UseHsts sur HTTPS
await next();
});

app.MapGet("/", () => Results.Ok("OK"));
app.Run(); 

Nginx dans un conteneur (ou App Service Linux Custom Container)

Si vous servez l’app via Nginx :

server {
  listen 443 ssl;
  server_name _;
  # ... config TLS ...

# CSP + HSTS (toujours présent, même sur 301/304)

add_header Strict-Transport-Security "max-age=31536000; includeSubDomains; preload" always;
add_header Content-Security-Policy "default-src 'self'; object-src 'none'; base-uri 'self'; frame-ancestors 'none'" always;

location / {
proxy_pass [http://app:3000](http://app:3000);
# proxies, headers, etc.
}
}

server {
listen 80;
return 301 https://$host$request_uri;
} 

Front Door, Application Gateway et CDN

Ajouter HSTS et CSP à l’edge est idéal lorsque plusieurs back‑ends existent ou que vous souhaitez centraliser la politique.

Front Door (Standard/Premium)

  1. Activez « Redirection HTTP → HTTPS » dans la route concernée.
  2. Créez une règle (Rule set) « SecurityHeaders » appliquée à la/aux route(s) : Action → Modifier l’en‑tête de réponse → Ajouter.
  3. Ajoutez Strict-Transport-Security valeur : max-age=31536000; includeSubDomains; preload.
  4. Ajoutez Content-Security-Policy valeur : default-src 'self'; object-src 'none'; base-uri 'self'; frame-ancestors 'none' (puis affinez).
Exemple Bicep (extrait de règle)
resource ruleSet 'Microsoft.Cdn/profiles/securityPolicies@2023-05-01' = {
  name: 'afdx-sec-headers' // exemple
  // ...
}

resource rule 'Microsoft.Cdn/profiles/ruleSets/rules@2023-05-01' = {
name: 'AddSecurityHeaders'
parent: ruleSet
properties: {
order: 1
actions: [
{
name: 'ModifyResponseHeader'
parameters: {
headerAction: 'Overwrite'
headerName: 'Strict-Transport-Security'
value: 'max-age=31536000; includeSubDomains; preload'
}
}
{
name: 'ModifyResponseHeader'
parameters: {
headerAction: 'Overwrite'
headerName: 'Content-Security-Policy'
value: "default-src 'self'; object-src 'none'; base-uri 'self'; frame-ancestors 'none'"
}
}
]
// conditions éventuelles: forcer HTTPS, chemins, etc.
}
} 

Application Gateway (avec ou sans WAF)

  1. Créez un Rewrite set (Règles de réécriture) et associez‑le au listener HTTPS.
  2. Ajoutez des actions « Ajouter un en‑tête de réponse » pour Strict-Transport-Security et Content-Security-Policy.
  3. Optionnel : condition « if scheme = https » pour ne pas émettre HSTS sur HTTP.

CDN (Standard Microsoft/Azure CDN classique)

Utilisez le moteur de règles : Action → Modifier l’en‑tête de réponse, ajoutez HSTS et CSP et appliquez‑les au /*. Assurez la redirection HTTPS au niveau CDN ou back‑end.

Azure Functions

Dans Functions, la méthode fiable est d’injecter les en‑têtes dans le code (middleware ou handler commun), ou à l’edge (Front Door/AppGW). À noter : host.json ne propose pas une clé universelle « headers » pour toutes les réponses ; utilisez plutôt les approches ci‑dessous.

.NET (processus isolé, v4)

Ajoutez un middleware Worker pour enrichir toutes les réponses HTTP :

// Program.cs
using Microsoft.Azure.Functions.Worker;
using Microsoft.Azure.Functions.Worker.Middleware;

var host = new HostBuilder()
.ConfigureFunctionsWorkerDefaults(worker => worker.UseMiddleware())
.Build();
host.Run();

public sealed class SecurityHeadersMiddleware : IFunctionsWorkerMiddleware
{
public async Task Invoke(FunctionContext context, FunctionExecutionDelegate next)
{
await next(context);

```
    var response = await context.GetHttpResponseDataAsync();
    if (response is not null)
    {
        response.Headers.Add("Strict-Transport-Security",
            "max-age=31536000; includeSubDomains; preload");
        response.Headers.Add("Content-Security-Policy",
            "default-src 'self'; object-src 'none'; base-uri 'self'; frame-ancestors 'none'");
    }
}
```

} 

Node.js (modèle de programmation v4)

Centralisez l’ajout d’en‑têtes dans un utilitaire que vous appelez depuis chaque fonction HTTP :

// headers.js
export function withSecurityHeaders(handler) {
  return async (request, context) => {
    const res = await handler(request, context);
    const headers = new Headers(res.headers || {});
    headers.set('Strict-Transport-Security', 'max-age=31536000; includeSubDomains; preload');
    headers.set('Content-Security-Policy', "default-src 'self'; object-src 'none'; base-uri 'self'; frame-ancestors 'none'");
    return new Response(res.body, { status: res.status, headers });
  };
}

// index.js
import { app } from '@azure/functions';
import { withSecurityHeaders } from './headers.js';

app.http('hello', {
methods: ['GET'],
authLevel: 'anonymous',
handler: withSecurityHeaders(async (req, ctx) => {
return { status: 200, body: 'OK' };
})
}); 

Static Web Apps

Pour une SWA, ajoutez un fichier staticwebapp.config.json à la racine de sortie (output) de votre build. Deux options :

{
  "globalHeaders": {
    "Strict-Transport-Security": "max-age=31536000; includeSubDomains; preload",
    "Content-Security-Policy": "default-src 'self'; object-src 'none'; base-uri 'self'; frame-ancestors 'none'"
  }
}

ou, si vous avez des politiques différentes selon les chemins :

{
  "routes": [
    {
      "route": "/*",
      "headers": {
        "Strict-Transport-Security": "max-age=31536000; includeSubDomains; preload",
        "Content-Security-Policy": "default-src 'self'; object-src 'none'; base-uri 'self'; frame-ancestors 'none'"
      }
    }
  ]
}

Exemples de politiques CSP prêtes à adapter

Base stricte pour application SPA/SSR

default-src 'self';
base-uri 'self';
object-src 'none';
frame-ancestors 'none';
script-src 'self';
style-src  'self';
img-src    'self' data:;
font-src   'self' data:;
connect-src 'self';

Avec Azure AD / Microsoft Entra ID (authentification)

Si vous utilisez des bibliothèques d’authentification Microsoft (MSAL), autorisez leurs hôtes.

default-src 'self';
base-uri 'self';
object-src 'none';
frame-ancestors 'none';
script-src 'self' https://js.monitor.azure.com https://aadcdn.msftauth.net https://aadcdn.msauth.net;
style-src  'self' 'unsafe-inline';
img-src    'self' data:;
font-src   'self' data:;
connect-src 'self' https://login.microsoftonline.com https://graph.microsoft.com;

Avec contenu statique Azure Storage

default-src 'self';
img-src 'self' data: https://*.blob.core.windows.net;
font-src 'self' data: https://*.blob.core.windows.net;
style-src 'self' 'unsafe-inline';
script-src 'self';
connect-src 'self';

Gestion des scripts inline : nonces et hachages

Évitez 'unsafe-inline'. Préférez des nonces ou des hachages :

  • Nonce : générez un nonce cryptographiquement sûr par requête, insérez‑le dans la balise <script nonce="..."> et dans script-src 'nonce-...'.
  • Hash : calculez un hachage (SHA256/384/512) du contenu exact du script inline, puis ajoutez‑le dans script-src 'sha256-...'.

Mode rapport uniquement

Pour tester sans bloquer, utilisez Content-Security-Policy-Report-Only avec la même politique et stabilisez‑la avant de passer en blocage. Pensez à définir un endpoint de collecte (Reporting API) si vous exploitez les rapports.


HSTS : recommandations et pièges

  • Paramètres : Strict-Transport-Security: max-age=31536000; includeSubDomains; preload
  • Portée : n’émettez HSTS que sur HTTPS. Le navigateur ignore HSTS reçu via HTTP.
  • Précharge : n’ajoutez preload que si tous vos sous‑domaines sont servis en HTTPS en permanence. Ensuite, enregistrez votre domaine dans la liste de préchargement HSTS.
  • Dév/Local : évitez HSTS en local pour ne pas « casser » http://localhost. Activez‑le uniquement en environnements d’intégration/staging/production en HTTPS.
  • Edge vs. Back‑end : émettre HSTS à l’edge (Front Door/CDN) garantit que c’est la première réponse vue par le navigateur, même si votre back‑end fait des redirections.

Plan de déploiement sûr

  1. Cartographier les origines nécessaires (API, polices, images, telemetry) et supprimer le superflu.
  2. Appliquer CSP en Report‑Only pendant quelques jours ; collecter les violations.
  3. Durcir progressivement : remplacez les jokers (*) par des hôtes précis, remplacez 'unsafe-inline' par des nonces/hachages.
  4. Activer HSTS après avoir validé la redirection HTTPS partout.
  5. Basculer CSP en blocage et surveiller les journaux (Application Insights, SIEM).
  6. Option précharge : ajoutez l’attribut preload et inscrivez le domaine au registre HSTS Preload lorsque tout est stable.

Tests et contrôle qualité

Vérifiez vos en‑têtes avec plusieurs méthodes :

  • Navigateurs : onglet Réseau, inspectez la première réponse HTML (status 200/304) pour y voir CSP et HSTS.
  • Ligne de commande : curl -I https://votre‑domaine doit afficher Strict-Transport-Security et Content-Security-Policy.
  • Scanners : utilisez des outils d’audit reconnus (SecurityHeaders, Mozilla Observatory, Qualys SSL Labs).

Dépannage : erreurs courantes et correctifs

SymptômeCause probableCorrectif
HSTS absent sur la première requêteRedirection HTTP→HTTPS gérée côté back‑end, en‑tête supprimé avant l’edgeDéplacez HSTS à l’edge (Front Door/CDN/AppGW) et activez la redirection HTTPS au même niveau
CSP trop permissive default-src *Politique de départ « tout ouvert »Remplacez * par des origines précises ; séparez connect-src, img-src, etc.
Fonctionnalité cassée après passage en blocageScripts inline bloquésImplémentez des nonces/hachages, ou refactorez pour éviter l’inline
Doublon d’en‑têtesEn‑têtes ajoutés à l’edge et dans l’appHarmonisez : soit edge, soit back‑end. Un doublon n’est pas critique, mais simplifiez la maintenance
Azure Functions : impossible d’ajouter via host.jsonMauvaise compréhension de host.jsonUtilisez un middleware (worker isolé .NET) ou un wrapper de réponse (Node/Python), ou ajoutez à l’edge

Exemples supplémentaires prêts à copier

Fichier web.config complet (IIS/App Service Windows)

<configuration>
  <system.webServer>
    <rewrite>
      <rules>
        <rule name="HTTP to HTTPS" enabled="true" stopProcessing="true">
          <match url="(.*)" />
          <conditions>
            <add input="{HTTPS}" pattern="off" />
          </conditions>
          <action type="Redirect" url="https://{HTTP_HOST}/{R:1}" redirectType="Permanent" />
        </rule>
      </rules>
    </rewrite>

```
&lt;httpProtocol&gt;
  &lt;customHeaders&gt;
    &lt;add name="Strict-Transport-Security" value="max-age=31536000; includeSubDomains; preload" /&gt;
    &lt;add name="Content-Security-Policy" value="default-src 'self'; object-src 'none'; base-uri 'self'; frame-ancestors 'none'" /&gt;
    &lt;add name="Referrer-Policy" value="no-referrer-when-downgrade" /&gt;
    &lt;add name="X-Content-Type-Options" value="nosniff" /&gt;
    &lt;add name="Permissions-Policy" value="geolocation=(), microphone=(), camera=()" /&gt;
  &lt;/customHeaders&gt;
&lt;/httpProtocol&gt;
```


 

Dockerfile Nginx (extrait)

FROM nginx:alpine
COPY ./nginx.conf /etc/nginx/conf.d/default.conf
# Nginx ajoute HSTS + CSP (voir nginx.conf ci‑dessus)

Bicep : règle de sécurité Front Door (variante)

resource fdRuleSet 'Microsoft.Cdn/profiles/ruleSets@2023-05-01' existing = {
  name: 'myRuleSet'
  scope: resourceGroup()
}

resource addCsp 'Microsoft.Cdn/profiles/ruleSets/rules@2023-05-01' = {
name: 'CspAndHsts'
parent: fdRuleSet
properties: {
order: 1
actions: [
{
name: 'ModifyResponseHeader'
parameters: {
headerAction: 'Overwrite'
headerName: 'Content-Security-Policy'
value: "default-src 'self'; object-src 'none'; base-uri 'self'; frame-ancestors 'none'"
}
}
{
name: 'ModifyResponseHeader'
parameters: {
headerAction: 'Overwrite'
headerName: 'Strict-Transport-Security'
value: 'max-age=31536000; includeSubDomains; preload'
}
}
]
}
} 

Checklist finale

  • HTTPS forcé au niveau pertinent (edge ou app)
  • HSTS actif sur toutes les réponses HTTPS
  • CSP en Report‑Only puis en blocage
  • Directives CSP affinées (nonces/hachages, origines minimales)
  • Tests croisés navigateurs + scanners
  • Surveillance (journaux violations CSP, erreurs réseau, télémétrie)
  • Option : préchargement HSTS une fois l’écosystème entièrement en HTTPS

Conclusion

Que votre application tourne sur App Service Windows, Linux, un conteneur, Azure Functions ou soit exposée via Front Door/CDN/Application Gateway, vous pouvez implémenter CSP et HSTS sans refonte majeure. Placez HSTS là où le navigateur le voit en premier, durcissez CSP progressivement, et validez par des tests outillés : votre prochaine analyse de sécurité devrait passer haut la main.

Sommaire