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

20

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

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

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

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

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

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

USR-местный ΕΨΗΕΛΩΝ
источник

Ответы:

20

Поскольку 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]

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

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

Шейн Мэдден
источник
1
Большой! Я тестировал с Opera Mobile (который является SNI-совместимым, но отсутствует в списке), и он не перенаправляет. С моим Firefox он перенаправляет!
usr-local-ΕΨΗΕΛΩΝ
6
Я предлагаю модулировать это решение с помощью директивы BrowserMatch от mod_setenvif httpd.apache.org/docs/2.2/mod/mod_setenvif.html . Используйте BrowserMatch, чтобы установить переменную окружения support_sni = y, затем напишите RewriteCond% {ENV: support_sni} = y. Таким образом, вы можете повторно использовать логику обнаружения SNI для любых других RewriteRules, которые у вас могут быть.
200_success
@ 200_success Хорошая идея!
Шейн Мэдден
8

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

  # 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 игнорируется и т. Д.), Но должно работать для большинства. Меньшинство все еще может принять ошибку сертификата.

malc_b
источник
3

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

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

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

voretaq7
источник
1

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

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

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

BMDan
источник
0

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

Если у вас есть несколько хостов и вы хотите, чтобы все они были с поддержкой 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>
USR-местный ΕΨΗΕΛΩΝ
источник
0

Как я писал здесь , вы можете проверить только поддержку 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 для получения полного списка поддерживаемых браузеров .

Саймон Ист
источник