Rediriger vers SSL uniquement si le navigateur prend en charge SNI

20

J'ai Apache 2.2 avec mod_ssl et un tas de sites en HTTPS sur la même IP / port avec VirtualHosting, donc le client doit prendre en charge SNI pour se connecter à ces hôtes virtuels.

Je souhaite configurer mon serveur de la manière suivante:

Lorsqu'un utilisateur tape www.dummysite.com et que son navigateur prend en charge SNI (Server Name Indication), toute demande HTTP est redirigée vers l' https://endroit où un en-tête HSTS est envoyé. Mais si le navigateur ne prend pas en charge SNI, la demande est servie par HTTP.

La règle ci-dessus, telle quelle, est en fait une règle de remplacement pour les personnes qui utilisent toujours d'anciens navigateurs, car Mozilla et Chrome n'ont pas ce problème, juste pour éviter de laisser ces utilisateurs hors du site.

Je voudrais faire cette redirection au niveau de la configuration Apache, peut-être avec un filtre sur l'agent utilisateur. Je ne voudrais pas toucher aux applications en cours d'exécution, sauf en m'assurant qu'aucune référence http: // directe n'est présente (sinon elles impliquent un avertissement de sécurité)

[Modifier] (lors de la modification de la question, j'ai oublié la question): quelle est la liste des agents utilisateurs compatibles SNI à rediriger?

usr-local-ΕΨΗΕΛΩΝ
la source

Réponses:

20

Étant donné que SNI se produit pendant la négociation SSL / TLS, il n'est pas possible de détecter la prise en charge du navigateur lorsque le client se connecte à HTTP.

Vous avez donc raison; un filtre user-agent est le seul moyen de le faire.

La grande question est de savoir si vous souhaitez agir sur une liste noire contre les navigateurs dont vous savez qu'ils n'écouteront pas SNI, ou sur une liste blanche de navigateurs connus pour la prendre en charge. Les appareils obscurs ou nouveaux ne pouvant pas utiliser le site semblent être une rupture, donc je dirais que la liste blanche pourrait être la meilleure option.

Dans votre 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]

Voici également l'option de liste noire - gardez à l'esprit que cela risque d'envoyer un client qui n'utilise pas SNI vers un site nécessaire à SNI, mais d'un autre côté, enverra les utilisateurs de quelque chose de nouveau comme IE 10 vers la droite endroit:

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

Il existe de nombreux navigateurs. J'ai été assez lâche avec les expressions et je n'ai pas couvert beaucoup de navigateurs - cela pourrait devenir un cauchemar à entretenir.

Quelle que soit l'option que vous choisissez .. bonne chance!

Shane Madden
la source
1
Génial! J'ai testé avec Opera Mobile (qui est compatible SNI mais pas dans la liste) et il ne redirige pas. Avec mon Firefox, il redirige!
usr-local-ΕΨΗΕΛΩΝ
6
Je suggère de modulariser cette solution avec la directive BrowserMatch de mod_setenvif httpd.apache.org/docs/2.2/mod/mod_setenvif.html . Utilisez BrowserMatch pour définir une variable d'environnement supports_sni = y, puis écrivez RewriteCond% {ENV: supports_sni} = y. De cette façon, vous pouvez réutiliser la logique de détection SNI pour tout autre RewriteRules que vous pourriez avoir.
200_success
@ 200_success Bonne idée!
Shane Madden
8

Ma solution est la suivante:

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

Si un ancien navigateur sans SNI tente d'accéder à https://www.example.com/ *, il lancera d'abord une erreur sur le navigateur, ce qui ne peut être évité car jusqu'à ce qu'apache réponde à un navigateur non SNI, il ne sait pas quel site il demande. Ensuite, il redirige vers une page indiquant à l'utilisateur que son navigateur est trop ancien (tant que les clics de l'utilisateur continuent sur le site Web).

Et pour les utilisateurs avec de nouveaux navigateurs, j'ai

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

Cela exclut la plupart des anciens navigateurs, y compris certains comme MSIE 5-8 sur Vista (9+ n'est que Vista / 7, donc prend en charge SNI). Ce n'est pas 100% (symbian est ignoré, etc.) mais devrait fonctionner pour la majorité. La minorité peut toujours choisir d'accepter l'erreur de certificat.

malc_b
la source
3

Pour autant que je sache, il n'y a pas vraiment de bon moyen de le faire - Vous pouvez utiliser une règle mod_rewrite ou similaire conditionnellement en fonction de l'en- User-agenttête, mais cela devrait être sur un vhost NON SSL: Si le navigateur ne le fait pas prend en charge SNI et il va sur un site sécurisé ( https://), il obtiendra le comportement Apache à l'ancienne de "Voici le premier certificat SSL que j'ai associé à cette adresse IP - J'espère que c'est ce que vous vouliez!" - Si ce n'est pas le certificat que le navigateur attendait, vous vous retrouverez avec un message d'erreur sur les incompatibilités de noms d'hôte.

Cela signifie essentiellement que les gens doivent accéder à une page interstitielle non SSL qui les redirigera - exposant éventuellement toutes les données qu'ils envoient dans leur demande. Cela peut ou non être une rupture de contrat (vous dites que vous allez les envoyer à un site non SSL de toute façon s'ils ne prennent pas en charge SNI, donc je suppose que vous ne vous souciez pas beaucoup de la sécurité. Si j'étais concevoir un système qui avait besoin de SSL comme couche de cryptage ou d'authentification, je serais un peu plus insisté à ce sujet ...)

Rien de tout cela n'empêche quelqu'un de mettre en signet le site sécurisé - et s'il utilise un service de signets partagés ou restaure ses signets sur une machine où le navigateur Web ne prend pas en charge SNI, il est de retour dans le cas des erreurs potentielles pour SSL .

voretaq7
la source
1

Je serais tenté de résoudre ce problème de trois manières:

  1. RewriteRulebasé sur les en- User-Agenttêtes.
  2. Charger un https: // URI dans une <SCRIPT>balise sur un VHost non par défaut; si le chargement réussit, c'est un peu de JS qui recharge toute la page sous HTTPS.
  3. Apprenez à mes visiteurs à utiliser quelque chose comme HTTPS Everywhere si c'est une priorité pour eux, forcez HTTPS sur les pages où cela devrait être nécessaire, et espérez que tout fonctionne à la fin.

Parmi ceux-ci, j'aime personnellement le meilleur, mais cela implique de modifier le code de vos sites.

BMDan
la source
0

Pour ceux qui en ont besoin.

Si vous avez plusieurs hôtes et que vous souhaitez qu'ils soient tous compatibles SSL dans VirtualHosting (et que vous avez acheté un certificat pour chacun), essayez le nouveau 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]

Usage:

<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-ΕΨΗΕΛΩΝ
la source
0

Comme je l'ai posté ici , vous ne pouvez tester le support SNI qu'avant de l'exiger. Autrement dit, vous ne pouvez pas forcer les utilisateurs à utiliser SNI HTTPS, puis à retomber s'ils ne le prennent pas en charge, car ils recevront une erreur comme celle-ci (de Chrome sur Windows XP) sans aucun moyen de continuer.

Donc (malheureusement), l'utilisateur doit réellement commencer par une connexion HTTP non sécurisée, puis être mis à niveau uniquement s'il prend en charge SNI.

Vous pouvez détecter la prise en charge SNI via:

  1. Script distant
    À partir de votre page HTTP standard, chargez un à <script>partir de votre serveur SNI HTTPS de destination et si le script se charge et s'exécute correctement, vous savez que le navigateur prend en charge SNI.

  2. AJAX inter-domaines (CORS)
    Semblable à l'option 1, vous pouvez essayer d'effectuer une demande AJAX inter-domaines de la page HTTP vers le HTTPS, mais sachez que CORS ne dispose que d'une prise en charge limitée du navigateur .

  3. Renifler l'agent utilisateur
    C'est probablement la méthode la moins fiable, et vous devrez décider entre avoir une liste noire de navigateurs (et systèmes d'exploitation) connue pour ne pas la prendre en charge, ou une liste blanche de systèmes connus qui le font.

    Nous savons que toutes les versions d'IE, Chrome et Opera sur Windows XP et ci-dessous ne prennent pas en charge SNI. Voir CanIUse.com pour la liste complète des navigateurs pris en charge .

Simon East
la source