• # En-tête Host et SNI

    Posté par (site web personnel, Mastodon) . En réponse au message [Résolu] Questions protocoles Internet (IP & RTSP). Évalué à 10. Dernière modification le 08 avril 2025 à 12:23.

    Bonjour,

    Prenons un exemple avec des requêtes à LinuxFR.

    $ host linuxfr.org
    linuxfr.org has address 213.36.253.103
    linuxfr.org mail is handled by 50 main.linuxfr.org.
    

    Le serveur de linuxfr.org est joignable à l'adresse IP 213.36.253.103. Pourtant, comme vous l'avez constaté sur d'autres sites, si on essaie de joindre https://213.36.253.103 sans précaution, on n'obtient pas la page d'accueil de LinuxFR. On peut reproduire dans un terminal avec curl ce que fait un navigateur :

    $ curl --insecure https://213.36.253.103
    <!DOCTYPE html>
    <!-- Managed by Ansible, do not edit by hand -->
    <html lang="fr">
    <head>
    <meta charset="utf-8">
    <title>Serveur oups.linuxfr.org - LinuxFr.org</title>
    </head>
    <body><h1>Serveur oups.linuxfr.org - LinuxFr.org</h1>
    <p>Si vous êtes arrivé(e) sur cette page, c'est probablement parce que vous kiffez l'HTML 2.</p>
    </body>
    </html>
    

    La raison à ceci est qu'un même serveur peut, sur une même IP, servir plusieurs sites Web différents. Quand il reçoit une connexion HTTP, il a besoin de savoir à quel site elle se rapporte. Or notre requête ne lui fournit pas cette information. Dans le protocole HTTP, elle est donnée dans l'en-tête Host, qu'on peut demander à curl d'envoyer comme ceci :

    $ curl --insecure -H "Host: linuxfr.org" https://213.36.253.103
    <!DOCTYPE html>
    <html lang="fr">
    <head>
    <meta charset="utf-8">
    <title>Accueil - LinuxFr.org</title>
    [...]
    

    Maintenant, il faut que j'explique pourquoi on a eu besoin de --insecure. Dans le bon vieux temps, les requêtes étaient faites en HTTP simple, et le Host suffisait. Mais est arrivé le HTTPS, qui rajoute une couche de chiffrement TLS par dessus le HTTP. Or, dans le début de la connexion TLS, avant même que des données soient transmises, le client est censé vérifier que le serveur a fourni un certificat, qui atteste qu'il est bien le serveur qu'il prétend être. Donc, si un serveur propose plusieurs sites, quand il reçoit une requête, il ne pouvait pas encore savoir quel site voulait le client, et devait forcément fournir un certificat valide pour tous les sites possibles. Le hic, c'est qu'il peut être compliqué, et un peu moins sécurisé aussi, d'obtenir un tel certificat. Pour pallier ce manque est arrivée la SNI, « Server Name Indication », une extension du protocole TLS qui permet au client de spécifier quel site il veut dès le début de la connexion TLS. Avec la commande ci-dessus, curl passe le Host mais pas la SNI, donc se plaint légitimement qu'il n'a pas de certificat valide pour 213.36.253.103 car le certificat envoyé par le serveur de LinuxFR n'est valide que pour linuxfr.org (et j'ai mis le --insecure pour lui demander de l'ignorer).

    curl n'a apparemment pas d'option pour régler manuellement le SNI, donc je ne peux pas terminer l'exemple aussi pédagogiquement que je le voudrais, mais dans la requête curl https://linuxfr.org, il utilise l'URL non seulement pour trouver l'IP 213.36.253.103 via DNS, mais aussi pour régler le SNI dans la connexion TLS et le Host dans la connexion HTTP qu'elle chiffre.

    Sur Wikipédia : https://en.wikipedia.org/wiki/List_of_HTTP_header_fields#Standard_request_fields et https://en.wikipedia.org/wiki/Server_Name_Indication

    HTH