Saltar al contenido principal

Detección de Tipo de Contenido

El ContentTypeDetectionMiddleware analiza los primeros bytes del archivo (magic bytes) para determinar el tipo MIME real, independientemente de lo que el cliente haya declarado en ContentType. Esto es fundamental para la seguridad: un cliente puede declarar image/jpeg mientras sube un .exe, y este middleware lo detecta.

Activación

.WithPipeline(p => p
.UseContentTypeDetection(c =>
{
c.OverrideClientContentType = true; // Sobreescribir el tipo declarado por el cliente
c.FallbackToExtension = true; // Usar extensión si los magic bytes no son reconocidos
c.FailOnUnknownType = false; // No fallar si no se puede detectar el tipo
})
.UseValidation(v =>
{
v.AllowedContentTypes = ["image/jpeg", "image/png", "image/webp"];
})
)
Advertencia

UseContentTypeDetection debe ir antes de UseValidation en el pipeline. De lo contrario, la validación de tipo MIME operará sobre el tipo declarado por el cliente (potencialmente falso) en lugar del tipo real detectado.

ContentTypeDetectionOptions

public class ContentTypeDetectionOptions
{
/// <summary>Si true, sobreescribe el ContentType del request con el tipo detectado. Por defecto: true.</summary>
public bool OverrideClientContentType { get; set; } = true;

/// <summary>Si true y los magic bytes no son reconocidos, intenta detectar el tipo por extensión del archivo. Por defecto: true.</summary>
public bool FallbackToExtension { get; set; } = true;

/// <summary>Si true, falla la subida cuando no se puede determinar el tipo. Por defecto: false.</summary>
public bool FailOnUnknownType { get; set; } = false;

/// <summary>Número de bytes a leer para la detección. Por defecto: 512 bytes (suficiente para la mayoría de formatos).</summary>
public int SniffBytes { get; set; } = 512;
}

Tabla de opciones

OpciónPor defectoDescripción
OverrideClientContentTypetrueSobreescribir el ContentType declarado con el tipo detectado
FallbackToExtensiontrueSi los magic bytes no coinciden, usar la extensión del archivo
FailOnUnknownTypefalseRechazar archivos cuyo tipo no se puede determinar
SniffBytes512Bytes a leer para la detección (más bytes = más precisión, más latencia)

Cómo funciona

loading...

Tipos MIME soportados

Imágenes

Magic BytesTipo MIMEExtensiones
FF D8 FFimage/jpeg.jpg, .jpeg
89 50 4E 47image/png.png
47 49 46 38image/gif.gif
52 49 46 46 ... 57 45 42 50image/webp.webp
42 4Dimage/bmp.bmp
49 49 2A 00 o 4D 4D 00 2Aimage/tiff.tiff
00 00 00 XX 66 74 79 70 61 76 69 66image/avif.avif

Documentos

Magic BytesTipo MIMEExtensiones
25 50 44 46application/pdf.pdf
50 4B 03 04 (Office XML)application/vnd.openxmlformats-officedocument.*.docx, .xlsx, .pptx
D0 CF 11 E0application/msword.doc, .xls, .ppt

Archivos comprimidos

Magic BytesTipo MIMEExtensión
50 4B 03 04application/zip.zip
1F 8Bapplication/gzip.gz
42 5A 68application/x-bzip2.bz2
37 7A BC AFapplication/x-7z-compressed.7z
52 61 72 21application/x-rar-compressed.rar

Audio y video

Magic BytesTipo MIMEExtensión
49 44 33 o FF FBaudio/mpeg.mp3
66 74 79 70 69 73 6F 6Dvideo/mp4.mp4
1A 45 DF A3video/webm.webm
52 49 46 46 ... 57 41 56 45audio/wav.wav

Ejecutables (detectar para bloquear)

Magic BytesTipo MIMEDescripción
4D 5Aapplication/x-msdownloadEjecutables Windows (.exe, .dll)
7F 45 4C 46application/x-elfELF binarios Linux
CA FE BA BEapplication/java-archiveBytecode Java (.class)

Ejemplo: Bloquear ejecutables disfrazados

.WithPipeline(p => p
.UseContentTypeDetection(c =>
{
c.OverrideClientContentType = true;
c.FallbackToExtension = false; // No confiar en la extensión
})
.UseValidation(v =>
{
// Bloquear tipos peligrosos detectados por magic bytes
v.BlockedContentTypes =
[
"application/x-msdownload", // .exe, .dll
"application/x-elf", // ELF binarios
"application/x-sh", // Shell scripts
"application/x-msdos-program" // Programas DOS
];
})
)

Configuración estricta (tipo desconocido = rechazo)

.UseContentTypeDetection(c =>
{
c.OverrideClientContentType = true;
c.FallbackToExtension = false; // No usar la extensión como fallback
c.FailOnUnknownType = true; // Rechazar archivos de tipo no reconocido
})

Con esta configuración, solo se aceptan archivos cuyo tipo MIME pueda detectarse por sus magic bytes. Archivos de tipo desconocido son rechazados con StorageErrorCode.ValidationError.

Inspeccionar el tipo detectado

El tipo detectado queda registrado en los metadatos del archivo almacenado:

var meta = await storage.GetMetadataAsync("uploads/imagen.jpg", ct);
if (meta.IsSuccess)
{
Console.WriteLine($"Tipo detectado: {meta.Value!.ContentType}");
// "image/jpeg" — aunque el cliente hubiera declarado "image/png"
}

Posición en el pipeline recomendada

loading...

UseContentTypeDetection debe ser el primer (o uno de los primeros) middlewares del pipeline para que todos los que vengan después trabajen con información de tipo MIME confiable.

Consejo

Combina siempre UseContentTypeDetection con UseValidation usando AllowedContentTypes para validar el tipo MIME real del archivo. Usar solo la extensión para validar el tipo es inseguro: cualquiera puede renombrar un archivo para cambiar su extensión.

Información

La detección por magic bytes es robusta para formatos binarios con firmas distintivas (JPEG, PNG, PDF, ZIP, ejecutables), pero los archivos de texto plano (CSV, XML, HTML, JSON) no tienen magic bytes únicos y se identifican principalmente por extensión. Para estos tipos, el fallback por extensión es suficiente en la mayoría de los casos.