Reference: PDF fraud signals — the field guide
Every serious document-fraud product has a model of what a real document from a
given institution looks like. Resistant AI calls it MODEL_MATCHED.
Inscribe has TEMPLATES and FINGERPRINT. Snappt claims
2,000-plus institutions.
The idea is simple enough to be irritating. If you know a Chase statement is always produced by a particular composition engine, at a particular PDF version, with a particular font set, then a file claiming to be a Chase statement and produced by Microsoft Word is worth a second look.
I wanted that, and I did not want to build it on a guess. So I downloaded 213 PDFs from 24 US financial institutions' own domains and parsed every one.
The result is split, and the half that failed is the more useful half.
For documents banks publish on their websites — fee schedules, account agreements, privacy notices, brochures — there is no usable issuer fingerprint. Across the 17 profiles with at least five samples, zero reached 80% agreement on the producer string. One reached it on the font set. Bank of America's nine documents carry seven distinct producer strings. Marcus's twelve carry ten.
For the one genuine statement pipeline in the corpus, the fingerprint is sharp. Six Huntington account-analysis statements agree 6/6 on PDF version, cross-reference style, page size, revision count and linearisation, 5/6 on the exact font set, and 6/6 carry no producer or creator string at all.
So the premise holds — but only for statement output, which is exactly the class of document you cannot get from a bank's website. I obtained a real statement pipeline for one of the 27 institutions I tried.
None of what follows is a claim about US banking. It is a description of 190 specific files. I am publishing it mostly because the negative result is the actionable one: it tells you which document classes a fraud baseline must not be built from.
The caveat that governs everything else
A marketing or disclosure PDF is not evidence about a statement pipeline.
Banks compose customer statements on dedicated output-management systems that run unattended at volume. They compose fee schedules and brochures in desktop publishing tools, over fifteen years, using whatever the marketing team had.
These toolchains have nothing in common but a logo.
Which means: build a "Chase baseline" out of Chase fee schedules, test a genuine Chase statement against it, and you flag the genuine document. That is not hypothetical — it is what this corpus is mostly made of.
That is why a profile's identity is <issuer>-<documentKind>
and kinds are never merged. A profile named chase-disclosure announces
in its own name that it is a baseline for Chase disclosure PDFs and not for Chase
statements.
If that seems like an excess of caution, consider the alternative: a confident mismatch finding on a real customer's real statement.
What the corpus is
- 27 institutions attempted; PDFs obtained from 24 domains; 23 issuers with at least one US-entity sample.
- 213 files, all from the institution's own official domain. 23 from non-US arms (TD Canada, Amex Australia and Austria, Citi EMEA, BNY EMEA, USAA EU and UK) are excluded from every profile, which is what leaves 190.
- Nothing from a template site or a document-sharing site. No real person's statement was obtained, and none is needed — everything here is a published specimen or a public disclosure.
-
Every sample's source URL, size and SHA-256 is recorded, and a
--checkmode re-derives the whole data set from the files and fails if it has drifted.
Institutions I could not get usable data for: Wells Fargo statements (only generic site PDFs), Charles Schwab, SoFi, Chime, Goldman Sachs' institutional site, and TD Bank US — every TD PDF I found belonged to TD Canada.
Citi and American Express are represented by two and one US samples, which is not enough to say anything, and I am not going to.
Finding 1 — producer strings are not an issuer tell for published documents
The /Producer string names whatever wrote the PDF bytes. It is the
first thing anyone reaches for, and across 190 files it looks like this.
Per institution, the spread is the finding.
The font sets behave the same way. Across those same seven institutions the counts are 9, 11, 16, 8, 11, 9 and 6 distinct font sets — in three cases one per file.
Two Bank of America online-banking agreements were printed straight out of a
browser — Skia/PDF m130 and Skia/PDF m135, with creator
strings that are literally Chrome user-agent strings.
Chase serves card rate-and-fee tables produced by Apache FOP, a server-side XSL-FO renderer — a real automated pipeline — sitting alongside brochures out of Adobe InDesign. Two document classes, one issuer, nothing in common.
The implication is uncomfortable and worth stating plainly: a document whose producer is "Microsoft Word" or "Chromium" is not, on that basis, anomalous. Real banks publish such files constantly. If you have seen a fraud vendor imply otherwise, that is the claim to press on.
Producer strings become evidence only when the baseline is drawn from the same
document class — and, as Finding 3 shows, then they become good evidence. The
metadata forensics guide covers what
/Producer and /Creator are specified to mean, and where
each one is written twice.
Finding 2 — font sets are near-unique per document, not per issuer
I expected the font set to be one of the strongest tells, on the theory that fonts follow from a template.
In this corpus they mostly follow from the document. 190 samples yield 167 distinct font sets, of which only 12 occur more than once. The top-ranked font set reaches 80% of samples in exactly one profile out of 17 — the Huntington statements.
Brand fonts do show up, and they are genuinely distinctive when they do:
PNCBrand, DiscoverSans, CitiSans,
Connections (Bank of America), Optimist (Capital One),
M&TBalto, Huntington_Apex. A file claiming to be from
Capital One with no trace of Optimist is a reasonable question to ask.
But the brand font appears alongside a different supporting cast in almost every file — whatever the author's machine had — so the joined set almost never repeats. And it is the joined set you would have to match on.
Finding 3 — the one real statement pipeline is sharply consistent
Huntington publishes six sample account-analysis statements — commercial treasury statements, not consumer checking — as raw pipeline output rather than a redrawn mock-up. Somebody exported them from the real system and put them on the website.
The huntington-statement-specimen profile
producer (none — 6/6 carry no producer string)
creator (none — 6/6)
pdfVersion 1.6 6/6
xrefStyle stream 6/6
pageSize 612x792 6/6
revisionCount 1 6/6
linearized true 6/6
signed false 6/6
fontSet EX_CFF_Arial|EX_CFF_Calibri|EX_CFF_CalibriBold|
EX_CFF_Huntington_Apex_Medium|Helvetica|ZapfDingbats 5/6
Two things stand out.
The complete absence of producer and creator metadata is itself the
signature. High-volume composition engines frequently emit none; nearly
every desktop tool announces itself by name and version. An empty
/Producer is not a null result here, it is a positive one.
The EX_CFF_ font-name prefix is an artefact of the composition
engine's font handling, not of any font a human chose. Neither would be
reproduced by accident by someone building a fake statement in Word or a browser.
That is what a usable baseline dimension looks like: stable within the issuer, and not reproducible without the issuer's toolchain.
One institution, one statement product. It does not generalise. But it is a concrete existence proof that the signal this feature depends on is real where it matters.
Finding 4 — customer-communication platforms are visible, and distinguishable
A handful of samples were produced not by desktop software but by customer-communications management platforms — the same category of tooling that generates statements.
- PNC:
Smart Communications - SC33/SC31, on four consumer documents. - USAA:
xPression 4.6SP1, an OpenText composition engine. - Wells Fargo:
Crawford Technologies,OAT. - Navy Federal:
Accessibil-IT Inc., an accessibility-remediation post-processor.
These strings are rare in general-purpose PDFs and hard to produce accidentally. PNC is also the tightest of the seven institutions above — three producer strings across eight samples, against KeyBank's eight across eight.
Where a bank routes even its disclosures through this tooling, its published documents sit structurally closer to its statements than a typical brochure does. I have no statement from any of those institutions to confirm it against, so treat that as a hypothesis with a clear test rather than a result.
Finding 5 — what is uniform is uniform because it is uniform everywhere
Among the 17 profiles with five or more samples, this is how many reach 80% agreement on each dimension.
| dimension | profiles reaching ≥0.8 |
|---|---|
| signed | 17 / 17 |
| pageSize | 11 / 17 |
| linearized | 10 / 17 |
| revisionCount | 9 / 17 |
| xrefStyle | 5 / 17 |
| pdfVersion | 3 / 17 |
| fontSet | 1 / 17 |
| producer | 0 / 17 |
| creator | 0 / 17 |
Not one of the 190 documents is digitally signed.
signed is perfectly consistent and therefore worthless as an issuer
discriminator: everyone matches it, forgeries included. pageSize is
mostly US Letter — 612×792 in 152 of 190 — for the same reason.
This is the trap in the whole approach, and it is easy to fall into if you grade yourself on agreement scores. A dimension stable across all issuers carries no issuer information.
The useful ones are stable within an issuer and varying between them. In this corpus only the Huntington statement set demonstrates that pattern.
One more number, because it contradicts something people repeat: 142 of 190 documents are single-revision, but 48 are not, and one carries 24 revisions. Multiple revisions in a genuine, published, bank-originated PDF are ordinary.
If you are counting %%EOF markers and treating two as suspicious, this
corpus is your counterexample set. The
edited-PDF guide explains the two directions that
count is wrong in, and what to walk instead.
What this data can and cannot support
It can support:
- That issuer structural baselines are viable for statement output.
- Calibration of what normal looks like for published bank PDFs, useful mainly for suppressing false positives on documents that are not statements at all.
- A concrete argument that baselines must be built per document class, and named that way.
It cannot support:
- Any claim of the form "Bank X's statements use producer Y" for any bank other than Huntington's account-analysis product — and not for Huntington's consumer statements either.
- Percentage claims about US banking. 23 non-randomly sampled issuers is not a population.
- Detection thresholds. With 16 of 17 eligible profiles failing the consistency gate on producer, the honest engine behaviour is to produce no expectation at all, which is what it does.
What we shipped, given all that
The issuer-baseline feature is live and labelled experimental, disabled as a production default.
Of 56 profiles, 39 sit below the minimum-samples threshold and can only ever report
unknown. Of the 17 that clear it, 16 are built from disclosure,
marketing and form documents whose internal consistency is too low to produce a
mismatch on the dimensions that matter — so they will also, correctly, stay quiet.
The operational value of this data set today is close to exactly one profile.
That is a strange thing to ship. The reason it shipped anyway is that the parser, the build script and the provenance format are the scaffolding for the data set that will actually work: tenant-supplied known-good statements.
If you hold originals — a lender with a year of real statements from the banks you actually see — those are the documents this was designed for, and a baseline built from them has none of the problems above. Structurally it is the same operation as comparing two files you both trust, run across a set instead of a pair.
That path is on the paid plans, and it is the only version of this feature I would defend.
The limits you should hold this against
A cleanly re-generated PDF launders every signal described here.
Print-to-PDF or save-as rewrites the byte structure; the revision chain, the
/ID pair, the producer string and the font-subset tags all come out
coherent and fresh. There is no version of this technique that survives that, and
anyone telling you their version does is selling you something.
Nobody has measured what fraction of real document fraud structural analysis catches. The only public figure I know of is one vendor's unmethodologised "80%". Every academic benchmark I could find is pixel-level image forensics; there is no PDF-structure equivalent. The defensible claim is high precision, unknown recall, and I am not going to put a number on the second one.
This reports signals, never a verdict. "The producer string does not match this issuer's baseline" is a statement about bytes. "This document is fraudulent" is a statement about a person, and it is not one a parser gets to make.
Run the same parser on a file of your own
Every number above came out of the parser that runs on the free checker. Drop in a PDF and you get the same report this corpus was built from — producer and creator strings, the revision chain, the font sets, page geometry. No account, nothing stored.
Everything in this post traces to a specific file with its source URL and SHA-256. If you hold a corpus of genuine statements from an institution and want to know whether it produces a usable baseline, that is the same measurement, run against documents you supply.