Interview question
Implement a bounded PDF upload endpoint
Validates size and signature, writes under a generated storage key, and keeps untrusted files outside the public web root.
TL;DR
Validates size and signature, writes under a generated storage key, and keeps untrusted files outside the public web root.
Request limits, file signatures, storage keys, quarantine, cancellation, ownership, and safe responses.
Practice the problem like a real interview: restate, reason, implement, and test.
Accept one claimed PDF up to 10 MB. Use its PDF signature only as an initial type check, store it under a server-generated quarantine key, validate and scan it with an approved pipeline, and persist ownership plus safe metadata only after acceptance.
Uploading invoice.exe renamed to .pdf fails signature validation and never becomes downloadable.
I reject impossible metadata early, use %PDF- only to reject obvious mismatches, and generate an opaque storage key. The file stays outside the web root in quarantine until deeper PDF validation and malware scanning succeed. A signature match alone does not prove the document is structurally valid or safe.
[Authorize]
[HttpPost("documents")]
[RequestSizeLimit(10_500_000)]
public async Task<ActionResult<DocumentResponse>> Upload(
IFormFile file, CancellationToken ct)
{
const long maxBytes = 10_000_000;
if (file.Length is <= 0 or > maxBytes)
return ValidationProblem(new() {
["file"] = ["Choose a non-empty PDF up to 10 MB."]
});
var storageKey = $"quarantine/{Guid.NewGuid():N}";
var retained = false;
try
{
await using var input = file.OpenReadStream();
await storage.WriteAsync(storageKey, input, maxBytes, ct);
var signature = await storage.ReadPrefixAsync(storageKey, 5, ct);
if (!signature.AsSpan().SequenceEqual("%PDF-"u8))
return ValidationProblem(new() {
["file"] = ["The file does not look like a PDF."]
});
var validation = await pdfValidator.ValidateAsync(storageKey, ct);
var scan = validation.IsStructurallyValid
? await scanner.ScanAsync(storageKey, ct)
: ScanResult.Rejected;
if (!validation.IsStructurallyValid || !scan.IsSafe)
return ValidationProblem(new() {
["file"] = ["The file could not be accepted."]
});
var document = Document.Create(User.RequireUserId(), storageKey,
FileNames.ForDisplay(file.FileName), file.Length, "application/pdf");
db.Documents.Add(document);
await db.SaveChangesAsync(ct);
retained = true;
return CreatedAtAction(nameof(GetDocument), new { id = document.Id },
DocumentResponse.From(document));
}
finally
{
if (!retained)
await storage.DeleteIfExistsAsync(storageKey, CancellationToken.None);
}
}
Signature sniffing is constant-size and only rejects obvious mismatches. Structural validation and malware scanning inspect the stored object and may be expensive, so larger or slower workflows should remain quarantined and complete asynchronously. Size limits, cleanup, generated keys, and download authorization are still required even after a scanner reports safe.
ContentType or the original extension.%PDF- as proof that a file is a valid and safe PDF.wwwroot.Next in API Implementation Labs: Authorized Download