" MicromOne: Detecting File Types from Base64 in C#: Prefix Matching vs Binary Inspection

Pagine

Detecting File Types from Base64 in C#: Prefix Matching vs Binary Inspection

When developing enterprise integrations, document management systems, Dynamics 365 plugins, or REST APIs, it is common to receive files as Base64 strings.

The challenge is determining the correct file extension and MIME type when the original metadata is missing or unreliable.

Recently, while troubleshooting a document download issue in a Dynamics 365 plugin, I compared two different approaches:

  • Version A: Detect files using Base64 prefixes
  • Version B: Detect files using binary signatures (magic bytes) and ZIP inspection

At first glance, both approaches seem valid. However, modern Office documents introduce challenges that make one solution significantly more reliable than the other.

The Real Problem

Suppose an external service returns a document as a Base64 string:


string retrieveResponse = GetDocumentFromExternalService();

The original filename might be incorrect:


Filename: report.csv
Content-Type: application/octet-stream

while the actual content could be:

  • PDF
  • PNG
  • JPEG
  • DOCX
  • XLSX
  • PPTX
  • ZIP
  • Legacy Microsoft Office Document

The application must inspect the content itself and determine the correct type.

Version A: Detecting Files Using Base64 Prefixes

The simplest approach consists of comparing the beginning of the Base64 string against a predefined dictionary of known signatures.


private readonly Dictionary<string,
(string Extension, string MimeType)> fileSignatures =
    new Dictionary<string,
        (string Extension, string MimeType)>
{
    { "iVBORw0KGgo",
        (".png", "image/png") },

    { "/9j/",
        (".jpg", "image/jpeg") },

    { "R0lGODdh",
        (".gif", "image/gif") },

    { "JVBER",
        (".pdf", "application/pdf") },

    { "UEsDBBQABgAIAAAAIQDfpNJsWg",
        (".docx",
        "application/vnd.openxmlformats-officedocument.wordprocessingml.document") },

    { "UEsDBBQABgAIAAAAIQBi7p1oXgE",
        (".xlsx",
        "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet") },

    { "UEsDBBQABgAIAAAAIQDfzBj1rQE",
        (".pptx",
        "application/vnd.openxmlformats-officedocument.presentationml.presentation") },

    { "UEsDB",
        (".zip", "application/zip") }
};

Detection is straightforward:


foreach (var signature in fileSignatures)
{
    if (base64.StartsWith(signature.Key))
    {
        return signature.Value;
    }
}

Advantages

  • Very simple
  • Easy to extend
  • Fast string comparisons
  • No ZIP processing

Disadvantages

  • Relies on specific sample files
  • Not reliable for OpenXML documents
  • Difficult to maintain
  • May fail with files created by different software

Why Base64 Prefix Matching Is Fragile

Modern Microsoft Office formats are actually ZIP containers.

  • .docx
  • .xlsx
  • .pptx

All of them typically begin with the ZIP signature:


50 4B 03 04

which becomes:


UEsDB

when converted to Base64.

The problem is that everything following the ZIP header depends on:

  • ZIP entry ordering
  • Metadata
  • Timestamps
  • Compression flags
  • Archive generation tool

Consider these real examples:


DOCX:
UEsDBBQABgAIAAAAIQDfpNJsWgEAACAFAAAT...

XLSX:
UEsDBBQABgAIAAAAIQBi7p1oXgEAAJAEAAAT...

PPTX:
UEsDBBQABgAIAAAAIQDfzBj1rQEAAEYMAAAT...

ZIP:
UEsDBBQAAAAIACt+I10vkupli2EAAJuCAAAL...

These values identify specific files, not the Office formats themselves.

A different XLSX document generated by another application may have a completely different prefix.

Key takeaway:
Long Base64 prefixes are not universal file signatures. They are characteristics of individual files.

Version B: Binary Signature Detection

A significantly more reliable solution is:

  1. Decode the Base64 content
  2. Inspect actual binary signatures
  3. Analyze ZIP structure when necessary

Step 1: Convert Base64 to Bytes


byte[] bytes =
    Convert.FromBase64String(base64);

Step II: Detect Magic Numbers

PDF


if (StartsWith(
    bytes,
    0x25, 0x50, 0x44, 0x46))
{
    return ".pdf";
}

PNG


if (StartsWith(
    bytes,
    0x89, 0x50, 0x4E, 0x47,
    0x0D, 0x0A, 0x1A, 0x0A))
{
    return ".png";
}

JPEG


if (StartsWith(
    bytes,
    0xFF, 0xD8, 0xFF))
{
    return ".jpg";
}

Notice that we only verify:


FF D8 FF

instead of:


FF D8 FF E0

which supports additional JPEG variants such as EXIF files.

Handling DOCX, XLSX and PPTX Correctly

Magic bytes alone cannot distinguish Office Open XML formats because they all begin as ZIP archives.

The solution is inspecting the package structure:


using var stream =
    new MemoryStream(bytes);

using var archive =
    new ZipArchive(
        stream,
        ZipArchiveMode.Read);

Then inspect the internal folders:


if (archive.Entries.Any(
    e => e.FullName.StartsWith("word/")))
{
    return ".docx";
}

if (archive.Entries.Any(
    e => e.FullName.StartsWith("xl/")))
{
    return ".xlsx";
}

if (archive.Entries.Any(
    e => e.FullName.StartsWith("ppt/")))
{
    return ".pptx";
}

If none of those folders exist:


return ".zip";

This approach remains valid regardless of:

  • Timestamps
  • Metadata
  • Compression level
  • ZIP ordering
  • Generating software

Performance Comparison

AspectVersion AVersion B
SpeedVery FastFast
Memory UsageLowModerate
Detect PDF/ImagesGoodExcellent
Detect DOCX/XLSX/PPTXUnreliableReliable
MaintainabilityDifficultExcellent
Production ReadyNoYes

Security Considerations

During troubleshooting it may be tempting to log the entire Base64 content:


tracingService.Trace(retrieveResponse);

This is usually a bad idea because:

  • Logs may truncate the content
  • Large files produce huge traces
  • Sensitive information could be exposed
  • Performance may degrade

A safer approach is logging only the first bytes:


string header =
    BitConverter.ToString(
        bytes,
        0,
        Math.Min(16, bytes.Length));

tracingService.Trace(
    $"Header: {header}");

Final Verdict

After comparing both implementations, the conclusion is clear.

Version A

  • Simple
  • Fast
  • Suitable for demonstrations
  • Not reliable for modern Office documents

Version B

  • Uses real binary signatures
  • Handles OpenXML documents correctly
  • Returns accurate MIME types
  • Production-ready
  • Much more robust