Programación Web ITI-07

Práctica: Mini Sistema Web Universitario - 3


Práctica: Mini Sistema Web Universitario - 3

Cierre de sesión y subida de archivos

Cierre de sesión y subida de archivos

logout.php

<?html
/**
 * logout.php - Cierre de sesión seguro
 * Destruye la sesión y redirige al login
 */

session_start();

// Limpiar todas las variables de sesión
$_SESSION = [];

// Destruir la cookie de sesión si existe
if (isset($_COOKIE[session_name()])) {
    setcookie(session_name(), "", time() - 3600, "/");
}

// Destruir la sesión
session_destroy();

// Redirigir al login con mensaje
header("Location: index.php");
exit();
?>

upload.php

<?php
/**
 * upload.php - Procesamiento de subida de archivos
 * Implementa validaciones de seguridad y manejo de archivos
 */

session_start();

// SEGURIDAD: Verificar autenticación
if (!isset($_SESSION["usuario_id"])) {
    header("Location: index.php");
    exit();
}

// SEGURIDAD: Solo aceptar peticiones POST
if ($_SERVER["REQUEST_METHOD"] !== "POST") {
    header("Location: dashboard.php");
    exit();
}

require_once "config/conexion.php";

// Configuración de subida
$directorio_destino = "uploads/";
$tamano_maximo = 5 * 1024 * 1024; // 5 MB en bytes
$extensiones_permitidas = ["pdf"];

// Crear directorio si no existe
if (!file_exists($directorio_destino)) {
    mkdir($directorio_destino, 0755, true);
}

// Función para sanitizar nombres de archivo
function sanitizarNombreArchivo($nombre)
{
    // Eliminar caracteres especiales y espacios
    $nombre = preg_replace("/[^a-zA-Z0-9._-]/", "_", $nombre);
    $nombre = preg_replace("/\.+/", ".", $nombre);
    return $nombre;
}

// Función para generar nombre único de archivo
function generarNombreUnico($extension)
{
    return uniqid("tarea_", true) . "_" . time() . "." . $extension;
}

// Validar que se haya enviado un archivo
if (
    !isset($_FILES["archivo"]) ||
    $_FILES["archivo"]["error"] === UPLOAD_ERR_NO_FILE
) {
    header(
        "Location: dashboard.php?error=" .
            urlencode("No se seleccionó ningún archivo"),
    );
    exit();
}

$archivo = $_FILES["archivo"];

// VALIDACIÓN 1: Verificar errores de subida
if ($archivo["error"] !== UPLOAD_ERR_OK) {
    $errores = [
        UPLOAD_ERR_INI_SIZE => "El archivo supera el tamaño máximo permitido por el servidor",
        UPLOAD_ERR_FORM_SIZE => "El archivo supera el tamaño máximo permitido",
        UPLOAD_ERR_PARTIAL => "El archivo se subió parcialmente",
        UPLOAD_ERR_NO_TMP_DIR => "Falta la carpeta temporal",
        UPLOAD_ERR_CANT_WRITE => "Error al escribir el archivo en el disco",
        UPLOAD_ERR_EXTENSION => "Una extensión de PHP detuvo la subida",
    ];

    $mensaje_error =
        $errores[$archivo["error"]] ?? "Error desconocido al subir el archivo";
    header("Location: dashboard.php?error=" . urlencode($mensaje_error));
    exit();
}

// VALIDACIÓN 2: Verificar tamaño del archivo
if ($archivo["size"] > $tamano_maximo) {
    $tamano_mb = round($tamano_maximo / (1024 * 1024), 2);
    header(
        "Location: dashboard.php?error=" .
            urlencode("El archivo supera el tamaño máximo de {$tamano_mb} MB"),
    );
    exit();
}

// VALIDACIÓN 3: Verificar extensión del archivo
$nombre_original = $archivo["name"];
$extension = strtolower(pathinfo($nombre_original, PATHINFO_EXTENSION));

if (!in_array($extension, $extensiones_permitidas)) {
    header(
        "Location: dashboard.php?error=" .
            urlencode("Solo se permiten archivos PDF"),
    );
    exit();
}

// VALIDACIÓN 4: Verificar tipo MIME real del archivo
$finfo = finfo_open(FILEINFO_MIME_TYPE);
$mime_type = finfo_file($finfo, $archivo["tmp_name"]);
finfo_close($finfo);

$mimes_permitidos = ["application/pdf"];
if (!in_array($mime_type, $mimes_permitidos)) {
    header(
        "Location: dashboard.php?error=" .
            urlencode("El archivo no es un PDF válido"),
    );
    exit();
}

// VALIDACIÓN 5: Verificar que el archivo no esté vacío
if ($archivo["size"] === 0) {
    header(
        "Location: dashboard.php?error=" . urlencode("El archivo está vacío"),
    );
    exit();
}

// Generar nombre único y sanitizado para el archivo
$nombre_archivo_unico = generarNombreUnico($extension);
$ruta_completa = $directorio_destino . $nombre_archivo_unico;

// Intentar mover el archivo al directorio de destino
if (move_uploaded_file($archivo["tmp_name"], $ruta_completa)) {
    // Guardar información en la base de datos
    $usuario_id = $_SESSION["usuario_id"];
    $nombre_original_sanitizado = sanitizarNombreArchivo($nombre_original);
    $tamano_bytes = $archivo["size"];

    // Usar prepared statement para prevenir SQL Injection
    $stmt = $conexion->prepare(
        "INSERT INTO tareas (usuario_id, nombre_archivo, nombre_original, tamano_bytes) VALUES (?, ?, ?, ?)",
    );
    $stmt->bind_param(
        "issi",
        $usuario_id,
        $nombre_archivo_unico,
        $nombre_original_sanitizado,
        $tamano_bytes,
    );

    if ($stmt->execute()) {
        // Éxito total
        $stmt->close();
        header(
            "Location: dashboard.php?mensaje=" .
                urlencode("Tarea subida correctamente"),
        );
        exit();
    } else {
        // Error al guardar en base de datos - eliminar archivo
        $stmt->close();
        unlink($ruta_completa);
        header(
            "Location: dashboard.php?error=" .
                urlencode("Error al registrar la tarea en la base de datos"),
        );
        exit();
    }
} else {
    // Error al mover el archivo
    header(
        "Location: dashboard.php?error=" .
            urlencode("Error al guardar el archivo en el servidor"),
    );
    exit();
}

// Cerrar conexión
cerrarConexion();
?>