PDFPipe

Document security / What is inside it

Files embedded inside a PDF, and what they can carry

A PDF can contain other files inside it, which is useful for machine-readable payloads and is a way for content to travel without being visible.

The exposure

The format supports embedding arbitrary files inside a document. That is deliberate and useful: an electronic invoice can carry a structured data file alongside the human-readable page, so one artefact serves both a person and a system. It also means a document can contain content that no page displays, which matters in two directions. A document you receive may carry something you did not expect, and a document you produce by transforming another may carry an embedded file forward without anybody noticing.

The decisions

Reasons rather than a description of the code. Each one has a default that is fine for a page and wrong for a file somebody keeps.

  • Know whether your documents carry embedded files, by inspecting a real one rather than assuming. Producing them is deliberate and inheriting them is not.
  • Where a document is built from an existing PDF, check what came along. Embedded files survive operations that only appear to touch the visible content.
  • Treat an embedded file in an inbound document as untrusted input, the same as any uploaded file, since nothing about it being inside a PDF makes it safer.
  • Where you embed deliberately, keep the embedded payload and the visible page consistent, because a machine reading one and a person reading the other must not disagree.
  • Know what this API does: there is no attachment or embedding option on the render, so embedding is a post-processing step in a tool you choose.
  • Include an embedded-file check in the same place you check metadata, since both are invisible on the page and both travel with the file.

In practice

A fragment, with the thing that goes wrong kept in a comment where it is the more instructive half.

js
// Inspect rather than assume: producing embedded files is deliberate,
// inheriting them is not.
const attachments = await listEmbeddedFiles(pdf);
if (attachments.length) {
  log.info("document carries embedded files", {
    documentId,
    names: attachments.map((a) => a.name),
  });
}

/* Two directions to worry about:

   inbound   a document you received may carry a file no page shows.
             Treat it as untrusted input, exactly like an upload. Being
             inside a PDF makes it no safer.

   inherited a document built by transforming an existing one can carry
             its embedded files forward. Operations that appear to touch
             only the visible content frequently preserve them.       */

// The check belongs next to the metadata check: both are invisible on
// the page, both travel with the file.
test("documents carry no unexpected embedded files", async () => {
  const pdf = await renderFixture("invoice", "typical");
  expect(await listEmbeddedFiles(pdf)).toHaveLength(0);
});

/* There is no attachment or embedding option on this render, so a
   deliberate embed is a post-processing step in a tool you choose. If
   you do it, keep the payload and the visible page consistent: a
   machine reading one and a person reading the other must not
   disagree about the amount.                                         */

What people do instead

Assuming a PDF is only what it displays. It is a container, and content inside it that no page shows is still content that was delivered, which matters both when receiving documents and when transforming them.

How this is found out

By inspection, or by a security tool scanning attachments. It is invisible in every normal interaction with the document, including a careful visual review of every page.

Frequently asked

Does this page tell me what the law requires?

No, and deliberately not. Retention periods, erasure obligations and residency rules vary by jurisdiction, by industry and by the kind of document, and they change. What these pages describe is the shape of the problem and the mechanisms a system needs in order to implement whatever answer your own advisers give you. Where a genuine tension exists, such as an immutable record against a right to erasure, it is named as a tension rather than resolved.

Why is so much of this about the contents rather than access control?

Because access control decides who can obtain a copy and has no opinion at all about what happens to the copy. Once a document is on somebody's laptop, forwarded to a colleague or printed, every control listed here has already stopped applying to it. What is inside the file is therefore the part that keeps mattering, which is the opposite of the balance you would strike for a page.

How much of this applies at a small volume?

Most of it, because these are decisions rather than infrastructure. Redacting by omitting rather than covering costs nothing. Deciding what goes on a template costs one review. Classification is one object in code. Legal hold and an audit trail are the two that take real work, and both are far cheaper to build before they are requested than under the deadline that comes with the request.

Related security topics

The decisions that depend on each other, then the rest of the same group.

Most of these are decisions rather than features, and the cheapest time to make them is before the first document is delivered rather than after one reaches the wrong person.