Inyecció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)
Guarda tu progreso
Inicia sesión para no perder tu progreso y accede a miles de tutoriales, ejercicios prácticos y nuestro asistente de IA.
Más de 25.000 desarrolladores ya confían en CertiDevs
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.
Aprendizajes 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.
Completa PHP y certifícate
Únete a nuestra plataforma y accede a miles de tutoriales, ejercicios prácticos, proyectos reales y nuestro asistente de IA personalizado para acelerar tu aprendizaje.
Asistente IA
Resuelve dudas al instante
Ejercicios
Practica con proyectos reales
Certificados
Valida tus conocimientos
Más de 25.000 desarrolladores ya se han certificado con CertiDevs