Analyzing Multipart File Upload Requests for Security Issues — A Practical Guide with SecureNow Tracing

Learn why multipart/form-data requests are a blind spot in most observability setups, what security risks they carry, and how to capture upload metadata in your traces without buffering file content — using SecureNow's new streaming multipart parser.

Lhoussine
Apr 4, 2026·12 min read

Analyzing Multipart File Upload Requests for Security Issues

File uploads are one of the most dangerous attack surfaces in any web application. They let external users push arbitrary binary content onto your server — content that could be a web shell, a polyglot payload, an oversized denial-of-service bomb, or a perfectly normal profile photo. The problem is that most observability pipelines completely skip multipart/form-data requests, leaving you blind to what was uploaded, by whom, and whether it looked suspicious.

This guide covers why multipart requests deserve first-class tracing, what security signals to look for in upload metadata, and how to enable zero-overhead streaming capture in SecureNow.

...

Why Multipart Requests Are an Observability Blind Spot

When you enable request body capture in any APM or tracing tool, you typically see JSON payloads, GraphQL queries, and form submissions. But the moment a request arrives as multipart/form-data, the trace shows something like:

http.request.body: [MULTIPART - NOT CAPTURED]

There are good reasons for this default: multipart bodies can be gigabytes of binary data, and blindly buffering them would crash your collector or your application. But "too expensive to capture" has turned into "not worth looking at," which is a dangerous assumption.

Attackers know that file upload endpoints receive less scrutiny. If your traces don't show what was uploaded, you cannot:

  • Detect extension spoofing (e.g., malware.php renamed to photo.jpg)
  • Spot content-type mismatches (a Content-Type: image/jpeg header on a .exe file)
  • Identify oversized payloads before they hit disk
  • Correlate upload patterns with known attack campaigns
  • Investigate incidents after the fact using trace data
...

The Security Risks Hidden in Multipart Metadata

You don't need the actual file bytes to detect most upload-based attacks. The metadata alone — filenames, content types, field names, and sizes — carries strong signals.

1. Web Shell Uploads

An attacker uploads a server-side script disguised as an innocent file:

filename: "profile-photo.jpg.php"
contentType: "image/jpeg"
size: 4096

The double extension and the suspiciously small size (4 KB for a "photo") are immediate red flags. A real JPEG would be tens or hundreds of kilobytes.

2. Content-Type Mismatch

The browser sets the content type based on the file extension, but attackers can forge it:

filename: "report.exe"
contentType: "application/pdf"

When the declared content-type doesn't match the file extension, something is wrong. This mismatch is trivially visible in trace metadata.

3. Path Traversal in Filenames

Some upload handlers use the client-provided filename to write to disk. Attackers exploit this:

filename: "../../../etc/cron.d/backdoor"
contentType: "text/plain"

Trace metadata makes path traversal attempts immediately visible, even if your application sanitizes them before they reach the filesystem.

4. Oversized Payloads (DoS)

A single 10 GB file (or 1000 tiny files) can exhaust disk space, memory, or processing time:

files: [
  { "field": "attachment", "filename": "data.bin", "contentType": "application/octet-stream", "size": 10737418240 }
]

Seeing the exact byte count in your trace lets you set alert rules for abnormal upload sizes.

5. Hidden Fields and Parameter Injection

Multipart requests can mix file uploads with text fields. Attackers sometimes inject unexpected fields:

fields: {
  "role": "admin",
  "isVerified": "true",
  "redirect_url": "https://evil.com/steal-token"
}

If your backend blindly spreads these fields into a database update or authorization check, you have a mass assignment vulnerability. Seeing all submitted fields in your trace reveals these attacks.

...

How SecureNow Captures Multipart Metadata Without Buffering Files

Starting in v5.8.0, the securenow npm package includes a streaming multipart parser that runs inside the OpenTelemetry HTTP instrumentation hook. Here's what makes it different from a naive buffer-then-parse approach:

The Problem with Buffering

A typical approach buffers the raw request body up to a size limit and then parses it:

Request: [file1: 5MB] [file2: 3MB] [description: "My files"] [file3: 2MB]
Buffer limit: 100KB
Result: Only the first 100KB of file1's binary data → no visibility into file2, file3, or description

If the first file is large, every subsequent part is invisible. You get the worst of both worlds: high memory usage and incomplete data.

The Streaming Approach

SecureNow's parser scans for multipart boundary markers as data streams through, without buffering file content:

Request: [file1: 5MB] [file2: 3MB] [description: "My files"] [file3: 2MB]
Memory used: ~80 bytes (boundary overlap window)
Result: All 3 files' metadata + description field value captured

The parser maintains a tiny rolling buffer (the length of the boundary string plus a small margin) to detect boundary markers that might span two TCP chunks. For file parts, it counts bytes and discards the content. For text fields, it captures the value (up to 1000 characters). The result: complete visibility with near-zero overhead.

...

Setting It Up

Step 1: Update to v5.8.0+

npm install securenow@latest

Step 2: Enable the Environment Variables

Add to your .env, PM2 ecosystem config, or Docker environment:

SECURENOW_CAPTURE_BODY=1
SECURENOW_CAPTURE_MULTIPART=1

SECURENOW_CAPTURE_BODY=1 enables general body capture (JSON, form, GraphQL). SECURENOW_CAPTURE_MULTIPART=1 additionally enables the streaming multipart parser.

Step 3: Verify in Startup Logs

When your application starts, you should see:

[securenow] 📝 Request body capture: ENABLED (max: 10240 bytes, redacting 16 sensitive fields)
[securenow] 📎 Multipart body capture: ENABLED (streaming — file content not buffered)

Step 4: Check Your Traces

Send a multipart request to your application and inspect the trace. The http.request.body attribute will contain structured JSON:

{
  "fields": {
    "description": "Quarterly report",
    "department": "engineering"
  },
  "files": [
    {
      "field": "document",
      "filename": "Q1-report.pdf",
      "contentType": "application/pdf",
      "size": 2048576
    },
    {
      "field": "attachment",
      "filename": "spreadsheet.xlsx",
      "contentType": "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
      "size": 512000
    }
  ]
}

Additional span attributes include http.request.body.files_count, http.request.body.fields_count, and http.request.body.size (total raw bytes).

...

Building Alert Rules Around Upload Metadata

Once multipart metadata is in your traces, you can create SecureNow alert rules to catch suspicious patterns in real time.

Alert on Executable Extensions

Flag uploads where the filename ends in a known executable extension:

Condition: http.request.body contains .php, .jsp, .exe, .sh, .bat, .ps1, .py, or .rb in a filename field.

Alert on Content-Type Mismatches

Flag uploads where the declared content-type doesn't match the file extension — for example, a .exe file with contentType: "image/jpeg".

Alert on Oversized Uploads

Flag requests where http.request.body.size exceeds your expected maximum — e.g., 50 MB for an endpoint that should only accept profile photos.

Alert on Unusual Field Names

Flag multipart requests that include text fields like role, admin, isAdmin, verified, or redirect_url — these suggest parameter injection attempts.

...

Investigating an Incident with Multipart Traces

Suppose your WAF logs show a web shell was written to disk at /uploads/avatar-abc123.php. Without multipart tracing, your investigation looks like:

  1. Check access logs for POST requests to the upload endpoint → you see the IP and timestamp
  2. Check application logs → maybe nothing, or a generic "file uploaded" message
  3. Guess what was uploaded and by whom

With multipart capture in your traces, you can:

  1. Query traces for http.request.body containing .php in the filename
  2. See the exact field name, original filename, declared content-type, and file size
  3. Correlate with http.client_ip to identify the attacker's source
  4. Check the same IP's other traces for reconnaissance patterns (directory traversal, authentication probing)
  5. Build a complete timeline of the attack
Trace: POST /api/users/avatar
  http.client_ip: 198.51.100.42
  http.request.body: {"fields":{},"files":[{"field":"avatar","filename":"shell.php","contentType":"image/jpeg","size":1847}]}
  http.request.body.files_count: 1
  http.status_code: 200

This single trace tells you: the attacker used the avatar upload field, named the file shell.php, lied about the content-type (image/jpeg), and the file was only 1.8 KB — a classic web shell size. The 200 status means your application accepted it.

...

Security Checklist for File Upload Endpoints

Beyond tracing, make sure your upload endpoints implement these defenses:

  • Validate content-type server-side — don't trust the client's Content-Type header; inspect magic bytes
  • Restrict allowed extensions — maintain an allowlist, not a blocklist
  • Rename files on disk — never use the client-provided filename for storage
  • Set a maximum file size — enforce at the reverse proxy layer (Nginx client_max_body_size) and application layer
  • Store uploads outside the web root — prevent direct execution of uploaded scripts
  • Scan with antivirus/malware detection — integrate ClamAV or a cloud scanning API
  • Rate-limit upload endpoints — prevent abuse through volume

With SECURENOW_CAPTURE_MULTIPART=1, you gain visibility into whether these controls are working: you can see every upload in your traces, spot bypasses, and investigate incidents with full context.

...

Summary

WhatHow
Enable multipart captureSECURENOW_CAPTURE_MULTIPART=1 + SECURENOW_CAPTURE_BODY=1
Memory overhead~80 bytes per request (streaming, no file buffering)
Captured for text fieldsField name + value (redacted if sensitive)
Captured for filesField name, filename, content-type, size
NOT capturedFile binary content (never buffered)
Parts limit100 per request
Minimum versionsecurenow@5.8.0

File uploads are too important to leave unmonitored. With streaming multipart capture, you get full visibility into what's being uploaded to your application — filenames, content types, sizes, and accompanying form fields — without the memory cost of buffering gigabytes of binary data. Set the two environment variables, redeploy, and start catching upload-based attacks in your traces.

Frequently Asked Questions

Does SecureNow store the actual file content in traces?

No. The streaming parser extracts only metadata — field names, filenames, content types, and byte sizes. File binary content is discarded as it streams through. Your traces stay small and your collector is never flooded with raw file data.

Will enabling multipart capture slow down my application?

No measurable impact. The parser piggybacks on Node.js request data events that are already firing. It scans for boundary markers in the stream and keeps a rolling buffer of ~80 bytes. There is no extra I/O, no file copying, and no blocking.

Can I use this with Express, Fastify, or Next.js?

Yes. The multipart capture runs inside the OpenTelemetry HTTP instrumentation request hook, which fires for every incoming HTTP request regardless of framework. If your app uses securenow/register or securenow/nextjs, it works automatically.

How do I know if an upload is malicious just from metadata?

Metadata reveals the most common attack patterns: executable extensions disguised as images (.jpg.exe), mismatched content-type headers, oversized payloads, and unusual field names that suggest injection attempts. Combine this with SecureNow's alert rules to get notified in real time.

What happens if a request has more than 100 parts?

The parser stops after 100 parts as a safety guard and captures whatever it has found so far. This protects against denial-of-service payloads designed to exhaust server resources with millions of tiny parts.

Recommended reading

Axios npm Compromise (March 31, 2026): How to Check Your Project and Runtime for Exposure

Step-by-step guide to auditing your Node.js project for the Axios supply chain attack that shipped a remote access trojan via plain-crypto-js. Includes a ready-to-paste AI audit prompt, IOC checklist, and SecureNow forensics queries to detect runtime compromise.

Mar 31
axios npm compromise detection guide
SOC Notification Triage: From Alert Overload to Actionable Incidents

Master the art of SOC notification triage with structured workflows. Learn to filter, prioritize, and resolve security alerts efficiently using status-based workflows and AI-powered investigation.

Mar 3
soc notification triage alert workflow
Eliminating False Positives: A SOC Team's Guide to Smarter Alerting

Reduce false positive rates in your SOC with AI-suggested exclusions, test-before-apply workflows, and intelligent path pattern matching. A practical guide to cleaner alerts.

Mar 1
reduce false positives soc alerting