(Il s'agit d'une version condensée de Oui, Virginie, vous pouvez utiliser SNI qui est apparu à l'origine sur le blog Stuff… And Things… de Spike)
Lorsque vous vous connectez à un serveur web de manière sécurisée en utilisant HTTPS, la sécurité est négociée en utilisant TLS. Deux choses se produisent : l'identité du serveur est vérifiée et la connexion est chiffrée.
La vérification est importante, peu importe si la connexion est chiffrée si vous avez été redirigé vers le serveur d'un méchant. Cependant, cette vérification peut poser problème si un serveur web dessert plusieurs noms d'hôte.
Vous pouvez lire les détails sordides, mais la version simplifiée du processus est que le serveur envoie un certificat de clé publique signé qui doit correspondre au nom d'hôte dans l'URL. Si un client navigue vers dojo4.com, le certificat doit être pour dojo4.com, sinon le navigateur affiche un grand avertissement effrayant.
Techniquement, il est possible d'avoir plusieurs noms d'hôte sur un certificat, en fait, il est courant d'avoir, par exemple, à la fois "dojo4.com" et "www.dojo.com", pour l'exhaustivité. Cependant, il est très pénible d'ajouter et de supprimer des noms d'hôte d'un certificat. Vous devez demander à l'émetteur d'en générer un nouveau et de révoquer l'ancien. Et, si vous travaillez avec un réseau de distribution de contenu, il est peu probable qu'ils ajoutent vos noms d'hôte à leur certificat.
À l'origine, TLS ne supportait qu'un certificat par serveur web (ou plus correctement, par adresse IP attachée au serveur web) Server Name Indication (SNI) a été ajouté à TLS pour résoudre ce problème. Au début de la négociation TLS, le client informe le serveur du nom de l'hôte auquel il essaie de se connecter et le serveur peut alors sélectionner et envoyer le bon fichier de certificat. Problème résolu !
Sauf… Tous les navigateurs ne supportent pas SNI. Tout le monde sait cela, et par conséquent, tend à sauter SNI et à passer directement aux IP dédiées par site ou même à plusieurs serveurs. C'est une option coûteuse, surtout lorsque l'on travaille avec des CDN comme CloudFront. Lorsque cela s'est produit pour moi, j'ai décidé de voir ce que "pas tous les navigateurs" signifiait vraiment.
Il s'avère que SNI est largement supporté, les principaux problèmes étant IE8 et versions antérieures et toute version de IE fonctionnant sous Windows XP (parce que la bibliothèque sous-jacente du système d'exploitation ne supporte pas SNI). Il existe également certaines anciennes versions d'Android qui manquent de support.
Donc, la plupart des visiteurs n'auront aucun problème avec SNI et le groupe qui en a est suffisamment petit pour que nous puissions le gérer comme un cas spécial.
Pour les navigateurs sans support SNI, la solution de contournement consiste à les rediriger vers un certificat qui fonctionnera ou une page sarcastique "mettez à jour votre navigateur". Si vous faites une recherche Google, vous trouverez un tas de solutions autour de la création de listes blanches de bons navigateurs et/ou de listes noires de mauvais navigateurs, puis de l'utilisation de ces listes dans les règles de redirection côté serveur. Moche. Les listes doivent être maintenues et, selon le serveur, cela casse le cache.
Il existe une méthode plus intelligente. En naviguant à travers une mer de configurations de redirection Apache, je l'ai trouvée dans ce poste. L'idée principale de ce poste peut être distillée en ceci : si un navigateur qui ne supporte pas SNI essaie de charger du contenu SNI, il obtiendra une erreur. Si nous testons cela en arrière-plan et faisons la différence entre l'erreur et le succès, nous pouvons alors rediriger le visiteur en conséquence. Et la manière la plus simple de le faire est d'essayer d'ajouter une image d'un pixel à la page.
En code, cela ressemble à :
function secure_redirect() {
var img=document.createElement('img'); // créez un élément img.
// Définissez la source sur une URL SNI d'une image d'un pixel
img.src='https://www.example.org/pixel.gif';
// Cela s'exécute si SNI fonctionne.
img.onload = function() {
// Redirigez vers la page sécurisée.
window.location.href = "https://example.org/";
};
// Cela s'exécute si SNI ne fonctionne pas.
img.onerror = function(e) {
// Redirigez ailleurs
window.location.href = "http://example.org/snarky-old-browser-message";
};
// N'affichez pas réellement l'image
img.style.display='none';
// mais ajoutez-la aux pages pour qu'elle soit chargée.
document.body.appendChild(img);
}
Ici, j'utilise deux rappels HTML sur la balise img, 'OnLoad' qui se déclenche lorsqu'une image finit de se charger, et 'OnError' qui se déclenche si l'image ne peut pas être chargée. Si un navigateur ne supporte pas SNI, l'image échouera à se charger en raison d'une erreur de certificat, déclenchant 'OnError'. Cependant, comme nous ajoutons l'image à une page déjà chargée, cela ne provoquera pas d'erreur dans le navigateur.
Maintenant, nous pouvons tester SNI et gérer le manque de support de manière élégante. Noël est sauvé !
Cependant, ce que nous avons vraiment trouvé est quelque chose de plus intelligent. Remarquez que le code ne teste pas réellement SNI, mais simplement la capacité à charger l'image de manière sécurisée. Si l'URL HTTPS en question ne nécessite pas réellement SNI, il n'y a qu'un seul certificat ou le premier certificat correspond au domaine demandé, cela fonctionne toujours. Le problème a été réduit à "Ce visiteur peut-il afficher le site sécurisé ou non ?" et à la fin de la journée, c'est tout ce qui nous importe vraiment.