Ir al contenido principal

Configurando el servicio PHP


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

error_reporting

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

error_log

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).

display_errors

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

date.timezone

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

default_charset

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).

session.save_path

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().

session.use_strict_mode

Si este modo está habilitado, el módulo no aceptará IDs de sesiones no inicializadas.
Valor recomendado: 1

session.use_cookies

Especifica si el módulo usará cookies para almacenar el id de sesión en la parte del cliente.
Valor recomendado: 1

session.use_only_cookies

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)

post_max_size

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.

extension_dir

En qué directorio debería buscar PHP extensiones que se pueden cargar dinámicamente.

sys_temp_dir

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.

cgi.force_redirect

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”.

file_uploads

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.

upload_tmp_dir

El directorio temporal usado para almacenar ficheros durante el proceso de subida. Si no se indica, usa el directorio temporal del sistema Operativo.

upload_max_filesize

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.

max_file_uploads

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.

expose_php

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

open_basedir

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.

memory_limit

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.

max_input_vars

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

disable_functions

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:

extension=curl

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.

extension=fileinfo

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.

extension=gd

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.

extension=openssl

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.

extension=sockets

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.

zend_extension=opcache

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:

extension=mysqli

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.

extension=pdo_mysql

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.

extension=pgsql

Soporte para bases de datos PostgreSQL (producto de código abierto).

extension=pdo_pgsql

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):

extension=gettext

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.

extension=mbstring

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 del php.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

Entradas populares de este blog

Sesión de usuarios en aplicaciones web

Uno de los módulos más importantes y a la vez menospreciados cuando se aborda la tarea de crear un sitio web de servicios, ya sea para una intranet corporativa o un sistema de gestión de información ( SGI ) es la gestión y administración  requerida para una correcta implementación de sesiones de usuario. Y es que llevamos tanto tiempo usando usuarios y contraseñas en Internet, en cualquiera de sus muchas variaciones, que se asume muchas veces que esto ya forma parte del ADN de toda solución web y como tal, se destina muy poco tiempo y estudio a este apartado cuando se planifican las actividades de desarrollo. Lo cierto es que cada aplicación acostumbra desarrollar su propio esquema de manejo de sesiones y asumir que es algo superfluo puede equivaler a “pegarse un tiro en el pie”, especialmente cuando un módulo de este tipo se diseña desde ceros. Al referirse al manejo de sesiones de usuario suele pensarse únicamente en el proceso de capturar el nombre de usuario ( username ) y su cont

Cómo resolver y/o crear un Sudoku usando PHP (parte 1)

C omo programador, he tenido que realizar proyectos profesionalmente, algunos con mayores retos que otros. Pero aparte de los retos profesionales, existen retos personales, programas que me nace escribir ya sea porque necesito solucionar una necesidad puntual o solamente por el placer de hacerlo. Uno de esos últimos retos fue el de solucionar un Sudoku . Si ya se, existen muchas aplicaciones allí afuera que lo hacen, pero el reto es hacerlo, no copiarlo. Habiendo aclarado las intenciones al respecto, lo primero a tener claro es cómo se define un Sudoku. Para esto, voy a apoyarme en la siempre disponible (aunque no siempre fiable) Wikipedia: Un Sudoku estándar contiene 81 celdas, dispuestas en una trama de 9×9, que está subdividida en nueve cajas. Cada caja está determinada por la intersección de tres filas con tres columnas. Cada celda puede contener un número del uno al nueve y cada número solo puede aparecer una vez en cada fila, cada columna o cada caja. Un sudoku comienza con algu