Gestion des CSP Headers sur Citrix NetScaler
En bref — Le Content-Security-Policy (CSP) est un header HTTP essentiel contre les attaques XSS. Sur NetScaler il intervient à plusieurs niveaux : pages natives de la Gateway, applications proxifiées via Rewrite Policies, module WAF. Depuis les builds 14.1-47.46 / 13.1-59.19, Citrix l’active par défaut — au prix de cassures d’authentification SAML/DUO. La bonne approche : garder la CSP par défaut, la remplacer à la demande par Gateway via une rewrite conditionnelle, puis valider avec le CSP Evaluator. Cet article décortique le fonctionnement, analyse une CSP Gateway réelle directive par directive, et fournit des configurations prêtes à l’emploi.
📋 Sommaire
- Pourquoi cet article
- Ce que fait l’activation CSP par défaut (et ce qu’elle casse)
- Qu’est-ce qu’un CSP Header ?
- Comment NetScaler gère les CSP Headers
- Analyse d’une CSP Citrix Gateway réelle
- Les schémas d’URI propriétaires Citrix
- Implémenter une CSP personnalisée (SAML / DUO)
- Valider avec le CSP Evaluator
- Cas pratique : application legacy
- Bonnes pratiques
- Références
1. Pourquoi cet article
Suite à plusieurs tests de sécurité (pentests) réalisés sur des gateways Citrix que j’administre pour mes clients, j’ai reçu des retours récurrents concernant les Content-Security-Policy headers : CSP absents, trop permissifs, ou mal configurés. Les rapports remontaient systématiquement ces findings, et les équipes sécurité demandaient des corrections — sans toujours comprendre ce que NetScaler faisait réellement avec ces headers, ni quelles corrections étaient légitimes.
Puis, avec les builds 14.1-47.46 et 13.1-59.19 (correctif des CVE-2025-5777 et CVE-2025-6543), Citrix a activé le CSP header par défaut sur toutes les réponses générées par la Gateway et les vServers d’authentification. Démarche « secure by default » louable, mais qui a provoqué des cassures d’authentification SAML chez de nombreux clients : pages de login « cassées », redirections IDP bloquées, intégrations DUO non fonctionnelles (voir CTX694826).
Résultat : des ouvertures de tickets en urgence, des rollbacks de firmware, et beaucoup de confusion entre équipes infra (« c’est le NetScaler qui bloque ») et équipes sécu (« il faut un CSP strict »). Cet article clarifie la situation :
- Comprendre ce que fait l’activation CSP par défaut (
set aaa parameter -defaultCSPHeader ENABLE) et ce qu’elle ne fait pas. - Identifier les problèmes que ça cause (SAML, IDP tiers, scripts custom sur la page de login).
- Implémenter une CSP personnalisée pour une Gateway/vServer donné, différente de la politique par défaut.
- Ajouter des CSP sur vos applications backend qui n’en ont pas, via Rewrite Policies.
2. Ce que fait l’activation CSP par défaut (et ce qu’elle casse)
Depuis les builds 14.1-47.46 / 13.1-59.19, le paramètre defaultCSPHeader est activé par défaut. Concrètement :
- NetScaler injecte un header
Content-Security-Policysur toutes les pages qu’il génère lui-même : page de login, portail VPN, pages d’erreur, réponses AAA. - La politique par défaut est restrictive :
default-src 'self'avec des exceptions limitées. - Toute ressource externe (scripts IDP, iframes SAML, redirections DUO, scripts reCAPTCHA) non whitelistée est bloquée par le navigateur.
Les symptômes typiques
- Page de login qui « ne charge pas » ou s’affiche sans styles/scripts.
- Authentification SAML qui boucle ou échoue silencieusement (redirection IDP bloquée par
frame-ancestorsouform-action). - iframe DUO qui ne s’affiche plus.
- reCAPTCHA invisible qui ne se charge pas.
Désactiver (seulement si nécessaire)
set aaa parameter -defaultCSPHeader DISABLE
save config
flush cache contentgroup ALL
Cela résout le problème immédiatement mais vous laisse sans protection CSP sur vos pages Gateway. À réserver au déblocage en urgence.
💡 La bonne approche : garder la défaut, remplacer à la demande
- Base saine, « secure by default »
- On ne touche rien tant que ça ne casse pas
- Activé partout par le firmware
- Si casse SAML / IDP / double-auth
- Ou pour durcir une Gateway précise
- Rewrite conditionnelle (
replace+.EXISTS)
Dans la majorité des cas, gardez le CSP par défaut activé. N’intervenez que sur demande, par Gateway/vServer : quand le défaut casse quelque chose (SAML, IDP tiers, double authentification à débugger), ou pour durcir une Gateway. Plutôt que de désactiver globalement puis réinjecter, l’approche la plus propre est de remplacer le header CSP existant via une Rewrite Policy conditionnelle — vous conservez le « secure by default » partout ailleurs. Validez ensuite avec le CSP Evaluator (section dédiée).
3. Qu’est-ce qu’un CSP Header ?
Le Content-Security-Policy est un header HTTP envoyé par le serveur au navigateur. C’est toujours le serveur qui l’émet — dans notre cas le NetScaler, jamais le client. Le navigateur se contente de l’appliquer. Son rôle : restreindre les sources depuis lesquelles le navigateur est autorisé à charger des ressources (scripts, images, styles, iframes…), ce qui protège notamment contre les attaques XSS.
4. Comment NetScaler gère les CSP Headers
NetScaler (ADC) peut intervenir sur les CSP de plusieurs façons :
- En reverse proxy : laisser passer les CSP du backend, en injecter, ou supprimer les existants via Rewrite Policies.
- Via le module WAF (AppFirewall) : interaction dans une logique de protection applicative.
- Via les Rewrite Policies : mécanisme le plus courant pour ajouter/remplacer un CSP côté NetScaler sur les applications proxifiées et les gateways.
CSP « par défaut » de NetScaler Gateway
La CSP par défaut s’applique uniquement aux pages servies directement par NetScaler : page de login, portail VPN, pages d’erreur. Elle ne s’applique pas aux applications backend proxifiées. En cas de double CSP (NetScaler + backend), le navigateur applique les deux simultanément : seule l’intersection la plus restrictive est retenue, ce qui peut provoquer des blocages inattendus — et empêcher la prise en compte d’une CSP spécifique à une Gateway si une CSP par défaut est active. Au debug, le navigateur affiche les deux politiques, mais une seule est réellement appliquée.
Tous les clients ne fournissent pas de CSP
C’est l’un des problèmes de sécurité les plus répandus : les applis modernes bien configurées fournissent leur propre CSP strict ; les applis legacy n’en ont souvent aucun ; les applis tierces ont parfois des CSP trop permissifs (ex. default-src *). NetScaler permet d’ajouter ou renforcer des CSP de façon centralisée via Rewrite Policies, sans modifier les backends.
5. Analyse d’une CSP Citrix Gateway réelle
Voici une CSP typique d’une gateway Citrix avec reCAPTCHA, directive par directive.
default-src 'self'
Règle de fallback. Toute ressource non couverte par une directive spécifique ne peut être chargée que depuis le domaine lui-même.
script-src
- Autorise le reCAPTCHA Google (
google.com/recaptcha,gstatic.com/recaptcha). 'unsafe-inline': autorise les scripts inline — risque XSS.'unsafe-eval': autoriseeval()— risque XSS.
Ces deux directives affaiblissent la protection CSP mais sont souvent imposées par des composants Citrix ou le reCAPTCHA.
connect-src 'self'
Restreint les appels réseau (fetch, XHR, WebSocket) au seul domaine. Aucune API externe appelable.
img-src
Très permissif : autorise http: et https: en général, ainsi que le base64 (data:). Le http://localhost:* est lié à des besoins internes Citrix.
style-src 'self' 'unsafe-inline'
Autorise les styles du domaine et les styles inline, souvent nécessaires aux composants Citrix.
font-src 'self' data:
Autorise les polices du domaine et les polices embarquées en base64.
form-action 'self'
Les formulaires ne peuvent soumettre qu’au domaine. Protège contre les redirections malveillantes.
object-src 'none'
Interdit tous les plugins (Flash, Java…). Bonne pratique.
report-uri /nscsp_violation/report_uri
Toute violation est reportée à NetScaler via cet endpoint natif, analysable dans Citrix ADM / NetScaler Console.
6. Les schémas d’URI propriétaires Citrix dans frame-src
La directive frame-src contient des schémas d’URI propriétaires Citrix qui ne sont pas des URLs web classiques. Ils fonctionnent comme des protocol handlers enregistrés sur le poste client (comme mailto: ou tel:), installés avec la Citrix Workspace App. Le navigateur délègue alors au système, qui lance l’application Citrix.
| Schéma | Rôle |
|---|---|
receiver:// | Ancien schéma Citrix Receiver, maintenu pour rétrocompatibilité |
citrixng:// | Schéma Citrix Workspace App (« next generation ») |
com.citrix.agmacepa:// | Déclenchement du scan EPA (End Point Analysis) |
com.citrix.nsgclient:// | Client VPN NetScaler Gateway (tunnel complet) |
nsgcepa:// | Variante du client EPA NetScaler Gateway |
application:// | Schéma générique pour lancer des applications locales |
Sans ces entrées, le navigateur bloquerait toute tentative de la page Gateway de déclencher ces protocoles. Ils sont le pont entre l’interface web de la Gateway et le client natif installé sur le poste.
7. Implémenter une CSP personnalisée sur un Gateway vServer (SAML / DUO)
C’est le scénario le plus courant post-mise à jour : le CSP par défaut casse votre authentification (SAML, IDP tiers, DUO), ou vous voulez durcir une Gateway. Deux approches, toutes deux via des Rewrite Policies liées au VPN vServer en mode RESPONSE (on cible les pages générées par la Gateway, pas un LB vServer) :
- Recommandé — remplacer le CSP par défaut, conditionnellement : on garde le
defaultCSPHeaderactivé et on remplace le header sur la Gateway concernée, sans désactivation globale. - Alternative — désactiver puis réinjecter : si vous préférez repartir d’une page « propre », désactivez le CSP global et injectez le vôtre.
Étape 1 (recommandé) — Remplacer le CSP par défaut, conditionnellement
On garde le CSP par défaut activé et on le remplace uniquement sur la Gateway voulue, lorsqu’il est présent. Action replace + policy conditionnée sur .EXISTS :
add rewrite action rw_act_replace_csp replace "HTTP.RES.HEADER(\"Content-Security-Policy\")" q{"default-src 'self'; script-src https://www.google.com/recaptcha/api.js https://www.gstatic.com/recaptcha/releases/ https://gateway.example.com 'self' 'unsafe-inline' 'unsafe-eval'; connect-src 'self'; img-src 'self' data: http: https:; style-src 'self' 'unsafe-inline'; font-src 'self' data:; frame-src https://www.google.com/recaptcha/ com.citrix.agmacepa://* receiver://* citrixng://* com.citrix.nsgclient://* nsgcepa://* application://* 'self'; child-src 'self' com.citrix.agmacepa://* receiver://* citrixng://* com.citrix.nsgclient://* nsgcepa://nsgcepa application://*; form-action 'self'; object-src 'none'; report-uri /nscsp_violation/report_uri"}
add rewrite policy rw_pol_replace_csp "HTTP.RES.HEADER(\"Content-Security-Policy\").EXISTS" rw_act_replace_csp
bind vpn vserver vs_gateway -policy rw_pol_replace_csp -priority 100 -gotoPriorityExpression END -type RESPONSE
La condition .EXISTS garantit qu’on ne remplace que les réponses portant déjà un CSP (celui du firmware), sur la Gateway ciblée. Vous gardez le « secure by default » partout ailleurs.
Alternative — Désactiver le CSP global puis réinjecter
Si vous préférez désactiver globalement et repartir d’un insert, désactivez d’abord le header par défaut, puis construisez votre CSP selon votre IDP.
set aaa parameter -defaultCSPHeader DISABLE
save config
Domaines à whitelister selon les cas courants : Azure AD / Entra ID (login.microsoftonline.com, login.microsoft.com, aadcdn.msftauth.net, aadcdn.msauth.net) ; ADFS on-prem (FQDN de votre serveur ADFS) ; DUO (*.duosecurity.com, *.duofederal.com) ; reCAPTCHA (www.google.com/recaptcha, www.gstatic.com/recaptcha).
add rewrite action rw_act_csp_gateway insert_http_header Content-Security-Policy "\"default-src 'self'; script-src 'self' 'unsafe-inline' 'unsafe-eval' https://aadcdn.msftauth.net https://aadcdn.msauth.net https://*.duosecurity.com; style-src 'self' 'unsafe-inline' https://aadcdn.msftauth.net; img-src 'self' data: https: http://localhost:*; font-src 'self' data:; connect-src 'self' https://login.microsoftonline.com https://login.microsoft.com https://*.duosecurity.com; frame-src 'self' https://login.microsoftonline.com https://*.duosecurity.com receiver: citrixng: com.citrix.agmacepa: com.citrix.nsgclient: nsgcepa: application:; frame-ancestors 'self'; form-action 'self' https://login.microsoftonline.com https://login.microsoft.com; object-src 'none'; base-uri 'self'; report-uri /nscsp_violation/report_uri\""
Puis la policy (ciblant les réponses HTML générées par la Gateway, et évitant les doublons) et le bind :
add rewrite policy rw_pol_csp_gateway "HTTP.RES.HEADER(\"Content-Type\").CONTAINS(\"text/html\") && HTTP.RES.HEADER(\"Content-Security-Policy\").EXISTS.NOT" rw_act_csp_gateway
bind vpn vserver vs_gateway -policy rw_pol_csp_gateway -priority 100 -gotoPriorityExpression END -type RESPONSE
Approche progressive : Report-Only
Avant d’appliquer en mode bloquant, vous pouvez déployer la politique en Report-Only pour identifier les ressources manquantes, analyser les violations (ADM / console navigateur) quelques jours, ajuster la whitelist, puis basculer.
add rewrite action rw_act_csp_gw_reportonly insert_http_header Content-Security-Policy-Report-Only "\"default-src 'self'; script-src 'self' 'unsafe-inline' 'unsafe-eval' https://aadcdn.msftauth.net https://*.duosecurity.com; form-action 'self' https://login.microsoftonline.com; report-uri /nscsp_violation/report_uri\""
Étape de validation
- Connectez-vous à la Gateway, ouvrez les DevTools (F12).
- Onglet Network → requête de la page de login → vérifiez le header
Content-Security-Policydans les Response Headers. - Onglet Console → vérifiez l’absence de violations CSP (messages en rouge).
- Testez l’authentification SAML complète (redirection IDP → callback → portail), et l’iframe DUO si applicable.
unsafe-inline ?
Possible sur le papier (remplacer 'unsafe-inline' par les hashes SHA-256 des scripts inline de la page de login), mais : non documenté par Citrix, fortement couplé à la version de firmware (toute MAJ peut invalider un hash et casser la page de login), pas de génération dynamique côté NetScaler, et nFactor / login schemas multiplient les hashes à maintenir. À réserver aux exigences absolues (banque, défense). Pour la majorité des déploiements, le compromis 'unsafe-inline' avec une CSP bien construite par ailleurs reste le meilleur ratio sécurité/maintenabilité — à documenter en réponse aux pentests comme contrainte produit.
8. Valider avec le CSP Evaluator
Une fois la politique en place, collez-la dans le CSP Evaluator de Google — d’abord la CSP par défaut, puis votre version durcie — pour voir, directive par directive, ce que chacune apporte et ce qu’elle laisse ouvert.
Ce que l’évaluateur va signaler (et pourquoi c’est normal sur une Gateway)
Attendez-vous à des alertes sur 'unsafe-inline' et 'unsafe-eval' dans script-src. Elles affaiblissent réellement le CSP (elles ré-autorisent les scripts inline et l’exécution de chaînes via eval()), mais le portail de logon Gateway/StoreFront et l’intégration reCAPTCHA en ont besoin — c’est précisément pour ça que la politique par défaut de Citrix les inclut. Les retirer casse la page de login, sauf à passer à un CSP 100 % nonce/hash.
Les alertes plus douces sur les hôtes en allowlist et 'self' (« make sure this URL doesn’t serve JSONP / Angular ») sont des mises en garde, pas des erreurs : un hôte autorisé ne devient un vecteur de contournement que s’il héberge un endpoint JSONP, une lib AngularJS, ou des fichiers uploadés servis en JS. Les URLs reCAPTCHA sont scopées au chemin (bien), et votre propre domaine est maîtrisé.
Verrouillez tout le reste (object-src 'none', form-action 'self', frame-src/child-src scopés, report-uri pour la télémétrie) et assumez unsafe-inline/unsafe-eval comme un compromis produit documenté — utile à mentionner en réponse aux rapports de pentest.
9. Cas pratique : ajouter un CSP sur une application legacy
Votre application RH interne n’envoie aucun header CSP. Voici comment en injecter un via NetScaler.
add rewrite action rw_act_csp_hrapp insert_http_header Content-Security-Policy "\"default-src 'self'; script-src 'self'; style-src 'self' 'unsafe-inline'; img-src 'self' data:; font-src 'self' data:; connect-src 'self'; frame-ancestors 'self'; form-action 'self'; object-src 'none'; base-uri 'self'\""
# Conditionnel : n'injecte que si le backend n'envoie pas déjà un CSP
add rewrite policy rw_pol_csp_hrapp "HTTP.REQ.URL.PATH.STARTSWITH(\"/hrapp\") && HTTP.RES.HEADER(\"Content-Security-Policy\").EXISTS.NOT" rw_act_csp_hrapp
bind lb vserver vs_hrapp -policyName rw_pol_csp_hrapp -priority 100 -gotoPriorityExpression END -type RESPONSE
Vérifiez ensuite via DevTools (F12) → Network → la requête → présence du header dans les Response Headers, Console pour les violations.
10. Bonnes pratiques
✅ Recommandations
replace conditionnel par Gateway..EXISTS / .EXISTS.NOT.report-uri natif + Citrix ADM ; évaluation avant/après.Synthèse
La CSP d’une gateway Citrix typique est fonctionnelle mais imparfaite : unsafe-inline, unsafe-eval et des directives permissives sur les images réduisent la protection réelle contre les XSS. Ces compromis sont souvent inévitables compte tenu des composants Citrix natifs et du reCAPTCHA. Depuis l’activation par défaut (14.1-47.46 / 13.1-59.19), la bonne hygiène consiste à garder la défaut et la remplacer à la demande, par Gateway, puis à valider. Pour vos applications backend, NetScaler offre un point de contrôle centralisé via les Rewrite Policies : un quick win de sécurité significatif sur des dizaines d’applications sans en toucher le code.
Ping : Elyleo