Przekieruj na SSL tylko wtedy, gdy przeglądarka obsługuje SNI

20

Mam Apache 2.2 z mod_ssl i kilka stron w HTTPS na tym samym IP / porcie z VirtualHosting, więc klient musi obsługiwać SNI, aby połączyć się z tymi wirtualnymi hostami.

Chciałbym skonfigurować mój serwer w następujący sposób:

Gdy użytkownik wpisze www.dummysite.com, a jego przeglądarka obsługuje SNI (Server Name Indication), każde żądanie HTTP jest przekierowywane do miejsca, w https://którym wysyłany jest nagłówek HSTS. Ale jeśli przeglądarka nie obsługuje SNI, żądanie jest obsługiwane przez HTTP.

Powyższa reguła, tak jak jest, jest w rzeczywistości regułą rezerwową dla osób, które nadal używają starych przeglądarek, ponieważ Mozilla i Chrome nie mają tego problemu, tylko po to, aby uniknąć pozostawienia tych użytkowników poza witryną.

Chciałbym zrobić to przekierowanie na poziomie konfiguracji Apache, być może z filtrem na kliencie użytkownika. Nie chciałbym dotykać uruchomionych aplikacji, z wyjątkiem upewnienia się, że nie ma bezpośrednich odnośników http: // (w przeciwnym razie oznacza to ostrzeżenie dotyczące bezpieczeństwa)

[Edit] (podczas edycji pytanie zapomniałem na pytanie): co to jest lista SNI obsługujących agentów użytkownika przekierować?

usr-local-ΕΨΗΕΛΩΝ
źródło

Odpowiedzi:

20

Ponieważ SNI występuje podczas uzgadniania SSL / TLS, nie można wykryć obsługi przeglądarki, gdy klient łączy się z HTTP.

Masz rację; filtr klienta użytkownika jest jedynym sposobem na to.

Najważniejsze pytanie dotyczy tego, czy chcesz działać na czarnej liście wbrew przeglądarkom, o których wiesz, że nie będą nasłuchiwać SNI, czy na białej liście przeglądarek, o których wiadomo, że je obsługują. Niewyraźne lub nowe urządzenia, które nie mogą korzystać z witryny, wydają się łamać porozumienia, więc powiedziałbym, że biała lista może być lepszą opcją.

W twoim HTTP <VirtualHost>:

# Internet Explorer 7, 8, 9, on Vista or newer
RewriteCond %{HTTP_USER_AGENT} MSIE\s7.*Windows\sNT\s6 [OR]
RewriteCond %{HTTP_USER_AGENT} MSIE\s8.*Windows\sNT\s6 [OR]
RewriteCond %{HTTP_USER_AGENT} MSIE\s9.*Windows\sNT\s6 [OR]
# Chrome on Windows, Mac, Linux
RewriteCond %{HTTP_USER_AGENT} Windows\sNT\s6.*Chrome [OR]
RewriteCond %{HTTP_USER_AGENT} Macintosh.*Chrome [OR]
RewriteCond %{HTTP_USER_AGENT} Linux.*Chrome [OR]
# Firefox - we'll just make the assumption that all versions in the wild support:
RewriteCond %{HTTP_USER_AGENT} Gecko.*Firefox
RewriteRule ^/(.*)$ https://ssl.hostname/$1 [R=301]

Oto także opcja czarnej listy - pamiętaj, że wiąże się to z ryzykiem wysłania klienta, który nie używa SNI do strony potrzebnej do SNI, ale z drugiej strony wyśle ​​użytkowników czegoś nowego, np. IE 10, po prawej stronie miejsce:

# IE 6
RewriteCond %{HTTP_USER_AGENT} !MSIE\s6
# Windows XP/2003
RewriteCond %{HTTP_USER_AGENT} !Windows\sNT\s5
# etc etc
RewriteRule ^/(.*)$ https://ssl.hostname/$1 [R=301]

Istnieje wiele przeglądarek. Byłem dość swobodny w wyrażeniach i nie obejmowałem wielu przeglądarek - może to stać się koszmarem do utrzymania.

Którąkolwiek opcję wybierzesz ... powodzenia!

Shane Madden
źródło
1
Świetny! Testowałem z Opera Mobile (która jest zgodna z SNI, ale nie ma jej na liście) i nie przekierowuje. W moim Firefoksie przekierowuje!
usr-local-ΕΨΗΕΛΩΝ
6
Sugeruję modularyzację tego rozwiązania za pomocą dyrektywy BrowserMatch z mod_setenvif httpd.apache.org/docs/2.2/mod/mod_setenvif.html . Użyj BrowserMatch, aby ustawić zmienną środowiskową wspiera_sni = y, a następnie napisz RewriteCond% {ENV: wspiera_sni} = y. W ten sposób możesz ponownie użyć logiki wykrywania SNI dla dowolnych innych RewriteRule, które możesz mieć.
200_sukces
@ 200_success Dobry pomysł!
Shane Madden
8

Moje rozwiązanie jest następujące:

  # Test if SNI will work and if not redirect to too old browser page
  RewriteCond %{HTTPS} on
  RewriteCond %{SSL:SSL_TLS_SNI} =""
  RewriteRule ^ http://www.example.com/too-old-browser [L,R=307]

Jeśli stara przeglądarka bez SNI spróbuje uzyskać dostęp do https://www.example.com/ *, najpierw wyświetli błąd w przeglądarce, którego nie można uniknąć, dopóki apache nie odpowie na przeglądarkę inną niż SNI, której nie zna o którą stronę prosi. Następnie przekierowuje na stronę informującą użytkownika, że ​​jego przeglądarka jest zbyt stara (o ile użytkownik kliknie, aby przejść do witryny).

A dla użytkowników z nowymi przeglądarkami mam

  #Test if new browser and if so redirect to https
  #new browser is not MSIE 5-8, not Android 0-3
  RewriteCond %{HTTPS} off
  RewriteCond %{HTTP_USER_AGENT} !MSIE\ [5-8]
  RewriteCond %{HTTP_USER_AGENT} !Android.*(Mobile)?\ [0-3]
  RewriteRule ^ https://%{HTTP_HOST}%{REQUEST_URI} [L,R=301]

To wyklucza większość starych przeglądarek, w tym niektóre takie jak MSIE 5-8 na Vista (9+ to tylko Vista / 7, więc obsługuje SNI). To nie jest 100% (symbian jest ignorowany itp.), Ale powinien działać dla większości. Mniejszość nadal może zaakceptować błąd certyfikatu.

malc_b
źródło
3

O ile mi wiadomo, nie ma naprawdę dobrego sposobu na zrobienie tego - możesz użyć reguły mod_rewrite lub podobnej warunkowo na podstawie User-agentnagłówka, ale musiałby to być vhost NON-SSL: Jeśli przeglądarka nie obsługuje SNI i przechodzi do bezpiecznej ( https://) strony, dzięki czemu zachowa się w starym stylu Apache: „Oto pierwszy certyfikat SSL, który skojarzyłem z tym adresem IP - mam nadzieję, że tego właśnie chciałeś!” - Jeśli nie jest to certyfikat, przeglądarka spodziewa się, że skończy się z komunikatem o błędzie dotyczącym niezgodności nazw hostów.

Zasadniczo oznacza to, że ludzie muszą trafić na stronę pełnoekranową bez SSL, która przekieruje ich - prawdopodobnie ujawniając wszelkie dane, które wysyłają w swoim żądaniu. Może to być, ale nie musi, złamać umowę (mówisz, że i tak wyślesz je na stronę bez SSL, jeśli nie obsługują SNI, więc zakładam, że nie dbasz o bezpieczeństwo. Gdybym był projektując system, który potrzebował SSL jako warstwy szyfrującej lub uwierzytelniającej, byłbym jednak nieco bardziej nalegany ...)

Nie powstrzymuje to jednak nikogo od dodawania zakładek do bezpiecznej witryny - a jeśli korzysta ze wspólnej usługi zakładek lub przywraca swoje zakładki do komputera, na którym przeglądarka internetowa nie obsługuje SNI, wraca do sprawy z potencjalnym błędem SSL .

voretaq7
źródło
1

Kusiłoby mnie, aby rozwiązać ten jeden z trzech sposobów:

  1. RewriteRulena podstawie User-Agentnagłówków.
  2. Załaduj https: // URI do <SCRIPT>znacznika na innym niż domyślny VHost; jeśli ładowanie się powiedzie, to trochę JS ponownie ładuje całą stronę pod HTTPS.
  3. Naucz moich odwiedzających, aby używali czegoś takiego jak HTTPS Wszędzie, jeśli jest to dla nich priorytetem, wymuszaj HTTPS na stronach, na których powinno być to wymagane, i mam nadzieję, że wszystko się ułoży.

Spośród nich osobiście najbardziej podoba mi się # 2, ale wymaga to modyfikacji kodu twoich witryn.

BMDan
źródło
0

Tylko dla każdego, kto tego potrzebuje.

Jeśli masz wiele hostów i chcesz, aby wszystkie z nich korzystały z protokołu SSL w VirtualHosting (i dla każdego kupiłeś certyfikat), wypróbuj nowy mod_djechelon_ssl

$ cat /etc/apache2/mod_djechelon_ssl.conf 
RewriteEngine on
# Internet Explorer 7, 8, 9, on Vista or newer
RewriteCond %{HTTP_USER_AGENT} MSIE\s7.*Windows\sNT\s6 [OR]
RewriteCond %{HTTP_USER_AGENT} MSIE\s8.*Windows\sNT\s6 [OR]
RewriteCond %{HTTP_USER_AGENT} MSIE\s9.*Windows\sNT\s6 [OR]
# Chrome on Windows, Mac, Linux
RewriteCond %{HTTP_USER_AGENT} Windows\sNT\s6.*Chrome [OR]
RewriteCond %{HTTP_USER_AGENT} Macintosh.*Chrome [OR]
RewriteCond %{HTTP_USER_AGENT} Linux.*Chrome [OR]
# Firefox - we'll just make the assumption that all versions in the wild support:
RewriteCond %{HTTP_USER_AGENT} Gecko.*Firefox [OR]
#Safari iThing
RewriteCond %{HTTP_USER_AGENT} Mozilla.*iPhone.*Safari [OR]
RewriteCond %{HTTP_USER_AGENT} Mozilla.*iPod.*Safari [OR]
RewriteCond %{HTTP_USER_AGENT} Mozilla.*iPad.*Safari [OR]
RewriteRule ^/(.*)$ https://%{HTTP_HOST}/$1 [R=permanent,L]

Stosowanie:

<VirtualHost ip:80>
ServerName www.yourhost.com

Include /path/to/mod_djechelon_ssl.conf

[plain old Apache directives]
</VirtualHost>

<VirtualHost ip:443>
ServerName www.yourhost.com

[SSL-related directives]

[Copy and paste directives from above host]
</VirtualHost>
usr-local-ΕΨΗΕΛΩΝ
źródło
0

Jak pisałem tutaj , możesz przetestować wsparcie SNI tylko przed jego wymaganiem. Oznacza to, że nie można zmusić użytkowników do korzystania z SNI HTTPS, a następnie wycofać się, jeśli nie obsługują tego, ponieważ otrzymają taki błąd (z Chrome w systemie Windows XP) bez możliwości kontynuowania.

Tak więc (niestety) użytkownik musi zacząć od niepewnego połączenia HTTP, a następnie zostać zaktualizowany tylko, jeśli obsługuje SNI.

Możesz wykryć obsługę SNI poprzez:

  1. Skrypt zdalny
    Ze zwykłej strony HTTP załaduj <script>z docelowego serwera SNI HTTPS, a jeśli skrypt ładuje się i działa poprawnie, wiesz, że przeglądarka obsługuje SNI.

  2. Cross-Domain AJAX (CORS)
    Podobnie jak w opcji 1, możesz spróbować wykonać żądanie AJAX między domenami ze strony HTTP do HTTPS, ale pamiętaj, że CORS ma ograniczoną obsługę przeglądarki .

  3. Sniff the user-agent
    Jest to prawdopodobnie najmniej niezawodna metoda i musisz zdecydować, czy masz czarną listę przeglądarek (i systemów operacyjnych) znaną z jej nieobsługiwania, czy białą listę znanych systemów, które to robią.

    Wiemy, że wszystkie wersje IE, Chrome i Opera na Windows XP i niższych nie obsługują SNI. Zobacz CanIUse.com, aby uzyskać pełną listę obsługiwanych przeglądarek .

Simon East
źródło