Spip derrière Varnish : port non-standard dans l'URL ? #3386

Closed
opened 8 years ago by miros · 37 comments
miros commented 8 years ago

Spip ajoute intempestivement le port aux URL qu'il génère quand celui-ci est non-standard (ie. ni 80 ni 443). Ça pose un problème quand le serveur applicatif (Apache+PHP, Nginx+PHP, etc.) se trouve derrière un reverse proxy (Varnish) sur la même machine, et écoute donc non pas sur 80 mais sur 81 ou 8080, par exemple.

Ça casse notamment l'URL de spip_admin.css et du SPIP-CRON en toute fin de page.

Pour « corriger » sur mon installation, j'ai modifié la fonction url_de_base() du fichier ecrire/inc/utils.php en commentant ce bloc :

  if (isset($_SERVER['SERVER_PORT'])
    AND $port=$_SERVER['SERVER_PORT']
    AND strpos($host,":")==false){
    if ($http=="http" AND $port!=80) $host.=":$port";
    if ($http=="https" AND $port!=443) $host.=":$port";
  }

Évidemment c'est un peu barbare... Pourquoi ne pas utiliser l'URL de base spécifiée dans l'interface d'admin (Configuration > Identité du site : Adresse (URL) du site public) ? Le commentaire de la fonction url_de_base() précise que c'est parce que meta(adresse_site) peut être fausse ; mais alors à quoi bon la demander et à quoi sert-elle ?

Bref, il devrait être possible de servir Spip sur un port non-standard sans pour autant casser les URLs.

Pour info :

  • L'architecture initiale (traditionnelle) : Client -> Nginx (port 80) -> PHP (Spip)
  • Nouvelle archi avec reverse proxy : Client -> Varnish (port 80) -> Nginx (port 81) -> PHP (Spip)

Versions :

  • SPIP 3.0.17 [21515]
  • Nginx 1.2.1
  • Varnish 3.0.2
  • Debian 7.8 Wheezy
Spip ajoute intempestivement le port aux URL qu'il génère quand celui-ci est non-standard (ie. ni 80 ni 443). Ça pose un problème quand le serveur applicatif (Apache+PHP, Nginx+PHP, etc.) se trouve derrière un reverse proxy (Varnish) sur la même machine, et écoute donc non pas sur 80 mais sur 81 ou 8080, par exemple. Ça casse notamment l'URL de `spip_admin.css` et du SPIP-CRON en toute fin de page. Pour « corriger » sur mon installation, j'ai modifié la fonction `url_de_base()` du fichier `ecrire/inc/utils.php` en commentant ce bloc : <pre> if (isset($_SERVER['SERVER_PORT']) AND $port=$_SERVER['SERVER_PORT'] AND strpos($host,":")==false){ if ($http=="http" AND $port!=80) $host.=":$port"; if ($http=="https" AND $port!=443) $host.=":$port"; } </pre> Évidemment c'est un peu barbare... Pourquoi ne pas utiliser l'URL de base spécifiée dans l'interface d'admin (_Configuration > Identité du site : Adresse (URL) du site public_) ? Le commentaire de la fonction `url_de_base()` précise que c'est parce que `meta(adresse_site)` peut être fausse ; mais alors à quoi bon la demander et à quoi sert-elle ? Bref, il devrait être possible de servir Spip sur un port non-standard sans pour autant casser les URLs. Pour info : * L'architecture initiale (traditionnelle) : Client -> Nginx (port 80) -> PHP (Spip) * Nouvelle archi avec reverse proxy : Client -> Varnish (port 80) -> Nginx (port 81) -> PHP (Spip) Versions : * SPIP 3.0.17 [21515] * Nginx 1.2.1 * Varnish 3.0.2 * Debian 7.8 Wheezy
Fil commented 8 years ago
Owner

Pour ma part j'utilise SPIP et Varnish sans aucun souci, avec une config que j'ai documentée ici http://zzz.rezo.net/Interfacer-Varnish-SPIP.html ; et cela sur 3 serveurs.

Je n'ai remarqué aucun souci avec le port "non standard" sur lequel tourne mon serveur apache.

Pour répondre à ta question, url_de_base() donne la base de l'URL sur laquelle on est appelé ; un même site SPIP peut très bien répondre sur plusieurs noms de domaines, sans se mélanger les pinceaux.

Pour ma part j'utilise SPIP et Varnish sans aucun souci, avec une config que j'ai documentée ici http://zzz.rezo.net/Interfacer-Varnish-SPIP.html ; et cela sur 3 serveurs. Je n'ai remarqué aucun souci avec le port "non standard" sur lequel tourne mon serveur apache. Pour répondre à ta question, url_de_base() donne la base de l'URL sur laquelle on est appelé ; un même site SPIP peut très bien répondre sur plusieurs noms de domaines, sans se mélanger les pinceaux.
Poster

Fil Up a écrit :

Pour ma part j'utilise SPIP et Varnish sans aucun souci, [...]
Je n'ai remarqué aucun souci avec le port "non standard" sur lequel tourne mon serveur apache.

Et un phpinfo() via Varnish sur tes serveurs montre bien $_SERVER["SERVER_PORT"] = 81 (ou 8080 dans ton cas) ? Si oui, saurais-tu m'expliquer pourquoi url_de_base() ne te renvoie pas http://hostname:8080/spip/, notamment pour spip_admin.css, quand tu es logué ? Ton _SERVER["HTTP_HOST"] contient peut-être déjà ":80" ?

M'enfin, je ne fait que remonter mon expérience ; si ça n'affecte que moi, tant mieux. ;-)

Fil Up a écrit : > Pour ma part j'utilise SPIP et Varnish sans aucun souci, [...] > Je n'ai remarqué aucun souci avec le port "non standard" sur lequel tourne mon serveur apache. Et un phpinfo() via Varnish sur tes serveurs montre bien $_SERVER["SERVER_PORT"] = 81 (ou 8080 dans ton cas) ? Si oui, saurais-tu m'expliquer pourquoi url_de_base() ne te renvoie pas http://hostname:8080/spip/, notamment pour spip_admin.css, quand tu es logué ? Ton _SERVER["HTTP_HOST"] contient peut-être déjà ":80" ? M'enfin, je ne fait que remonter mon expérience ; si ça n'affecte que moi, tant mieux. ;-)
Fil commented 8 years ago
Owner

Ah en effet, je n'ai pas le problème que tu signales car mon phpinfo() me dit (à tort !) qu'il est appelé par le port 80.
Est-ce que ça peut être un effet du mod_rpaf que j'ai installé dans apache ?

Exemple sur www.spip.net :

HTTP_X_FORWARDED_FOR 	78.202.xx.xx, 78.202.xx.xx
HTTP_X_VARNISH 	1559171843
SERVER_SIGNATURE 	Apache/2.2.22 (Debian) Server at www.spip.net Port 80
SERVER_NAME 	www.spip.net
SERVER_ADDR 	127.0.0.1
SERVER_PORT 	80
REMOTE_ADDR 	78.202.xx.xx

(78.202.xx.xx est l'adresse ip de mon navigateur)

Ah en effet, je n'ai pas le problème que tu signales car mon phpinfo() me dit (à tort !) qu'il est appelé par le port 80. Est-ce que ça peut être un effet du mod_rpaf que j'ai installé dans apache ? Exemple sur www.spip.net : <pre> HTTP_X_FORWARDED_FOR 78.202.xx.xx, 78.202.xx.xx HTTP_X_VARNISH 1559171843 SERVER_SIGNATURE <address>Apache/2.2.22 (Debian) Server at www.spip.net Port 80</address> SERVER_NAME www.spip.net SERVER_ADDR 127.0.0.1 SERVER_PORT 80 REMOTE_ADDR 78.202.xx.xx </pre> (78.202.xx.xx est l'adresse ip de mon navigateur)
Poster

Bien vu. La doc de mod_rpaf précise en effet qu'il réécrit « REMOTE_ADDR, HTTPS, and HTTP_PORT to the values provided by an upstream proxy ».

Ça n'a pas l'air d'être le cas du module Realip de Nginx (que j'utilise), ni de mod_remoteip, qui remplace mod_rpaf depuis Apache 2.4.
http://wiki.nginx.org/HttpRealipModule
https://httpd.apache.org/docs/2.4/mod/mod_remoteip.html

On pourrait forcer, dans la conf Varnish, un entête X-Forwarded-Port qui serait parsé directement par Spip. Quelque chose comme ça :

  • Varnish :

Soit :

sub vcl_recv {
    set req.http.X-Forwarded-Port = server.port;
}

Soit :

sub vcl_recv {
    if (req.http.X-Forwarded-Proto == "https" ) {
        set req.http.X-Forwarded-Port = "443";
    } else {
        set req.http.X-Forwarded-Port = "80";
    }
}
  • Spip, dans ecrire/inc/utils.php :
  if (isset($_SERVER['SERVER_PORT'])
    AND $port=$_SERVER['SERVER_PORT']
    AND strpos($host,":")==false){
    # X-Forwarded-Port doit être ajouté par le proxy inverse (Varnish, etc.)
    if (isset($_SERVER['HTTP_X_FORWARDED_PORT'])
      AND $xport=$_SERVER['HTTP_X_FORWARDED_PORT']){
      if ($http=="http" AND $xport!=80) $host.=":$xport";
      if ($http=="https" AND $xport!=443) $host.=":$xport";
    } else {
      if ($http=="http" AND $port!=80) $host.=":$port";
      if ($http=="https" AND $port!=443) $host.=":$port";
    }    
  }
Bien vu. La doc de `mod_rpaf` précise en effet qu'il réécrit « REMOTE_ADDR, HTTPS, and HTTP_PORT to the values provided by an upstream proxy ». Ça n'a pas l'air d'être le cas du module Realip de Nginx (que j'utilise), ni de mod_remoteip, qui remplace mod_rpaf depuis Apache 2.4. http://wiki.nginx.org/HttpRealipModule https://httpd.apache.org/docs/2.4/mod/mod_remoteip.html On pourrait forcer, dans la conf Varnish, un entête `X-Forwarded-Port` qui serait parsé directement par Spip. Quelque chose comme ça : * *Varnish* : Soit : <pre> sub vcl_recv { set req.http.X-Forwarded-Port = server.port; } </pre> Soit : <pre> sub vcl_recv { if (req.http.X-Forwarded-Proto == "https" ) { set req.http.X-Forwarded-Port = "443"; } else { set req.http.X-Forwarded-Port = "80"; } } </pre> * *Spip*, dans `ecrire/inc/utils.php` : <pre> if (isset($_SERVER['SERVER_PORT']) AND $port=$_SERVER['SERVER_PORT'] AND strpos($host,":")==false){ # X-Forwarded-Port doit être ajouté par le proxy inverse (Varnish, etc.) if (isset($_SERVER['HTTP_X_FORWARDED_PORT']) AND $xport=$_SERVER['HTTP_X_FORWARDED_PORT']){ if ($http=="http" AND $xport!=80) $host.=":$xport"; if ($http=="https" AND $xport!=443) $host.=":$xport"; } else { if ($http=="http" AND $port!=80) $host.=":$port"; if ($http=="https" AND $port!=443) $host.=":$port"; } } </pre>
Fil commented 8 years ago
Owner

Mathieu MD a écrit :

On pourrait forcer, dans la conf Varnish, un entête X-Forwarded-Port qui serait parsé directement par Spip.

trop compliqué à mon avis ; il me semblerait plus simple d'avoir un define pour indiquer à SPIP qu'il peut sauter cette partie du code ; ou bien, pour la liste des ports à ignorer :
define('_PORTS_STANDARDS', '80,443')

Mathieu MD a écrit : > On pourrait forcer, dans la conf Varnish, un entête `X-Forwarded-Port` qui serait parsé directement par Spip. trop compliqué à mon avis ; il me semblerait plus simple d'avoir un define pour indiquer à SPIP qu'il peut sauter cette partie du code ; ou bien, pour la liste des ports à ignorer : `define('_PORTS_STANDARDS', '80,443')`
Poster

Fil Up a écrit :

On pourrait forcer, dans la conf Varnish, un entête X-Forwarded-Port
trop compliqué à mon avis ;

Hum... Comparativement à toute la conf Varnish à faire pour SPIP, pas si compliqué que ça, quand même. ;-)

En tout cas ça serait plutôt un entête X-Forwarded-Host: www.example.com:80 qu'il faudrait utiliser, puisque X-Forwarded-Port ne semble pas très standard ni commun : ni Squid ni Apache ne le mentionnent.
https://en.wikipedia.org/wiki/List_of_HTTP_header_fields#Common_non-standard_request_fields

il me semblerait plus simple d'avoir un define pour indiquer à SPIP qu'il peut sauter cette partie du code ;

Ça me semble plus propre de n'avoir rien à configurer à la main dans SPIP. Ainsi, on ajoute et supprime les proxies/load balancers à la volée sans impact sur le logiciel.

D'ailleurs, le sysadmin des serveurs n'a pas nécessairement un compte d'administrateur SPIP.

ou bien, pour la liste des ports à ignorer :
define('_PORTS_STANDARDS', '80,443')

Certes, mais ça sort du cadre de ce ticket, non ?

Fil Up a écrit : > > On pourrait forcer, dans la conf Varnish, un entête `X-Forwarded-Port` > trop compliqué à mon avis ; Hum... Comparativement à toute la conf Varnish à faire pour SPIP, pas si compliqué que ça, quand même. ;-) En tout cas ça serait plutôt un entête *X-Forwarded-Host: www.example.com:80* qu'il faudrait utiliser, puisque `X-Forwarded-Port` ne semble pas très standard ni commun : ni Squid ni Apache ne le mentionnent. https://en.wikipedia.org/wiki/List_of_HTTP_header_fields#Common_non-standard_request_fields > il me semblerait plus simple d'avoir un define pour indiquer à SPIP qu'il peut sauter cette partie du code ; Ça me semble plus propre de n'avoir rien à configurer à la main dans SPIP. Ainsi, on ajoute et supprime les proxies/load balancers à la volée sans impact sur le logiciel. D'ailleurs, le sysadmin des serveurs n'a pas nécessairement un compte d'administrateur SPIP. > ou bien, pour la liste des ports à ignorer : > `define('_PORTS_STANDARDS', '80,443')` Certes, mais ça sort du cadre de ce ticket, non ?
Fil commented 8 years ago
Owner

ou bien, pour la liste des ports à ignorer :
define('_PORTS_STANDARDS', '80,443')

Certes, mais ça sort du cadre de ce ticket, non ?

Puisqu'on parle d'un bug de SPIP (qui réécrit l'URL dans un cas où il ne faudrait pas), il faut d'abord trouver une solution interne à SPIP.

SPIP doit pouvoir marcher avec un varnish de base, sans conf spécifique. Parfois l'hébergeur te met un varnish en front et ne te permet pas de le configurer. C'est pour ça que je ne suis pas pour le fait d'exiger un réglage spécifique côté Varnish.

À noter: il est certainement possible de bidouiller la valeur problématique depuis le fichier mes_options.php

> > ou bien, pour la liste des ports à ignorer : > > `define('_PORTS_STANDARDS', '80,443')` > > Certes, mais ça sort du cadre de ce ticket, non ? Puisqu'on parle d'un bug de SPIP (qui réécrit l'URL dans un cas où il ne faudrait pas), il faut d'abord trouver une solution interne à SPIP. SPIP *doit* pouvoir marcher avec un varnish de base, sans conf spécifique. Parfois l'hébergeur te met un varnish en front et ne te permet pas de le configurer. C'est pour ça que je ne suis pas pour le fait d'exiger un réglage spécifique côté Varnish. À noter: il est certainement possible de bidouiller la valeur problématique depuis le fichier mes_options.php

quote : "Parfois l'hébergeur te met un varnish en front et ne te permet pas de le configurer. C'est pour ça que je ne suis pas pour le fait d'exiger un réglage spécifique côté Varnish." Tout à fait. C'est le cas notamment de Gandi Simple Hosting pour lequel l'hébergé ne peut aucunement paramétrer varnish.

quote : "Parfois l'hébergeur te met un varnish en front et ne te permet pas de le configurer. C'est pour ça que je ne suis pas pour le fait d'exiger un réglage spécifique côté Varnish." Tout à fait. C'est le cas notamment de Gandi Simple Hosting pour lequel l'hébergé ne peut aucunement paramétrer varnish.
Owner

Je propose le patch suivant :

  • si il y a un $_SERVER['X-Forwarded-Host'] on le prend en priorité
  • sinon comme avant mais en comparant les ports avec un define personalisable
  • on prend en compte le $_SERVER['X-Forwarded-Host'] dans les signatures de cache pour ne pas se faire empoisonner le cache par des requêtes malicieuses

Comme ça on couvre les 2 approches, qui peut le plus peut le moins. Ça vous convient ?

Index: ecrire/inc/utils.php
===================================================================
--- ecrire/inc/utils.php        (revision 22347)
+++ ecrire/inc/utils.php        (working copy)
`` -1540,14 +1540,21 ``
                OR (isset($_SERVER['HTTPS']) AND
                    test_valeur_serveur($_SERVER['HTTPS']))
        ) ? 'https' : 'http';
-       # note : HTTP_HOST contient le :port si necessaire
-       $host = $_SERVER['HTTP_HOST'];
-       if (isset($_SERVER['SERVER_PORT'])
-               AND $port=$_SERVER['SERVER_PORT']
-               AND strpos($host,":")==false){
-               if ($http=="http" AND $port!=80) $host.=":$port";
-               if ($http=="https" AND $port!=443) $host.=":$port";
+       if (isset($_SERVER['X-Forwarded-Host'])){
+               $host = $_SERVER['X-Forwarded-Host'];
        }
+       else {
+               # note : HTTP_HOST contient le :port si necessaire
+               $host = $_SERVER['HTTP_HOST'];
+               if (isset($_SERVER['SERVER_PORT'])
+                       AND $port=$_SERVER['SERVER_PORT']
+                       AND strpos($host,":")==false){
+                       if (!defined('_PORT_HTTP_STANDARD')) define('_PORT_HTTP_STANDARD','80');
+                       if (!defined('_PORT_HTTPS_STANDARD')) define('_PORT_HTTPS_STANDARD','443');
+                       if ($http=="http" AND !in_array($port,explode(',',_PORT_HTTP_STANDARD))) $host.=":$port";
+                       if ($http=="https" AND !in_array($port,explode(',',_PORT_HTTPS_STANDARD))) $host.=":$port";
+               }
+       }
        if (!$GLOBALS['REQUEST_URI']){
                if (isset($_SERVER['REQUEST_URI'])) {
                        $GLOBALS['REQUEST_URI'] = $_SERVER['REQUEST_URI'];
Index: ecrire/public/assembler.php
===================================================================
--- ecrire/public/assembler.php (revision 22347)
+++ ecrire/public/assembler.php (working copy)
`` -187,6 +187,7 ``
        $contexte_implicite = array(
                'squelettes' => $GLOBALS['dossier_squelettes'], // devrait etre 'chemin' => $GLOBALS['path_sig'], ?
                'host' => $_SERVER['HTTP_HOST'],
+               'X-Forwarded-Host' => $_SERVER['X-Forwarded-Host'],  // envoye par un reverse-proxy
                'https' => (isset($_SERVER['HTTPS']) ? $_SERVER['HTTPS'] : ''),
                'espace' => test_espace_prive(),
                'marqueur' => (isset($GLOBALS['marqueur']) ?  $GLOBALS['marqueur'] : ''),

Version cible mise à 3.1

Je propose le patch suivant : - si il y a un `$_SERVER['X-Forwarded-Host']` on le prend en priorité - sinon comme avant mais en comparant les ports avec un define personalisable - on prend en compte le `$_SERVER['X-Forwarded-Host']` dans les signatures de cache pour ne pas se faire empoisonner le cache par des requêtes malicieuses Comme ça on couvre les 2 approches, qui peut le plus peut le moins. Ça vous convient ? <pre> Index: ecrire/inc/utils.php =================================================================== --- ecrire/inc/utils.php (revision 22347) +++ ecrire/inc/utils.php (working copy) `` -1540,14 +1540,21 `` OR (isset($_SERVER['HTTPS']) AND test_valeur_serveur($_SERVER['HTTPS'])) ) ? 'https' : 'http'; - # note : HTTP_HOST contient le :port si necessaire - $host = $_SERVER['HTTP_HOST']; - if (isset($_SERVER['SERVER_PORT']) - AND $port=$_SERVER['SERVER_PORT'] - AND strpos($host,":")==false){ - if ($http=="http" AND $port!=80) $host.=":$port"; - if ($http=="https" AND $port!=443) $host.=":$port"; + if (isset($_SERVER['X-Forwarded-Host'])){ + $host = $_SERVER['X-Forwarded-Host']; } + else { + # note : HTTP_HOST contient le :port si necessaire + $host = $_SERVER['HTTP_HOST']; + if (isset($_SERVER['SERVER_PORT']) + AND $port=$_SERVER['SERVER_PORT'] + AND strpos($host,":")==false){ + if (!defined('_PORT_HTTP_STANDARD')) define('_PORT_HTTP_STANDARD','80'); + if (!defined('_PORT_HTTPS_STANDARD')) define('_PORT_HTTPS_STANDARD','443'); + if ($http=="http" AND !in_array($port,explode(',',_PORT_HTTP_STANDARD))) $host.=":$port"; + if ($http=="https" AND !in_array($port,explode(',',_PORT_HTTPS_STANDARD))) $host.=":$port"; + } + } if (!$GLOBALS['REQUEST_URI']){ if (isset($_SERVER['REQUEST_URI'])) { $GLOBALS['REQUEST_URI'] = $_SERVER['REQUEST_URI']; Index: ecrire/public/assembler.php =================================================================== --- ecrire/public/assembler.php (revision 22347) +++ ecrire/public/assembler.php (working copy) `` -187,6 +187,7 `` $contexte_implicite = array( 'squelettes' => $GLOBALS['dossier_squelettes'], // devrait etre 'chemin' => $GLOBALS['path_sig'], ? 'host' => $_SERVER['HTTP_HOST'], + 'X-Forwarded-Host' => $_SERVER['X-Forwarded-Host'], // envoye par un reverse-proxy 'https' => (isset($_SERVER['HTTPS']) ? $_SERVER['HTTPS'] : ''), 'espace' => test_espace_prive(), 'marqueur' => (isset($GLOBALS['marqueur']) ? $GLOBALS['marqueur'] : ''), </pre> **Version cible mise à 3.1**
Fil commented 8 years ago
Owner

il faut vraiment être sûrs qu'on ne prend pas de risque d'injection de PHP ou de js ?

il faut vraiment être sûrs qu'on ne prend pas de risque d'injection de PHP ou de js ?
Owner

Bien vu, je propose donc
$host = strtr($_SERVER['X-Forwarded-Host'], "<>?\"' \r\n", '________');
pour sanitizer $_SERVER['X-Forwarded-Host']

Bien vu, je propose donc `$host = strtr($_SERVER['X-Forwarded-Host'], "<>?\"' \r\n", '________');` pour sanitizer $_SERVER['X-Forwarded-Host']
Owner

Appliqué par commit r22351.
Statut changé à Fermé

Appliqué par commit r22351. **Statut changé à Fermé**
Owner

Commit avec le bon nom de variable HTTP_X_FORWARDED_HOST !

Commit avec le bon nom de variable HTTP_X_FORWARDED_HOST !

Il me semble que c’est un peu dangereux de faire confiance à l’en-tête X-Forwarded-Host. Via cette en-tête, on peut maitriser l’URL de base de la page HTML qui sera générée et donc de définir un emplacement arbitraire depuis lequel seront téléchargés les fichiers CSS et JavaScript.

Olivier;

Il me semble que c’est un peu dangereux de faire confiance à l’en-tête `X-Forwarded-Host`. Via cette en-tête, on peut maitriser l’URL de base de la page HTML qui sera générée et donc de définir un emplacement arbitraire depuis lequel seront téléchargés les fichiers CSS et JavaScript. Olivier;

Salut tout le monde.

Je rencontre actuellement le même "problème" sur 3.1 Revision: 22380. SPIP n'arrive pas à déterminer "seul" le bon port.

Un trick (qui fait le job) est de rajouter $_SERVER['SERVER_PORT']='443'; dans mes_options.php.

En en parlant avec l'hébergeur, il suggère un truc pas con, c'est de se baser sur HTTP_X_FORWARDED_PROTO qui, lui, renvoi vraiment le protocole.

Dans mon cas, j'ai le résultat suivant (de simples echo hors SPIP donc directement sur le serveur) :

SERVER_PORT : 80
HTTP_X_FORWARDED_HOST : 
HTTP_X_FORWARDED_PROTO : https

Se baser sur HTTP_X_FORWARDED_PROTO serait donc une solution non ?

Salut tout le monde. Je rencontre actuellement le même "problème" sur 3.1 Revision: 22380. SPIP n'arrive pas à déterminer "seul" le bon port. Un trick (qui fait le job) est de rajouter $_SERVER['SERVER_PORT']='443'; dans mes_options.php. En en parlant avec l'hébergeur, il suggère un truc pas con, c'est de se baser sur HTTP_X_FORWARDED_PROTO qui, lui, renvoi vraiment le protocole. Dans mon cas, j'ai le résultat suivant (de simples echo hors SPIP donc directement sur le serveur) : <pre> SERVER_PORT : 80 HTTP_X_FORWARDED_HOST : HTTP_X_FORWARDED_PROTO : https </pre> Se baser sur HTTP_X_FORWARDED_PROTO serait donc une solution non ?

Hello,

Je rebondis sur ce ticket, où on a le même soucis sur une autre instrastructure avec un Apache qui sert de reverse proxy. Comme le signale xdjuj, pourquoi ne pas se baser sur HTTP_X_FORWARDED_PROTO ? Car c'est effectivement la bonne valeur avec cette globale.

Hello, Je rebondis sur ce ticket, où on a le même soucis sur une autre instrastructure avec un Apache qui sert de reverse proxy. Comme le signale xdjuj, pourquoi ne pas se baser sur HTTP_X_FORWARDED_PROTO ? Car c'est effectivement la bonne valeur avec cette globale.
b_b commented 7 years ago
Owner

Pour info, j'ai aussi eu un problème de port ajouté dans les urls du site, voici la discussion pour mémoire :

< cerdic‎ >  sinon teste juste un if ($_SERVER["HTTP_X_FORWARDED_PROTO"]=='https') $_SERVER['HTTPS'] = 'on';
< b_b‎ >  \o/ bingo cerdic 
< cerdic‎ >  voila
< b_b‎ >  dans mes bras grand !
< cerdic‎ >  il manquait le https
< b_b‎ >  donc conf de merde coté hébergeur non ?
< b_b‎ >  je vais leur signaler le bug 
< cerdic‎ >  ben je crois que non tu peux pas trop fixer ça cote conf hebergement
< b_b‎ >  ha mince, alors on a un bug dans spip ?
< cerdic‎ >  je pense qu'ils on un nginx sur le 443 qui rentre ensuite dans apache en 80
< cerdic‎ >  un truc comme ça
< cerdic‎ >  le probleme c'est que $_SERVER['X-FORWARDED-PROTO'] n'est pas safe, il peut etre injecte par n'importe qui
< cerdic‎ >  SI on sait que cet entete est safe (car conf hebergeur licite) alors on force a la main
< cerdic‎ >  ou alors
< cerdic‎ >  SI il y a l'entte $_SERVER["HTTP_X_FORWARDED_PROTO"] ET que l'URL principale du site est en https
< cerdic‎ >  on force https
< cerdic‎ >  pour eviter se se faire injecter des urls en 443 dans le cache a cause d'un header malicieux
< cerdic‎ >  ça peut le faire ça
Pour info, j'ai aussi eu un problème de port ajouté dans les urls du site, voici la discussion pour mémoire : <pre> < cerdic‎ > sinon teste juste un if ($_SERVER["HTTP_X_FORWARDED_PROTO"]=='https') $_SERVER['HTTPS'] = 'on'; < b_b‎ > \o/ bingo cerdic < cerdic‎ > voila < b_b‎ > dans mes bras grand ! < cerdic‎ > il manquait le https < b_b‎ > donc conf de merde coté hébergeur non ? < b_b‎ > je vais leur signaler le bug < cerdic‎ > ben je crois que non tu peux pas trop fixer ça cote conf hebergement < b_b‎ > ha mince, alors on a un bug dans spip ? < cerdic‎ > je pense qu'ils on un nginx sur le 443 qui rentre ensuite dans apache en 80 < cerdic‎ > un truc comme ça < cerdic‎ > le probleme c'est que $_SERVER['X-FORWARDED-PROTO'] n'est pas safe, il peut etre injecte par n'importe qui < cerdic‎ > SI on sait que cet entete est safe (car conf hebergeur licite) alors on force a la main < cerdic‎ > ou alors < cerdic‎ > SI il y a l'entte $_SERVER["HTTP_X_FORWARDED_PROTO"] ET que l'URL principale du site est en https < cerdic‎ > on force https < cerdic‎ > pour eviter se se faire injecter des urls en 443 dans le cache a cause d'un header malicieux < cerdic‎ > ça peut le faire ça </pre>
Owner

Peut être avec le patch

Index: ecrire/inc/utils.php
===================================================================
--- ecrire/inc/utils.php        (revision 22614)
+++ ecrire/inc/utils.php        (working copy)
`` -1253,6 +1253,12 ``
                OR (isset($_SERVER['HTTPS']) AND
                    test_valeur_serveur($_SERVER['HTTPS']))
        ) ? 'https' : 'http';
+       if ($http=='http'
+               AND isset($_SERVER['HTTP_X_FORWARDED_PROTO'])
+         AND $_SERVER['HTTP_X_FORWARDED_PROTO']=='https'
+         AND strncmp($GLOBALS['meta']['adresse_site'],'https',5)==0){
+               $http = 'https';
+       }
        // note : HTTP_HOST contient le :port si necessaire
        $host = $_SERVER['HTTP_HOST'];
        // si on n'a pas trouvé d'hôte du tout, en dernier recours on utilise adresse_site comme fallback

pour prendre en compte HTTP_X_FORWARDED_PROTO quand il semble licite sans risquer d'injection là ou il ne l'est pas ?

Peut être avec le patch <pre> Index: ecrire/inc/utils.php =================================================================== --- ecrire/inc/utils.php (revision 22614) +++ ecrire/inc/utils.php (working copy) `` -1253,6 +1253,12 `` OR (isset($_SERVER['HTTPS']) AND test_valeur_serveur($_SERVER['HTTPS'])) ) ? 'https' : 'http'; + if ($http=='http' + AND isset($_SERVER['HTTP_X_FORWARDED_PROTO']) + AND $_SERVER['HTTP_X_FORWARDED_PROTO']=='https' + AND strncmp($GLOBALS['meta']['adresse_site'],'https',5)==0){ + $http = 'https'; + } // note : HTTP_HOST contient le :port si necessaire $host = $_SERVER['HTTP_HOST']; // si on n'a pas trouvé d'hôte du tout, en dernier recours on utilise adresse_site comme fallback </pre> pour prendre en compte HTTP_X_FORWARDED_PROTO quand il semble licite sans risquer d'injection là ou il ne l'est pas ?
Owner

Statut changé à En cours

**Statut changé à En cours**
b_b commented 7 years ago
Owner

On en est où avec ce bug, on peut fermer le ticket ? Je m'y perds entre les différentes fermtures/ouvertures ^^

On en est où avec ce bug, on peut fermer le ticket ? Je m'y perds entre les différentes fermtures/ouvertures ^^

Ne faut-il pas utiliser la fonction getallheaders() pour récupérer ces entêtes, même en cas d'un Reverse Proxy Apache ?

Ne faut-il pas utiliser la fonction getallheaders() pour récupérer ces entêtes, même en cas d'un Reverse Proxy Apache ?
Owner

Le patch de Cerdic https://core.spip.net/issues/3386#note-18 n'a pas été intégré en fait.
J'ai bien l'impression qu'il serait pourtant très utile (on vient de tomber aussi sur un cas qui semble en avoir besoin).

Sur cet hébergement, Nginx reçoit le site en https sur le port 443, il redirige sur Apache par le port 80, il définit

  • HTTP_X_FORWARDED_PROTO (https),
  • HTTP_X_FORWARDED_FOR (ip),
  • HTTP_X_FORWARDED_SSL (on),
  • HTTP_X_REAL_IP (ip)
  • SERVER_PORT (80)

Mais pas :

  • HTTP_X_FORWARDED_HOST
  • HTTP_X_FORWARDED_PORT

Si ça peut aider.

Le patch de Cerdic https://core.spip.net/issues/3386#note-18 n'a pas été intégré en fait. J'ai bien l'impression qu'il serait pourtant très utile (on vient de tomber aussi sur un cas qui semble en avoir besoin). Sur cet hébergement, Nginx reçoit le site en https sur le port 443, il redirige sur Apache par le port 80, il définit - HTTP_X_FORWARDED_PROTO (https), - HTTP_X_FORWARDED_FOR (ip), - HTTP_X_FORWARDED_SSL (on), - HTTP_X_REAL_IP (ip) - SERVER_PORT (80) Mais pas : - HTTP_X_FORWARDED_HOST - HTTP_X_FORWARDED_PORT Si ça peut aider.
Owner

Donc pour que ça fonctionne tel quel, on a du intégrer le patch de cerdic dans url_de_base()

	if (
		$http == 'http'
		AND isset($_SERVER['HTTP_X_FORWARDED_PROTO'])
		AND $_SERVER['HTTP_X_FORWARDED_PROTO'] == 'https'
		AND strncmp($GLOBALS['meta']['adresse_site'], 'https', 5) == 0)
	{
		$http = 'https';
	}

Ainsi qu'ajouter dans mes_options le port 80 comme étant normal pour du https (sinon il ajoutait :80 aux urls) :

// considérer le port 80 comme un port https
// nginx:443 => apache:80 => spip…
define('_PORT_HTTPS_STANDARD', '443,80');
Donc pour que ça fonctionne tel quel, on a du intégrer le patch de cerdic dans `url_de_base()` <pre> if ( $http == 'http' AND isset($_SERVER['HTTP_X_FORWARDED_PROTO']) AND $_SERVER['HTTP_X_FORWARDED_PROTO'] == 'https' AND strncmp($GLOBALS['meta']['adresse_site'], 'https', 5) == 0) { $http = 'https'; } </pre> Ainsi qu'ajouter dans mes_options le port 80 comme étant normal pour du https (sinon il ajoutait :80 aux urls) : <pre> // considérer le port 80 comme un port https // nginx:443 => apache:80 => spip… define('_PORT_HTTPS_STANDARD', '443,80'); </pre>
Owner

Nous avons encore eu un signalement sur IRC de ce problème (romrom), que le patch + le define corrige.
Je viens donc d'intégrer le patch (reformaté) en 3.2 avec r23403

Reporte-t-on en 3.1 ?

Nous avons encore eu un signalement sur IRC de ce problème (romrom), que le patch + le define corrige. Je viens donc d'intégrer le patch (reformaté) en 3.2 avec r23403 Reporte-t-on en 3.1 ?
Owner

J'ai pris la liberté de reporter en 3.1 avec r23404

Je pense qu'on peut clore.

J'ai pris la liberté de reporter en 3.1 avec r23404 Je pense qu'on peut clore.
Owner

En fait mon patch était bien intégré par r23093 :)

En fait mon patch était bien intégré par r23093 :)
Owner

Sur cet hébergement, Nginx reçoit le site en https sur le port 443, il redirige sur Apache par le port 80, il définit

  • HTTP_X_FORWARDED_PROTO (https),
  • HTTP_X_FORWARDED_FOR (ip),
  • HTTP_X_FORWARDED_SSL (on),
  • HTTP_X_REAL_IP (ip)
  • SERVER_PORT (80)

Mais pas :

  • HTTP_X_FORWARDED_HOST
  • HTTP_X_FORWARDED_PORT

Quand HTTP_X_FORWARDED_HOST et HTTP_X_FORWARDED_PORT sont manquante il suffit de les peupler avec le host et 443 pour que tout marche. Soit on dit que c'est le mes_options qui fera ça, soit on insere ce teste avant https://core.spip.net/projects/spip/repository/revisions/23093/entry/spip/ecrire/inc_version.php#L212 mais il faut absolument eviter d'en disperser partout, et surtout de define _PORT_HTTPS_STANDARD est horrible !

> Sur cet hébergement, Nginx reçoit le site en https sur le port 443, il redirige sur Apache par le port 80, il définit > - HTTP_X_FORWARDED_PROTO (https), > - HTTP_X_FORWARDED_FOR (ip), > - HTTP_X_FORWARDED_SSL (on), > - HTTP_X_REAL_IP (ip) > - SERVER_PORT (80) > > Mais pas : > - HTTP_X_FORWARDED_HOST > - HTTP_X_FORWARDED_PORT Quand HTTP_X_FORWARDED_HOST et HTTP_X_FORWARDED_PORT sont manquante il suffit de les peupler avec le host et 443 pour que tout marche. Soit on dit que c'est le mes_options qui fera ça, soit on insere ce teste avant https://core.spip.net/projects/spip/repository/revisions/23093/entry/spip/ecrire/inc_version.php#L212 mais il faut absolument eviter d'en disperser partout, et surtout de define _PORT_HTTPS_STANDARD est horrible !
Owner

Suite à discussion, la correction proposée par cerdic est différente.

  • r23406 (3.2)
  • r23407 (report 3.1)

Il semblerait qu'on ait aucunement besoin du define _PORT_HTTPS_STANDARD pour définir le port.
Au pire il faut peupler les variables $_SERVER['HTTP_X_FORWARDED_nnn'] dans le fichier config/mes_options.php avec les propriétés adaptés au site.
(je n'ai pas vérifié).

Suite à discussion, la correction proposée par cerdic est différente. - r23406 (3.2) - r23407 (report 3.1) Il semblerait qu'on ait aucunement besoin du define _PORT_HTTPS_STANDARD pour définir le port. Au pire il faut peupler les variables `$_SERVER['HTTP_X_FORWARDED_nnn']` dans le fichier config/mes_options.php avec les propriétés adaptés au site. (je n'ai pas vérifié).
Owner

On va dire que tout est bon. On ferme.
Statut changé à Fermé

On va dire que tout est bon. On ferme. **Statut changé à Fermé**
Owner

Statut changé à En cours

**Statut changé à En cours**
Owner

EN 3.1.8 avec les formulaires de formidable il y a la possibilité de stocker l'IP du visiteur, avec la config du serveur de Nfrance sur un site, l'IP stocké était systématiquement celle du serveur.
J'ai résolu ce souci en mettant la condition REMOTE_ADDR devant

//
// On note le numero IP du client dans la variable $ip
//
if (isset($_SERVER['REMOTE_ADDR'])) {
	$ip = $_SERVER['REMOTE_ADDR'];
}

https://core.spip.net/projects/spip/repository/revisions/23406/entry/spip/ecrire/inc_version.php#L253

voir aussi $_SERVER["REMOTE_ADDR"] gives server IP rather than visitor IP

EN 3.1.8 avec les formulaires de formidable il y a la possibilité de stocker l'IP du visiteur, avec la config du serveur de Nfrance sur un site, l'IP stocké était systématiquement celle du serveur. J'ai résolu ce souci en mettant la condition REMOTE_ADDR devant <pre> // // On note le numero IP du client dans la variable $ip // if (isset($_SERVER['REMOTE_ADDR'])) { $ip = $_SERVER['REMOTE_ADDR']; } </pre> https://core.spip.net/projects/spip/repository/revisions/23406/entry/spip/ecrire/inc_version.php#L253 voir aussi $_SERVER["REMOTE_ADDR"] gives server IP rather than visitor IP
b_b commented 5 years ago
Owner

`touti je pense qu'il serait plus clair de créer un ticket spécifique pour ce bug plutôt que d'ouvrir celui-ci qui était fermé depuis un an (même si le contexte est le même, le bug lui ne l'est pas).

`touti je pense qu'il serait plus clair de créer un ticket spécifique pour ce bug plutôt que d'ouvrir celui-ci qui était fermé depuis un an (même si le contexte est le même, le bug lui ne l'est pas).
Owner

Statut changé à Fermé

**Statut changé à Fermé**
Owner

`b_b j'ai fermé le ticket, effectivement ce n'est pas le même contexte !

`b_b j'ai fermé le ticket, effectivement ce n'est pas le même contexte !
Poster

Bonjour,

Je me permet de ré-ouvrir ce ticket car j'ai encore le problème sur des sites en 3.2.1 à jour avec svn.

Quand j'active le cache chez mon hébergeur, cela rajotue des ports 80 dans les chemin d'image et du coup le site perds la css et les images dans le BO de SPIP

Je cite mon hébergeur :

De mémoire, j'ai fait la même chose que pour le dernier site, c'est-à-dire ajouté un bout de code dans le fichier de configuration du site (sauf que pour l'autre site, je l'ai mis dans le index.php à la place).

C'est le code là :

if ($_SERVER['HTTP_X_FORWARDED_PROTO'] == 'https') {
$_SERVER['HTTPS']='on';
$_SERVER['SERVER_PORT'] = 443;
}
if (isset($_SERVER['HTTP_X_FORWARDED_HOST'])) {
$_SERVER['HTTP_HOST'] = $_SERVER['HTTP_X_FORWARDED_HOST'];
}

La subtilité avec Varnish est que Varnish renvoi sur le port non HTTPS de Apache donc certaines variables renvoyés par Apache ne correspondent pas à la réalité.
C'est à dire : HTTPS, SERVER_PORT, REQUEST_SCHEME
C'est pour cela que des headers supplémentaires HTTP_X_FORWARDED_PROTO, HTTP_X_FORWARDED_HOST comme sont passés.

Il faut également faire attention avec le "SERVER_PORT", quelque chose de 443 peut aussi être du HTTPS, dans notre cas, c'est 4430 par exemple car un reverse proxy écoute déjà le 443 normal. (sauf que dans ce cas précis, le reverse proxy renvois sur le port SSL d'apache).

En résumé, il y a deux cas différents:

  • reverse proxy écoute sur SSL (443) : renvoi sur le port SSL d'apache (4430, le port choisi est arbitraire). Généralement cela pose pas de problème car cela passe par le circuit HTTPS d'apache.
  • reverse proxy écoute sur SSL (443) : renvoi sur le port HTTP d'apache (8080 par exemple), en passant des headers supplémentaires (HTTP_X_FORWARDED_PROTO par exemple). C'est du "offload" de SSL, cela passe par le circuit HTTP d'apache donc certaines variables peuvent être définie différemment. L'application hébergé doit contrôler les headers supplémentaires et essayer de "détecter" cela.

Un réglage via mes_options serait jouable ?

Merci

Bonjour, Je me permet de ré-ouvrir ce ticket car j'ai encore le problème sur des sites en 3.2.1 à jour avec svn. Quand j'active le cache chez mon hébergeur, cela rajotue des ports 80 dans les chemin d'image et du coup le site perds la css et les images dans le BO de SPIP Je cite mon hébergeur : > De mémoire, j'ai fait la même chose que pour le dernier site, c'est-à-dire ajouté un bout de code dans le fichier de configuration du site (sauf que pour l'autre site, je l'ai mis dans le index.php à la place). > > C'est le code là : > > ``` > if ($_SERVER['HTTP_X_FORWARDED_PROTO'] == 'https') { > $_SERVER['HTTPS']='on'; > $_SERVER['SERVER_PORT'] = 443; > } > if (isset($_SERVER['HTTP_X_FORWARDED_HOST'])) { > $_SERVER['HTTP_HOST'] = $_SERVER['HTTP_X_FORWARDED_HOST']; > } > ``` > > > La subtilité avec Varnish est que Varnish renvoi sur le port non HTTPS de Apache donc certaines variables renvoyés par Apache ne correspondent pas à la réalité. > C'est à dire : HTTPS, SERVER_PORT, REQUEST_SCHEME > C'est pour cela que des headers supplémentaires HTTP_X_FORWARDED_PROTO, HTTP_X_FORWARDED_HOST comme sont passés. > > Il faut également faire attention avec le "SERVER_PORT", quelque chose de 443 peut aussi être du HTTPS, dans notre cas, c'est 4430 par exemple car un reverse proxy écoute déjà le 443 normal. (sauf que dans ce cas précis, le reverse proxy renvois sur le port SSL d'apache). > > En résumé, il y a deux cas différents: > - reverse proxy écoute sur SSL (443) : renvoi sur le port SSL d'apache (4430, le port choisi est arbitraire). Généralement cela pose pas de problème car cela passe par le circuit HTTPS d'apache. > - reverse proxy écoute sur SSL (443) : renvoi sur le port HTTP d'apache (8080 par exemple), en passant des headers supplémentaires (HTTP_X_FORWARDED_PROTO par exemple). C'est du "offload" de SSL, cela passe par le circuit HTTP d'apache donc certaines variables peuvent être définie différemment. L'application hébergé doit contrôler les headers supplémentaires et essayer de "détecter" cela. Un réglage via mes_options serait jouable ? Merci
Owner

A partir du moment où les
$_SERVER['HTTP_X_FORWARDED_PROTO']==='https'
et où
$_SERVER['HTTP_X_FORWARDED_HOST']
et
$_SERVER['HTTP_X_FORWARDED_PORT']
sont renseignées tout marche très bien
Regarde ta config et complète si besoin les valeurs manquantes, mais on ne peut pas prévoir tous les cas possibles, c'est donc à toi de gérer ça dans ton mes_options en renseignant ces variables si besoin

A partir du moment où les `$_SERVER['HTTP_X_FORWARDED_PROTO']==='https'` et où `$_SERVER['HTTP_X_FORWARDED_HOST']` et `$_SERVER['HTTP_X_FORWARDED_PORT']` sont renseignées tout marche très bien Regarde ta config et complète si besoin les valeurs manquantes, mais on ne peut pas prévoir tous les cas possibles, c'est donc à toi de gérer ça dans ton mes_options en renseignant ces variables si besoin
Poster

Ok mais je vois pas comment modifier mon mes_options.
Je vire varnish et on verra plus tard.

Ok mais je vois pas comment modifier mon mes_options. Je vire varnish et on verra plus tard.
Sign in to join this conversation.
No Milestone
No project
No Assignees
10 Participants
Notifications
Due Date

No due date set.

Dependencies

This issue currently doesn't have any dependencies.

Loading…
There is no content yet.