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.
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.phprenamed tophoto.jpg) - Spot content-type mismatches (a
Content-Type: image/jpegheader on a.exefile) - 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:
- Check access logs for POST requests to the upload endpoint → you see the IP and timestamp
- Check application logs → maybe nothing, or a generic "file uploaded" message
- Guess what was uploaded and by whom
With multipart capture in your traces, you can:
- Query traces for
http.request.bodycontaining.phpin the filename - See the exact field name, original filename, declared content-type, and file size
- Correlate with
http.client_ipto identify the attacker's source - Check the same IP's other traces for reconnaissance patterns (directory traversal, authentication probing)
- 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-Typeheader; 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
| What | How |
|---|---|
| Enable multipart capture | SECURENOW_CAPTURE_MULTIPART=1 + SECURENOW_CAPTURE_BODY=1 |
| Memory overhead | ~80 bytes per request (streaming, no file buffering) |
| Captured for text fields | Field name + value (redacted if sensitive) |
| Captured for files | Field name, filename, content-type, size |
| NOT captured | File binary content (never buffered) |
| Parts limit | 100 per request |
| Minimum version | securenow@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
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
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
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