Skip to content

APPT-659 Booking reference update for scaling and security#1168

Draft
jsed-nhs wants to merge 4 commits intomainfrom
APPT-659_booking_ref_update_3
Draft

APPT-659 Booking reference update for scaling and security#1168
jsed-nhs wants to merge 4 commits intomainfrom
APPT-659_booking_ref_update_3

Conversation

@jsed-nhs
Copy link
Copy Markdown
Contributor

@jsed-nhs jsed-nhs commented Nov 18, 2025

Overview

This PR introduces a new secure, deterministic, and human-readable booking reference format for much higher volume generation than the current format. It has been designed to prevent brute-force and sequential-guessing attacks while remaining operationally simple.

The current format for references collides after 1 million bookings against a site group, which is likely to be hit soon and especially in a pandemic traffic event. Despite the RNG part in the middle, this is only 1/100 chance of hitting every time so is not future proof.

Current Reference Format
SiteGroup (2) + RNG (2) + Sequence (6)
Displayed as: XX-XX-XXXXXX

Current format consecutive references: 04-61-000002, 04-30-000003, 04-58-000004

Proposed New Reference Format
PPYY (4) + PERM8 (8) + Luhn (1)
Displayed as: XXXX-XXXXX-XXXX

New format booking references that are all 'consecutive' in generation, but this is not obvious:

7625-96725-0462
7625-19588-2334
7625-42451-4203

  • PPYY: Partition key (4-day partition index 00–91 + 2-digit year), ensures predictable, time-based bucketing.
  • PERM8: 8-digit value derived from a coprime permutation of the sequence number, making references non-sequential externally.
  • Luhn check: 1-digit checksum for typo and junk filtering.

Benefits

  • Fast: extremely low per-ID generation cost.
  • Secure: unpredictable without key.
  • Scale: 200M+/week capacity for 100 years.
  • Human-readable & typo-checked.
  • Maintainable: rotation path ready, minimal moving parts.

Sequence Multiplier Derivation

  • Uses the HMAC secret (unique per env) and partition PPYY to derive a multiplier S deterministically.
  • This multiplier is then manipulated to be coprime with 10^8 by picking a last digit from {1, 3, 7, 9}.
  • Ensures a full unique bijection of the 100M possible values per partition.
  • Different partition keys generate different multipliers per environment, making the sequence multiplier secure as protected by a secret key.
  • Caches the derived multiplier per (HmacKeyVersion, PPYY) covering the partition-days plus overlap, so it isn't computed for every single booking ref generation.

Each booking generation after that is just:

PERM8 = (S * i) % 100_000_000 where S is the derived multiplier and i is the DB sequence number increment

Luhn Digit

  • A way of verifying if a provided booking reference is valid without any DB checks for ID existence.
  • Protects against guesswork attacks as not all booking refs of the format XXXX-XXXXX-XXXX are valid.
  • Can log out to Splunk logs to see if our APIs are being hit with attacks trying to find a valid ID.

Example: 4016-90927-6174 is valid, but 4016-90927-6175 is not despite it having the correct formatted string.

Key Management

  • Single key & version ("v1") for MVP, loaded from config and injected into the service.
  • Uses per-environment keys (Dev/Int/Stag/Prod) so a leak in one env does not affect others.
  • Potential future rotation: upversion to "v2" without changing the reference format; old partitions could continue using "v1" format.

Security

  • Large unique values (100M per partition) + partitioning gives ~200M unique references per week.
  • Partition Key format means pattern will last for 100 years before being reused and colliding.
  • Uses seqeunceNumber increment in cosmosDB so DB access is needed.
  • Unpredictable multiplier is derived from a HMAC secret key which means it protects against sequential/brute-force attacks.
  • Luhn check digit filters junk traffic early and protects against invalid references.
  • Per-environment key isolation improves security for Production.
  • Deterministic verification: known PPYY at generation allows multiplier derivation without a further DB lookup.

Tests

  • Prove uniqueness algebraically (multiplier coprime to 10^8 guarantees a bijection).
  • Full permutation tests use smaller modulus to validate logic efficiently.
  • Sample-based tests on real modulus for format + Luhn + uniqueness checks.
  • Unit test to demonstrate collisions between different HMAC keys (expected, not a flaw).

Checklist:

  • My work is behind a feature toggle (if appropriate)
  • If my work is behind a feature toggle, I've added a full suite of tests for both the ON and OFF state
  • The ticket number is in the Pull Request title, with format "APPT-XXX: My Title Here"
  • I have ran npm tsc / lint (in the future these will be ran automatically)
  • My code generates no new .NET warnings (in the future these will be treated as errors)
  • If I've added a new Function, it is disabled in all but one of the terraform groups (e.g. http_functions)
  • If I've added a new Function, it has both unit and integration tests. Any request body validators have unit tests also
  • If I've made UI changes, I've added appropriate Playwright and Jest tests
  • If I've added/updated an end-point, I've added the appropriate annotations and tested the Swagger documentation reflects the change

@jsed-nhs jsed-nhs marked this pull request as draft November 18, 2025 10:31
@jsed-nhs jsed-nhs force-pushed the APPT-659_booking_ref_update_3 branch from 0b06441 to 9c221f8 Compare November 19, 2025 12:36
commit 33e7069
Author: jsed-nhs <193929923+jsed-nhs@users.noreply.github.com>
Date:   Fri Oct 31 15:18:05 2025 +0000

    Add BookingRef V2 flag enabled/disabled tests to all the Booking Integration tests

commit ff85c28
Author: jsed-nhs <193929923+jsed-nhs@users.noreply.github.com>
Date:   Fri Oct 31 11:00:16 2025 +0000

    Update casing of Reference Hmac Key and add to vars, and tf settings

commit f5748ad
Author: jsed-nhs <193929923+jsed-nhs@users.noreply.github.com>
Date:   Thu Oct 30 21:26:06 2025 +0000

    Add ReferenceNumberHmacKey to dockercompose. TODO - add to env tf vars

commit a9630c4
Author: jsed-nhs <193929923+jsed-nhs@users.noreply.github.com>
Date:   Thu Oct 30 19:26:16 2025 +0000

    Add new flag to all env json files

commit 7b7bab4
Author: jsed-nhs <193929923+jsed-nhs@users.noreply.github.com>
Date:   Thu Oct 30 19:20:33 2025 +0000

    booking_reference removal from cosmosDB seeder and add creation if not exists on first use in an env

commit 8822fd4
Author: jsed-nhs <193929923+jsed-nhs@users.noreply.github.com>
Date:   Thu Oct 30 15:00:29 2025 +0000

    Add V2 Provider reference

commit 207aa69
Author: jsed-nhs <193929923+jsed-nhs@users.noreply.github.com>
Date:   Thu Oct 30 14:43:44 2025 +0000

    Limit regex matchTimeout to 100ms

commit 13ef7ad
Author: jsed-nhs <193929923+jsed-nhs@users.noreply.github.com>
Date:   Thu Oct 30 14:09:27 2025 +0000

    Rebase fix

commit e67686a
Author: jsed-nhs <193929923+jsed-nhs@users.noreply.github.com>
Date:   Thu Oct 30 14:05:27 2025 +0000

    Cache tests

commit 30b946e
Author: jsed-nhs <193929923+jsed-nhs@users.noreply.github.com>
Date:   Thu Oct 30 13:41:55 2025 +0000

    Add obsolete flags to V1 code so that it is marked for deletion in time

commit 56ec2f4
Author: jsed-nhs <193929923+jsed-nhs@users.noreply.github.com>
Date:   Thu Oct 30 12:46:16 2025 +0000

    Luhn invalid check logs warning

commit 1b3b968
Author: jsed-nhs <193929923+jsed-nhs@users.noreply.github.com>
Date:   Thu Oct 30 12:25:33 2025 +0000

    V1 IsValidBookingReference supported

commit 7a11ade
Author: jsed-nhs <193929923+jsed-nhs@users.noreply.github.com>
Date:   Thu Oct 30 12:15:38 2025 +0000

    Add tests in for V1/V2 reference use in BWS tests. Include a transition test where initial uses V1, and reschedule uses V2.

commit b44ac1c
Author: jsed-nhs <193929923+jsed-nhs@users.noreply.github.com>
Date:   Wed Oct 29 13:54:17 2025 +0000

    Booking reference generation behind feature flag

commit 424e9d4
Author: jsed-nhs <193929923+jsed-nhs@users.noreply.github.com>
Date:   Wed Oct 29 13:43:51 2025 +0000

    Readd current RefProvider and split into V1 and V2 namespaces

commit ea865bf
Author: jsed-nhs <193929923+jsed-nhs@users.noreply.github.com>
Date:   Tue Oct 28 14:23:39 2025 +0000

    Fix tests

commit 0fc1478
Author: jsed-nhs <193929923+jsed-nhs@users.noreply.github.com>
Date:   Tue Oct 28 12:11:35 2025 +0000

    Reapplied WIP

commit 3945324
Author: jsed-nhs <193929923+jsed-nhs@users.noreply.github.com>
Date:   Tue Oct 28 11:04:24 2025 +0000

    Booking Reference Docs

# Conflicts:
#	src/api/Nhs.Appointments.Core/Bookings/ReferenceNumberProvider.cs
#	src/api/Nhs.Appointments.Core/Sites/ISiteStore.cs
#	src/api/Nhs.Appointments.Persistance/Reference/ReferenceGroupCosmosDocumentStore.cs
#	src/api/Nhs.Appointments.Persistance/SiteStore.cs
#	tests/Nhs.Appointments.Api.Integration/Scenarios/Booking/BookingBaseFeatureSteps.cs
#	tests/Nhs.Appointments.Api.Integration/Scenarios/Booking/QueryBookingByNhsNumber.cs
#	tests/Nhs.Appointments.Core.UnitTests/ReferenceNumber/V1/ReferenceNumberProviderTests.cs
@jsed-nhs jsed-nhs force-pushed the APPT-659_booking_ref_update_3 branch from 86aefc8 to 011a648 Compare November 20, 2025 12:33
@sonarqubecloud
Copy link
Copy Markdown

@jsed-nhs jsed-nhs changed the title APPT-659 Booking reference update for scaling and security (folder rebase) APPT-659 Booking reference update for scaling and security Dec 17, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant