How to Threat Model for PCI DSS v4.0 (Worked Example)

PCI DSS never asks for a document called a threat model. But since v4.0, several of its requirements ask for things a threat model produces: a data-flow diagram of every path the card number takes, a scope you re-confirm every year, defences against attacks on your own code, and a written risk analysis behind every control whose frequency you choose yourself. Teams that build these separately end up with four documents that disagree with each other. Teams that build one threat model get all four from the same diagram.

This guide builds that model for one system, a fintech app that takes card payments, and shows what each step produces for your assessor. Along the way it covers the decision that matters most for a PCI programme's cost: where the card number goes, and how a small design change can take most of your services out of scope.

The short version: draw a data-flow diagram that marks every flow carrying the primary account number (PAN), then draw the cardholder data environment (CDE) around every component those flows touch. Redesign to shrink it where you can. Build attack trees for the outcomes that matter (stolen card data and stolen money), score each threat, and map each control to the PCI DSS requirement it supports. Reuse the same threats and likelihood reasoning to write your targeted risk analyses, and re-run the model whenever you re-confirm scope.

Where a threat model meets PCI DSS v4.0

These are the requirements a threat model feeds directly. The requirement numbers are from PCI DSS v4.0.1, the current version, whose future-dated requirements have been mandatory since 31 March 2025:

RequirementWhat it asks forWhat the threat model supplies
1.2.4Accurate data-flow diagrams showing all account data flows across systems and networksThe DFD itself, with PAN flows marked
12.5.2Scope documented and confirmed every 12 months and after significant change, including every place account data is stored, processed or transmittedThe CDE boundary, drawn from the flows rather than from memory
6.2.4Engineering techniques that prevent common attacks on bespoke software, including attacks on business logic and access controlThe threats against your own code, with the control for each
12.3.1A targeted risk analysis for each requirement whose frequency you chooseThe assets, threats and likelihood factors each analysis needs
12.3.2A targeted risk analysis for each requirement met with the customized approachThe same, for a control you designed yourself

If you build payment software for other companies, the PCI Secure SLC Standard goes further: it has a control objective dedicated to identifying and mitigating threats to your software, and assessors expect to see that done for each release.

The worked example: a fintech app that takes card payments

Our fintech lets customers top up a wallet with a card and pay out to their bank account. A mobile and web app sends payment requests through an API gateway to a payment service, which handles authorization, capture and posting to a double-entry ledger. A tokenizer swaps each card number for a token and keeps the mapping in an HSM-backed token vault, so the ledger and everything after it only ever see tokens. The acquiring bank authorizes the payment and sends status updates back by webhook. A support team uses an internal console to look up customers' payments.

Step 1: Draw where the card number goes (Requirement 1.2.4)

Start with a data flow diagram: every component, every flow between them, and a label on each flow saying what it carries. For PCI, the label that matters is whether a flow carries account data: the PAN, or sensitive authentication data such as the CVV, which must never be stored after authorization (Requirement 3.3.1), not even encrypted.

Then follow the PAN past the obvious flows. A component that handles it also tends to write it to places nobody drew: the gateway's request logs, the payment service's error traces, an analytics event, a support ticket where a customer pasted their card number. Each of those is a place account data is stored, and 12.5.2 asks you to find them, including the ones outside the CDE you thought you had.

Step 2: Draw the CDE boundary, then shrink it (Requirement 12.5.2)

The CDE is every system that stores, processes or transmits account data, plus anything that could affect its security. Draw it as a trust boundary around every component a PAN flow touches. Our first draft of the payment flow, panel A below, puts the card number through the gateway and the payment service on its way to the tokenizer, so all four sit inside the CDE, along with their logs, hosts, deploy pipelines and the people who administer them.

Two versions of the same card payment flow. Panel A, the first draft: the cardholder sends the PAN and CVV to the API gateway, which passes them to the payment service; the payment service exchanges the PAN for a token at the tokenizer, which stores the mapping in the token vault, and sends the authorization with the PAN to the acquirer. Only tokens go to the ledger database. The cardholder data environment encloses the gateway, payment service, tokenizer and vault. Panel B, the redesign: the card form posts the PAN and CVV straight to the tokenizer through hosted card fields, while the app sends only a token and the order to the gateway and payment service. The payment service asks the tokenizer to authorize a token, and the tokenizer sends the authorization with the PAN to the acquirer. The cardholder data environment now encloses only the tokenizer and vault, and the payment service is marked as in scope as a connected-to system.
The same payment flow before and after moving card entry to the tokenizer. Red flows carry the card number; everything they touch is in the CDE.

Panel B changes one thing: the card form is served by the tokenizer as hosted fields, so the card number goes straight from the customer's device to the tokenizer, and the app gets a token back. The gateway and payment service only see tokens. When the payment service needs an authorization, it asks the tokenizer, which swaps the token back for the PAN and sends it to the acquirer itself. The CDE shrinks to the tokenizer and the vault.

Scope doesn't vanish, though. The payment service can still trigger an authorization through the tokenizer, so it stays in scope as a connected-to system. What drops out is everything downstream that only stores tokens: the ledger, its database, the payment service's logs. The page that embeds the hosted fields also stays relevant, because a script on that page could replace the real fields with a fake form. Agree the result with your QSA before you rely on it.

Step 3: Build attack trees for the outcomes that matter

A fintech has two outcomes worth an attack tree each: someone steals card data, and someone steals money. PCI DSS cares about the first, but the second is the one that empties customers' wallets, and a threat model that only follows the standard would miss it.

Move funds without authorization. An OR gate splits into two paths. Account takeover is an AND gate: the attacker needs the customer's password, from credential stuffing, and a way past the SMS one-time code, such as a SIM swap. Forged webhook is the other AND gate: the webhook endpoint accepts unsigned payloads, and the attacker sends a fake "payment succeeded" event so a top-up that never happened gets credited to the wallet.

Steal cardholder data. Here an OR gate gives three paths. The attacker can skim the card form as customers type, harvest PANs from places they leak to, such as logs or the support console, or break into the CDE and use a credential that can turn tokens back into PANs. Skimming has real precedent: in 2018, attackers changed a single script on British Airways' website and sent the card details of hundreds of thousands of customers to their own server, and the same year a compromised third-party chat widget on Ticketmaster's payment pages did the same thing. The UK's data protection regulator fined the two companies £20 million and £1.25 million.

Ten threats come out of the two trees. Scores use a 5×5 risk matrix, rated against the first-draft design with only the controls you can verify today:

ThreatWhereLikelihoodImpactScore
R1 Credential stuffing on customer loginGateway3515
R2 SIM swap defeats SMS one-time codesCustomer account3412
R3 Webhook endpoint accepts unsigned payloadsPayment service3515
R4 Forged "payment succeeded" event credits a walletPayment service3515
R5 Malicious script skims the card formCheckout page3515
R6 PAN written to gateway or application logsGateway, payment service3412
R7 Stolen service credential calls the detokenize APITokenizer2510
R8 Support agent views or copies full PANsSupport console248
R9 Network route from the corporate network into the CDECDE boundary2510
R10 Vulnerable third-party library in the payment servicePayment service2510

Write down why each likelihood was chosen. "The checkout page loads three third-party scripts, and nobody is alerted when one changes" is a reason an assessor can check. A bare "3" isn't, and step 5 reuses these reasons.

Step 4: Map controls to requirements and re-rate

For each threat, choose controls, name the PCI DSS requirement each one supports, and re-score. The team's appetite is a score of 6 or below; anything above that needs an owner to accept it:

ThreatControlsPCI DSS v4.0Score before → after
R1Rate limiting and bot detection on login; breached-password checks; step-up authentication before adding a payout accountNone: Req 8 excludes cardholder accounts15 → 5
R2Passkeys or in-app approval for payouts; SMS kept only as a fallback for customers who can't use eitherNone12 → 8, accepted
R3Verify the acquirer's HMAC signature with a constant-time comparison; reject old timestamps and replays6.2.415 → 5
R4Treat a webhook as a hint only: confirm the payment status with the acquirer's API before crediting, with idempotency keys6.2.415 → 5
R5Card fields hosted by the tokenizer; inventory of every checkout-page script with its justification and integrity check; alerts on changes to scripts and security headers6.4.3, 11.6.115 → 5
R6The redesign keeps the PAN out of the gateway and payment service; PAN-pattern redaction in the logging library; scheduled scans of log storage for card numbers3.5.1, 12.5.212 → 4
R7Only the tokenizer can detokenize; workload identity instead of static secrets in config; alerts on unusual detokenize volume7.2.5, 8.6.2, 10.4.110 → 5
R9CDE in its own network segment, reachable only from the payment service on one port; segmentation tested at least yearly1.3.1, 11.4.510 → 5

Three rows are worth a closer look. R1 and R2 map to no PCI DSS requirement, because Requirement 8 covers access to your systems, not your customers' accounts. They stay in the model anyway, because account takeover is how money actually leaves a fintech; their controls map to your fraud programme instead. R2 stays above appetite: the fallback exists for customers without a smartphone, so the head of fraud signs it off as accepted residual risk, and a date to revisit it once passkey adoption has been measured.

R6 shows what the redesign buys. Redaction and log scanning would bring the score down on their own, but they depend on a pattern match catching every format a card number can appear in. Keeping the PAN out of those services removes the problem rather than filtering it. R8 and R10 follow the same pattern as the rows above: the support console shows only the last four digits (3.4.1), and the payment service's third-party components are inventoried, with critical patches applied within a month (6.3.2, 6.3.3).

Step 5: Write targeted risk analyses from the same model (Requirement 12.3.1)

Several PCI DSS v4.0 requirements let you set how often a control runs, as long as a targeted risk analysis justifies the choice: how often to scan for malware on systems not at risk from it (5.3.2.1), how often to review logs of lower-risk components (10.4.2.1), or how often to check payment pages for tampering (11.6.1). Each analysis must name the assets, the threats, the factors that make the threat more or less likely and the resulting frequency, and be reviewed at least every 12 months.

That's the same information the threat model already holds. Here's the analysis for 11.6.1, which defaults to a check at least once every seven days:

ElementTargeted risk analysis for 11.6.1
Asset protectedThe checkout page that embeds the tokenizer's card fields, its scripts and its security headers
ThreatR5: a changed or injected script captures or redirects card data
Likelihood and impact factorsSeveral deployments a day; three third-party scripts; skimming groups actively target checkout pages; one undetected day could expose every card entered that day
ResultCheck on every deployment, plus continuous alerts from the Content Security Policy's violation reports and an hourly comparison of script hashes. More often than the weekly default, because a weekly check would leave up to seven days of exposure
ReviewEvery 12 months, and whenever a third-party script is added to the checkout page

A targeted risk analysis can justify running a control more often than the default as well as less often. Either way, the assessor looks for the reasoning, and the threat model is where that reasoning already lives.

Step 6: Keep it current

PCI DSS gives you the schedule. Re-confirm scope at least every 12 months and after any significant change (every six months if you're a service provider), and review each targeted risk analysis yearly. Use those dates to re-run the threat model, and run it between them when a change touches a PAN flow: a new payment method, a new third-party script on the checkout page, a new service that can call the tokenizer. Our guide on when to re-threat model lists the other usual triggers.

Where PCI DSS threat models go wrong

  • Scope drawn from the network diagram. A network diagram shows where systems sit, not where the PAN goes. Logs, analytics events and support tools only show up when you follow the data.
  • Tokens treated as out of scope by default. Anything that can turn a token back into a PAN, or reach the systems that can, stays in scope.
  • Threats mapped to the standard instead of the system. Account takeover and forged webhooks move money without touching a card number. A model built from the requirement list misses them.
  • Webhooks trusted as the source of truth. A payment status you didn't fetch yourself is a claim, not a fact.
  • Targeted risk analyses with no threats in them. A frequency chosen because it was convenient, with no named threat or likelihood factor, won't survive an assessor's questions.
  • Scope confirmed once, at assessment time. The CDE changes with every deploy that touches a PAN flow, not once a year.

Frequently asked questions

Does PCI DSS require threat modeling?

Not by that name. PCI DSS v4.0 requires an accurate data-flow diagram of account data (Requirement 1.2.4), scope confirmed at least every 12 months (12.5.2), defences against common software attacks in your own code (6.2.4) and targeted risk analyses (12.3.1 and 12.3.2). A threat model is the most direct way to produce all of them. Payment software vendors assessed under the PCI Secure SLC Standard also have a control objective dedicated to identifying and mitigating threats.

Does tokenization take systems out of PCI DSS scope?

It can shrink scope. Systems that only ever see tokens, can't retrieve the PAN and have no connection into the cardholder data environment may fall out of scope. The tokenization system, anything that can turn a token back into a PAN, and systems connected to the CDE stay in scope. Your QSA confirms the final scope.

What is a targeted risk analysis in PCI DSS v4.0?

Requirement 12.3.1 asks for a documented analysis for each requirement that lets you choose how often a control runs. It names the assets protected, the threats, the factors that drive likelihood and impact, and the resulting frequency with its justification, and it's reviewed at least every 12 months. Requirement 12.3.2 asks for one for every requirement you meet with the customized approach.

Does PCI DSS Requirement 8 apply to customer accounts?

No. Requirement 8 covers access to system components and explicitly excludes accounts used by consumers (cardholders). Customer account takeover still belongs in your threat model because it moves money, but its controls map to your fraud programme, and in the EU to PSD2 strong customer authentication, rather than to a PCI DSS requirement.

How often should a PCI DSS threat model be updated?

At least as often as you confirm scope: every 12 months and after any significant change to the in-scope environment, or every six months for service providers. Review it as well when the annual review of a targeted risk analysis finds that its threats or likelihood factors have changed.

Moving money in the EU? DORA applies to most financial entities alongside PCI DSS, and the same threat model supplies its ICT asset map and risk assessment. For how both standards map to ThreatTree, see threat modeling for fintech.

Build your PCI DSS threat model in ThreatTree

Draw the payment flow as a data flow diagram with the CDE as a trust boundary, build an attack tree for each outcome, and keep a risk register with controls and owners, free. On Pro, the Fintech module classifies data stores as cardholder data, tags snapshots with a release version, and puts PCI DSS v4.0 and DORA at the top of the standards picker, so each mitigation is mapped to the requirement it supports. The Fintech Payment Flow template gives you a tokenized payment flow with its CDE boundary, the fund-movement attack tree and its four scored risks to start from.

Get started free See the industry templates →

Not ready to sign up? Get new threat-modeling guides by email instead.