← Blog

Cómo Desarrollar Aplicaciones Más Seguras con PHP

Una guía práctica de desarrollo seguro en PHP: prevén SQL injection, XSS, inclusión de archivos y command injection, y refuerza dependencias con SCA.

Bruno Baldo·Sep 14, 2026·7 min de lectura·Revisado por Rainforest Technologies

PHP todavía impulsa una enorme parte de la web, desde sitios pequeños hasta algunas de las plataformas más grandes en línea. Ese alcance convierte al desarrollo seguro en PHP en una necesidad práctica y no en una idea de último momento. La buena noticia es que la seguridad en PHP es muy aprendible: la mayoría de los incidentes se remonta a una lista corta de errores bien comprendidos, y cada uno tiene una solución confiable y bien respaldada. Esta guía recorre las vulnerabilidades que más importan y las prácticas de codificación segura en PHP que las neutralizan.

La mentalidad que lo une todo es simple: trata cada fragmento de entrada como no confiable hasta que se demuestre lo contrario, y trata cada fragmento de salida como peligroso hasta que esté debidamente codificado para su destino.

SQL injection y prepared statements

El SQL injection sigue siendo una de las fallas de mayor impacto en las aplicaciones PHP, y aparece cada vez que la entrada del usuario se concatena directamente en una cadena de consulta. La solución es dejar de mezclar datos y código. Usa PDO con consultas parametrizadas para que el motor de la base de datos, y no la concatenación de cadenas, decida qué es dato.

Prepara una sentencia como $stmt = $pdo->prepare('SELECT * FROM users WHERE email = ?'); y luego $stmt->execute([$email]);. El valor del usuario se vincula como parámetro y nunca puede alterar la estructura de la consulta. Haz de PDO tu capa de base de datos predeterminada, habilita las excepciones con PDO::ATTR_ERRMODE y evita los prepares emulados para que se utilice el binding real del lado del servidor.

Inclusión de archivos (LFI y RFI)

El include y el require de PHP son poderosos, y ese poder es precisamente el problema cuando la entrada del usuario llega a ellos. Pasar un parámetro de solicitud a include($_GET['page'] . '.php') abre la puerta al local file inclusion (leer o ejecutar archivos que ya están en el servidor) y, en entornos mal configurados, al remote file inclusion.

Nunca construyas una ruta de include a partir de la entrada del usuario directamente. En su lugar, usa una allowlist: asigna un pequeño conjunto de claves conocidas y válidas a rutas de archivo fijas, y rechaza cualquier cosa que no esté en la lista. Mantén allow_url_include y allow_url_fopen deshabilitados, a menos que tengas una razón específica y revisada para cambiarlos.

Command injection

Funciones como system, exec, shell_exec, passthru y el operador de comillas invertidas entregan la entrada al shell del sistema operativo. Si cualquier valor controlado por el usuario fluye hacia ellas sin escape, un atacante puede encadenar sus propios comandos. Siempre que sea posible, evita recurrir al shell por completo y usa una función o biblioteca nativa de PHP en su lugar. Cuando realmente debas llamar a un programa externo, escapa los argumentos con escapeshellarg() y el nombre del comando con escapeshellcmd(), y valida primero la entrada contra un formato esperado estricto.

Deserialización insegura

Llamar a unserialize() sobre entrada no confiable es peligroso porque PHP instanciará objetos y disparará métodos mágicos durante el proceso, lo que los atacantes pueden aprovechar para convertirlo en cadenas de object injection y ejecución remota de código. La regla es directa: nunca ejecutes unserialize() sobre datos que provengan de un usuario, de una cookie o de cualquier fuente externa. Prefiere json_decode() para el intercambio, ya que produce estructuras de datos simples en lugar de objetos vivos. Si debes aceptar entrada serializada, pasa la opción allowed_classes para restringir lo que puede instanciarse.

Cross-site scripting (XSS)

El XSS ocurre cuando los datos proporcionados por el usuario se escriben en una página HTML sin codificación, lo que permite que el script de un atacante se ejecute en el navegador de la víctima. La codificación de salida es la respuesta. Codifica los valores dinámicos para su contexto con htmlspecialchars($value, ENT_QUOTES, 'UTF-8') antes de imprimirlos en el HTML. Para una defensa robusta, combina la codificación de salida con una cabecera Content Security Policy y usa un motor de plantillas que haga auto-escape de forma predeterminada.

Cross-site request forgery (CSRF)

El CSRF engaña al navegador de un usuario autenticado para que envíe una solicitud que no tenía intención de hacer. Defiende las solicitudes que cambian el estado con tokens anti-CSRF: genera un token criptográficamente aleatorio, almacénalo en la sesión, incorpóralo como un campo oculto en cada formulario y verifícalo al enviarlo con una comparación de tiempo constante como hash_equals(). Configurar las cookies con el atributo SameSite añade una valiosa segunda capa.

Type juggling y comparaciones flexibles

El operador de comparación flexible de PHP (==) realiza una coerción de tipos que puede producir resultados sorprendentes, lo que se convierte en un problema de seguridad en verificaciones como la comparación de tokens o hashes. Usar == donde dos valores de tipos diferentes pueden tratarse como iguales puede permitir que un atacante eluda una verificación. Usa siempre comparación estricta (===) para las decisiones de seguridad, y utiliza funciones diseñadas para ese fin, como hash_equals() para comparar secretos y password_verify() para contraseñas.

Fortalecimiento de sesión y configuración

Muchos problemas clásicos de PHP fueron problemas de configuración. La era de register_globals quedó atrás hace mucho tiempo, pero su lección se mantiene: los valores predeterminados inseguros provocan filtraciones. Mantén display_errors desactivado en producción, registra los errores en su lugar y configura las cookies de sesión con las flags HttpOnly, Secure y SameSite. Regenera el ID de sesión al iniciar sesión con session_regenerate_id(true), almacena los secretos en variables de entorno en lugar de en el código fuente, y mantén el propio PHP actualizado en una versión con soporte.

Riesgo de dependencias con Composer

Las aplicaciones PHP modernas incorporan decenas de paquetes a través de Composer y Packagist, y cada dependencia es código en el que estás confiando. Una vulnerabilidad en un paquete transitivo es tu vulnerabilidad. Haz commit de tu archivo composer.lock para que las builds sean reproducibles, revisa lo que agregas, elimina los paquetes que ya no usas y ejecuta composer audit para verificar los paquetes instalados contra advisories conocidos. La software composition analysis automatizada va más allá, señalando de forma continua los componentes vulnerables y desactualizados en todo tu árbol de dependencias.

Llévalo a la CI: SAST y SCA

La revisión manual es valiosa, pero no escala. El static application security testing (SAST) analiza tu código fuente PHP en busca de patrones inseguros, mientras que el SCA vigila tus dependencias. Integrar ambos en tu pipeline de integración continua significa que cada commit y pull request se verifica automáticamente, de modo que los problemas se detectan cuando aún son baratos de corregir en lugar de después del despliegue. Si estás sopesando dónde encaja cada técnica, nuestra comparación SAST vs DAST desglosa los trade-offs, y estas herramientas se ubican de forma natural dentro de un ciclo más amplio de desarrollo seguro de software.

Una lista de verificación rápida de PHP seguro

  • Usa consultas parametrizadas con PDO en cada interacción con la base de datos.
  • Codifica toda la salida con htmlspecialchars() para su contexto.
  • Usa allowlist en cualquier ruta dinámica de include/require.
  • Escapa los argumentos del shell con escapeshellarg(), o evita recurrir al shell por completo.
  • Nunca uses unserialize() sobre entrada no confiable; prefiere json_decode().
  • Usa === y hash_equals() para las comparaciones sensibles a la seguridad.
  • Protege cada formulario que cambia el estado con un token CSRF.
  • Refuerza las cookies de sesión y mantén desactivada la visualización de errores en producción.
  • Fija y audita las dependencias de Composer.
  • Ejecuta SAST y SCA en cada build.

Cómo ayuda Rainforest

Rainforest está diseñado para hacer del desarrollo seguro en PHP una parte de tu flujo de trabajo habitual y no una tarea aparte. Nuestra plataforma de application security testing analiza tu código en busca de las clases de vulnerabilidad anteriores y se integra directamente en CI/CD, de modo que los hallazgos aparecen en el pull request con orientación clara sobre dónde está el problema y cómo remediarlo. En el lado de las dependencias, la software composition analysis rastrea tus paquetes de Composer y te alerta cuando un componente contiene una vulnerabilidad conocida. Cimentar tu programa en las categorías de riesgo de OWASP mantiene a tu equipo enfocado en lo que los atacantes realmente atacan.

Las aplicaciones seguras provienen de buenos hábitos reforzados por buenas herramientas. Si deseas ver cómo Rainforest encaja en tu stack de PHP, agenda una demostración y la recorreremos contigo.

Preguntas frecuentes

¿Es seguro PHP?

PHP puede ser muy seguro. El lenguaje y su ecosistema moderno ofrecen herramientas sólidas, como PDO, password_hash() y las plantillas con auto-escape. La mayoría de las filtraciones en PHP provienen de prácticas de codificación inseguras y dependencias desactualizadas, no del lenguaje en sí. Permanecer en una versión de PHP con soporte y seguir las prácticas de esta guía aborda la gran mayoría del riesgo del mundo real.

¿Cuáles son las vulnerabilidades de seguridad comunes de PHP?

Las más frecuentes son SQL injection, cross-site scripting (XSS), cross-site request forgery (CSRF), inclusión local y remota de archivos (LFI/RFI), command injection y deserialización insegura. Los errores de comparación flexible (type juggling) y las configuraciones débiles de sesión o de entorno completan la lista. Todas ellas se pueden prevenir con técnicas bien establecidas.

¿Cómo prevengo el SQL injection en PHP?

Usa prepared statements de PDO con parámetros vinculados en cada consulta, de modo que la entrada del usuario siempre se trate como dato y nunca como SQL ejecutable. Habilita el manejo de errores en modo de excepción, deshabilita los prepares emulados para forzar el binding real del lado del servidor, y valida la entrada contra el formato que esperas antes de que llegue a la base de datos.

¿Cómo gestiono el riesgo de dependencias en PHP?

Trata las dependencias de Composer como parte de tu superficie de ataque. Haz commit de composer.lock para builds reproducibles, revisa y minimiza lo que agregas, y ejecuta composer audit con regularidad. Para una cobertura continua, usa software composition analysis para detectar automáticamente paquetes vulnerables y desactualizados en tus dependencias directas y transitivas.

¿Cómo analizo el código PHP en busca de vulnerabilidades?

Combina el static application security testing (SAST), para analizar tu código fuente, con la software composition analysis (SCA), para verificar tus dependencias, y ejecuta ambos automáticamente en tu pipeline de CI/CD. Rainforest ofrece ambos y reporta los hallazgos directamente en tus pull requests, de modo que los problemas se corrijan antes de que se desplieguen.

Bruno Baldo

Escrito por

Bruno Baldo

CMO

Um pouco de marketing e um pouco de curiosidade e temos a receita pra criar um apaixonado por cyber!

Siga leyendo