En el capítulo anterior (PHP con Apache sobre Windows) vimos como configurar PHP para ejecutarse desde un servidor web usando Apache. A continuación veremos los elementos a configurar directamente en PHP para garantizar una ejecución responsable y sin tropiezos de nuestros scripts.
Algunos se preguntarán ¿por qué molestarse en configurar manualmente PHP cuando frameworks como Laravel ya te entregan un docker con todo preinstalado y preconfigurado? Bueno, la verdad prefiero tener control de qué está ejecutándose en mi maquina y no me gusta, en lo particular, requerir de un entorno propietario para cada aplicación desarrollada cuando puedo tener uno para todas y no desperdiciar espacio en disco, memoria y/o procesador ejecutando en cada proyecto un servidor wen y/o PHP por separado. Si, se que muy probablemente soy una minoría en este aspecto, mea culpa. Y en segundo lugar, nunca se sabe cuando tendrás que entrar y ajustar tu configuración de PHP, así que cuando ese día llegue, sabrás que tienes este recurso a tu alcance para guiarte en el proceso.
Entrando ya en tema y para comenzar, asumiremos que partimos con una copia recién instalada de PHP. Debemos crear por tanto el archivo de configuración php.ini
y para esto, PHP nos da una manito. Usualmente, la instalación original contiene dos archivos que pueden usarse como guía para la creación de este archivo de configuración, a saber:
php.ini-development
: Contiene ejemplo de una configuración recomendada para equipos de desarrollo, donde se habilitan los mensajes de error y depuración y se habilitan funcionalidades y otros, de uso general, asumiendo que se trabaja en un “entorno de confianza”.php.ini-production
: Contiene ejemplo de una configuración recomendada para equipos de producción donde se restringen mensajes de error y otras funcionalidades que pueden generar vulnerabilidades cuando el servidor está habilitado al público en general, sea de una red privada o de la Internet.
Como sugerencia, lo mejor es partir de la configuración de producción, esto porque así se garantiza que la versión de desarrollo será lo más cercana posible a la de producción y esto ayudará a detectar cualquier posible inconsistencia, de forma que al instalar en producción se tenga un mínimo o ningún impase. Por supuesto, hay que tener en cuenta que dependiendo de cada proyecto es posible que se quieran habilitar funcionalidades que el proyecto A puede requerir (por ejemplo uso de bases de datos Oracle) que no requiera el proyecto B y que aunque dichas funcionalidades deban mantenerse activas en nuestro servidor de desarrollo, no las necesitaremos todas ellas en producción. Esto es de tener en cuenta para que al instalar una copia en producción, no nos confiemos con realizar un simple “Copy+Paste” de nuestro archivo php.ini
de trabajo, sino que se siempre deberán revisarse los requerimientos particulares de cada proyecto para evitar habilitar servicios que no sean requeridos.
Téngase en cuenta que habilitar en producción servicios que los scripts en un proyecto dado no usarán puede llevar a que:
- Se consuma mayor memoria y procesador en cada consulta al servidor, porque PHP cargará todos los módulos indicados, sea que se usen o no. Esto puede afectar el rendimiento, tiempos de respuesta y llevar incluso a negaciones del servicio.
- En ciertos casos, puede exponer vulnerabilidades, especialmente si no se ha blindado debidamente el servidor (por ejemplo, permitir subir al servidor archivos de PHP y luego permitir libremente su ejecución).
Procedamos entonces a la configuración de nuestro archivo de configuración.
Como ya se mencionó, lo primero es realizar una copia del archivo php.ini-production
y nombrarlo como php.ini
. No recomiendo renombrar directamente el archivo existente para mantener una copia del archivo original en caso que se quiera realizar comparaciones o generar un archivo nuevo para algún proyecto particular. Una vez realizada esta copia, abrimos el archivo php.ini
para su edición usando cualquier editor de texto disponible, tales como el Bloc de notas nativo de Windows o el Visual Studio Code. Al abrir el archivo encontraremos que tiene líneas que comienzan con el carácter punto y coma “;”, esas líneas son comentarios y no afectarán la configuración como tal. Las líneas de interés no llevan punto y coma al inicio y son del tipo:
[nombre de parámetro] = [valor]
como por ejemplo:
short_open_tag = Off precision = 14 default_mimetype = "text/html"
El valor puede ser:
- Una cadena texto, enmarcado preferiblemente entre comillas dobles para reducir el riesgo de interpretaciones erróneas.
- Un valor numérico, sea positivo o negativo.
- Una expresión booleana (on/off). Valores numéricos como “1” y “0” pueden usarse también en remplazo de “on” y “off” respectivamente.
- Cadena vacía o valor en blanco.
Estos parámetros se editan uno por línea. Pueden también adicionarse comentarios en la línea de parámetros usando el carácter punto y coma “;” después de asignado el valor. Si el parámetro completo se encuentra comentado (con un punto y coma por delante del nombre de parámetro) PHP usará como valor para dicho parámetro el valor por defecto, usualmente indicado en los comentarios asociados.
Algunos de estos parámetros pueden ser modicados durante la ejecución del script ya sea usando una función especifica (aplica para algunos parámetros, no para todos) o la función nativa ini_set(). Otros por el contrario, solamente pueden asignarse directamente en el archivo php.ini
. Todos pueden ser consultados en ejecución usando la función ini_get().
A continuación enumero los nombres de parámetros cuyos valores deberíamos siempre revisar y en algunos casos modificar para adecuarse a nuestro entorno de trabajo, tanto en desarrollo como en producción (se incluye el enlace al manual de PHP para ampliar la información de cada parámetro).
Parámetros modificables desde el script
Determina si PHP debe publicar mensajes de error (afectan la ejecución del script), warnings (pueden o no afectar el comportamiento del script) y notices (avisos sobre variables mal definidas y funciones obsoletas, entre otros). Como alternativa, puede configurarse usando la función error_reporting().
Desarrollo: E_ALL
Producción: E_ALL & ~E_DEPRECATED & ~E_STRICT
Nombre del archivo donde los errores del script deberían ser registrados. Tener presente que se debe tener una política de control sobre el tamaño de este script (PHP no lo hace por ti) para prevenir que crezca en tamaño al infinito o más allá. Esto puede hacerse en cada script o en uno que se ejecute periódicamente con este fin.
Valor por defecto: syslog
(envía mensajes directamente al gestor de eventos del sistema operativo).
Determina si los errores deberían ser impresos en pantalla como parte de la salida o si deberían ocultarse al usuario.
Desarrollo: On
Producción: Off
Fija la zona horaria predeterminada empleada por todas las funciones de fecha/hora. Como alternativa, puede configurarse usando la función date_default_timezone_set(). El listado de zonas horarias permitidas por PHP puede consultarse el manual de PHP.
Valor recomendado para aplicaciones en Colombia: America/Bogota
Codificación de caracteres predeterminada. En caso que todos los scripts se guarden como ISO-8859-1
y sea esta la codificación deseada (equivale a latin-1
en muchas bases de datos), puede cambiarse aquí el valor para que PHP interprete correctamente los scripts y datos.
Valor por defecto: UTF-8
(se recomienda en este caso que los archivos script y los datos en bases de datos usen esta misma codificación).
Define el argumento que es pasado al gestor de almacenamiento. Si se elige el gestor de archivos por defecto, éste es la ruta donde los archivos son creados. En este caso, se debería (como recomendación) tener una política de control para remover archivos usados en sesiones ya vencidas, que quedan en la mayoría de los casos llenando el directorio de archivos de tamaño pequeño pero que pueden a la larga, afectar el comportamiento de la gestión de directorios especialmente en Windows, que suele no reaccionar bien al consultar directorios que contienen varios miles de archivos. Como alternativa, puede configurarse usando la función session_save_path().
Si este modo está habilitado, el módulo no aceptará IDs de sesiones no inicializadas.
Valor recomendado: 1
Especifica si el módulo usará cookies para almacenar el id de sesión en la parte del cliente.
Valor recomendado: 1
Especifica si el módulo solamente usará cookies para almacenar el id de sesión en la parte del cliente.
Valor recomendado: 1
Tip de seguridad: Es importante revisar otras directivas de manejo de sesiones para reducir los riesgos implícitos al usar esta funcionalidad de PHP. Para esto, recomiendo consultar artículos tales como Sesiones y seguridad o How To Secure PHP Sessions?, de los que pueden encontrarse varios disponibles en Internet.
Parámetros fijos (no modificables desde el script)
Define el tamaño máximo de datos de POST permitidos. PHP acepta abreviaturas para valores de byte, incluyendo K (kilo), M (mega) y G (giga).
Importante: Este valor debe ser mayor al asignado en upload_max_filesize
.
En qué directorio debería buscar PHP extensiones que se pueden cargar dinámicamente.
Directorio donde se ubicarán los archivos temporales usados por PHP o al invocar funciones nativas de manejo de archivos temporales tales como tempnam(). Si no se indica, usa por defecto el directorio temporal del sistema Operativo.
Usado para proporcionar seguridad al ejecutar PHP como CGI bajo la mayoría de servidores web.
Información adicional sobre el comportamiento proporcionado por esta directiva puede consultarse en el manual de PHP.
Importante: En Windows usando IIS debe fijarse en “off”.
Si permitir o no la subida de archivos al servidor mediante HTTP.
Importante: Por seguridad, fijar en “off” si el proyecto no requiere esta funcionalidad.
El directorio temporal usado para almacenar ficheros durante el proceso de subida. Si no se indica, usa el directorio temporal del sistema Operativo.
El tamaño máximo de un archivo subido. Debe ser menor al valor indicado en post_max_size y se debe tener en cuenta también el valor asignado en max_file_uploads
.
Importante: Si su aplicación requiere la carga de archivos grandes (100MB, 2GB, etc.) se recomienda usar algún tipo de script que realice la carga del archivo en bloques de menor tamaño, para mantener este parámetro dentro de límites aceptables, especialmente si los proyectos pueden ser usados por múltiples usuarios en simultánea. Recuerde que el tamaño asignado afectará el rendimiento del servidor y posibles eventos de negación del servicio a los usuarios.
El número máximo de archivos a subir de forma simultánea. Téngase en cuenta que la suma del tamaño de los archivos subidos en simultanea, no debe ser superior al valor indicado en post_max_size
.
Presenta en los headers de respuesta que fue generada usando PHP y la versión empleada, por ejemplo X-Powered-By: PHP/8.1.10
.
Esta información puede facilitar que un usuario malintencionado aproveche alguna vulnerabilidad especifica de la versión para atacar al sistema. Se recomienda deshabilitar esta presentación (el porqué está habilitada por defecto es un misterio).
Valor recomendado: Off
Limita los directorios de donde PHP puede leer y/o escribir archivos, ya sea usando funciones de manejo de archivos tales como fopen()
y file_get_contents()
, como también para la inclusión de librerías usando sentencias como include
y require
. Esto es especialmente importante si la aplicación permite la apertura y/o creación de archivos y/o directorios usando datos del usuario para crear el path del archivo, lo que podría dar acceso o eliminar archivos sensibles del servidor.
En Windows, se deben separar los directorio usando punto y coma, en tanto que para otros sistemas operativos se usan comas.
Durante ejecución, puede usarse para limitar el acceso a un subdirectorio de alguno de los indicados en el archivo php.ini usando ini_set()
(o a cualquiera si no se especificó alguno).
Ejemplo: open_basedir
= "C:/var/www/test/uploads"
Configuración avanzada
Estos elementos no deberían modificarse excepto que sea explícitamente requerido por las condiciones especiales de alguno de los proyectos trabajados.
Establece el máximo de memoria en bytes que un script puede consumir. Usualmente viene prefijado a 128M, que es suficiente para la mayoría de proyectos. Tenga en cuenta que si un script usualmente consume el máximo indicado, puede afectar el rendimiento, tiempos de respuesta e incluso negación del servicio cuando el servidor atienda múltiples solicitudes al mismo script.
Cuantas variables de entrada pueden ser aceptadas (el límite se aplica a los arrays superglobales $_GET, $_POST y $_COOKIE de forma separada). Por defecto acepta 1000. Ampliar en caso que algún formulario envíe al servidor más de 1000 elementos, sean individuales o como parte de arreglos de datos, de lo contrario limitará los datos a los primeros 1000 recibidos
Permite desactivar ciertas funciones por razones de seguridad, listadas separadas por comas. Se recomienda en entornos de producción, deshabilitar funciones como: eval, exec, passthru, shell_exec, system,proc_open, popen,show_source.
Extensiones dinámicas
Las extensiones dinámicas permiten el uso de funciones especializadas no nativas en PHP, tales como consultas a bases de datos, conexiones seguras y otras, que pueden ser o no usadas en cada proyecto. Se habilitan manualmente en el archivo php.ini
descomentando las líneas con el formato:
extension = [nombre de extensión]
Algunas extensiones disponen de directivas de configuración adicionales, disponibles en el mismo archivo php.ini
. Usualmente se encuentran buscando en el archivo el grupo de directivas que tengan por nombre “(nombre de extensión).(nombre de parámetro)”, como por ejemplo mysqli.max_persistent
. Ahora, dado que esta sintaxis no es obligatoria o que pueden existir directivas no incluidas por defecto en el archivo .ini
usado como base, se recomienda consultar el manual de ayudas de PHP o de la extensión deseada cuando se tenga alguna duda.
En general, las siguientes son algunas de las extensiones que se recomienda habilitar:
Permite conectarse y comunicarse con diferentes tipos de servidores y diferentes tipos de protocolos tales como http, https, ftp, gopher, telnet, dict, file y ldap.
Las funciones en este módulo tratan de averiguar el tipo de contenido y la codificación de un archivo buscando ciertas secuencias de bytes mágicas en una posición específica del mismo.
Permite crear y manipular archivos de imágenes en una variedad de diferentes formatos tales como GIF, PNG, JPEG, WBMP y XPM.
Extensión requerida siempre que se use Drupal.
Este módulo utiliza las funciones para la generación y verificación de firmas y para cifrar descifrar datos o para establecer consultas seguras a servicios remotos con cURL usando SSL.
Extensión requerida siempre que se use Drupal.
Implementa una interfaz de bajo nivel para las funciones de comunicación de sockets (también referidos como websockets), proporcionando la posibilidad de actuar tanto como servidor como cliente del socket.
Mejora el rendimiento de PHP almacenando el código de bytes de un script precompilado en la memoria compartida, eliminando así la necesidad de que PHP cargue y analice los script en cada petición.
Extensión requerida siempre que se use Drupal.
Nótese que esta extensión se habilita usando como nombre de parámetro “zend_extension” en lugar de simplemente “extensión”.
Para manejo de bases de datos, se recomiendan:
La extensión mysqli (MySQL mejorada) permite acceder a bases de datos MySQL y MariaDB (producto de código abierto para bases de datos similares a MySQL, esta última es propiedad de Oracle pero que alguna vez fue de código abierto).
Extensión requerida siempre que se use Wordpress.
Controlador que implementa la interfaz de Objetos de Datos de PHP (PDO) para el acceso a bases de datos de MySQL y MariaDB.
Extensión requerida siempre que se use Drupal.
Soporte para bases de datos PostgreSQL (producto de código abierto).
Controlador que implementa la interfaz de Objetos de Datos de PHP (PDO) para el acceso a bases de datos PostgreSQL.
Importante: Considerar las recomendaciones de seguridad de PHP para manejo de bases de datos descritas en el manual de PHP, para limitar los riesgos y vulnerabilidades.
Finalmente, estas son algunas extensiones que pueden resultar de interés cuando se requiera soporte de diferentes idiomas (internacionalización):
Implementa una API de Soporte de Lenguaje Nativo (NLS de las siglas en inglés Native Language Support) la cual se puede utilizar para internacionalizar las aplicaciones de PHP.
Proporciona funciones específicas para cadenas de texto multibyte (como UTF-8) que ayudan a tratar codificaciones multibyte en PHP.
Extensión requerida siempre que se use Drupal.
Recomendaciones finales
Cuando se ingresen valores para parámetros que correspondan a directorios o a rutas de archivos, tener siempre presente:
- Enmarcar siempre entre comillas dichas rutas y usar el carácter “/” como separador de subdirectorios (incluso en Windows, es compatible) para evitar que por ejemplo en un valor del tipo
xxx\txxx
, el conjunto “\t” sea interpretado como un tabulador por el interpretador delphp.ini
. En su defecto, usar “\\” como separador de subdirectorios en Windows. - El directorio indicado debe tener permisos de lectura/escritura para el usuario asignado por defecto al servicio HTTP
Si luego de configurar el archivo php.ini
la ejecución de un script PHP resulta fallida, se recomienda consultar los logs de errores declarados. Opcionalmente, si no es posible determinar por este medio el error, ejecutar por linea de comando una consulta a PHP y verificar si se presenta algún error. Esto puede hacerse ejecutando desde el directorio en que está instalado PHP el siguiente comando:
php -v
Asegúrese siempre que esté habilitada la presentación de errores en pantalla, asignando la directiva
display_errors = On
En caso que no esté cargando correctamente el archivo de configuración deseado, puede ejecutarse:
php -c “(ruta del archivo php.ini deseado)” -v
Es importante también confirmar que se esté leyendo el archivo de configuración correcto, lo que puede verse ejecutando un script con la linea:
<? phpinfo() ?>
y buscar el valor asignado al parámetro “Loaded Configuration File”.
Si el archivo de configuración que aparece listado no es el deseado, fijar el atributo “PHPIniDir” en el archivo de configuración del servidor Apache (ver la entrada PHP con Apache sobre Windows para más detalles). Para IIS sobre Windows, una opción es forzar el uso del archivo php.ini
en la configuración de PHP en IIS.
Para verificar que las diferentes extensiones dinámicas deseadas se hayan cargado correctamente, puede usar el mismo script que ejecuta phpinfo(), ya sea por web o usando la siguiente línea de comando:
php -i
o
php -c “(ruta del archivo php.ini deseado)” -i
Usualmente cada extensión presentará detalles de su configuración al ejecutar phpinfo()
. Si por el contrario, no se encuentra rastro alguno del nombre de alguna extensión, significa que no fue cargada, posiblemente por falta de alguna otra extensión de soporte. En dicho evento, no queda más remedio que consultar las ayudas de la extensión o realizar una búsqueda en Internet a ver qué se encuentra.
Y ya con esto terminamos la configuración de nuestro PHP y podemos proceder a una ejecución más o menos segura de nuestros proyectos. Ha quedado algo extensa esta entrada pero creo que está completa y cubre lo más relevante en lo que ha configuración de PHP respecta.
Si encuentras alguna inconsistencia o tienes alguna sugerencia que pueda complementar esta entrada, por favor no dudes en compartirlo en los comentarios.
Sin más por el momento, nos vemos en una próxima entrada.
Nota: Actualizado en septiembre 08 / 2023
Imagen de portada editada de un original de Aleksey Nemiro tomada de Pixabay
Comentarios
Publicar un comentario