security guide

How to Safely Convert Untrusted Markdown to PDF

Secure a Markdown-to-PDF backend with HTML sanitization, CSP, network restrictions, SSRF protection, timeouts, and output validation.

Convert Markdown to PDF

To safely convert user-provided Markdown to PDF, sanitize the rendered HTML, restrict browser requests, block unsafe URL schemes and private network targets, enforce a content security policy, limit time and file size, isolate each job, and validate the final PDF.

Markdown is plain text, but a Markdown renderer can produce HTML and trigger browser behavior. Once conversion happens in headless Chromium, untrusted content becomes a browser-security and network-security problem.

For the full conversion architecture, read the Markdown-to-PDF guide.

Why is user-provided Markdown dangerous?

Markdown may contain or generate:

  • raw HTML;
  • <script> elements;
  • inline event handlers such as onerror;
  • javascript: URLs;
  • remote images;
  • local file URLs;
  • localhost or private-network targets;
  • data URLs;
  • embedded SVG;
  • extremely large resources;
  • malformed content designed to consume CPU or memory.

Even when scripts are disabled, remote resource loading can expose internal services or leak metadata.

What does the md-to-pdf project warn about?

The official md-to-pdf README tells integrators to sanitize Markdown content they do not control. It also warns that the temporary local server used by the process can expose readable files in the served folder to the local network for the duration of the process.

This is an important comparison point: a flexible CLI can be safe in a trusted local workflow, while a multi-tenant backend needs an explicit security layer around parsing, browser requests, storage, and artifacts.

Where should sanitization happen?

Sanitize after Markdown has been rendered into HTML, because that is the structure the browser will execute and display.

A safe pipeline is:

Markdown source
→ protected math/code handling
→ Markdown parser
→ generated HTML
→ allowlist sanitization
→ controlled document template
→ CSP
→ restricted browser
→ PDF

Sanitizing only the raw Markdown can miss dangerous HTML generated or transformed by plugins.

What should an HTML allowlist contain?

Allow only tags and attributes needed for visible document content.

Possible safe categories:

  • headings and paragraphs;
  • lists;
  • blockquotes;
  • tables;
  • code and preformatted text;
  • links;
  • images under URL policy;
  • details and summary;
  • kbd, sub, and sup;
  • figure and figcaption.

Remove:

  • scripts;
  • event-handler attributes;
  • dangerous URL schemes;
  • arbitrary inline styles;
  • iframes unless there is a separately designed sandbox policy;
  • form controls that have no purpose in a static PDF;
  • unsupported embedded objects.

SolConverter currently keeps only narrowly defined table-cell alignment styles and removes arbitrary style declarations.

How should image URLs be controlled?

Images are the most common reason a renderer needs network access.

A strict policy can allow:

  • public https:// and optionally http:// image URLs;
  • approved data-image MIME types;
  • controlled object-storage URLs.

Block:

  • javascript:;
  • file:;
  • local paths;
  • localhost;
  • private IP literals;
  • link-local ranges;
  • unsupported schemes;
  • non-image browser requests.

SolConverter currently allows public image requests, supports valid data images, blocks local/private literal targets, and converts failed images into local placeholders.

Is blocking private IP literals enough to prevent SSRF?

No. It is a useful control, but production SSRF defense may also need:

  • DNS resolution checks;
  • protection against DNS rebinding;
  • redirect validation at every hop;
  • egress firewall rules;
  • metadata-service blocking;
  • hostname allowlists for sensitive deployments;
  • proxy-level policy;
  • network isolation for renderer workers.

SolConverter blocks private and local URL literals. That is one layer of SSRF defense, not a claim that the renderer provides a formally verified, SSRF-proof network architecture.

What should the content security policy do?

A renderer CSP should restrict:

  • script sources;
  • style sources;
  • fonts;
  • images;
  • connections;
  • frames and objects.

When MathJax, Mermaid, ZenUML, CSS, and fonts are bundled locally, the policy can be narrower than a renderer that loads third-party CDNs.

CSP is defense in depth. It does not replace HTML sanitization or browser request interception.

Why is browser request interception necessary?

CSP controls what the page is allowed to load, but request interception gives the renderer an enforcement point for each outgoing request.

The renderer can inspect:

  • resource type;
  • scheme;
  • hostname;
  • resolved address where implemented;
  • redirects;
  • content type;
  • size;
  • timeout.

SolConverter currently blocks browser requests that are not permitted image requests.

How should data images be handled?

Data images avoid network requests, but they still need validation.

Allow only intended MIME types, such as:

  • PNG;
  • GIF;
  • JPEG;
  • WebP;
  • SVG under a defined SVG policy.

Apply size limits. SVG can contain active or external content in some contexts, so sanitize or render it under a controlled policy.

What should happen when an image fails?

A missing image should not necessarily fail the document.

A safe fallback can show:

  • a neutral placeholder;
  • the original alt text;
  • no external request retry loop;
  • an internal diagnostic entry.

This preserves document flow and avoids indefinite waits.

How do timeouts and size limits improve security?

They limit resource exhaustion.

Apply limits to:

  • Markdown input size;
  • decoded data-image size;
  • number of images;
  • remote resource size;
  • total render time;
  • diagram render time;
  • final PDF size;
  • concurrent jobs per tenant.

A timeout should terminate or recycle the affected page/context so the next job starts cleanly.

How should raw HTML tables be supported safely?

Real documents often use rowspan, colspan, and alignment that standard Markdown tables cannot express.

A safe compromise is:

  • allow table-related tags;
  • allow only documented attributes;
  • coerce rowspan and colspan to bounded positive integers;
  • retain only safe alignment styles;
  • remove all unrelated CSS.

This provides useful layout without accepting arbitrary browser styling.

What does localized failure mean for security?

Localized failure is for recoverable content errors, not policy violations that could compromise isolation.

Examples:

  • malformed equation → source fallback;
  • invalid Mermaid → source fallback;
  • unavailable public image → placeholder.

Examples that should be blocked:

  • script execution;
  • private network request;
  • unsupported scheme;
  • oversized resource;
  • unauthorized artifact access.

The renderer can complete the document while still enforcing security boundaries.

Secure Markdown-to-PDF checklist

Input

  • [ ] Authenticate the user or service.
  • [ ] Authorize source and job access.
  • [ ] Validate extension and size.
  • [ ] Generate server-side identifiers.
  • [ ] Avoid trusting client MIME type alone.

HTML

  • [ ] Parse with a known Markdown configuration.
  • [ ] Sanitize generated HTML with an allowlist.
  • [ ] Remove event handlers and dangerous URLs.
  • [ ] Limit attributes and styles.
  • [ ] Treat SVG under an explicit policy.

Browser

  • [ ] Apply CSP.
  • [ ] Intercept requests.
  • [ ] Block local/private destinations.
  • [ ] Allow only required resource types.
  • [ ] Use isolated pages or contexts.
  • [ ] Reset document state.
  • [ ] Enforce timeouts.

Storage and artifacts

  • [ ] Use private storage by default.
  • [ ] Issue expiring access URLs.
  • [ ] Enforce tenant ownership.
  • [ ] Validate %PDF- signature.
  • [ ] Limit output size.
  • [ ] Define retention and deletion.

Operations

  • [ ] Log blocked resources without leaking secrets.
  • [ ] Monitor timeouts and browser crashes.
  • [ ] Patch browser and dependencies.
  • [ ] Test malicious fixtures.
  • [ ] Review egress policy.

Frequently asked questions

Is Markdown safe because it is plain text?

No. Markdown can generate HTML and trigger resource requests in a browser-based renderer.

Is sanitization enough?

No. Use sanitization, CSP, request restrictions, isolation, limits, and output validation together.

Should remote images be disabled?

Not always. They can be allowed under a public-image-only policy with timeouts and size limits.

Can failed images be skipped?

Yes. A placeholder with alt text is often better than a document-level failure.

Does a valid PDF signature prove the file is safe?

No. It confirms basic file type, not complete semantic or malware safety. Use deeper validation according to your threat model.

Next step

Build the renderer threat model before exposing an upload form. The policy must cover HTML, network access, browser lifecycle, storage, and artifact authorization.

Read how to build the full Markdown-to-PDF API workflow or try the Markdown-to-PDF tool.

References