M1 · Fundamentos de
programación
Antes de PHP, antes de WordPress, antes de cualquier vulnerabilidad: necesitas entender cómo piensa un ordenador. Variables, operadores, bucles y funciones son los bloques con los que se construye cualquier programa — y también con los que se construyen la mayoría de las vulnerabilidades.
Al finalizar este módulo serás capaz de:
- Declarar y manipular variables de distintos tipos de datos
- Aplicar operadores aritméticos, lógicos y de comparación correctamente
- Controlar el flujo de un programa con condicionales y bucles
- Crear funciones reutilizables con parámetros, retorno y ámbito correcto
- Trabajar con arrays indexados, asociativos y multidimensionales
- Depurar código identificando y corrigiendo errores comunes
¿Por qué empezar por aquí?
Una vulnerabilidad de inyección SQL no existe porque MySQL sea inseguro. Existe porque un programador concatenó una variable directamente en una consulta sin validarla. Un XSS no existe porque JavaScript sea peligroso. Existe porque el código imprimió datos del usuario sin escaparlos.
La mayoría de las vulnerabilidades son errores de programación. Y para identificarlos — y explotarlos o prevenirlos — necesitas pensar como un programador. Este módulo te da esa base.
Usamos PHP porque es el lenguaje de WordPress. Pero los conceptos — variables, bucles, funciones — son universales. Si los aprendes aquí, entenderás Python, JavaScript o cualquier otro lenguaje sin dificultad.
Guardar información
Las variables son contenedores que guardan datos en memoria. $nombre = "Ana" reserva espacio y etiqueta el valor.
Tomar decisiones
Los condicionales ejecutan código solo si se cumple una condición. if ($edad >= 18) es una bifurcación lógica.
Repetir acciones
Los bucles ejecutan el mismo bloque múltiples veces. foreach ($plugins as $p) recorre una lista completa.
Reutilizar código
Las funciones agrupan instrucciones bajo un nombre. sanitizar($input) puede usarse miles de veces sin reescribir.
Variables y tipos de datos
Una variable es un nombre que apunta a un valor en memoria. En PHP todas empiezan por $. PHP es de tipado dinámico: infiere el tipo según el valor asignado, lo cual es cómodo pero fuente de vulnerabilidades si no se controla.
// INTEGER — números enteros
$edad = 25;
$puerto = 8080;
$negativo = -42;
// FLOAT — decimales
$precio = 99.99;
// STRING — texto
$nombre = "SecurityWP";
$ruta = '/var/www/html';
// BOOLEAN — verdadero o falso
$activo = true;
$admin = false;
// NULL — ausencia de valor
$resultado = null;
// Verificar tipo
var_dump($edad); // int(25)
gettype($nombre); // "string"
| Tipo | Ejemplo | Uso habitual | Riesgo de seguridad |
|---|---|---|---|
int | 42 | IDs, puertos, contadores | Verificar rangos positivos |
float | 3.14 | Precios, métricas | Comparaciones imprecisas |
string | "admin" | Nombres, SQL, HTML | El tipo más peligroso sin sanitizar |
bool | true | Flags, permisos | Coerciones inesperadas con == |
null | null | Valores ausentes | Comportamientos inesperados |
array | [1,2,3] | Listas, configs | Type juggling en comparaciones |
Type juggling — el mayor peligro de PHP
PHP convierte tipos automáticamente al comparar con ==. Esto ha causado vulnerabilidades reales de bypass de autenticación en producción:
// Con == PHP convierte tipos antes de comparar
0 == "hola" // true ← 0 == cualquier string no numérico
1 == "1abc" // true ← "1abc" se convierte a 1
true == "cualquier" // true
false == "" // true
null == false // true
100 == "1e2" // true ← notación científica
// VULNERABILIDAD REAL — bypass de login con hash mágico
$hash_guardado = "0e123456789"; // hash MD5 "mágico"
$hash_enviado = "0e987654321"; // otro hash "mágico"
if ($hash_guardado == $hash_enviado) { // true ← ambos = 0^e = 0
// BYPASS: acceso concedido sin contraseña correcta
}
// SOLUCIÓN: usar siempre === (idéntico: mismo valor Y mismo tipo)
if ($hash_guardado === $hash_enviado) { // false ← correcto
// Acceso correcto
}
Usa siempre === (tres iguales) para comparar en PHP. El operador == convierte tipos antes de comparar y ha causado vulnerabilidades de autenticación reales en aplicaciones de producción. Aplica especialmente al comparar hashes, tokens y contraseñas.
Operadores
Los operadores combinan variables y valores para producir un resultado. Son la base de toda lógica de programa.
// ── ARITMÉTICOS ────────────────────────────────────
$a = 10; $b = 3;
$suma = $a + $b; // 13
$resta = $a - $b; // 7
$producto = $a * $b; // 30
$division = $a / $b; // 3.33...
$modulo = $a % $b; // 1 ← útil para paginación y paridad
$potencia = $a ** $b; // 1000
$a++; $a--; // incremento / decremento
$a += 5; $a .= " texto"; // asignación compuesta
// ── COMPARACIÓN (siempre usa ===) ──────────────────
$a === $b // idéntico: mismo valor Y tipo ← USAR ESTO
$a !== $b // no idéntico ← USAR ESTO
$a == $b // igual con conversión ← EVITAR
$a > $b // mayor que
$a >= $b // mayor o igual
$a <=> $b // spaceship: -1, 0 o 1 (para ordenar)
// ── LÓGICOS ────────────────────────────────────────
$a && $b // AND: ambos true
$a || $b // OR: al menos uno true
!$a // NOT: invierte
// ── STRINGS ────────────────────────────────────────
$completo = "Security" . "WP"; // concatenación con punto
$saludo = "Hola, {$nombre}"; // interpolación
htmlspecialchars($input, ENT_QUOTES); // escapar HTML ← fundamental
// ── NULL COALESCING ─────────────────────────────────
$nombre = $_POST['nombre'] ?? 'Anónimo'; // si no existe usa default
Estructuras de control
Las estructuras de control determinan qué código se ejecuta y cuántas veces. Son la lógica central del programa.
Condicionales
// Control de acceso por rol
$rol = "editor";
if ($rol === "admin") {
echo "Acceso completo";
} elseif ($rol === "editor") {
echo "Puede editar contenido";
} else {
echo "Sin acceso";
exit;
}
// Operador ternario — forma corta
$msg = ($rol === "admin") ? "Admin" : "Usuario";
// match (PHP 8) — más seguro que switch, usa ===
$tipo = match($_SERVER['REQUEST_METHOD']) {
'GET' => 'lectura',
'POST' => 'escritura',
'DELETE', 'PUT' => 'modificación',
default => 'desconocido',
};
Bucles
// FOR — cuando sabes cuántas iteraciones
for ($i = 0; $i < 10; $i++) {
echo "Intento {$i}\n";
}
// WHILE — mientras se cumpla una condición
$intentos = 0;
while ($intentos < 5) {
// lógica...
$intentos++;
}
// FOREACH — recorrer arrays (el más usado en WordPress)
$plugins = ['woocommerce', 'yoast', 'elementor'];
foreach ($plugins as $plugin) {
echo "Analizando: {$plugin}\n";
}
// FOREACH con clave => valor
$vulns = ['CVE-2025-1234' => 'crítico', 'CVE-2025-5678' => 'alto'];
foreach ($vulns as $cve => $severidad) {
echo "{$cve}: {$severidad}\n";
}
// break y continue
foreach ($plugins as $plugin) {
if ($plugin === 'inactivo') continue; // saltar
if ($plugin === 'malware') break; // parar todo
}
Un while(true) sin salida bloqueará el proceso PHP y puede causar un DoS involuntario en producción. Asegúrate siempre de que el bucle tiene una condición de salida garantizada.
Funciones y ámbito
Una función es un bloque de código con nombre que puedes reutilizar. Son la piedra angular del código limpio y seguro: en lugar de copiar-pegar validaciones por todo el código, creas una función validar_email() y la llamas donde la necesites.
// Definición básica
function saludar($nombre) {
return "Hola, {$nombre}";
}
echo saludar("Ana"); // "Hola, Ana"
// Parámetros por defecto
function conectar($host = 'localhost', $puerto = 3306) {
return "{$host}:{$puerto}";
}
// Tipos declarados (PHP 7+) — más seguro
function sanitizar(string $valor, int $max = 255): string {
$valor = trim($valor);
$valor = strip_tags($valor);
$valor = mb_substr($valor, 0, $max);
return htmlspecialchars($valor, ENT_QUOTES | ENT_HTML5, 'UTF-8');
}
// ÁMBITO: las variables dentro no salen fuera
$secreto = "clave_api_xyz";
function mal_ejemplo() {
// $secreto NO existe aquí — ámbito diferente
// necesitarías: global $secreto; ← evitar globals
}
// Arrow functions (PHP 7.4+) — capturan ámbito automáticamente
$multiplicar = fn($x, $y) => $x * $y;
echo $multiplicar(3, 4); // 12
// Pasar función como argumento
$numeros = [3, 1, 4, 1, 5];
usort($numeros, fn($a, $b) => $a <=> $b);
Cada función debe hacer una sola cosa y hacerla bien. validar_y_guardar_y_enviar_email() es una señal de alerta. Separa: validar(), guardar(), notificar(). El código seguro es código legible y auditable.
Arrays y objetos
Los arrays son la estructura de datos más usada en PHP y en WordPress. La base de datos devuelve arrays, los posts son arrays, las opciones de WordPress son arrays. Dominarlos es imprescindible.
// Array indexado
$plugins = ['woocommerce', 'yoast', 'elementor'];
$plugins[0]; // 'woocommerce'
$plugins[] = 'akismet'; // añadir al final
// Array asociativo (clave => valor)
$usuario = [
'id' => 42,
'nombre' => 'Ana García',
'email' => 'ana@ejemplo.com',
'rol' => 'editor',
];
$usuario['nombre']; // 'Ana García'
$usuario['rol'] = 'admin'; // modificar
// Array multidimensional
$vulns = [
['cve' => 'CVE-2025-1234', 'sev' => 'crítico'],
['cve' => 'CVE-2025-5678', 'sev' => 'alto'],
];
$vulns[0]['cve']; // 'CVE-2025-1234'
// Funciones esenciales
count($plugins); // número de elementos
in_array('yoast', $plugins, true); // ¿existe? (estricto)
array_key_exists('email', $usuario); // ¿existe la clave?
array_map('trim', $plugins); // aplicar función a cada elemento
array_filter($plugins, fn($p) => strlen($p) > 5); // filtrar
array_merge($plugins, ['nuevo']); // combinar
// LISTA BLANCA — patrón de seguridad fundamental
$permitidos = ['auditoria', 'mantenimiento', 'consultoria'];
$servicio = $_POST['servicio'] ?? '';
if (!in_array($servicio, $permitidos, true)) {
die('Servicio no válido'); // 3er param true = comparación estricta
}
Introducción a objetos
class Auditoria {
private string $url;
private array $vulns = [];
public function __construct(string $url) {
$this->url = $url;
}
public function agregar(string $cve, string $sev): void {
$this->vulns[] = ['cve' => $cve, 'sev' => $sev];
}
public function resumen(): string {
$total = count($this->vulns);
return "{$this->url}: {$total} vulnerabilidades";
}
}
$audit = new Auditoria('https://ejemplo.com');
$audit->agregar('CVE-2025-1234', 'crítico');
echo $audit->resumen(); // "https://ejemplo.com: 1 vulnerabilidades"
Depuración básica
Depurar es encontrar y corregir errores. En seguridad, la depuración también significa entender qué hace realmente el código, no solo qué se supone que debería hacer. Es una habilidad crítica al auditar plugins de WordPress.
| Tipo de error | Ejemplo | Consecuencia |
|---|---|---|
| Parse Error | Falta un ; o } | El script no arranca |
| Fatal Error | Llamar función inexistente | El script se detiene |
| Warning | Incluir archivo inexistente | Continúa pero con problemas |
| Notice | Usar variable no definida | Continúa — potencialmente inseguro |
| Logic Error | Algoritmo incorrecto | Sin error visible pero resultado incorrecto |
// var_dump — muestra tipo Y valor
$datos = ['user' => 'admin', 'pass' => '1234'];
var_dump($datos);
// array(2) { ["user"]=> string(5) "admin" ["pass"]=> string(4) "1234" }
// print_r — más legible para arrays
print_r($datos);
// Parar ejecución en un punto (inspeccionar estado)
var_dump($variable_sospechosa); die('STOP');
// Error reporting en desarrollo — SOLO en local
error_reporting(E_ALL);
ini_set('display_errors', '1'); // NUNCA en producción
// Logs en lugar de pantalla (producción)
error_log('[SecurityWP] Error: ' . $mensaje);
// Escribe en el error_log del servidor
// Activar WP_DEBUG para auditar plugins de WordPress
// En wp-config.php:
define('WP_DEBUG', true);
define('WP_DEBUG_LOG', true); // guarda en /wp-content/debug.log
define('WP_DEBUG_DISPLAY', false); // no mostrar en pantalla
Al auditar un plugin de WordPress, var_dump() y error_log() te permiten entender qué valores llegan a cada función y detectar inputs no validados. Activa WP_DEBUG = true en el entorno de desarrollo para ver todos los errores y notices.
Ejercicios prácticos
Detectar type juggling
~20 minCrea un archivo PHP en local y prueba estas comparaciones con == y con ===. Anota los resultados que te sorprendan y razona por qué ocurren.
$casos = [[0,"hola"],[true,"1"],[null,""],[false,"0"],[100,"1e2"]];
foreach ($casos as [$a, $b]) {
$laxa = ($a == $b) ? 'true' : 'false';
$estricta = ($a === $b) ? 'true' : 'false';
printf("%s == %s → %s | === → %s\n",
var_export($a,true), var_export($b,true), $laxa, $estricta);
}
Función de validación de contraseña
~30 minEscribe la función validar_password(string $pass): array que devuelva un array con los errores encontrados. Si el array está vacío, la contraseña es válida.
Analizador de plugins desactualizados
~45 minDado el array de plugins con versión instalada y versión actual, escribe código que identifique los desactualizados e imprima un informe ordenado por urgencia.
$plugins = [
['nombre'=>'WooCommerce', 'instalada'=>'8.3.0', 'actual'=>'8.5.1'],
['nombre'=>'Yoast SEO', 'instalada'=>'22.1', 'actual'=>'22.1'],
['nombre'=>'Contact Form 7', 'instalada'=>'5.7.0', 'actual'=>'5.9.2'],
['nombre'=>'Elementor', 'instalada'=>'3.19.0','actual'=>'3.21.0'],
];
Resumen y siguiente módulo
Variables y tipos
- Tipos:
int,float,string,bool,null - Tipado dinámico → type juggling peligroso
- Usar siempre
===para comparar
Operadores
- Aritméticos:
+ - * / % ** - Comparación estricta:
=== !== - Lógicos:
&& || ! - Null coalescing:
??
Control de flujo
if / elseif / elsematch(PHP 8, usa===)for / while / foreachbreak / continue
Funciones y arrays
- Tipos declarados en parámetros y retorno
- Arrays indexados, asociativos y multidim.
- Lista blanca con
in_array(..., true) var_dumpyerror_logpara debug
Profundizamos en PHP: superglobales ($_POST, $_GET, $_SERVER), manejo de errores con excepciones, programación orientada a objetos completa y Composer. Todo con foco en las implicaciones de seguridad de cada concepto.
Ir al Módulo 2 →