Situación actual: PHP2Shell. Dos vulnerabilidades críticas de PHP (CVSS 9.2) permiten ejecutar código malicioso sin autenticación. Parches desde el 21/10/2026.Qué hacer ahora →

Funciones de seguridad integradas en PHP

PHP incluye componentes seguros para contraseñas, valores aleatorios, escapado, criptografía y sesiones. Quien los usa de forma sistemática evita los fallos de aplicación más habituales.

Actualizado 6 min de lecturaConocimiento de PHP

En resumen: para las tareas de seguridad más habituales, PHP incluye funciones ya hechas y contrastadas: password_hash() para contraseñas, random_bytes() y random_int() para tokens, htmlspecialchars() contra el cross-site scripting, consultas preparadas con PDO contra la inyección SQL, hash_equals() para comparaciones seguras y la extensión Sodium para la criptografía. Quien usa estos componentes de forma sistemática evita la mayoría de las vulnerabilidades de aplicación. Frente a fallos del propio intérprete de PHP, como en PHP2Shell, no ayudan; ahí solo ayuda la actualización.

¿Por qué funciones de seguridad integradas en PHP?

Escribir uno mismo el código de seguridad es propenso a errores. Un hash de contraseñas propio, un generador de tokens hecho a mano o un filtro casero para la salida HTML parecen correctos y, aun así, no lo son. Las funciones integradas están documentadas, probadas y se mantienen con las actualizaciones de PHP.

Para quienes toman decisiones significa esto: no pregunte a su agencia o a su equipo de desarrollo si las funciones de seguridad se han construido a medida, sino si se utilizan las funciones estándar del lenguaje. La tabla ofrece una visión general.

Tarea Función Disponible desde Indicación importante
Guardar contraseñas password_hash(), password_verify() PHP 5.5 columna de hasta 255 caracteres; bcrypt recorta a 72 bytes
Tokens aleatorios random_bytes(), random_int() PHP 7.0 nunca rand() ni mt_rand() para secretos
Aleatoriedad orientada a objetos Random\Randomizer PHP 8.2 misma seguridad, interfaz más flexible
Escapar la salida HTML htmlspecialchars() desde siempre los flags por defecto desde 8.1 escapan también las comillas simples
Comparación en tiempo constante hash_equals() PHP 5.6 la entrada del usuario como segundo argumento
Criptografía moderna Sodium (sodium_*) PHP 7.2 en lugar de cifrado propio
Secretos en stack traces #[\SensitiveParameter] PHP 8.2 el valor no aparece en el trace
Comprobar entradas filter_var() PHP 5.2 comprueba el formato, no sustituye al escapado

Contraseñas y tokens

password_hash() genera un hash con sal y password_verify() lo comprueba. Con PASSWORD_DEFAULT, PHP usa actualmente bcrypt; desde PHP 8.4, según el manual, el factor de coste por defecto es 12 en lugar de 10. Como el algoritmo por defecto puede cambiar, el manual recomienda una columna de base de datos de 255 caracteres. Con bcrypt, las contraseñas se truncan a los 72 bytes, algo que debe tenerse en cuenta en frases de contraseña muy largas.

<?php
// Registro
$hash = password_hash($passwort, PASSWORD_DEFAULT);

// Inicio de sesión, con actualización automática de hashes antiguos
if (password_verify($eingabe, $hash)) {
    if (password_needs_rehash($hash, PASSWORD_DEFAULT)) {
        $hash = password_hash($eingabe, PASSWORD_DEFAULT); // guardar de nuevo
    }
}

// Token para un enlace de restablecimiento de contraseña
$token = bin2hex(random_bytes(32));

Para comparar secretos, por ejemplo una firma, debe usarse hash_equals(). Según el manual, una comparación normal con === puede revelar por el tiempo de ejecución a partir de qué posición se diferencian dos valores. Con más detalle: Guardar contraseñas en PHP con seguridad y números aleatorios y tokens seguros.

Escapar la salida y consultar la base de datos de forma segura

El cross-site scripting se produce cuando las entradas del usuario llegan sin filtrar al HTML. htmlspecialchars() convierte los caracteres especiales en entidades HTML. Desde PHP 8.1, los flags por defecto son ENT_QUOTES | ENT_SUBSTITUTE | ENT_HTML401; antes no se convertían las comillas simples. Quien deba admitir versiones antiguas indica los flags de forma expresa.

La inyección SQL la evitan las consultas preparadas. Los valores se transmiten separados del texto SQL y no pueden alterar la consulta:

<?php
$stmt = $pdo->prepare('SELECT id, name FROM kunden WHERE email = :email');
$stmt->execute(['email' => $email]);
$kunde = $stmt->fetch();

echo htmlspecialchars($kunde['name'] ?? '', ENT_QUOTES, 'UTF-8');

Los detalles figuran en Evitar el cross-site scripting en PHP y en la página sobre inyección SQL.

Comprobar las entradas es un segundo paso, independiente. filter_var() determina si un valor tiene un formato válido, por ejemplo una dirección de correo o un entero dentro de un rango permitido. Pero eso no sustituye al escapado en la salida ni a una consulta preparada, porque incluso los valores formalmente válidos pueden contener caracteres especiales.

Mantener los secretos fuera de los mensajes de error

Los stack traces son una fuga de datos infravalorada. Cuando se produce un error en una función, PHP registra la cadena de llamadas junto con los argumentos. Si ahí hay una contraseña o una clave de API, acaba en el log. Desde PHP 8.2, el atributo #[\SensitiveParameter] marca esos parámetros; su valor aparece entonces en el trace solo como objeto SensitiveParameterValue.

<?php
function anmelden(string $benutzer, #[\SensitiveParameter] string $passwort): bool
{
    // ... comprobación ...
    throw new RuntimeException('Datenbank nicht erreichbar');
}

Según una nota del manual, el atributo solo actúa en la implementación concreta, no en el método de una interfaz. Colóquelo, por tanto, donde el código se ejecuta realmente.

Sesiones: ajustes seguros y nuevos valores por defecto

Las sesiones son un objetivo frecuente de ataque. Tres ajustes son centrales: session.use_strict_mode=1 rechaza los ID de sesión que el servidor no ha generado él mismo; session.cookie_httponly=1 impide el acceso por JavaScript a la cookie de sesión; session.cookie_samesite=Lax dificulta que se envíe desde sitios ajenos. Según el RFC aprobado, precisamente estos tres valores pasan a ser el estándar con PHP 8.6. Fíjelos usted hasta entonces y también después de forma expresa, para que la configuración no dependa del valor por defecto.

session.use_strict_mode = 1
session.cookie_httponly = 1
session.cookie_samesite = Lax
session.cookie_secure = 1

Más información en Proteger las sesiones en PHP.

Lo que las funciones integradas no logran

El proyecto PHP es claro en su política de seguridad: PHP no es un sandbox. Las elusiones de open_basedir o disable_functions, las entradas maliciosas enviadas a unserialize() y los argumentos sin comprobar enviados a funciones como mysqli_query() no se consideran vulnerabilidades de seguridad de PHP. Para los operadores esto significa: esos ajustes son un refuerzo útil, pero no un límite de seguridad, y unserialize() no tiene nada que ver con datos ajenos.

El segundo punto afecta a PHP2Shell. Según php.net, CVE-2026-109618 y CVE-2026-109619 permiten ejecutar código antes o durante la ejecución de su aplicación; en mod_php se demostró un exploit antes de que se ejecute siquiera código de la aplicación. Ninguna función de su código puede interceptarlo. La programación segura y una versión actual de PHP son dos obligaciones distintas, y ambas deben cumplirse.

¿Cómo ayuda PHP SOS en concreto?

En la auditoría de seguridad para aplicaciones PHP comprobamos de forma específica:

  • Gestión de contraseñas y tokens: ¿se usan password_hash() y random_bytes() o hay desarrollos propios?
  • Salidas y consultas: ¿dónde llegan las entradas sin filtrar a HTML, SQL o comandos de shell?
  • Configuración de sesiones: ¿están fijados Strict Mode, HttpOnly, SameSite y Secure, y no se pueden sobrescribir?
  • Tiempo de ejecución: ¿se ejecuta una versión de PHP con soporte y con las correcciones del 21/10/2026?

Preguntas frecuentes

¿Cómo se guardan correctamente las contraseñas en PHP?

Con password_hash() y PASSWORD_DEFAULT; se comprueban con password_verify(). Según el manual, la columna de la base de datos debería admitir hasta 255 caracteres, porque el algoritmo por defecto puede cambiar en el futuro.

¿Es rand() adecuado para tokens?

No. Para tokens, claves y enlaces de contraseña deben usarse random_bytes() o random_int(). Según el manual, ofrecen valores aleatorios criptográficamente seguros.

¿Desde cuándo incluye PHP Sodium?

La extensión Sodium para criptografía moderna forma parte de PHP desde PHP 7.2.0. En versiones anteriores solo se podía añadir mediante PECL.

¿Protegen estas funciones frente a PHP2Shell?

No. Protegen frente a fallos de su aplicación. PHP2Shell está en el propio intérprete y solo se cierra con una actualización de PHP, seguida del reinicio de todos los procesos de PHP.

¿Cambian los valores por defecto de las sesiones en PHP 8.6?

Sí, según el RFC aprobado, session.use_strict_mode y session.cookie_httponly se activan por defecto y session.cookie_samesite pasa a Lax. Aun así, debería fijar usted mismo de forma expresa las configuraciones existentes.

Fuentes

Contacto inmediato

Cada hora cuenta. Escríbanos ahora.

Describa brevemente el sistema, la versión de PHP y lo que ha observado. Nos pondremos en contacto, aclararemos la situación y trabajaremos en remoto hasta que el incidente quede cerrado y documentado.

Ayuda inmediata