Fix: Seguridad de Imágenes

This commit is contained in:
2026-01-30 15:23:50 -03:00
parent a5f501e88e
commit 8f0b9546d4

View File

@@ -78,11 +78,27 @@ public class ImageStorageService : IImageStorageService
{
string hex = BitConverter.ToString(headerBytes.Take(8).ToArray());
_logger.LogWarning("Firma de archivo inválida para {Extension}: {HexBytes}", ext, hex);
throw new Exception($"El archivo parece corrupto o tiene una firma inválida ({hex}). El sistema acepta JPG, PNG y WEBP reales.");
throw new Exception($"El archivo parece corrupto o tiene una firma inválida ({hex}).");
}
}
try
{
// 4. PREVENCIÓN DoS: Validar dimensiones sin cargar la imagen completa en memoria
// Esto evita que una imagen de 1KB que dice ser de 50000x50000px cuelgue el servidor
using (var stream = file.OpenReadStream())
{
var info = await Image.IdentifyAsync(stream);
if (info == null) throw new Exception("No se pudo identificar el formato de la imagen.");
const int MaxDimension = 5000; // 5000px es más que suficiente para un aviso
if (info.Width > MaxDimension || info.Height > MaxDimension)
{
_logger.LogWarning("Intento de subir imagen con dimensiones excesivas: {W}x{H}", info.Width, info.Height);
throw new Exception($"Las dimensiones de la imagen ({info.Width}x{info.Height}) exceden el límite de seguridad de {MaxDimension}px.");
}
}
// 1. Definir rutas
var uploadFolder = Path.Combine(_env.WebRootPath, "uploads", "ads", adId.ToString());
if (!Directory.Exists(uploadFolder)) Directory.CreateDirectory(uploadFolder);
@@ -94,7 +110,7 @@ public class ImageStorageService : IImageStorageService
var filePath = Path.Combine(uploadFolder, fileName);
var thumbPath = Path.Combine(uploadFolder, thumbName);
// 2. Cargar y Procesar con ImageSharp
// 2. Cargar y Procesar con ImageSharp (Aquí ya es seguro porque validamos dimensiones e identidad)
using (var image = await Image.LoadAsync(file.OpenReadStream()))
{
// A. Guardar imagen principal (Optimized: Max width 1280px)
@@ -102,22 +118,21 @@ public class ImageStorageService : IImageStorageService
{
image.Mutate(x => x.Resize(new ResizeOptions
{
Size = new Size(1280, 0), // 0 mantiene aspect ratio
Size = new Size(1280, 0),
Mode = ResizeMode.Max
}));
}
await image.SaveAsJpegAsync(filePath);
// B. Generar Thumbnail (Max width 400px para grillas)
// B. Generar Thumbnail
image.Mutate(x => x.Resize(new ResizeOptions
{
Size = new Size(400, 300),
Mode = ResizeMode.Crop // Recorte inteligente para que queden parejitas
Mode = ResizeMode.Crop
}));
await image.SaveAsJpegAsync(thumbPath);
}
// Retornar ruta relativa web de la imagen principal
return $"/uploads/ads/{adId}/{fileName}";
}
catch (Exception ex)