En resumen: para la seguridad de API en PHP cuentan sobre todo cinco puntos: solo HTTPS, autenticación en cada endpoint no público, comprobación de permisos para cada objeto individual, límites estrictos de método, tipo de contenido y tamaño de la petición, y mensajes de error breves sin detalles internos. A ello se suman la limitación de tasa y un registro de eventos relevantes para la seguridad. Frente a vulnerabilidades del propio intérprete de PHP como PHP2Shell no ayuda; para eso hace falta la actualización de PHP.
La interfaz detrás del sitio web
Muchas empresas revisaron sus sitios web en la semana posterior al 21 de octubre de 2026. Las interfaces que hay detrás figuraron con menos frecuencia en la lista. Sin embargo, justo allí se ejecutan procesos sensibles: la app del portal de clientes descarga facturas, el sistema de gestión de mercancías escribe existencias en la tienda, un socio notifica pedidos mediante webhook. A menudo estas API se construyeron deprisa, para un único fin, y no se volvieron a mirar.
Una API no tiene interfaz que haga visibles los errores. Una comprobación de permisos ausente no llama la atención de nadie en el día a día, mientras todos los clientes solo consulten lo que deben. Un atacante, en cambio, prueba exactamente lo que no estaba previsto.
El primer paso es, por ello, un inventario. ¿Qué endpoints existen siquiera? Con frecuencia se encuentran versiones antiguas como /api/v1/, que nunca se desactivaron tras el paso a /v2/, endpoints de prueba del desarrollo o una descripción de la interfaz accesible públicamente que enumera funciones internas. Todo endpoint que ya no se necesite debería desactivarse. Todo el que permanezca necesita un responsable.
Seguridad de API en PHP: las reglas básicas
La hoja de referencia OWASP REST Security Cheat Sheet resume reglas acreditadas. Para proyectos PHP pueden traducirse en una lista corta:
- Solo HTTPS. Los tokens y las credenciales nunca deben transmitirse sin cifrar.
- Cada endpoint comprueba por sí mismo. La autenticación puede hacerse de forma centralizada; la decisión sobre el acceso se toma en el endpoint.
- Permiso por objeto. No solo «está autenticado», sino «puede acceder precisamente a este registro».
- Fijar los métodos permitidos. Lo que no está expresamente permitido se rechaza con 405.
- Comprobar el tipo de contenido y el tamaño. Un tipo incorrecto da 415; las peticiones demasiado grandes, 413.
- Validar las entradas. Tipo, longitud, formato y rango de valores, en el servidor.
- Sin secretos en las URL. Los tokens y las claves pertenecen a las cabeceras, no a la query string.
- Errores breves. Sin stack traces, rutas ni mensajes SQL hacia el cliente.
- Limitación de tasa. Las peticiones excesivas se responden con 429.
- Blindar los endpoints de administración. No accesibles públicamente o solo con autenticación fuerte.
Permiso por objeto: el error más frecuente
Un ejemplo hace tangible el problema. Un endpoint entrega facturas en /api/rechnungen/{id}. Se comprueba si hay un token válido. No se comprueba si la factura pertenece al cliente del token. Quien aumenta en uno su propio número de factura ve la del vecino.
La solución es sencilla y, aun así, debe aplicarse en todos los puntos: la propia consulta restringe al ámbito autorizado.
<?php
// Permitir solo el método GET
if ($_SERVER['REQUEST_METHOD'] !== 'GET') {
http_response_code(405);
header('Allow: GET');
exit;
}
$kundeId = $auth->kundeId(); // del token ya comprobado
$id = filter_var($routeId, FILTER_VALIDATE_INT);
if ($id === false) {
http_response_code(400);
exit;
}
// El permiso está en la consulta: solo las facturas propias
$stmt = $pdo->prepare('SELECT nummer, betrag, datum FROM rechnungen WHERE id = ? AND kunde_id = ?');
$stmt->execute([$id, $kundeId]);
$rechnung = $stmt->fetch(PDO::FETCH_ASSOC);
if (!$rechnung) {
http_response_code(404); // no revelar si el ID existe
exit;
}
header('Content-Type: application/json');
echo json_encode($rechnung, JSON_THROW_ON_ERROR);
La consulta con marcadores de posición protege además frente a la inyección SQL; véase Evitar la inyección SQL en PHP. Cómo pone PHP a disposición datos de la petición como método, cabeceras y cuerpo lo explica Superglobales: $_GET, $_POST, $_FILES y compañía.
Comprobar bien los tokens
Muchas API trabajan con JSON Web Tokens. La hoja de referencia de OWASP fija para ello reglas claras: los tokens deben estar firmados, los tokens sin firmar se rechazan y el algoritmo de comprobación se fija en el servidor, no se toma del token. El emisor, el destinatario y la hora de caducidad forman también parte de la comprobación. Use para ello una biblioteca mantenida en lugar de implementaciones propias.
Los tokens propios, por ejemplo para webhooks o enlaces de un solo uso, deben proceder de un generador de números aleatorios criptográficamente seguro y compararse en tiempo constante. Cómo se hace en PHP lo muestra Números aleatorios y tokens seguros en PHP.
Dé a cada cliente y a cada socio una clave propia con exactamente los permisos que necesita. Así, una clave filtrada puede bloquearse individualmente sin interrumpir todas las demás conexiones.
Fijar límites: tamaño, tipo y ritmo
Una API solo debería aceptar lo que espera. Eso empieza por el tipo de contenido: un endpoint que procesa JSON rechaza todo lo demás. El tamaño máximo de una petición se limita en varios niveles, en el servidor web y en PHP mediante post_max_size. Este límite figura también entre las medidas transitorias que php.net citó para PHP2Shell. Cómo elegirlo con sentido lo describe Limitar con sentido post_max_size y upload_max_filesize.
La limitación de tasa protege frente al ensayo de identificadores y frente a la sobrecarga. Puede aplicarse en el servidor web, en un proxy previo o en la aplicación. Es importante que los límites rijan por clave o por usuario y no solo por dirección IP.
Para las API que se invocan directamente desde el navegador se añade CORS, es decir, las reglas sobre qué sitios web ajenos pueden hacer peticiones. Establezca estas cabeceras solo si tales llamadas están realmente previstas e indique los orígenes permitidos uno por uno, en lugar de admitir cualquier origen. Un comodín que permita todos los sitios web es, en endpoints con autenticación, casi siempre un error.
Por último, el registro: los inicios de sesión fallidos, los tokens rechazados y los accesos denegados pertenecen al log; los tokens en sí, no. Sin esas entradas, un ataque a la API permanece invisible.
¿Cómo ayuda PHP SOS en concreto?
- Registramos todas las interfaces de sus sistemas PHP, también las olvidadas y las internas, y comprobamos cuáles son accesibles públicamente.
- Leemos la lógica de permisos y probamos de forma dirigida si se pueden obtener registros ajenos.
- Fijamos límites de tamaño, tipo y ritmo en el servidor web y en PHP y los acordamos con sus clientes.
- Para una revisión completa desde fuera, lo combinamos con una auditoría de seguridad y prueba de penetración.
Conclusión: una API es tan segura como su endpoint más descuidado. Quien comprueba los permisos por objeto, fija límites claros y mantiene breves los errores cierra las vulnerabilidades más frecuentes mucho antes de que alguien las busque de forma dirigida.
Preguntas frecuentes
¿Basta una clave de API como protección?
Para datos sensibles, no. Una clave de API suele identificar una aplicación, no a un usuario, y se transmite con facilidad. La hoja de referencia OWASP REST Security Cheat Sheet aconseja no usar claves de API como única protección de recursos sensibles.
¿Qué es el permiso por objeto y por qué falta tan a menudo?
Se trata de comprobar si el usuario autenticado puede ver o modificar precisamente este registro. Falta con frecuencia porque los desarrolladores solo comprueban si hay alguien autenticado. Entonces basta cambiar un identificador en la URL para obtener datos ajenos.
¿Protege una API bien protegida frente a PHP2Shell?
No. PHP2Shell está en el intérprete de PHP y actúa en los modos de funcionamiento afectados antes de que se ejecute siquiera su código. Frente a ello solo ayuda una actualización de PHP con reinicio. Aun así, los límites de tamaño de las peticiones son útiles y forman parte también de las medidas transitorias de php.net.
¿Qué códigos de estado debería devolver una API ante errores de seguridad?
Lo habitual es 401 sin autenticación válida, 403 ante falta de permiso, 405 con método no permitido, 413 con petición demasiado grande, 415 con tipo de contenido incorrecto y 429 con demasiadas peticiones. La respuesta en sí se mantiene breve y sin detalles internos.