Guía completa de seguridad en PHP para desarrolladores backend

php Guía completa de seguridad en PHP para desarrolladores backend

Guía completa de seguridad en PHP para desarrolladores backend

La seguridad en aplicaciones PHP no es opcional: es una obligación. En esta guía práctica verás las amenazas más comunes, ejemplos de código seguros, estructura recomendada del proyecto y por qué cada medida reduce riesgo real. Voy directo al grano, con ejemplos listos para integrar.

1. Principios básicos y configuración segura

  • Siempre usa HTTPS y HSTS. HTTP es inseguro para transporte de credenciales y cookies.
  • Desactiva la salida de errores en producción: display_errors = Off en php.ini y controla logs.
  • Usa versiones de PHP soportadas y aplica parches. Mantén dependencias actualizadas con composer.
; php.ini (relevante para producción)
display_errors = Off
error_reporting = E_ALL
log_errors = On
session.cookie_httponly = 1
session.cookie_secure = 1
session.use_strict_mode = 1

2. Estructura de proyecto recomendada

mi-app-php/
  composer.json
  public/             # Document root
    index.php
  src/
    Auth/
      Password.php
    Security/
      Session.php
      Csrf.php
    Controllers/
  config/
    config.php        # variables de entorno via env
  storage/
    uploads/          # fuera del webroot o con reglas estrictas
  logs/
  tests/

Separar concerns facilita aplicar políticas de seguridad coherentes (p. ej. todas las sesiones desde Security/Session).

3. Validación y saneamiento de entrada

Valida por tipo y usa listas blancas. Nunca confíes en el cliente.

// Ejemplo de validación mínima
$age = filter_input(INPUT_POST, 'age', FILTER_VALIDATE_INT, ['options' => ['min_range' => 0, 'max_range' => 120]]);
$email = filter_input(INPUT_POST, 'email', FILTER_VALIDATE_EMAIL);
if ($age === false || $email === false) {
  http_response_code(400);
  echo 'Entrada inválida';
  exit;
}

Por qué: filtrar evita tipos inesperados y reduce superficie de ataque (inyecciones, lógica rota).

4. Prevención de SQL Injection: PDO con prepared statements

// db.php (ejemplo simple)
$pdo = new PDO('mysql:host=localhost;dbname=test;charset=utf8mb4', 'user', 'pass', [
  PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
  PDO::ATTR_EMULATE_PREPARES => false,
]);

$stmt = $pdo->prepare('SELECT id, name FROM users WHERE email = :email');
$stmt->execute([':email' => $email]);
$user = $stmt->fetch(PDO::FETCH_ASSOC);

Por qué: los prepared statements separan datos de la lógica SQL y eliminan la posibilidad de manipular la consulta.

5. Autenticación y gestión de contraseñas

Usa password_hash() y password_verify(). No inventes algoritmos propios. Considera sal + pepper si quieres añadir defensa adicional.

// Password.php
function createHash(string $password): string {
  // PASSWORD_ARGON2ID si está disponible, si no PASSWORD_DEFAULT
  return password_hash($password, PASSWORD_DEFAULT);
}

function verifyPassword(string $password, string $hash): bool {
  return password_verify($password, $hash);
}

Por qué: password_hash maneja sal y parámetros de coste. Cambia el algoritmo con el tiempo y re-hashear si necesario.

6. Sesiones seguras

// Security/Session.php
function startSecureSession(array $options = []) {
  $defaults = [
    'cookie_httponly' => true,
    'cookie_secure' => true, // requiere HTTPS
    'use_strict_mode' => true,
    'cookie_samesite' => 'Lax'
  ];
  ini_set('session.use_only_cookies', '1');
  foreach ($defaults as $k => $v) ini_set('session.' . $k, $v ? '1' : '0');
  session_start();
  if (empty($_SESSION['initiated'])) {
    session_regenerate_id(true);
    $_SESSION['initiated'] = true;
  }
}

Por qué: regenerar id previene session fixation; SameSite y secure reducen riesgo de CSRF y robo de cookie.

7. CSRF (Cross-Site Request Forgery)

Implementa tokens sincronizados (Synchronizer Token Pattern) o doble cookie. Ejemplo sencillo:

// Security/Csrf.php
function csrfToken(): string {
  if (empty($_SESSION['csrf'])) {
    $_SESSION['csrf'] = bin2hex(random_bytes(32));
  }
  return $_SESSION['csrf'];
}

function validateCsrf(string $token): bool {
  return hash_equals($_SESSION['csrf'] ?? '', $token);
}

// Uso en formulario
$token = csrfToken();
// <input type='hidden' name='csrf' value='<?= $token ?>'>

Por qué: los tokens imparables por un atacante externo evitan que formularios maliciosos se sometan en nombre del usuario.

8. Cross-Site Scripting (XSS) y Content Security Policy (CSP)

Escapa toda salida dinámica según el contexto. Para HTML use htmlspecialchars. Para atributos y JS, aplicar escapes adecuados.

function e(string $s): string { return htmlspecialchars($s, ENT_QUOTES | ENT_SUBSTITUTE, 'UTF-8'); }
// En plantilla: <div><?= e($user['name']) ?></div>

Ejemplo de header CSP:

header("Content-Security-Policy: default-src 'self'; script-src 'self' 'nonce-...'; object-src 'none';");

Por qué: CSP reduce el impacto de inyecciones de scripts y obliga a cargar recursos sólo desde orígenes confiables.

9. Manejo seguro de uploads

  • Guarda archivos fuera del webroot o con nombres aleatorios.
  • Valida tipo MIME y extensión, pero confía más en inspección del contenido (getimagesize para imágenes).
  • Limita tamaño y establece permisos de fichero mínimos.
// upload simple
if (isset($_FILES['file'])) {
  $f = $_FILES['file'];
  if ($f['error'] !== UPLOAD_ERR_OK) throw new RuntimeException('Upload error');
  if ($f['size'] > 2 * 1024 * 1024) throw new RuntimeException('Archivo demasiado grande');
  $finfo = finfo_open(FILEINFO_MIME_TYPE);
  $mime = finfo_file($finfo, $f['tmp_name']);
  if (!in_array($mime, ['image/jpeg', 'image/png'])) throw new RuntimeException('Tipo no permitido');
  $target = __DIR__ . '/../storage/uploads/' . bin2hex(random_bytes(16));
  move_uploaded_file($f['tmp_name'], $target);
}

10. Cabeceras HTTP útiles

header('Strict-Transport-Security: max-age=31536000; includeSubDomains; preload');
header('X-Frame-Options: DENY');
header('X-Content-Type-Options: nosniff');
header('Referrer-Policy: no-referrer-when-downgrade');

Por qué: estas cabeceras cierran vectores comunes (clickjacking, content type sniffing, etc.).

11. Gestión de secretos y configuración

No guardes credenciales en el repo. Usa variables de entorno y control de accesos. Ejemplo con phpdotenv:

composer require vlucas/phpdotenv

Y en config/config.php lee $_ENV o getenv. Protege copias de seguridad y backups.

12. Registro, monitoreo y respuesta

  • Registra eventos de seguridad (login fallidos, cambios de contraseña, uploads) con contexto mínimo.
  • Instrumenta alertas y revisa logs con regularidad.

13. Herramientas y pruebas

  • Static analysis: PHPStan, Psalm
  • Dependencias: composer audit o SensioLabs Security Checker (según disponibilidad)
  • Escaneo dinámico: OWASP ZAP, Burp Suite
  • Fuzzing y tests automatizados de endpoints

14. Errores comunes y cómo evitarlos

  • Exponer detalles de error en producción — desactivar display_errors.
  • Construir consultas con concatenación de strings — usar prepared statements.
  • Almacenar archivos subidos en webroot sin validación — mover fuera del webroot.
  • Confiar en JavaScript para validaciones críticas — validar en servidor también.

15. Checklist rápido para deploy seguro

  1. TLS configurado y forzado (HSTS)
  2. php.ini con display_errors=Off y logs habilitados
  3. Cookies con Secure, HttpOnly y SameSite
  4. Prepared statements y escapes por contexto
  5. Cabeceras CSP, X-Frame-Options, X-Content-Type-Options
  6. Escaneo de dependencias y análisis estático pasados

La seguridad es un proceso continuo. Empieza por las defensas básicas (HTTPS, sesiones seguras, prepared statements) y añade capas: validación estricta, CSP, escaneo regular y respuesta a incidentes. Un paso práctico ahora: instrumenta logs de autenticación y activa alertas para múltiples intentos fallidos. Si quieres, puedo generar el esqueleto del proyecto con los archivos de ejemplo listos para arrancar y tests básicos para validar estas políticas.

Consejo avanzado: considera mover funciones críticas (p. ej. validación, encriptado de secretos) a procesos o servicios aislados y delega autenticación a proveedores externos confiables cuando la seguridad y cumplimiento sean críticos.

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