Nginx : sécurité
Des réglages simples réduisent fortement la surface d’attaque d’un serveur Nginx. Les exemples utilisent des noms génériques.
Masquer la version
Section intitulée « Masquer la version »Dans /etc/nginx/nginx.conf, contexte http, modifier la ligne existante :
server_tokens off;| Ubuntu | Ligne d’origine |
|---|---|
| 24.04 | # server_tokens off; (en commentaire : retirer le #) |
| 26.04 | server_tokens build; (remplacer build par off) |
Les en-têtes de réponse et les pages d’erreur n’indiquent plus la version de Nginx. Vérifier : curl -sI http://localhost | grep -i server doit afficher server: nginx seulement.
En-têtes de sécurité
Section intitulée « En-têtes de sécurité »sudo tee /etc/nginx/snippets/securite.conf <<'CONF'add_header X-Content-Type-Options "nosniff" always;add_header X-Frame-Options "SAMEORIGIN" always;add_header Referrer-Policy "strict-origin-when-cross-origin" always;add_header Permissions-Policy "camera=(), microphone=(), geolocation=()" always;CONFserver { # … include snippets/securite.conf; add_header Strict-Transport-Security "max-age=31536000" always;}| En-tête | Rôle |
|---|---|
X-Content-Type-Options: nosniff |
Empêche le navigateur de deviner le type des fichiers |
X-Frame-Options: SAMEORIGIN |
Interdit l’affichage du site dans une iframe d’un autre site |
Referrer-Policy |
Limite les informations transmises dans l’en-tête Referer |
Permissions-Policy |
Désactive des fonctions du navigateur inutiles |
Strict-Transport-Security |
Impose HTTPS (voir HTTPS et certificats) |
Content-Security-Policy |
Liste les origines autorisées pour scripts, styles, images (à tester soigneusement) |
Authentification basique
Section intitulée « Authentification basique »sudo apt install apache2-utils # fournit htpasswdsudo htpasswd -c /etc/nginx/.htpasswd alice # crée le fichier avec un premier utilisateursudo htpasswd /etc/nginx/.htpasswd bob # ajoute un utilisateursudo chown root:www-data /etc/nginx/.htpasswdsudo chmod 640 /etc/nginx/.htpasswdlocation /prive/ { auth_basic "Zone privée"; auth_basic_user_file /etc/nginx/.htpasswd; try_files $uri $uri/ =404;}Pour protéger tout le site, placer les deux directives dans le bloc server. Pour exempter un chemin : auth_basic off; dans son location.
Restreindre par adresse IP
Section intitulée « Restreindre par adresse IP »location /admin/ { allow 192.168.1.0/24; allow 203.0.113.10; deny all; proxy_pass http://127.0.0.1:9000;}Les règles sont évaluées dans l’ordre. Combiner IP et mot de passe : satisfy all; (les deux requis) ou satisfy any; (l’un des deux suffit).
Bloquer les fichiers cachés et sensibles
Section intitulée « Bloquer les fichiers cachés et sensibles »# Fichiers cachés (.git, .env…) sauf .well-known (certificats)location ~ /\.(?!well-known) { deny all;}
# Sauvegardes et fichiers de configurationlocation ~* \.(bak|sql|log|conf|ini|swp)$ { deny all;}Ne jamais déployer .git ni .env dans le dossier servi, même avec ces règles.
Limiter la taille des envois et les délais
Section intitulée « Limiter la taille des envois et les délais »client_max_body_size 10m; # corps de requêteclient_body_timeout 15s;client_header_timeout 15s;send_timeout 30s;keepalive_timeout 30s;Limiter le débit : limit_req et limit_conn
Section intitulée « Limiter le débit : limit_req et limit_conn »# Contexte httplimit_req_zone $binary_remote_addr zone=general:10m rate=10r/s;limit_req_zone $binary_remote_addr zone=connexion:10m rate=5r/m;limit_conn_zone $binary_remote_addr zone=parip:10m;
server { location / { limit_req zone=general burst=20 nodelay; # 10 requêtes/s, avec rafales jusqu'à 20 limit_conn parip 20; # 20 connexions simultanées par IP }
location = /connexion { limit_req zone=connexion burst=3 nodelay; # 5 par minute : freine les essais de mots de passe limit_req_status 429; proxy_pass http://127.0.0.1:3000; }}| Directive | Rôle |
|---|---|
limit_req_zone |
Définit une zone (clé, mémoire, débit) dans le contexte http |
limit_req |
Applique la limite (burst : rafale tolérée, nodelay : pas d’attente) |
limit_conn |
Nombre de connexions simultanées par clé |
limit_req_status 429 |
Code renvoyé (503 par défaut) |
Sans nodelay, les requêtes en excès dans la limite de burst ne sont pas refusées mais retardées pour respecter le débit (12 secondes d’attente chacune avec 5 requêtes par minute) : nodelay les traite aussitôt et refuse celles qui dépassent le burst. Avec burst=3 nodelay, les 4 premières requêtes passent puis les suivantes reçoivent 429.
Derrière un proxy ou un CDN, remplacer $binary_remote_addr par l’adresse réelle du client (voir Reverse proxy).
Refuser les méthodes inutiles
Section intitulée « Refuser les méthodes inutiles »if ($request_method !~ ^(GET|HEAD|POST)$) { return 405;}Fail2ban : bloquer les adresses abusives
Section intitulée « Fail2ban : bloquer les adresses abusives »sudo apt install fail2bansudo tee /etc/fail2ban/jail.d/nginx.local <<'CONF'[nginx-http-auth]enabled = true
[nginx-limit-req]enabled = true
[nginx-botsearch]enabled = trueCONFsudo systemctl restart fail2bansudo fail2ban-client status nginx-http-auth| Jail | Détecte |
|---|---|
nginx-http-auth |
Échecs de l’authentification basique |
nginx-limit-req |
Dépassements de limit_req (journalisés dans error.log) |
nginx-botsearch |
Recherche de scripts et pages inexistantes |
Limiter les protocoles à TLSv1.2 et TLSv1.3 (valeur par défaut du nginx.conf d’Ubuntu 26.04) : voir HTTPS et certificats.
Privilèges et système
Section intitulée « Privilèges et système »- Nginx tourne sous
www-data: ne pas donner à cet utilisateur de droit d’écriture sur le code du site. - Pare-feu UFW actif (voir Ubuntu Server : actions après l’installation).
- Mises à jour automatiques de sécurité (
unattended-upgrades). - Surveiller
error.logetaccess.log(voir Performances et journaux).
Vérifier
Section intitulée « Vérifier »sudo nginx -t && sudo systemctl reload nginxcurl -sI https://exemple.com | grep -iE "server|strict|x-content|x-frame|referrer"curl -I https://exemple.com/.git/config # doit répondre 403 ou 404curl -u alice:mauvais https://exemple.com/prive/ # 401for i in $(seq 1 30); do curl -s -o /dev/null -w "%{http_code} " https://exemple.com/; done; echo # 429 ou 503 attendus après la limiteDépannage
Section intitulée « Dépannage »| Symptôme | Piste |
|---|---|
| Les en-têtes de sécurité disparaissent sur certaines pages | add_header d’un location qui remplace ceux du server (voir l’avertissement ci-dessus) |
401 en boucle |
Mot de passe incorrect, ou fichier .htpasswd illisible pour www-data |
Des visiteurs légitimes reçoivent 429 ou 503 |
Augmenter rate ou burst ; clé de limitation incorrecte derrière un proxy |
limit_req_zone : unknown directive |
Directive à placer dans le contexte http, pas dans server |
Le fichier .well-known est bloqué (certificats) |
Garder l’exception (?!well-known) dans la règle des fichiers cachés |
Ubuntu 24.04 et 26.04 : différences à connaître
Section intitulée « Ubuntu 24.04 et 26.04 : différences à connaître »Pour aller plus loin
Section intitulée « Pour aller plus loin »- Documentation : Module
ngx_http_limit_req_module - Suite : Performances et journaux