Every service that turns user-supplied video, audio or images into thumbnails, previews or new formats is running a very large parser over hostile input, usually with network access and cloud credentials it does not need. FFmpeg and ImageMagick sit behind upload features, chat attachments, video platforms, document previews and AI ingestion pipelines. Both are powerful because they understand hundreds of formats and protocols, and that breadth is the attack surface: a playlist that fetches internal URLs, a container that reads local files, or a malformed image that triggers a memory bug. This guide covers how media processing gets attacked, the specific FFmpeg and ImageMagick options that narrow it, how to sandbox transcoding workers, and how to detect and measure the result. It is written for application security engineers, platform teams and developers who own media pipelines.
Key takeaways
- Defaults favour flexibility, not safety. FFmpeg allows all protocols, demuxers and decoders by default, and ImageMagick ships an open policy unless you choose otherwise.
- Playlists and container formats can make the parser fetch things. HLS playlists, concat lists and image formats with URL references turn a file upload into server-side requests or local file reads.
- Allowlists beat filename checks. Pin
protocol_whitelist,format_whitelistandcodec_whitelistin FFmpeg and amoduleallowlist in ImageMagick'spolicy.xml. - Assume the parser will eventually be compromised. Run each job in an isolated, short-lived worker with no network egress, a seccomp profile, a read-only filesystem and strict CPU, memory and time limits.
- The worker's identity is the prize. A transcoding worker that can reach a cloud metadata endpoint or holds broad storage credentials turns a parser bug into a data breach.
Why media processing is a high-risk parser
Media libraries decode complex binary formats, mostly in C, and are designed to recover gracefully from damaged files, which produces a steady flow of memory-safety vulnerabilities. FFmpeg lists CVEs per release on its security page and asks that reports go to its security address with a reproducible test case; automated submissions are not accepted. You will run vulnerable media code at some point, so the controls around it matter as much as patch speed.
Four risk classes cover almost every real issue:
- Memory corruption in decoders: a crafted frame or image header triggers an overflow. CISA's Known Exploited Vulnerabilities (KEV) catalogue includes CVE-2023-4863, a heap buffer overflow in the WebP image library used by Chromium, added on 13 September 2023.
- Indirect reads and server-side request forgery (SSRF): formats that reference other resources make the processor open files or URLs chosen by the uploader. CVE-2017-9993 in FFmpeg allowed arbitrary file reads through crafted HTTP Live Streaming (HLS) playlist data because filename extensions and demuxer names were not properly restricted (fixed in 2.8.12, 3.1.9, 3.2.6 and 3.3.2). ImageMagick's CVE-2016-3718 is an SSRF entry in KEV.
- Command execution through delegates and metadata tools: ImageMagick's CVE-2016-3714 (the "ImageTragick" input validation flaw) was added to KEV on 9 September 2024, years after disclosure, because it was still being exploited. ExifTool's CVE-2021-22204, often run to read or strip metadata, is also in KEV.
- Resource exhaustion: a file declaring enormous dimensions, thousands of streams or an endless duration ties up CPU, memory and disk. This is a denial of service against the pipeline and often against the whole node (MITRE ATT&CK T1499 Endpoint Denial of Service).
See the Secure File Upload Handling Guide for the upload side (size limits, naming, quarantine and serving files back). This guide starts when a worker opens the file.
Playlist SSRF and indirect file access
An HLS playlist (.m3u8) is a text file listing segment URLs. If a user uploads a "video" that is really a playlist, or a container that FFmpeg probes as one, the transcoder may fetch every listed URI. Those URIs can point at internal admin interfaces, a cloud instance metadata service such as 169.254.169.254, or file: paths on the worker. Similar indirection exists in the concat demuxer (a text list of files) and in image formats such as SVG and MVG that ImageMagick can render, which can reference external resources.
FFmpeg exposes the controls needed to close this, but none are restrictive by default:
| FFmpeg option | What it does (per FFmpeg documentation) | Recommended setting for untrusted uploads |
|---|---|---|
protocol_whitelist | Comma-separated list of allowed protocols. All protocols are allowed by default; nested protocols are restricted to a per-protocol subset | file only (add pipe if you stream through stdin). Never allow http, https, tcp or udp for uploads |
format_whitelist | Comma-separated list of allowed demuxers; by default all are allowed | Only the demuxers for containers you accept (for example the MOV/MP4 and Matroska/WebM demuxers, using the exact names shown by ffmpeg -demuxers). Exclude hls, concat and image sequence demuxers unless required |
codec_whitelist | Comma-separated list of allowed decoders; by default all are allowed | Only the decoders your product supports, using the names shown by ffmpeg -decoders (for example H.264, VP9, AAC and Opus decoders) |
HLS allowed_extensions / extension_picky | Limit segment extensions the HLS demuxer may open; extension_picky is enabled by default, and the documentation recommends setting whitelists correctly rather than relying on extensions | Only relevant if you must ingest HLS from trusted sources; keep defaults and pair with a protocol allowlist |
concat safe | When 1 (the default), rejects file paths with a protocol specification, absolute paths and unusual characters | Never set safe 0 on lists built from user input |
Pass these as input options before -i (or set them through the libavformat options dictionary when calling the library), and force the input format with -f where your product only accepts one container.
Network controls are the backstop. Even with a correct allowlist, deny all egress from transcoding workers at the network layer and block link-local metadata addresses. See the SSRF Prevention Guide for the wider pattern. The Capital One breach shows why: an SSRF-style request to the metadata endpoint returned cloud role credentials that could read far more data than the component needed.
Hardening ImageMagick and image libraries
ImageMagick's security policy page describes policy.xml as the control point. ImageMagick is open by default, the last matching policy wins (so place broad denials first and exceptions after), and paths are checked in canonical form to stop symlink and traversal bypasses. From version 7.1.1-16 there are predefined policies selectable at build time: open (default), limited, secure and websafe; websafe restricts I/O to GIF, JPEG and PNG and blocks filters and indirect reads.
| Policy domain | Example entry from ImageMagick documentation | Purpose |
|---|---|---|
| module | <policy domain="module" rights="read | write" pattern="{GIF,JPEG,PNG,WEBP}"/> | Allow only the formats you need |
| delegate | <policy domain="delegate" rights="none" pattern="*"/> | Disable external delegate programs (the ImageTragick path) |
| filter | <policy domain="filter" rights="none" pattern="*"/> | Block image filters you do not use |
| path | <policy domain="path" rights="none" pattern="/etc/*"/> | Deny reads of sensitive locations, including indirect @file reads |
| resource | <policy domain="resource" name="memory" value="256MiB"/> | Cap memory, map, disk, width, height, area, time, threads and list-length |
Confirm what is actually loaded with magick identify -list policy; distributions ship different defaults, and a container image built from a different base may silently revert to the open policy. Glob patterns were case-sensitive before 7.1.1-16, so use uppercase coder names on older versions.
The same thinking applies to every helper in the pipeline. Metadata tools (ExifTool), PDF renderers (Ghostscript, which also appears in KEV) and thumbnailers are separate parsers with separate bugs. Inventory them, pin their versions and give them the same sandbox as the main transcoder.
Sandboxing transcoding workers
Allowlists reduce what the parser will attempt; isolation limits what a compromised parser can do. Treat each job as untrusted code execution.
| Layer | Control | What it stops |
|---|---|---|
| Process | Run as a dedicated non-root user; -nostdin; drop all Linux capabilities; no shell or package manager in the image | Privilege escalation and living off the land after a decoder exploit |
| System calls | Docker's default seccomp profile is an allowlist that disables around 44 of 300+ system calls; a custom profile (--security-opt seccomp=) can go further for a fixed workload | Kernel attack surface reachable from a compromised process (D3FEND D3-SCF System Call Filtering) |
| Filesystem | Read-only root; one writable scratch directory per job, deleted afterwards; input mounted read-only | Persistence, tampering with other jobs' files |
| Network | No egress at all; storage access through a pre-signed URL fetched by a separate orchestrator | Playlist SSRF, data exfiltration, command and control (D3FEND D3-OTF Outbound Traffic Filtering) |
| Resources | cgroup CPU and memory limits; FFmpeg -t (input duration), -fs (output size), max_pixels, max_streams; ImageMagick resource policy; a wall-clock kill timer | Decompression bombs, huge dimensions, infinite streams |
| Lifetime | One job per container or microVM, then destroy it | A compromise carrying over to the next customer's file |
Standard containers share the host kernel; for mutually untrusted tenants, a user-space kernel such as gVisor or a microVM reduces the blast radius at some performance cost. See the Container Runtime Security Guide for the runtime options and their trade-offs.
Keep the probe step inside the sandbox too. Running ffprobe on the API server to "check the file first" puts the full parser in your most privileged tier. FFmpeg's probesize (5,000,000 bytes by default) and analyzeduration (5 seconds by default) bound how much data probing reads; lower them for small user uploads.
Where identity fits
The transcoding worker is a non-human identity, and in most breaches of this kind the parser bug is only the entry point. What matters next is what the worker's credentials can reach.
- Scope storage access per job: give the worker a pre-signed URL for one input object and one output prefix, not a role that can list or read the whole media bucket.
- Remove metadata access: on AWS, require IMDSv2 and set the hop limit so containers cannot reach the endpoint, or block it entirely for worker pods. See the EC2 Instance Metadata Service Hardening Guide.
- Separate the orchestrator from the worker: the component that talks to the queue, database and storage APIs should never run the parser.
Implementation roadmap
- Inventory: find every place media is parsed: upload handlers, preview services, chat attachments, AI ingestion, CI jobs that generate thumbnails. Record library versions and the identity each runs as.
- Patch and pin: move to supported FFmpeg and ImageMagick releases, pin versions in the image build, and subscribe to the projects' security pages and distribution advisories.
- Allowlist in audit mode: log the container format, codecs and any protocol opened for every job for two to four weeks (ffprobe output inside the sandbox is enough). Build the allowlists from that data.
- Enforce allowlists: set
protocol_whitelist,format_whitelistandcodec_whitelist, and deploy a restrictivepolicy.xml. Roll out to a canary share of jobs first; rollback is a configuration revert. - Isolate: move workers into job-scoped containers with no egress, seccomp, read-only root and resource limits. Test with deliberately oversized and malformed sample files from your QA corpus.
- Fix identity: replace broad worker roles with pre-signed, per-object access and block metadata endpoints.
- Test continuously: add hostile samples (playlist files, oversized dimensions, unsupported codecs) to the pipeline's regression tests so a library upgrade or base image change that loosens policy is caught.
Decision guidance
| Situation | Recommended approach | Trade-off |
|---|---|---|
| Small product, low volume, one or two formats | Managed transcoding service or a single hardened container per job with strict allowlists | Less control over codecs; the provider's isolation becomes part of your trust boundary |
| Multi-tenant platform processing public uploads | Self-managed workers in microVM or user-space kernel sandboxes, no egress, per-job identity | Higher operating cost and cold-start latency |
| You must ingest remote streams (HLS, RTMP) from customers | Separate ingest tier with an egress proxy that allows only resolved public addresses; never in the same worker as uploads | Extra component to operate; DNS rebinding must be handled at the proxy |
| Images only (avatars, product photos) | Decode and re-encode to one output format with stripped metadata, using a library with a restrictive format policy | Loss of original metadata and some quality |
Detection and measurement
Log at the job level, not just the API. Useful signals:
- Outbound connection attempts from workers: with no-egress policies, any blocked connection, especially to link-local or internal ranges, is a high-fidelity alert.
- Unexpected protocols or formats: FFmpeg errors referencing protocols not on the allowlist or rejected demuxers show probing attempts.
- Crashes and kills: segmentation faults, out-of-memory kills and timeouts per input hash. A cluster of crashes from one uploader is worth investigating.
- Process anomalies: a worker spawning a shell or any binary other than the transcoder.
Metrics that show the programme is working:
- Percentage of media-processing workloads with all three FFmpeg allowlists and a non-default ImageMagick policy (target 100 percent).
- Percentage of workers with no network egress and no metadata endpoint access.
- Median time from an FFmpeg, ImageMagick or codec library security release to production rollout.
- Blocked egress attempts and policy rejections per week, with investigation outcomes.
Common failure patterns
- Trusting the file extension: FFmpeg and ImageMagick detect formats by content, so a file named
.mp4can be probed as a playlist or a different image format. Force the format and use allowlists. - Parsing on the web tier: calling ffprobe or ImageMagick inside the API process to validate uploads gives a decoder exploit the API's credentials and network access.
- Worker with the platform's full cloud role: one compromised job can read every customer's media. Per-job, per-object access prevents it.
- Policy lost in a rebuild: a new base image reverts
policy.xmlto the distribution default. Verify the active policy in CI. - No resource ceilings: a single file with extreme dimensions or duration exhausts a node and stalls the queue for everyone.
Framework mapping
| Framework | Reference | How it applies |
|---|---|---|
| NIST SP 800-53 Rev. 5 | SI-10 Information Input Validation; CM-7 Least Functionality; SC-39 Process Isolation; SC-7 Boundary Protection; RA-5 Vulnerability Monitoring and Scanning | Format and codec allowlists; disabling unused protocols and delegates; sandboxed workers; no egress; tracking library CVEs |
| NIST CSF 2.0 | PR.PS-01; PR.PS-02; ID.RA-01; DE.CM-09 | Hardened configuration; patching media libraries; recording vulnerabilities; monitoring worker runtime behaviour |
| CIS Controls v8.1 | Control 2 (Software Assets), 4 (Secure Configuration), 7 (Continuous Vulnerability Management), 16 (Application Software Security) | Inventory of parsers, secure configuration, patching, pipeline design |
| ISO/IEC 27001:2022 Annex A | A.8.8 Management of technical vulnerabilities; A.8.9 Configuration management; A.8.26 Application security requirements | Library patching, policy baselines, requirements for upload features |
| MITRE ATT&CK | T1190 Exploit Public-Facing Application; T1499 Endpoint Denial of Service | Exploiting a decoder through an upload feature; resource exhaustion |
| MITRE CAPEC / D3FEND | CAPEC-664 Server Side Request Forgery; D3-SCF System Call Filtering; D3-OTF Outbound Traffic Filtering; D3-FA File Analysis | Playlist SSRF; seccomp; no-egress workers; content inspection before processing |
Practitioner checklist
NHI Mgmt Group recommends the following checks.
- List every service that parses media and the identity, network access and library versions of each.
- Set
protocol_whitelist,format_whitelistandcodec_whiteliston every FFmpeg invocation that touches user files. - Deploy a restrictive ImageMagick
policy.xml(module allowlist, delegates and indirect reads disabled, resource limits) and verify it withmagick identify -list policyin CI. - Run parsing in short-lived, non-root, read-only workers with seccomp and no network egress.
- Apply CPU, memory, duration, pixel, stream and output size limits plus a hard timeout.
- Give workers per-object pre-signed access and block cloud metadata endpoints.
- Patch FFmpeg, ImageMagick, codec libraries and metadata tools on a defined timescale, and watch KEV for media library entries.
- Alert on blocked egress, crashes, out-of-memory kills and unexpected child processes from workers.
Standards and references
- FFmpeg: Protocols Documentation
- FFmpeg: Formats Documentation
- FFmpeg: Security
- ImageMagick: Security Policy
- OWASP: File Upload Cheat Sheet
- NIST NVD: CVE-2017-9993
- CISA: Known Exploited Vulnerabilities Catalog
- Docker: Seccomp security profiles for Docker
Related NHI Mgmt Group resources: Secure File Upload Handling Guide · SSRF Prevention Guide · Container Runtime Security Guide · PDF Security Guide