Threat Modeling OTP Flows · Chapter 6 of 6
Scoring and documenting mitigations
A four-level severity rubric, applied the same way everywhere, turns a list of threats into decisions leadership can actually make.
Chapters 1 to 5 each ended with a small risk table using four severity badges: low, medium, high and critical. This chapter defines exactly what those badges mean, how to assign them, and how to document mitigations so the register remains useful a year from now. Consistency is the whole point. A register where one engineer’s high is another’s medium cannot be prioritised, and leadership will stop reading it.
The rubric
Severity combines impact — what happens if the threat succeeds — with likelihood — how easily a realistic actor can make it happen today. Pinecrest uses a deliberately small rubric, because small rubrics are applied consistently and large ones are argued about. Each threat gets an impact level and a likelihood level from one to three, and the product maps to a badge.
- Impact 3: account takeover with ability to change recovery details, exposure of health or financial records, or unbounded financial loss.
- Impact 2: read access to sensitive data, bounded financial loss, or service disruption for many users.
- Impact 1: minor data exposure, inconvenience, or bounded cost below an agreed threshold.
- Likelihood 3: commodity tooling exists and the attack needs only public information.
- Likelihood 2: requires targeted effort, a specific victim interaction or an insider.
- Likelihood 1: requires rare capability or several independent failures.
The mapping is fixed: scores of one or two are low, three or four medium, six high, and nine critical. Because impact and likelihood are each one to three, the only possible products are 1, 2, 3, 4, 6 and 9. That gap between four and six, and between six and nine, is a feature. It forces the room to decide whether a threat is genuinely in the next band rather than splitting the difference.
Inherent and residual
Every row carries two badges. Inherent severity assumes no controls specific to this threat beyond your baseline. Residual severity is the rating after the listed mitigations are in place and verified. The difference between them is the value of the mitigation, and the residual is the risk the organisation is consciously accepting. Showing both prevents a common failure: a register full of low ratings that silently assume controls nobody has built.
| Threat | Inherent | Mitigation | Residual |
|---|---|---|---|
| SIM swap → phone-only recovery | Critical | Second independent factor for recovery | Medium |
| AiTM relay → change recovery details | Critical | Passkey step-up for sensitive changes | Medium |
| Push fatigue → blind approval | High | Number matching, 3-prompt limit | Low |
| Pumping via unauthenticated signup | Critical | Allow-list, prefix breaker, spend cap | Low |
| TOTP replay within window | Medium | lastStep compare-and-set | Low |
| Seed table exfiltration | Critical | Envelope encryption, KMS-scoped decrypt | Medium |
The table above is Pinecrest’s consolidated register after this series, drawn from the earlier chapters plus two rows from series 1. It fits on one screen. Ilse Brandvold presents it quarterly with a single question for leadership: which residual ratings are we willing to accept until the next review, and which need investment?
Attacker view
How I read your register
- Rows rated low with no verified mitigation are my best targets.
- Mitigations with no owner are rarely maintained.
- Recovery and support rows reveal the weakest door.
- Stale review dates mean the flow changed since.
Defender view
What keeps it honest
- Residual ratings only count controls that are tested.
- Every row has a named owner and a review date.
- Changes to the flow trigger re-scoring of affected rows.
- Accepted risks are signed off by someone with budget authority.
Documenting a mitigation
A mitigation entry should let a new engineer verify that it exists and works. Write what the control is, where it lives, how it is tested, and what metric shows it is operating. “Rate limiting” is not a mitigation entry; “three-prompt limit per account per ten minutes in the IdP policy, covered by integration test push_limit_spec, with denied-push rate on the security dashboard” is.
mitigation: Prefix breaker on verify-rate collapse
where: send-service/breaker.ts, config: breaker.prefix
behaviour: if sent>50/h and verify<10% for prefix → block prefix, page on-call
tested: breaker.spec.ts (unit), quarterly game day (last: 2026-10-08)
metric: breaker_trips_total, dashboard “OTP abuse”
owner: W. Hartigan (SRE) · review 2027-01-12Badges used consistently
Across this site, the four badges always mean the same thing and always look the same: green for low, cyan for medium, red outline for high, solid red for critical. Use the same colours and words in your internal tooling, dashboards and incident reports. When someone sees a critical badge in a pull-request description, they should know it means impact three and likelihood three without opening the rubric.
A register that is small, consistent and owned is one of the highest-leverage documents a security team can maintain. It turns scattered fears into a ranked list, makes accepted risk explicit, and gives every later chapter on this site a place to plug in.
Scoring sessions work best with a small, fixed group. At Pinecrest the group is the flow owner, one security engineer, one SRE and one product manager. Each scores impact and likelihood independently on a sticky note before discussion, which prevents the most senior voice from anchoring everyone else. Disagreements of more than one level on either axis are discussed; smaller ones take the higher value. A full register of twenty to thirty rows takes about an hour this way.
Keep the history. When a row's rating changes, record the old and new values, the date and the reason. Over a year this becomes a record of how the threat landscape and your controls evolved, and it is invaluable when someone asks why a decision was made. It also lets you spot rows that have quietly drifted from critical to low without any new control being built.
Above all, keep the register visible. Link it from the runbooks, the architecture docs and the onboarding guide, so every engineer knows where the current risks are written down.
// bench check
0/3 correct · 0 answered
1. Impact 2, likelihood 3. Which badge?
2. What does the residual rating represent?
3. Why is “rate limiting” alone a poor mitigation entry?