Comment fait le code d'une application web pour savoir sur quel domaine il opère ? Dans bien des cas, il se base sur la valeur de l'en-tête HTTP Host.
Or cette valeur n'est pas du tout fiable et peut être surchargée par le premier pimpin venu :
curl --header "Host: attacker.com" http://127.0.0.1:8000
All the headers in HTTP requests are belong to us.
Une fausse valeur d'hôte a la capacité d'être exploitée par un attaquant via Cross-Site Request Forgery, empoisonnement de cache, empoisonnement des liens des e-mails etc.
On parle alors d'attaques par en-tête HTTP Host
. En anglais : Host Header Injection ou Host Header Attack.
Django permet de mitiger cette attaque avec une liste blanche via le setting ALLOWED_HOSTS
inspecté dans get_host
à chaque requête si CommonMiddleware
est installé.
Si vous êtes derrière un proxy qui vous force à utiliser l'en-tête X-Forwarded-Host
, alors il vous faudra le déclarer explicitement dans les settings de Django. Cette syntaxe d'en-têtes non standards avec des préfixes en X-
est désormais dépréciée (RFC 6648) et remplacée (en théorie) par une alternative détaillée dans le RFC 7239, mais rien n'est plus permanent qu'une solution temporaire :
[…] when non-standard headers prefixed with
X-
become standard, removing theX-
prefix breaks backwards compatibility, forcing application protocols to support both names […]
Les attaques par en-tête HTTP Host
sont exploitées dans le wild en masse par tous les outils de hacking automatisés.
Avec Django ça se voit au nombre d'erreurs Invalid HTTP_HOST header
qui remontent.
Une fois que votre application Django est bien configurée, il est possible de refuser les requêtes HTTP avec des en-têtes Host
illégitimes en amont au niveau d'Nginx en renvoyant un code de statut 444
:
if ($http_host != my.domain.com) {
return 444;
}
Sinon, si vous n'avez pas la main sur le serveur web, vous pouvez aussi taire ces erreurs en modifiant la configuration du logging :
'handlers': {
'null': {
'class': 'logging.NullHandler',
},
},
'loggers': {
'django.security.DisallowedHost': {
'handlers': ['null'],
'propagate': False,
},
},
Enfin, pour faire taire Sentry, vous pouvez utiliser ignore_logger
:
from sentry_sdk.integrations.logging import ignore_logger
ignore_logger("django.security.DisallowedHost")