PHP

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ícate

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 y htmlspecialchars.
  • 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 en php.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 o Referer, 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:

  1. Validar el formato de los datos: Asegurar que los valores cumplen con lo esperado.
  2. 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.

Para seguir leyendo hazte Plus

¿Ya eres Plus? Accede a la app

20 % DE DESCUENTO

Plan mensual

19.00 /mes

15.20 € /mes

Precio normal mensual: 19 €
58 % DE DESCUENTO

Plan anual

10.00 /mes

8.00 € /mes

Ahorras 132 € al año
Precio normal anual: 120 €
Aprende PHP GRATIS online

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

PHP

Introducción Y Entorno

Instalación Y Primer Programa De Php

PHP

Introducción Y Entorno

Tipos De Datos, Variables Y Constantes

PHP

Sintaxis

Operadores Y Expresiones

PHP

Sintaxis

Estructuras De Control

PHP

Sintaxis

Funciones Y Llamada De Funciones

PHP

Sintaxis

Cadenas De Texto Y Manipulación

PHP

Sintaxis

Manejo De Números

PHP

Sintaxis

Manejo De Fechas Y Tiempo

PHP

Sintaxis

Manejo De Arrays

PHP

Sintaxis

Introducción A La Poo En Php

PHP

Programación Orientada A Objetos

Clases Y Objetos

PHP

Programación Orientada A Objetos

Constructores Y Destructores

PHP

Programación Orientada A Objetos

Herencia

PHP

Programación Orientada A Objetos

Encapsulación

PHP

Programación Orientada A Objetos

Polimorfismo

PHP

Programación Orientada A Objetos

Interfaces

PHP

Programación Orientada A Objetos

Traits

PHP

Programación Orientada A Objetos

Namespaces

PHP

Programación Orientada A Objetos

Autoloading De Clases

PHP

Programación Orientada A Objetos

Manejo De Errores Y Excepciones

PHP

Programación Orientada A Objetos

Manejo De Archivos

PHP

Programación Orientada A Objetos

Patrones De Diseño

PHP

Programación Orientada A Objetos

Introducción A Los Formularios En Php

PHP

Formularios

Procesamiento De Datos De Formularios

PHP

Formularios

Manejo De Archivos En Formularios

PHP

Formularios

Redirecciones Y Retroalimentación Al Usuario

PHP

Formularios

Formularios Dinámicos Y Separación De Lógica

PHP

Formularios

Introducción A La Persistencia En Php

PHP

Persistencia

Conexión A Bases De Datos

PHP

Persistencia

Consultas Y Operaciones Crud

PHP

Persistencia

Gestión De Transacciones

PHP

Persistencia

Manejo De Errores Y Excepciones En Base De Datos

PHP

Persistencia

Patrones De Acceso A Datos

PHP

Persistencia

Concepto De Sesiones En Php

PHP

Sesiones Y Cookies

Configuración De Sesiones

PHP

Sesiones Y Cookies

Cookies

PHP

Sesiones Y Cookies

Manejo Avanzado De Sesiones Y Cookies

PHP

Sesiones Y Cookies

Principales Vulnerabilidades En Php

PHP

Seguridad

Seguridad En Formularios Y Entrada De Datos

PHP

Seguridad

Protección Frente A Inyección Sql

PHP

Seguridad

Gestión De Contraseñas Y Cifrado

PHP

Seguridad

Seguridad En Sesiones Y Cookies

PHP

Seguridad

Configuraciones De Php Para Seguridad

PHP

Seguridad

Introducción Al Testing En Php

PHP

Testing

Phpunit

PHP

Testing

Cobertura De Código En Testing

PHP

Testing

Test Doubles (Mocks, Stubs, Fakes, Spies)

PHP

Testing

Pruebas De Integración Y Funcionales

PHP

Testing

Accede GRATIS a PHP y certifícate

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.