PHP
Tutorial PHP: Principales vulnerabilidades en PHP
PHP: Aprende a prevenir vulnerabilidades como inyecciones de código, CSRF e inyección de cabeceras en aplicaciones para asegurar tus datos y proteger usuarios.
Aprende PHP GRATIS y certifícateInyección de código (SQL Injection, XSS, inyección de archivos)
La inyección de código es una de las vulnerabilidades más críticas en aplicaciones web desarrolladas con PHP. Consiste en la introducción de código malicioso a través de entradas de datos no validadas o mal gestionadas por la aplicación. Las principales formas de inyección de código son:
- Inyección SQL (SQL Injection)
- Cross-Site Scripting (XSS)
- Inyección de archivos
Inyección SQL (SQL Injection)
La inyección SQL ocurre cuando un atacante manipula las consultas SQL de una aplicación para acceder, modificar o eliminar datos no autorizados. Esto es posible si las entradas de usuario se incorporan directamente en las consultas sin una sanitización adecuada.
Ejemplo de código vulnerable:
<?php
$id = $_GET['id'];
$query = "SELECT * FROM usuarios WHERE id = $id";
$resultado = $conexion->query($query);
while ($fila = $resultado->fetch_assoc()) {
echo "Usuario: " . $fila['nombre'] . "\n";
}
En este ejemplo, un atacante podría enviar una URL como http://localhost:8000? id=1; DROP TABLE usuarios
, lo que provocaría la eliminación de la tabla usuarios
.
Solución segura utilizando sentencias preparadas:
<?php
$id = $_GET['id'];
$stmt = $conexion->prepare("SELECT * FROM usuarios WHERE id = ?");
$stmt->bind_param("i", $id);
$stmt->execute();
$resultado = $stmt->get_result();
while ($fila = $resultado->fetch_assoc()) {
echo "Usuario: " . $fila['nombre'] . "\n";
}
Al utilizar sentencias preparadas, los parámetros se tratan de forma segura, evitando que el atacante manipule la consulta SQL.
Cross-Site Scripting (XSS)
El Cross-Site Scripting permite a un atacante inyectar código JavaScript malicioso en páginas vistas por otros usuarios. Esto sucede cuando la aplicación refleja entradas de usuario en la salida sin un correcto escapado o validación.
Ejemplo de código vulnerable:
<?php
$comentario = $_GET['comentario'];
echo "Comentario: $comentario\n";
Un atacante podría aprovecharse enviando ?comentario=<script>alert('XSS')</script>
, lo que ejecutaría una alerta en el navegador del usuario.
Prevención mediante escapado de salida:
<?php
$comentario = $_GET['comentario'];
$comentario_seguro = htmlspecialchars($comentario, ENT_QUOTES, 'UTF-8');
echo "Comentario: $comentario_seguro\n";
La función htmlspecialchars
convierte caracteres especiales en entidades HTML, impidiendo la ejecución de código malicioso y protegiendo contra XSS.
Inyección de archivos
La inyección de archivos se produce cuando un atacante consigue que la aplicación cargue archivos no autorizados, potencialmente maliciosos. Esto ocurre al utilizar entradas de usuario en funciones como include
o require
sin una validación estricta.
Ejemplo de código vulnerable:
<?php
$pagina = $_GET['pagina'];
include "$pagina.php";
Con una petición como ?pagina=../../archivo_malicioso
, un atacante podría incluir archivos sensibles del servidor.
Mitigación implementando una lista blanca de archivos:
<?php
$pagina = $_GET['pagina'];
$paginas_permitidas = ['inicio', 'contacto', 'productos'];
if (in_array($pagina, $paginas_permitidas)) {
include "$pagina.php";
} else {
echo "Página no encontrada.\n";
}
Al establecer una lista blanca de archivos permitidos, se evita la inclusión de contenidos no autorizados.
Recomendaciones generales
- Validar y sanitizar todas las entradas del usuario antes de utilizarlas.
- Utilizar funciones nativas de PHP para la protección, como
filter_input
yhtmlspecialchars
. - Emplear sentencias preparadas para las consultas a la base de datos.
- Restricción de archivos incluidos mediante listas blancas o rutas absolutas.
- Deshabilitar la directiva
allow_url_include
enphp.ini
para evitar inclusiones remotas. - Mantener el entorno de ejecución actualizado y seguir las prácticas recomendadas de seguridad.
La aplicación rigurosa de estas medidas es esencial para proteger las aplicaciones PHP de ataques de inyección de código, salvaguardando tanto la integridad de los datos como la privacidad de los usuarios.
Cross-Site Request Forgery (CSRF)
El Cross-Site Request Forgery (CSRF) es una vulnerabilidad que permite a un atacante ejecutar acciones no autorizadas en una aplicación web en nombre de un usuario autenticado. Esto ocurre cuando la aplicación no verifica adecuadamente la procedencia de las solicitudes, permitiendo que comandos maliciosos se envíen a través del navegador del usuario sin su conocimiento.
En un ataque CSRF, el atacante induce al usuario a realizar una petición no deseada, explotando la confianza que la aplicación tiene en las credenciales de sesión del usuario. Esto puede tener consecuencias graves, como cambios de contraseñas, transferencias bancarias o eliminación de datos.
Ejemplo de vulnerabilidad CSRF
Consideremos un formulario sencillo para actualizar el correo electrónico del usuario:
<?php
session_start();
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
$email = $_POST['email'];
$usuario_id = $_SESSION['usuario_id'];
// Actualizar el correo electrónico en la base de datos
$stmt = $conexion->prepare("UPDATE usuarios SET email = ? WHERE id = ?");
$stmt->bind_param("si", $email, $usuario_id);
$stmt->execute();
echo "Correo electrónico actualizado.\n";
}
?>
<form method="POST">
<input type="email" name="email">
<input type="submit" value="Actualizar">
</form>
En este código, no se verifica la procedencia de la solicitud ni se implementa ningún mecanismo de protección. Un atacante podría crear un formulario malicioso que envíe una petición POST a esta misma ruta, cambiando el correo electrónico del usuario sin su consentimiento.
Protección mediante tokens CSRF
La forma más efectiva de prevenir ataques CSRF es implementando tokens de seguridad únicos por sesión. Estos tokens se generan en el servidor y se incluyen en cada formulario, verificándose posteriormente en el procesamiento de la solicitud.
Generación y almacenamiento del token:
<?php
session_start();
if (empty($_SESSION['csrf_token'])) {
$_SESSION['csrf_token'] = bin2hex(random_bytes(32));
}
Aquí, utilizamos la función random_bytes
para generar un token aleatorio y lo almacenamos en la sesión del usuario.
Incluir el token en el formulario:
<form method="POST">
<input type="email" name="email">
<input type="hidden" name="csrf_token" value="<?php echo $_SESSION['csrf_token']; ?>">
<input type="submit" value="Actualizar">
</form>
El token se añade como un campo oculto en el formulario, enviándose junto con los datos al procesar la solicitud.
Validar el token en el procesamiento:
<?php
session_start();
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
if (!hash_equals($_SESSION['csrf_token'], $_POST['csrf_token'])) {
echo "Solicitud no válida.\n";
exit;
}
$email = $_POST['email'];
$usuario_id = $_SESSION['usuario_id'];
$stmt = $conexion->prepare("UPDATE usuarios SET email = ? WHERE id = ?");
$stmt->bind_param("si", $email, $usuario_id);
$stmt->execute();
echo "Correo electrónico actualizado.\n";
}
Utilizamos hash_equals
para comparar de forma segura el token recibido con el almacenado en la sesión. Si no coinciden, se rechaza la solicitud, protegiendo al usuario frente a ataques CSRF.
Recomendaciones adicionales
- Limitar la validez del token, regenerándolo después de cada solicitud o en intervalos de tiempo definidos.
- Configurar las cookies de sesión con la opción SameSite para restringir su envío en solicitudes de terceros:
<?php
session_set_cookie_params([
'samesite' => 'Strict',
'secure' => true,
'httponly' => true,
]);
session_start();
- Evitar el uso de GET para operaciones que modifican el estado en el servidor, ya que las solicitudes GET son más fáciles de manipular.
- Validar el origen de las solicitudes comprobando los encabezados
Origin
oReferer
, aunque no es un método infalible y debe combinarse con tokens CSRF.
Implementación en aplicaciones grandes
Para aplicaciones más complejas, es recomendable utilizar librerías o frameworks que ya incorporen protección contra CSRF. Por ejemplo:
- Symfony incluye automáticamente protección CSRF en sus formularios.
- Laravel proporciona middleware para verificar tokens CSRF en todas las solicitudes POST, PUT, PATCH y DELETE.
Importancia de la protección CSRF
Implementar medidas contra CSRF es esencial para mantener la integridad y seguridad de las aplicaciones web en PHP. Al garantizar que cada solicitud proviene del usuario legítimo y no de un sitio malicioso, se protege tanto la información sensible como las operaciones críticas de la aplicación.
Inyección de cabeceras (Header Injection)
La inyección de cabeceras es una vulnerabilidad que ocurre cuando una aplicación PHP permite a los usuarios inyectar datos no controlados en los campos de cabecera HTTP. Esto permite a un atacante manipular la respuesta HTTP enviada al navegador, lo cual puede conducir a ataques como HTTP Response Splitting, redireccionamientos maliciosos o ejecución de scripts en el navegador del usuario.
Cómo ocurre la inyección de cabeceras
En PHP, es común utilizar la función header()
para enviar cabeceras HTTP al cliente. Si los valores de las cabeceras incluyen datos proporcionados por el usuario sin una validación adecuada, se abre la posibilidad de inyección de contenido malicioso.
Ejemplo de código vulnerable:
<?php
$location = $_GET['url'];
header("Location: $location");
exit;
En este ejemplo, un atacante podría manipular el parámetro url
para inyectar caracteres especiales como \n
o %0d%0a
, creando cabeceras adicionales no deseadas.
Posible explotación:
Si un usuario malintencionado accede a:
http://localhost:8000/?url=%0D%0ASet-Cookie:%20malicioso=1%0D%0A
La respuesta HTTP podría ser manipulada para incluir nuevas cabeceras y contenido, permitiendo la ejecución de código arbitrario en el navegador.
Impacto de la vulnerabilidad
La inyección de cabeceras puede llevar a:
- División de respuesta HTTP (HTTP Response Splitting): Se inyectan nuevas respuestas HTTP, alterando la estructura prevista.
- Cache poisoning: Al manipular las cabeceras, se pueden almacenar respuestas maliciosas en cachés compartidas.
- Cross-Site Scripting (XSS): Inyección de scripts en la respuesta, ejecutándose en el contexto del dominio legítimo.
- Redireccionamientos no autorizados: Desviar al usuario a sitios maliciosos sin su conocimiento.
Prevención de la inyección de cabeceras
Para prevenir esta vulnerabilidad, es esencial validar y sanitizar cualquier dato que se incluya en las cabeceras. Esto implica:
- Validar el formato de los datos: Asegurar que los valores cumplen con lo esperado.
- Sanitizar entradas del usuario: Eliminar o codificar caracteres especiales que puedan terminar prematuramente una cabecera.
Implementación segura del ejemplo anterior:
<?php
$location = $_GET['url'] ?? '/';
if (filter_var($location, FILTER_VALIDATE_URL) === false) {
$location = '/';
}
header("Location: $location");
exit;
En este código, se utiliza filter_var()
con el filtro FILTER_VALIDATE_URL
para validar que el parámetro url
es una URL válida. Si no lo es, se redirige al usuario a la raíz (/
).
Otra opción es codificar los datos:
<?php
$location = $_GET['url'] ?? '/';
$location = urlencode($location);
header("Location: $location");
exit;
Sin embargo, en el caso de la cabecera Location
, es preferible validar la URL en lugar de codificarla, para evitar redireccionamientos a sitios externos no deseados.
Uso de códigos de estado HTTP
Es importante especificar el código de estado al realizar redirecciones, utilizando el tercer parámetro de header()
:
<?php
header("Location: /", true, 302);
exit;
Esto asegura que el servidor envía el código de respuesta correcto, evitando comportamientos inesperados en el navegador.
Recomendaciones adicionales
- Nunca incluir datos no validados en las cabeceras: Evitar utilizar directamente entradas del usuario en funciones como
header()
. - Utilizar listas blancas de valores permitidos: Si se espera un conjunto limitado de valores, compararlos contra una lista predefinida.
- Desinfectar caracteres de nueva línea: Eliminar
\n
,\r
y sus representaciones codificadas (%0a
,%0d
):
<?php
function limpiarCabecera($valor)
{
$valor = str_replace(["\r", "\n"], '', $valor);
return $valor;
}
$filename = $_GET['file'];
$filename = limpiarCabecera($filename);
header("Content-Disposition: attachment; filename=\"$filename\"");
En este ejemplo, se asegura que el nombre del archivo no contiene caracteres que puedan interrumpir la cabecera.
Protección en funciones de correo electrónico
La función mail()
de PHP es susceptible a la inyección de cabeceras a través de los parámetros de correo o cabeceras adicionales.
Código vulnerable:
<?php
$to = $_POST['email'];
$subject = $_POST['subject'];
$message = $_POST['message'];
$headers = "From: remitente@example.com\r\n";
$headers .= "Reply-To: $to\r\n";
mail($to, $subject, $message, $headers);
Si $to
no es validado, un atacante podría agregar cabeceras adicionales.
Solución segura:
- Validar las direcciones de correo:
<?php
$to = $_POST['email'];
if (!filter_var($to, FILTER_VALIDATE_EMAIL)) {
// Manejar error
exit;
}
- Evitar líneas nuevas en los campos de correo:
<?php
function limpiarEmail($email)
{
return str_replace(["\r", "\n"], '', $email);
}
$to = limpiarEmail($_POST['email']);
Uso de librerías y funciones seguras
Considerar el uso de librerías para el envío de correos electrónicos que manejan internamente la sanitización de cabeceras, como PHPMailer o Symfony Mailer.
Ejemplo con PHPMailer:
<?php
use PHPMailer\PHPMailer\PHPMailer;
$mail = new PHPMailer();
$mail->setFrom('remitente@example.com');
$mail->addAddress($_POST['email']);
$mail->Subject = $_POST['subject'];
$mail->Body = $_POST['message'];
if (!$mail->send()) {
// Manejar error
}
Estas librerías se encargan de validar y sanitizar correctamente los campos, reduciendo el riesgo de inyección.
Todas las lecciones de PHP
Accede a todas las lecciones de PHP y aprende con ejemplos prácticos de código y ejercicios de programación con IDE web sin instalar nada.
Introducción A Php
Introducción Y Entorno
Instalación Y Primer Programa De Php
Introducción Y Entorno
Tipos De Datos, Variables Y Constantes
Sintaxis
Operadores Y Expresiones
Sintaxis
Estructuras De Control
Sintaxis
Funciones Y Llamada De Funciones
Sintaxis
Cadenas De Texto Y Manipulación
Sintaxis
Manejo De Números
Sintaxis
Manejo De Fechas Y Tiempo
Sintaxis
Manejo De Arrays
Sintaxis
Introducción A La Poo En Php
Programación Orientada A Objetos
Clases Y Objetos
Programación Orientada A Objetos
Constructores Y Destructores
Programación Orientada A Objetos
Herencia
Programación Orientada A Objetos
Encapsulación
Programación Orientada A Objetos
Polimorfismo
Programación Orientada A Objetos
Interfaces
Programación Orientada A Objetos
Traits
Programación Orientada A Objetos
Namespaces
Programación Orientada A Objetos
Autoloading De Clases
Programación Orientada A Objetos
Manejo De Errores Y Excepciones
Programación Orientada A Objetos
Manejo De Archivos
Programación Orientada A Objetos
Patrones De Diseño
Programación Orientada A Objetos
Introducción A Los Formularios En Php
Formularios
Procesamiento De Datos De Formularios
Formularios
Manejo De Archivos En Formularios
Formularios
Redirecciones Y Retroalimentación Al Usuario
Formularios
Formularios Dinámicos Y Separación De Lógica
Formularios
Introducción A La Persistencia En Php
Persistencia
Conexión A Bases De Datos
Persistencia
Consultas Y Operaciones Crud
Persistencia
Gestión De Transacciones
Persistencia
Manejo De Errores Y Excepciones En Base De Datos
Persistencia
Patrones De Acceso A Datos
Persistencia
Concepto De Sesiones En Php
Sesiones Y Cookies
Configuración De Sesiones
Sesiones Y Cookies
Cookies
Sesiones Y Cookies
Manejo Avanzado De Sesiones Y Cookies
Sesiones Y Cookies
Principales Vulnerabilidades En Php
Seguridad
Seguridad En Formularios Y Entrada De Datos
Seguridad
Protección Frente A Inyección Sql
Seguridad
Gestión De Contraseñas Y Cifrado
Seguridad
Seguridad En Sesiones Y Cookies
Seguridad
Configuraciones De Php Para Seguridad
Seguridad
Introducción Al Testing En Php
Testing
Phpunit
Testing
Cobertura De Código En Testing
Testing
Test Doubles (Mocks, Stubs, Fakes, Spies)
Testing
Pruebas De Integración Y Funcionales
Testing
En esta lección
Objetivos de aprendizaje de esta lección
- Comprender la naturaleza de las vulnerabilidades de inyección de código en PHP.
- Implementar medidas de seguridad para prevenir inyecciones SQL y XSS.
- Utilizar tokens CSRF para evitar solicitudes no autorizadas.
- Aprender a validar y sanitizar entradas del usuario.
- Conocer métodos para proteger cabeceras HTTP de inyecciones.