Security Advisory: NI Number Transmitted via URL Query Parameter
Security Advisory: NI Number Transmitted via URL Query Parameter
Severity: High
Version affected: ≤ 1.0.39
Affected file: src/app/api/hmrc/authorise/route.ts
Status: Remediation required
Summary
The HMRC OAuth authorisation flow exposes the landlord's National Insurance (NI) number as a plain-text URL query parameter. This constitutes a serious data exposure risk under GDPR and is incompatible with HMRC's data-handling requirements for the Making Tax Digital API.
Background
To authenticate with the HMRC Making Tax Digital API, the application must initiate an OAuth 2.0 authorisation flow on behalf of the landlord. As part of this flow, the landlord's NI number is required by HMRC to identify the individual taxpayer.
In the current implementation, this NI number is passed to the authorisation endpoint as a URL query parameter:
GET /api/hmrc/authorise?ni_number=AB123456C
The NI number is also embedded within the redirect URL constructed by the HMRC tRPC getAuthoriseUrl procedure, meaning the value appears in the URL passed to HMRC's own authorisation server.
Risk Analysis
Data Classification
The National Insurance number is:
- Highly sensitive PII under UK GDPR and the Data Protection Act 2018
- A mandatory credential required by HMRC for Making Tax Digital submissions
- Sufficient, in combination with basic personal details, to enable identity fraud
Exposure Surfaces
URL query parameters are exposed across numerous surfaces by default, many of which are outside the application's control:
| Surface | Exposure mechanism |
|---|---|
| Server access logs | Full request URL logged on every request |
| Browser history | URL persisted locally on the user's device |
| CDN / reverse-proxy logs | Edge infrastructure logs full upstream URLs |
HTTP Referer header | Appended automatically on navigation to third-party pages |
| Analytics & monitoring tools | URL parameters captured by APM, error tracking, and analytics platforms |
Affected Code
File: src/app/api/hmrc/authorise/route.ts
The NI number is read from the incoming request's query string and subsequently embedded into the HMRC OAuth redirect URL via the getAuthoriseUrl tRPC call.
Recommended Remediation
NI numbers must never appear in URLs. Replace the query-parameter approach with a server-side session nonce pattern:
Step 1 — Store the NI number server-side before the redirect
Before redirecting the user to HMRC's authorisation endpoint, persist the NI number in a short-lived server-side session (e.g. Redis, an encrypted database record, or an in-memory store with a TTL of ~10 minutes). Key the record by a cryptographically random nonce:
import { randomBytes } from 'crypto';
const nonce = randomBytes(32).toString('hex');
await sessionStore.set(nonce, { niNumber }, { ttl: 600 }); // 10-minute TTL
Step 2 — Pass only the nonce in the OAuth state parameter
Set the nonce as an HTTP-only, Secure, SameSite cookie and include it in the OAuth state parameter. Do not include the NI number anywhere in the URL:
response.cookies.set('oauth_state', nonce, {
httpOnly: true,
secure: true,
sameSite: 'lax',
maxAge: 600,
path: '/',
});
// Build the HMRC redirect URL using only the nonce as state
const authoriseUrl = buildHmrcAuthoriseUrl({ state: nonce });
return Response.redirect(authoriseUrl);
Step 3 — Retrieve the NI number during the callback
In the OAuth callback handler, read the nonce from the state cookie, look up the NI number from the session store, and immediately delete the session record:
const nonce = request.cookies.get('oauth_state')?.value;
if (!nonce) return unauthorised();
const session = await sessionStore.get(nonce);
if (!session) return unauthorised(); // expired or tampered
await sessionStore.delete(nonce); // single-use — delete immediately
const { niNumber } = session;
Additional hardening
- Validate that the
stateparameter returned by HMRC in the callback matches the cookie value before proceeding. - Ensure the session store TTL is as short as practically possible.
- Audit server, CDN, and proxy log retention policies to determine whether NI numbers from previous requests need to be purged from existing logs.
Compliance Notes
| Requirement | Status |
|---|---|
| UK GDPR — data minimisation (Art. 5(1)(c)) | ❌ NI number unnecessarily exposed in URL |
| UK GDPR — integrity and confidentiality (Art. 5(1)(f)) | ❌ PII transmitted in a form not protected against inadvertent disclosure |
| HMRC MTD data-handling requirements | ❌ Sensitive identifier present in loggable request URLs |