@drawoharajag ❤️ det här! << klicka på mig 🐛 🫖 🧚
/multi-domain-https-with-server-name-indication
publicerad den: 2014-12-18

(Detta är en förkortad version av Ja, Virginia, du kan använda SNI som ursprungligen publicerades på Spikes Saker… Och Grejer… blogg)

När du ansluter till en webbserver säkert med HTTPS förhandlas säkerheten med hjälp av TLS. Två saker händer, serverns identitet verifieras och anslutningen krypteras.

Verifieringen är viktig, det spelar ingen roll om anslutningen är krypterad om du på något sätt har omdirigerats till en ondskingsfulls server. Dock kan verifieringen vara problematisk om en webbserver betjänar mer än ett domännamn.

Du kan läsa de blodiga detaljerna, men den förenklade versionen av processen är att servern skickar ett signerat offentligt nyckelcertifikat som måste matcha domännamnet i URL:en. Om en klient bläddrar till dojo4.com måste certifikatet vara för dojo4.com, om det inte är det visar webbläsaren ett stort skrämmande varning.

Tekniskt sett är det möjligt att ha flera domännamn på ett certifikat, faktiskt är det vanligt att ha till exempel både “dojo4.com” och “www.dojo.com” för fullständighet. Dock är det en enorm smärta i rumpan att lägga till och ta bort domännamn från ett certifikat. Du måste få utfärdaren att generera ett nytt och återkalla det gamla. Och om du arbetar med ett Content Delivery Network, är de ganska osannolika att lägga till dina domännamn till deras certifikat.

Ursprungligen stödde TLS endast ett certifikat per webbserver (eller mer korrekt, per IP-adress som är kopplad till webbservern). Server Name Indication (SNI) lades till i TLS för att lösa detta problem. I början av TLS-förhandlingen berättar klienten för servern namnet på den värd den försöker ansluta till och servern kan då välja och skicka en korrekt certifikatfil. Problem löst!

Men… Inte alla webbläsare stödjer SNI. Alla vet detta, och som ett resultat tenderar de att hoppa över SNI och gå direkt till dedikerade IP-adresser per plats eller till och med flera servrar. Detta är en dyr alternativ, speciellt när man arbetar med CDN:er som CloudFront. När detta kom upp för mig bestämde jag mig för att se vad “inte alla webbläsare” verkligen betydde.

Det visar sig att SNI stöds brett, med de stora problemen som är IE8 och lägre och någon version av IE som körs på Windows XP (eftersom det underliggande operativsystemsbiblioteket inte stödjer SNI). Det finns även några gamla versioner av Android där som saknar stöd.

Så, de flesta besökare kommer inte att ha några problem med SNI och gruppen som gör det är liten nog att vi kan hantera dem som ett specialfall.

För de webbläsare utan SNI-stöd är lösningen att omdirigera dem till ett certifikat som kommer att fungera eller en sarkastisk “uppgradera din webbläsare”-sida. Om du googlar hittar du en massa lösningar kring att bygga vita listor för bra webbläsare och/eller svarta listor för dåliga och sedan använda dessa listor i serversida omdirigeringsregler. Fult. Listorna måste underhållas och beroende på servern bryter cachen.

Det finns ett smartare sätt. Medan jag vadade genom ett hav av exempel på Apache-omdirigeringskonfigurationer hittade jag det i denna post. Postens kärnidé kan destilleras ner till detta, om en webbläsare som inte stödjer SNI försöker ladda SNI-innehåll kommer den att få ett fel. Om vi testar detta i bakgrunden och skiljer mellan fel och framgång, kan vi omdirigera besökaren enligt det. Och det enklaste sättet att göra det är att försöka lägga till en pixeldocka till sidan.

I kod ser det ut så här:

function secure_redirect() {
   var img=document.createElement('img'); // skapa ett img-element.
   // Ange src till en SNI-URL för en pixeldocka
   img.src='https://www.example.org/pixel.gif';
   // Detta utförs om SNI fungerar.
   img.onload = function() {
      // Omdirigera till den säkra sidan.
      window.location.href = "https://example.org/";
   };
   // Detta utförs om SNI inte fungerar.
   img.onerror = function(e) {
      // Omdirigera annorstädes
      window.location.href = "http://example.org/snarky-old-browser-message";
   };
   // Visa inte faktiskt bilden
   img.style.display='none';
   // men lägg till den till sidorna så att den laddas.
   document.body.appendChild(img);
  }

Här utnyttjar jag två HTML-callbacks på img-taggen, ‘OnLoad’ som avfyras när en bild har slutat laddas, och ‘OnError’ som avfyras om bilden inte kan laddas. Om en webbläsare inte stödjer SNI kommer bilden att misslyckas med att ladda på grund av ett certifikatfel, och ‘OnError’ kommer att avfyras. Men eftersom vi lägger till bilden till en redan laddad sida kommer den inte att orsaka ett fel i webbläsaren.

Nu kan vi testa för SNI och hantera brist på stöd med elegans. Julen är räddad!

Dock är det vi egentligen kommit fram till något mer slugt. Observera att koden inte testar för SNI, bara förmågan att säkert ladda bilden. Om HTTPS-URL:en i fråga inte verkligen kräver SNI, finns det bara ett certifikat eller det första certifikatet matchar den begärda domänen, fungerar det fortfarande. Problemet har reducerats till “Kan denna besökarens webbläsare visa den säkra sidan eller inte?” och när allt kommer omkring är det allt vi egentligen bryr oss om.