They can be, with care, but GraphQL was not designed for binary data and uploads need extra defences. The GraphQL project recommends avoiding uploads through GraphQL altogether. Instead, request a signed upload URL through a mutation, upload directly to storage, then link the file with a second mutation.
Why uploads are tricky in GraphQL
The GraphQL project's file upload guide explains that GraphQL requests are typically JSON, which cannot carry binary data directly. Base64 inside JSON is inefficient and does not suit larger files or streamed processing. A common choice is multipart form data, using a community convention called the GraphQL multipart request specification. The guide warns that people implementing it must pay close attention to avoid security or reliability problems.
Risks the guide lists
| Risk | What happens | Defence |
|---|---|---|
| Repeated variables | An upload variable used several times drains or re reads the stream, risking memory exhaustion | Allow each upload variable once |
| Stream leaks on failed operations | Validation or authorization fails and streams are never consumed, leaking memory | End all streams when the request finishes |
| CSRF | Multipart form data counts as a simple request in CORS and sends no preflight, so another origin may post uploads | Add explicit CSRF protection |
| Oversized or extra payloads | Huge files or extra files under unused names overload the server | Set request size caps, reject unreferenced files |
| Untrusted metadata | File names, types and contents may be hostile | Sanitize names, sniff types, validate contents |
The guide also mentions format specific exploits such as zip bombs and maliciously crafted PDFs. Declared MIME types should not be trusted, so detect the type from the content and reject mismatches.
The signed URL alternative
The guide calls the signed URL approach the most secure and scalable option. The flow has three steps:
- The client calls a mutation that asks for a signed upload URL from your storage provider.
- The client uploads the file straight to storage using that URL.
- A second mutation, or an automatic trigger, associates the uploaded file with your data.
Because steps one and two alone could be abused to fill storage, the guide says to retain such uploads for a short period so an attacker who completes just those steps cannot exhaust your space. In step three the file moves to permanent storage as appropriate.
This keeps binary handling off your GraphQL server and lets existing storage controls such as size limits and expiry do their work.
If you still need uploads through GraphQL
The guide lists a minimum set of practices:
- Use a well maintained implementation of the multipart request spec.
- Enforce a rule that upload variables are referenced once.
- Stream uploads to disk or cloud storage and do not buffer in memory.
- Always terminate streams at the end of the request.
- Apply strict request size limits and validate every field.
- Treat file names, types and contents as untrusted.
Add the general limits too. OWASP's API Security Top 10 uses a GraphQL upload mutation as an example of unrestricted resource consumption: an attacker batches hundreds of upload operations into one request to bypass a per request rate limit and exhaust memory. Limits on maximum upload size and operations per request apply here, as do batching limits.
Review checklist
- Do you really need uploads in GraphQL, or can storage take them directly?
- Is the multipart library maintained and up to date?
- Is the request size cap enforced before buffering?
- Does one request carry limited operations?
- Is CSRF protection active for form encoded posts?
- Are file names sanitized and types detected from content?
- Is temporary storage cleaned up after failed requests?
Try it yourself
Click through it at your own pace, or open it full screen.
Look at how a dynamic scan probes inputs on a live app, and consider adding oversized and malformed uploads to your own tests.
Quick check
Can I send a file as base64 inside a GraphQL JSON request?
You can, but the GraphQL guide calls it inefficient and unsuited to larger files because streaming becomes hard. Signed URLs avoid the problem.
Why is CSRF a concern for multipart uploads?
Multipart form data is a simple request under CORS, so browsers send it without a preflight check. Add explicit CSRF protection.
Safeguard scans your dependencies and SBOMs for known vulnerabilities, including multipart and GraphQL libraries. A free scan is at gold.safeguard.sh/scan.
Sources
Last reviewed: 19 October 2026.