¿La opción PHP 'cgi.fix_pathinfo' es realmente peligrosa con Nginx + PHP-FPM?

51

Ha habido un montón de hablar sobre un problema de seguridad con respecto a la cgi.fix_pathinfoopción de PHP se utiliza con Nginx (generalmente PHP-FPM, Fast CGI).

Como resultado, el archivo de configuración nginx predeterminado solía decir:

# NOTE: You should have "cgi.fix_pathinfo = 0;" in php.ini

Sin embargo, ahora, el wiki "oficial" de Nginx establece que PATH_INFO puede manejarse correctamente sin deshabilitar la opción PHP anterior. ¿Y qué?

Preguntas

  • ¿Puedes explicar claramente qué hace cgi.fix_pathinfo? (el documento oficial solo dice : "Para obtener más información sobre PATH_INFO, consulte las especificaciones de CGI")
  • ¿Qué hará realmente PHP con estas PATH_INFOy SCRIPT_FILENAMEvariables?
  • ¿Por qué y cómo puede ser peligroso con Nginx? ( ejemplos detallados )
  • ¿Existe el problema en versiones recientes de estos programas?
  • ¿Apache es vulnerable?

Estoy tratando de entender el problema en cada paso. Por ejemplo, no entiendo por qué usar el socket php-fpm Unix podría evitar este problema.

Totor
fuente
1
Puede responder a su propia pregunta entendiendo la diferencia entre PATH_INFO y PATH_TRANSLATED
Giovanni Tirloni

Respuestas:

79

TL; DR: la solución (que quizás ni siquiera necesites) es MUY SENCILLA y al final de esta respuesta.

Intentaré responder a sus preguntas específicas, pero su malentendido de lo que es PATH_INFO hace que las preguntas en sí mismas sean un poco incorrectas.

  • La primera pregunta debería ser "¿Qué es este negocio de información de ruta?"

  • Su siguiente pregunta debería haber sido: "¿Cómo determina PHP qué PATH_INFOy qué SCRIPT_FILENAMEson?"

    • Las versiones anteriores de PHP eran ingenuas y técnicamente ni siquiera eran compatibles PATH_INFO, por lo que lo que se suponía que debía ser PATH_INFOse utilizó en lo SCRIPT_FILENAMEque, sí, se rompe en muchos casos. No tengo una versión suficientemente antigua de PHP para probar, pero creo que vio SCRIPT_FILENAMEtodo el shebang: "/path/to/script.php/THIS/IS/PATH/INFO" en el ejemplo anterior (con el prefijo el docroot como siempre).
    • Con cgi.fix_pathinfo habilitado, PHP ahora encuentra correctamente "/ THIS / IS / PATH / INFO" para el ejemplo anterior y lo coloca PATH_INFOy SCRIPT_FILENAMEobtiene solo la parte que apunta al script que se solicita (prefijado con el docroot, por supuesto).
    • Nota: cuando PHP llegó a admitir realmente PATH_INFO, tuvieron que agregar una configuración de configuración para la nueva característica para que las personas que ejecutaban scripts que dependían del comportamiento anterior pudieran ejecutar nuevas versiones de PHP. Es por eso que incluso hay un interruptor de configuración para ello. Debería haber sido incorporado (con el comportamiento "peligroso") desde el principio.
  • Pero, ¿cómo sabe PHP qué parte es el script y cuál es la información de ruta? ¿Qué pasa si el URI es algo así como:

    http://example.com/path/to/script.php/THIS/IS/PATH/INFO.php?q=foo

    • Esa puede ser una pregunta compleja en algunos entornos. Lo que sucede en PHP es que encuentra la primera parte de la ruta de URI que no se corresponde con nada debajo del docroot del servidor. Para este ejemplo, ve que en su servidor no tiene "/docroot/path/to/script.php/THIS" pero ciertamente tiene "/docroot/path/to/script.php", así que ahora el SCRIPT_FILENAMEha sido determinado y PATH_INFOobtiene el resto.
    • Así que ahora el buen ejemplo del peligro que se detalla muy bien en los documentos de Nginx y en la respuesta de Hrvoje Špoljar (no puede ser quisquilloso con un ejemplo tan claro) se vuelve aún más claro: dado el ejemplo de Hrvoje (" http: // ejemplo. com / foo.jpg / nonexistent.php "), PHP ve un archivo en su docroot" /foo.jpg "pero no ve nada llamado" /foo.jpg/nonexistent.php "así que SCRIPT_FILENAMEobtiene" /foo.jpg " (de nuevo, con el prefijo docroot) y PATH_INFOobtiene "/nonexistent.php".
  • Por qué y cómo puede ser peligroso ahora debería quedar claro:

    • El servidor web realmente no tiene la culpa: simplemente está enviando el URI a PHP, que inocentemente encuentra que "foo.jpg" en realidad contiene contenido PHP, por lo que lo ejecuta (¡ahora ha sido pwned!). Esto NO es particular de Nginx per se.
  • El problema REAL es que permite que se cargue contenido no confiable en algún lugar sin desinfectar y permite otras solicitudes arbitrarias en la misma ubicación, que PHP ejecuta felizmente cuando puede.
  • Nginx y Apache podrían construirse o configurarse para evitar solicitudes utilizando este truco, y hay muchos ejemplos de cómo hacerlo, incluida la respuesta del usuario2372674 . Este artículo de blog explica muy bien el problema, pero le falta la solución correcta.

  • Sin embargo, la mejor solución es asegurarse de que PHP-FPM esté configurado correctamente para que nunca ejecute un archivo a menos que termine con ".php". Vale la pena señalar que las versiones recientes de PHP-FPM (~ 5.3.9 +?) Tienen esto por defecto, por lo que este peligro ya no es un problema.

La solución

Si tiene una versión reciente de PHP-FPM (~ 5.3.9 +?), Entonces no necesita hacer nada, ya que el comportamiento seguro a continuación ya es el predeterminado.

De lo contrario, busque el www.confarchivo php-fpm (tal vez /etc/php-fpm.d/www.conf, depende de su sistema). Asegúrate de tener esto:

security.limit_extensions = .php

Nuevamente, eso es predeterminado en muchos lugares en estos días.

Tenga en cuenta que esto no impide que un atacante suba un archivo ".php" a una carpeta de carga de WordPress y lo ejecute utilizando la misma técnica. Aún necesita tener una buena seguridad para sus aplicaciones.

usuario109322
fuente
55
¡Buena respuesta! Para aclarar: si, como usted dice, PHP determina qué SCRIPT_FILENAMEes, ¿por qué hay una fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;línea en mi nginxconf? ¿Anula el esfuerzo de PHP para descubrir el valor de SCRIPT_FILENAMEsí mismo?
Totor
¿Hay una función para obtener el valor de security.limit_extensions? Lo intenté phpinfo(), ini_get(security.limit_extensions)y ini_get_all()sin éxito.
elbowlobstercowstand
Gracias, si las versiones recientes de PHP-FPM (~ 5.3.9 +?) Tienen esto por defecto, ¿por qué php7.1 lo necesita? ¿O está mal este artículo?
Yevgeniy Afanasyev
14

En esencia, sin esto, puede cargar un archivo con código php llamado 'foo.jpg' al servidor web; luego solicítelo como http: //domain.tld/foo.jpg/nonexistent.php y la pila del servidor web dirá erróneamente oh; esto es un PHP; Necesito procesar esto, no podrá encontrar foo.jpg / nonexistent.php, por lo que volverá a foo.jpg y procesará foo.jpg como código php. Eso es peligroso ya que abre el sistema a intrusiones muy fáciles; cualquier aplicación web que permita la carga de imágenes, por ejemplo, se convierte en una herramienta para cargar la puerta trasera.

Con respecto al uso de php-fpm con unix socket para evitarlo; OMI no resolverá el problema.

Hrvoje Špoljar
fuente
Solo repite lo que se puede leer en los enlaces que proporcioné. No explicas el mecanismo real. Su respuesta necesita valor agregado en mi humilde opinión.
Totor
66
Eso puede ser cierto, pero su título tiene una pregunta y la respuesta a esa pregunta está en mi respuesta. Si lo quieres explícitamente; si es peligroso; muy peligroso.
Hrvoje Špoljar
1 / Mi respuesta no se limita a su título: tiene un cuerpo. 2 / user109322 ha demostrado que está equivocado: cualquier valor utilizado nocgi.fix_pathinfo es peligroso, porque la configuración predeterminada es segura (solo ejecutará archivos con la extensión). php-fpm.php
Totor
2

En la wiki de Nginx como medida de seguridad

if (!-f $document_root$fastcgi_script_name) {
    return 404;
}

está incluido en el bloque de ubicación. En otros tutoriales

try_files $uri =404;

se usa, lo que debería hacer lo mismo, pero puede generar problemas según el wiki de Nginx. Con estas opciones, cgi.fix_pathinfo=1ya no debería ser un problema. Más información se puede encontrar aquí .

usuario2372674
fuente