Vulnerabilidades críticas en PHP y cómo solucionarlas

php Vulnerabilidades críticas en PHP y cómo solucionarlas

Vulnerabilidades críticas en PHP y cómo solucionarlas

Esta guía repasa las vulnerabilidades más comunes en aplicaciones PHP, ejemplos de código vulnerables y soluciones prácticas y reproducibles. Está pensada para desarrolladores que quieren asegurar sus aplicaciones sin vaporware: código real, razones y buenas prácticas inmediatas.

1) Inyección SQL

Qué ocurre: concatenar entrada del usuario en consultas SQL permite que un atacante manipule la consulta.

<?php
// VULNERABLE
$username = $_POST['username'];
$sql = "SELECT * FROM users WHERE username = '" . $username . "'";
$rows = $db->query($sql);
?>

Solución: usar sentencias preparadas (PDO) y validación mínima.

<?php
// SEGURO (PDO)
$stmt = $pdo->prepare('SELECT id, username FROM users WHERE username = :u');
$stmt->execute([':u' => $_POST['username']]);
$user = $stmt->fetch();
?>

2) Cross-Site Scripting (XSS)

Qué ocurre: salida no escapada permite inyección de JavaScript en el navegador de la víctima.

<?php
// VULNERABLE
echo 'Comentario: ' . $comment_from_db;
?>

Solución: escapar la salida con htmlspecialchars usando la codificación correcta y ENT_QUOTES.

<?php
echo 'Comentario: ' . htmlspecialchars($comment_from_db, ENT_QUOTES | ENT_SUBSTITUTE, 'UTF-8');
?>

Adicional: para HTML rico, usa una whitelist (ej. HTML Purifier) en lugar de strip_tags.

3) CSRF (Cross-Site Request Forgery)

Qué ocurre: acciones sensibles (cambio de contraseña, transferencia) se ejecutan sin consentimiento por petición forjada.

<?php
// Añadir token al formulario
session_start();
if (empty($_SESSION['csrf_token'])) {
    $_SESSION['csrf_token'] = bin2hex(random_bytes(32));
}
?>
<form method='post' action='/save'>
  <input type='hidden' name='csrf_token' value='<?= $_SESSION['csrf_token'] ?>' />
  ...
</form>

<?php
// Validar al recibir
if (!hash_equals($_SESSION['csrf_token'], $_POST['csrf_token'] ?? '')) {
    http_response_code(403);
    exit('CSRF token invalido');
}
?>

4) Deserialización insegura (unserialize)

Qué ocurre: unserialize puede reconstruir objetos y ejecutar métodos mágicos que llevan a RCE o escalado.

<?php
// VULNERABLE
$data = $_POST['payload'];
$object = unserialize($data); // peligroso
?>

Solución: usa formatos seguros como JSON. Si debes usar unserialize, limita clases permitidas (PHP 7+).

<?php
// SEGURO
$decoded = json_decode($raw, true);

// Si usas unserialize por compatibilidad
$object = unserialize($data, ['allowed_classes' => false]);
?>

5) Subida de archivos insegura

Qué ocurre: subir archivos sin validación puede permitir ejecución de código, sobrescritura o exfiltración.

<?php
// RECOMENDACIONES
// 1) Validar MIME con finfo
$finfo = new finfo(FILEINFO_MIME_TYPE);
$mime = $finfo->file($_FILES['file']['tmp_name']);
$allowed = ['image/png', 'image/jpeg'];
if (!in_array($mime, $allowed, true)) {
    throw new RuntimeException('Tipo no permitido');
}

// 2) Generar nombre aleatorio y guardar fuera del webroot
$target = '/var/www/uploads/' . bin2hex(random_bytes(16));
move_uploaded_file($_FILES['file']['tmp_name'], $target);
// 3) Establecer permisos restrictivos
chmod($target, 0640);
?>

6) Gestión de sesiones y fijación

Qué ocurre: sesiones robadas o fijadas por un atacante permiten suplantación.

<?php
// Mejores prácticas al iniciar sesión
session_start();
// Regenerar id tras login
session_regenerate_id(true);
// Configurar cookies seguras en bootstrap de la app
ini_set('session.cookie_httponly', '1');
ini_set('session.cookie_secure', '1'); // HTTPS
ini_set('session.use_strict_mode', '1');
?>

7) Command injection

Qué ocurre: concatenar entrada en llamadas al sistema (exec, shell_exec, system) permite ejecución de comandos arbitrarios.

<?php
// VULNERABLE
$user = $_GET['user'];
$output = shell_exec('ls ' . $user);

// SEGURO: evitar la shell, usar escapeshellarg o mejor aún APIs nativas
$escaped = escapeshellarg($user);
$output = shell_exec('ls ' . $escaped);
?>

Mejor: evitar shell a favor de funciones nativas de PHP o bibliotecas.

8) Directory traversal

<?php
// VULNERABLE
$file = $_GET['file'];
include 'pages/' . $file . '.php';

// SEGURO: whitelist y realpath
$allowed = ['home', 'about', 'contact'];
if (!in_array($file, $allowed, true)) {
    http_response_code(404);
    exit;
}
include 'pages/' . $file . '.php';

// O validar ruta
$base = realpath(__DIR__ . '/pages');
$path = realpath($base . '/' . $file . '.php');
if ($path === false || strpos($path, $base) !== 0) {
    http_response_code(404);
    exit;
}
include $path;
?>

9) Criptografía y almacenamiento de contraseñas

Qué ocurre: usar MD5/SHA1 o cifrados mal configurados hace que contraseñas sean fáciles de romper.

<?php
// INCORRECTO: md5
$hash = md5($password);

// CORRECTO: password_hash / password_verify
$hash = password_hash($password, PASSWORD_DEFAULT);
if (!password_verify($attempt, $hash)) {
    // falla
}

// Para cifrado de datos sensibles, usa libsodium (ext-sodium)
$key = sodium_crypto_secretbox_keygen();
$ciphertext = sodium_crypto_secretbox($plaintext, $nonce, $key);
?>

10) Filtrado y validación insuficiente

No confíes en el cliente ni en filtros superficiales. Valida tipo, longitud, formato y rango. Aplica whitelist más que blacklist.

Herramientas recomendadas y prácticas

  • Usar PDO y sentencias preparadas.
  • Escapar siempre salida con htmlspecialchars o plantillas seguras (Twig, Blade).
  • password_hash y libsodium para contraseñas y cifrado.
  • Content Security Policy (CSP) bien definida para mitigar XSS.
  • HTTPS estricto, HSTS y cookies Secure/HttpOnly/SameSite.
  • Regenerar session id al login y habilitar session.use_strict_mode.
  • Revisión de dependencias: composer audit, getcomposer.org/alerts o herramientas de SCA.
  • Escaneo estático: PHPStan, Psalm con reglas de seguridad; escaneo dinámico: OWASP ZAP, Burp.

Checklist rápida para deploy seguro

  1. No mostrar errores en producción (display_errors=0), loggear en fichero.
  2. Desactivar funciones peligrosas si no se usan (exec, system, passthru).
  3. Limitar permisos de archivos y directorios (principio de menor privilegio).
  4. Almacenar secretos fuera del repo; usar vaults o variables de entorno.
  5. Escanear dependencias y aplicar actualizaciones regularmente.

Para no quedarte en lo teórico: integra en tu CI un pipeline que incluya PHPStan/Psalm, composer audit y un escaneo dinámico nocturno con OWASP ZAP. Esto encuentra regresiones antes de salir a producción.

Consejo avanzado: combina análisis estático con fuzzing en endpoints críticos y pruebas de inyección automatizadas. No confíes solo en filtros del lado cliente ni en validaciones ad-hoc — la seguridad debe ser parte del ciclo de desarrollo.

Si quieres, puedo generar plantillas de middleware para CSRF, validadores de entrada, configuración segura de PHP-FPM/Nginx o un workflow de GitHub Actions que incluya PHPStan + Trivy para escaneo de dependencias; dime cuál prefieres y lo preparo.

Comentarios
¿Quieres comentar?

Inicia sesión con Telegram para participar en la conversación


Comentarios (0)

Aún no hay comentarios. ¡Sé el primero en comentar!

Iniciar Sesión