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

Un repaso práctico a las vulnerabilidades más comunes en aplicaciones PHP y patrones concretos para mitigarlas. Aquí encontrarás ejemplos de código seguros, configuraciones recomendadas y herramientas para automatizar la detección.

1) Inyección SQL

Problema: concatenar entradas de usuario en consultas SQL permite inyección.

// MAL: vulnerable a SQL injection
$id = $_GET['id'];
$sql = "SELECT * FROM users WHERE id = $id";
$result = $db->query($sql);

// BIEN: usar PDO con prepared statements
$pdo = new PDO('mysql:host=localhost;dbname=mydb', 'user', 'pass', [PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION]);
$stmt = $pdo->prepare('SELECT * FROM users WHERE id = :id');
$stmt->execute(['id' => $_GET['id']]);
$user = $stmt->fetch();

Por qué: los prepared statements separan estructura de datos, evitando que un input modifique la consulta.

2) Cross-Site Scripting (XSS)

Problema: imprimir datos no sanitizados en HTML permite ejecución de scripts maliciosos.

// MAL
echo "
Comentario: " . $comment . "
"; // BIEN: escapar según el contexto echo '
Comentario: ' . htmlspecialchars($comment, ENT_QUOTES | ENT_SUBSTITUTE, 'UTF-8') . '
';

Consejo: usa funciones de escape contextuales. Para atributos HTML usa htmlspecialchars, para URLs usa rawurlencode y para datos JS serialízalos con json_encode dentro de un script seguro.

3) CSRF (Cross-Site Request Forgery)

Protege las acciones que cambian estado con tokens anti-CSRF.

// Generar token al inicio de sesión o al cargar el form
function csrf_token() {
    if (!isset($_SESSION['csrf'])) {
        $_SESSION['csrf'] = bin2hex(random_bytes(32));
    }
    return $_SESSION['csrf'];
}

// En el formulario
echo "";

// Verificar al procesar
if (!hash_equals($_SESSION['csrf'] ?? '', $_POST['csrf'] ?? '')) {
    http_response_code(403);
    exit('Invalid CSRF token');
}

Usa hash_equals para evitar timing attacks.

4) Subida de archivos insegura

Problema: aceptar archivos sin validar puede llevar a ejecución remota o traversal.

// Validación mínima segura
$allowed = ['image/jpeg','image/png'];
$finfo = new finfo(FILEINFO_MIME_TYPE);
$mime = $finfo->file($_FILES['file']['tmp_name']);
if (!in_array($mime, $allowed, true)) {
    throw new RuntimeException('Tipo de archivo no permitido');
}

$ext = pathinfo($_FILES['file']['name'], PATHINFO_EXTENSION);
$filename = bin2hex(random_bytes(16)) . '.' . $ext; // nombre aleatorio
$target = __DIR__ . '/uploads/' . $filename; // carpeta fuera del webroot preferible
move_uploaded_file($_FILES['file']['tmp_name'], $target);

Recomendaciones: almacenar fuera del webroot, establecer permisos estrictos, no confiar en la extension y validar con finfo, limitar tamaño y procesar archivos (p. ej. re-encodificar imágenes) antes de servirlos.

5) Remote Code Execution y eval

Evita eval(), create_function() y cualquier ejecución de código basado en input del usuario. Si necesitas cargar módulos dinámicamente, usa una lista blanca de rutas o nombres:

$allowed = ['reports','invoices'];
$module = $_GET['module'] ?? '';
if (!in_array($module, $allowed, true)) {
    http_response_code(400);
    exit('Módulo inválido');
}
require __DIR__ . '/modules/' . $module . '.php';

6) Manejo inseguro de sesiones

Configura cookies de sesión y regenera el id al autenticar.

 0,
    'path' => '/',
    'domain' => 'example.com',
    'secure' => true, // solo HTTPS
    'httponly' => true,
    'samesite' => 'Lax',
]);
session_start();

// Al iniciar sesión
session_regenerate_id(true);

Habilita session.use_strict_mode en php.ini para evitar session fixation.

7) Contraseñas y autenticación

Usa password_hash y password_verify. Nunca reinventes esquemas de hashing.

Agrega límites de intentos y bloqueo gradual (rate limiting) y, cuando sea posible, MFA.

8) Deserialización insegura

Evita unserialize() en datos no confiables. Usa json_encode/json_decode o implementa un deserializador seguro con lista blanca de clases.

9) Exposición de errores y configuración

No muestres errores en producción. En php.ini establece:

display_errors = Off
log_errors = On
error_log = /var/log/php_errors.log

Evita revelar stack traces o rutas internas al usuario.

10) Inclusiones y traversal

Valida rutas y usa realpath para garantizar que un archivo resida en un directorio permitido:

$base = realpath(__DIR__ . '/pages');
$file = realpath($base . '/' . $_GET['page'] . '.php');
if ($file === false || strpos($file, $base) !== 0) {
    http_response_code(404);
    exit('Not found');
}
require $file;

Encabezados HTTP de seguridad

Herramientas y automatización

  • Composer audit: detectar dependencias con vulnerabilidades.
  • PHPStan / Psalm: análisis estático para encontrar bugs y problemas de seguridad.
  • Semgrep / SonarQube / Snyk: escaneo de reglas de seguridad y secretos.
  • Dependabot / Renovate: mantener dependencias actualizadas.

Checklist rápido

  • Usar prepared statements en todas las consultas.
  • Escapar todas las salidas según contexto.
  • Implementar tokens CSRF en formularios y endpoints state-changing.
  • Validar y procesar uploads, almacenarlos fuera del webroot.
  • Configurar cookies: Secure, HttpOnly, SameSite.
  • No usar eval/exec con input; usar whitelists para includes.
  • Deshabilitar display_errors en producción y registrar errores.
  • Ejecutar análisis estático y auditorías de dependencias en CI.

Avanza: implementar pruebas de seguridad automatizadas en tu pipeline (Semgrep + composer audit + PHPStan), desplegar CSP de forma incremental y activar un WAF para mitigar ataques en tiempo real. Advertencia: no asumas que una sola medida te hace seguro; aplica defensa en profundidad y monitoriza logs y métricas para detectar anomalías.

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!

© 2026 Space Howen
Hecho con ❤️ para el 🌍