7 errores críticos que todos cometen con PHP (y cómo resolverlos)
Este post va directo al grano: errores reales que veo en código PHP de producción y cómo corregirlos de forma práctica. Por cada error tienes el por qué, un ejemplo vulnerable y la corrección recomendada.
Error 1 — No usar consultas preparadas (riesgo de SQL Injection)
Por qué duele: concatenar entrada del usuario en SQL permite inyección y compromete la base de datos.
<?php
// vulnerable
$id = $_GET['id'];
$result = $pdo->query("SELECT * FROM users WHERE id = $id");
?>
Solución: siempre usar consultas preparadas (PDO/parameter binding) y validar el tipo si corresponde.
<?php
// seguro
$stmt = $pdo->prepare('SELECT * FROM users WHERE id = :id');
$stmt->execute(['id' => (int)$_GET['id']]);
$user = $stmt->fetch();
?>
Error 2 — Usar funciones obsoletas o APIs inseguras (mysql_*)
Por qué duele: las funciones mysql_* fueron eliminadas y fomentan malas prácticas. Usa PDO o mysqli con prepared statements.
// Evita: mysql_query(), mysql_connect()
// Usa:
<?php
$pdo = new PDO('mysql:host=localhost;dbname=app', 'user', 'pass', [
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
]);
?>
Error 3 — Validación/filtrado insuficiente (confianza en el cliente)
Por qué duele: todo input del cliente es potencialmente malicioso. Validar y sanear en servidor es mandatorio.
// Vulnerable: confiar en datos del formulario
$email = $_POST['email'];
// Mejora: validar
if (!filter_var($email, FILTER_VALIDATE_EMAIL)) {
throw new InvalidArgumentException('Email inválido');
}
Error 4 — Manejo inseguro de uploads (sin validación, risk de RCE/path traversal)
Por qué duele: permiten subir scripts, sobrescribir archivos o almacenar fuera de control.
<?php
// Vulnerable: guardando el nombre original sin comprobaciones
move_uploaded_file($_FILES['file']['tmp_name'], __DIR__ . '/uploads/' . $_FILES['file']['name']);
?>
Solución: validar tamaño, tipo (whitelist), generar nombre aleatorio, almacenar fuera del webroot y evitar extensiones peligrosas.
<?php
$allowed = ['png','jpg','jpeg','pdf'];
$ext = strtolower(pathinfo($_FILES['file']['name'], PATHINFO_EXTENSION));
if (!in_array($ext, $allowed)) {
throw new RuntimeException('Tipo de archivo no permitido');
}
if ($_FILES['file']['size'] > 2_000_000) {
throw new RuntimeException('Archivo demasiado grande');
}
$targetDir = __DIR__ . '/private_uploads/';
$filename = bin2hex(random_bytes(16)) . '.' . $ext; // nombre seguro
move_uploaded_file($_FILES['file']['tmp_name'], $targetDir . $filename);
?>
Error 5 — Mostrar errores en producción (display_errors = On)
Por qué duele: la salida de errores puede filtrar rutas, consultas, credenciales y stack traces.
// No hagas esto en producción
ini_set('display_errors', '1');
// Haz esto en producción
ini_set('display_errors', '0');
ini_set('log_errors', '1');
error_log('Mensaje de error');
En producción, registra errores en ficheros y usa un sistema de monitoreo (Sentry, Bugsnag, etc.).
Error 6 — Manejo incorrecto de contraseñas (md5/sha1 sin sal)
Por qué duele: algoritmos obsoletos son triviales de romper. Usa APIs modernas y resistentes.
// Vulnerable
$hash = md5($password);
// Seguro
$hash = password_hash($password, PASSWORD_DEFAULT);
// Verificación
if (password_verify($password, $hash)) {
// OK
}
Error 7 — Sesiones inseguras (no regenerar ID, cookies mal configuradas)
Por qué duele: session fixation, robo de sesión y exposición en conexiones no seguras.
<?php
// Configuración recomendada al iniciar la app
session_set_cookie_params([
'lifetime' => 0,
'path' => '/',
'domain' => 'tu-dominio.com',
'secure' => true, // solo HTTPS
'httponly' => true, // imposible acceder via JS
'samesite' => 'Lax',
]);
session_start();
// Al autenticar:
session_regenerate_id(true);
?>
Buenas prácticas extra (rápidas y accionables)
- Usa declare(strict_types=1) y tipado donde puedas para detectar errores temprano.
- Integra PHPStan/Psalm en tu CI para análisis estático.
- Gestiona dependencias y revisa vulnerabilidades: composer audit / GitHub Dependabot.
- Aplica Content Security Policy y headers: X-Frame-Options, X-Content-Type-Options, Referrer-Policy.
- Evita reinventar la rueda: usa librerías probadas para autenticación (OAuth libraries, JWT libs bien mantenidas).
Este conjunto de correcciones evita la mayoría de incidentes comunes en aplicaciones PHP. Siguiente paso: añade pruebas automatizadas que verifiquen tus controles (fuzzing de endpoints, tests de integración que comprueben validación y seguridad) y ejecuta análisis estático en cada PR. Un consejo avanzado: aplica mínimo privilegio para la cuenta DB y separa servicios para limitar blast radius.
¿Quieres comentar?
Inicia sesión con Telegram para participar en la conversación