Can a PDF contain malware? How to check before you open it

The four markers that make a document active instead of static, how to read them by hand, and where a byte-level check ends and an antivirus begins.

Yes — a PDF is not a passive picture of a page. The format can embed JavaScript, declare actions that run automatically on open, ask the reader to launch external programs, and carry entire files inside itself. Every one of those capabilities announces itself in the file's object dictionaries, which means you can read what a document intends to do before any reader executes it. This page shows the manual check, what each marker means, and the automated version.

Scope, stated plainly. A structural check reads what the document asks its reader to do. It does not decode payloads, emulate JavaScript or match virus signatures — that is an antivirus's job, and the two checks answer different questions. "This bank statement carries JavaScript and an auto-run action" is a fact worth acting on regardless of whether any scanner recognises the payload.

1. How a PDF becomes active

Four dictionary keys turn a static document into one that does things:

  • /JavaScript and /JS — embedded script, run by readers that support it (Acrobat does; most browser viewers heavily restrict it).
  • /OpenAction — an action executed automatically the moment the document opens, before the user does anything. The classic delivery mechanism is an OpenAction pointing at embedded JavaScript.
  • /Launch — a request to run an external program or open another file. Modern readers warn or refuse, but the intent is recorded in the file either way.
  • /EmbeddedFile — a complete file carried inside the PDF, of any type, extractable by the reader.

Historically, PDF exploits have also lived in malformed structures — fonts, images and streams crafted to crash a specific parser. That is exactly why nothing in this page involves opening the file: every check below reads bytes without rendering them.

2. Check by hand with pdfid

The standard first-look tool is Didier Stevens' pdfid, a small Python script that counts the interesting names in a file without parsing — deliberately, so a malformed document cannot exploit the tool that inspects it:

A file that does things when opened

$ pdfid.py statement.pdf
 /JS                    1
 /JavaScript            1
 /OpenAction            1
 /Launch                0
 /EmbeddedFile          0

Non-zero counts for /JS, /JavaScript, /OpenAction or /Launch are your cue to look closer (Stevens' companion pdf-parser.py extracts the actual objects) — or simply to not open the file outside a sandbox. One caveat comes with the counting approach: pdfid matches name strings anywhere in the raw bytes, so a PDF about JavaScript — this page printed to PDF, say — can trigger counts from its own prose. Position matters: a marker only means something in a real dictionary-key position, which is what a structural parser checks.

3. What each marker means in a document workflow

Context decides severity. In a form distributed by a tax authority, JavaScript is furniture. In a bank statement, a payslip or a proof of address — documents that are generated, printed-equivalent artifacts — there is no legitimate reason for any of the four markers to exist. A statement that carries an /OpenAction is anomalous twice over: something added active content, and it did so to a document class that never carries it. The question "is this PDF dangerous?" and the question "was this PDF tampered with?" overlap here: active content in a generated document is evidence something other than the generator wrote to the file.

4. Benign causes — most active PDFs are legitimate

Interactive forms validate fields with JavaScript. Government and insurance PDFs auto-fill dates on open. E-invoice standards (ZUGFeRD, Factur-X) attach machine-readable XML as embedded files — an /EmbeddedFile is the point of the format. Portfolios carry whole documents inside a cover PDF. Which is why a counter of markers is a screening tool, not a verdict: the finding is "this document asks its reader to run things", and whether that is normal depends entirely on what the document claims to be.

5. What an antivirus adds, and when to use one

A structural check tells you a payload exists; a scanner tries to tell you whether the payload is known to be hostile — signature matching, heuristics, sometimes detonation in a sandbox. For a file you personally received and intend to open, VirusTotal runs seventy-odd engines and is the right second step (with the obvious caveat that uploading confidential documents to a public multiscanner shares them). For a pipeline receiving customer documents, the two layers answer different business questions: the scanner protects your infrastructure; the structural check protects your decision — it flags the statement that carries script as not being the artifact a bank generates, whether or not the script matches any signature.

6. Screening uploads automatically

Tamperlens reports active content as one of its ten signal families: active-content fires on JavaScript, OpenAction, Launch actions and embedded files, with the object positions as evidence. Detection is scoped to genuine dictionary-key positions in parsed objects — a /JS that merely appears inside a page's text does not fire, which removes the false-positive class that raw byte counting carries.

The engine never executes or opens anything it inspects: documents are parsed in memory as bytes, no JavaScript runs, no embedded file is extracted, nothing is written to disk. The same POST returns the other nine families — revision history, metadata mismatches, signature coverage — so an upload pipeline gets the security screen and the tampering screen in one report.

Check a PDF now, free

Drop a file into the checker on the home page — no account, nothing stored, full report including active content. When you want it in your pipeline, an API key with 25 documents a month is free: see the API quickstart or create an account.

Tamperlens reports risk signals, not authenticity verdicts — and it is not an antivirus. Signals can have benign causes; combine them with your own decision logic.

Related reading