Перенаправление на SSL, только если браузер поддерживает SNI

У меня есть Apache 2.2 с mod_ssl и несколько сайтов в HTTPS на одном IP/ порту с VirtualHosting, поэтому клиент должен поддерживать SNI для подключения к этим виртуальным хостам.

Я хотел бы настроить свой сервер следующим образом:

Когда пользователь вводит www.dummysite.com и его браузер поддерживает SNI (указание имени сервера), любой HTTP-запрос перенаправляется на https:// куда отправляется заголовок HSTS. Но если браузер не поддерживает SNI, то запрос обслуживается по HTTP.

Вышеупомянутое правило, сформулированное как есть, на самом деле является запасным правилом для тех людей, которые все еще используют старые браузеры, поскольку у Mozilla и Chrome такой проблемы нет, просто чтобы не оставлять этих пользователей за пределами сайта.

Я хотел бы сделать это перенаправление на уровне конфигурации Apache, возможно, с фильтром на пользовательский агент. Я не хотел бы трогать запущенные приложения, кроме как убедиться, что нет прямых ссылок http:// (в противном случае они означают предупреждение безопасности)

[Изменить] (при редактировании вопроса я забыл вопрос): что такое список пользовательских агентов с поддержкой SNI для перенаправления?

6 ответов

Решение

Поскольку SNI происходит во время рукопожатия SSL/TLS, невозможно обнаружить поддержку браузера, когда клиент подключается к HTTP.

Итак, вы правы; фильтр пользовательского агента - единственный способ сделать это.

Большой вопрос заключается в том, хотите ли вы действовать в черном списке против браузеров, которые, как вы знаете, не будут прослушивать SNI, или из белого списка браузеров, которые, как известно, поддерживают его. Непонятные или новые устройства, неспособные использовать сайт, кажутся нарушителями договоренностей, поэтому я бы сказал, что белый список может быть лучшим вариантом.

В вашем 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]

Здесь также есть опция черного списка - имейте в виду, что это рискует отправить клиента, который не использует SNI, на сайт, необходимый для SNI, но, с другой стороны, отправит пользователям что-то новое, например IE 10, справа место:

# 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]

Есть много браузеров там. Я был довольно свободен с выражениями и не охватывал множество браузеров - это может превратиться в настоящий кошмар для обслуживания.

Какой бы вариант вы ни выбрали.. удачи!

Мое решение таково:

  # 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]

Если старый браузер без SNI пытается получить доступ к https://www.example.com/*, он сначала выдаст ошибку в браузере, чего нельзя избежать, так как до тех пор, пока apache не ответит на браузер не SNI, который он не знает какой сайт он запрашивает. Затем он перенаправляет на страницу, сообщающую пользователю, что его браузер слишком стар (до тех пор, пока пользователь нажимает на веб-сайт).

А для пользователей с новыми браузерами у меня есть

  #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]

Это исключает большинство старых браузеров, в том числе такие, как MSIE 5-8 в Vista (9+ - это только Vista/7, поэтому поддерживает SNI). Это не 100% (symbian игнорируется и т. Д.), Но должно работать для большинства. Меньшинство все еще может принять ошибку сертификата.

Насколько я знаю, на самом деле не очень хороший способ сделать это - вы можете использовать правило mod_rewrite или подобное условно на основе User-agent заголовок, но он должен быть на хосте без протокола SSL: если браузер не поддерживает SNI и он переходит в безопасное состояние (https://) на сайте он получит устаревшее поведение Apache "Вот первый SSL-сертификат, который я связал с этим IP-адресом - надеюсь, это то, что вы хотели!" - Если это не тот сертификат, который ожидал браузер, вы получите сообщение об ошибке несоответствия имени хоста.

По сути, это означает, что люди должны перейти на промежуточную страницу без SSL, которая будет перенаправлять их - возможно, предоставляя любые данные, которые они отправляют, в своем запросе. Это может или не может быть нарушителем соглашения (вы говорите, что в любом случае отправите их на сайт без SSL, если они не поддерживают SNI, поэтому я предполагаю, что вас не слишком заботит безопасность. Если бы я был При проектировании системы, которая нуждается в SSL в качестве уровня шифрования или аутентификации, я бы немного настаивал на этом...)

Однако ничто из этого не мешает кому-либо создавать закладки для защищенного сайта - и если они используют службу общих закладок или восстанавливают свои закладки на компьютере, где веб-браузер не поддерживает SNI, они возвращаются в дело "Возможные ошибки SSL".,

Я хотел бы решить это одним из трех способов:

  1. RewriteRule основанный на User-Agent заголовки.
  2. Загрузите https:// URI в <SCRIPT> тег на VHost не по умолчанию; если загрузка успешна, это немного JS, который перезагружает всю страницу под HTTPS.
  3. Научите моих посетителей использовать что-то вроде HTTPS Everywhere, если для них это является приоритетом, запустите HTTPS на тех страницах, где это необходимо, и надеюсь, что в конце концов все получится.

Из них мне лично больше всего нравится № 2, но это включает в себя изменение кода вашего сайта.

Просто для тех, кто в этом нуждается.

Если у вас есть несколько хостов и вы хотите, чтобы все они были с поддержкой SSL в VirtualHosting (и вы купили сертификат для каждого), попробуйте новый 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]

Использование:

<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>

Как я писал здесь, вы можете проверить только поддержку SNI, прежде чем потребовать ее. Таким образом, вы не можете принудительно заставить пользователей использовать SNI HTTPS, а затем откатиться, если они его не поддерживают, потому что они получат такую ​​ошибку (из Chrome в Windows XP) без возможности продолжить.

Поэтому (к сожалению) пользователь должен фактически начать через незащищенное HTTP-соединение, а затем обновляться, только если он поддерживает SNI.

Вы можете обнаружить поддержку SNI через:

  1. Удаленный скрипт
    Со своей простой HTTP-страницы загрузите <script> с целевого HTTPS-сервера SNI, и если скрипт загружается и работает правильно, вы знаете, что браузер поддерживает SNI.

  2. Междоменный AJAX (CORS)
    Подобно варианту 1, вы можете попробовать выполнить междоменный запрос AJAX со страницы HTTP на HTTPS, но имейте в виду, что CORS имеет только ограниченную поддержку браузера.

  3. Нюхать пользовательский агент
    Это, вероятно, наименее надежный метод, и вам нужно будет выбрать между черным списком браузеров (и операционных систем), о которых известно, что они не поддерживают его, или белым списком известных систем, которые его поддерживают.

    Мы знаем, что все версии IE, Chrome & Opera для Windows XP и ниже не поддерживают SNI. Смотрите CanIUse.com для получения полного списка поддерживаемых браузеров.

Другие вопросы по тегам