Chapter 17 — Pseudonymisation for extracts
Lesson map
Lesson 17 published. Map, example, EX049.
flowchart TD
N0["Learning outcomes"]
N1["17.1 The need"]
N2["Lab — Keyed-hash pseudonyms for tenant identifiers i"]
N3["17.2 How it works"]
N4["17.3 Protecting the key"]
N0 --> N1 --> N2 --> N3 --> N4
N4 --> EX["Worked example"]
EX --> Q["Exercise EX049"]
Logical drawing
flowchart LR
N0["Learning outcomes"]
N1["17.1 The need"]
N2["Lab — Keyed-hash pseudonyms for tenant identifiers i"]
N3["17.2 How it works"]
N4["17.3 Protecting the key"]
N0 --> N1 --> N2 --> N3 --> N4 --> Q["EX049"]
[Learning outcomes]
|
v
[17.1 The need]
|
v
[Lab — Keyed-hash pseudonyms for tenant identifiers i]
|
v
[17.2 How it works]
|
v
[17.3 Protecting the key]
|
v
[Worked example] --> [Exercise EX049]
Learning outcomes
You will (1) pseudonymise identifiers with keyed hashes; (2) preserve linkage for analysis; (3) explain the limits of pseudonymisation.
17.1 The need
An analyst wants to study whether some tenants report the same problem repeatedly, which could indicate poor repairs. The study needs to link jobs for the same tenant but not to know who the tenant is. Ravi proposes replacing tenant identifiers with pseudonyms in the extract. He explains that pseudonymisation reduces risk if the extract is lost or misused, while still allowing the analyst to count jobs per tenant and look for patterns. The tenancy owner approves the extract with pseudonyms in place.
Lab — Keyed-hash pseudonyms for tenant identifiers in a synthetic extract
"""Mask tenant identifiers in a synthetic extract with keyed hashes and check linkage (stdlib only)."""
import hmac, hashlib
key = b"synthetic-teaching-key"
def p(t): return hmac.new(key, t.encode(), hashlib.sha256).hexdigest()[:12]
rows = [("T1001", "leak"), ("T1002", "boiler"), ("T1001", "damp"), ("T1003", "lock"), ("T1001", "leak")]
masked = [(p(t), j) for t, j in rows]
print(f"rows {len(rows)}; distinct tenants {len({t for t,_ in rows})}; distinct pseudonyms {len({t for t,_ in masked})}")
print(f"T1001 pseudonym {p('T1001')}; jobs linked {sum(1 for t,_ in masked if t == p('T1001'))}")
Run with python3 lab_mask.py. Verified output:
rows 5; distinct tenants 3; distinct pseudonyms 3
T1001 pseudonym 083752155d81; jobs linked 3
17.2 How it works
The lab uses Python's hmac and hashlib modules to create a keyed hash of each tenant identifier with a secret key held by the data team. In a five-row synthetic extract with three distinct tenants, the result has three distinct pseudonyms. Tenant T1001 becomes 083752155d81, and all three of that tenant's jobs share the same pseudonym, so the analyst can see that one tenant had three jobs, including two leaks, without knowing the identifier. The analyst confirms that the linkage meets the study's needs.
17.3 Protecting the key
The key is what makes pseudonyms hard to reverse. Without the key, someone with a list of tenant identifiers cannot recompute pseudonyms. Ravi stores the key in the data team's restricted vault, not in the script, and limits access to two people. He sets a rule that the same key is used only for one project, so pseudonyms from different extracts cannot be linked by others. Each use of the key is recorded. Access to the vault is reviewed every quarter by the owner.
17.4 Limits
Ravi explains that pseudonymised data remain personal data under the GDPR, because the data team can re-identify tenants with the key and because other details, such as a rare repair at a specific property, might identify someone. The extract therefore still needs access controls and a retention date. Ms. Moreau notes that pseudonymisation is a valuable safeguard but not anonymisation, and that NIST SP 800-188 discusses why de-identification must consider all fields together. The extract is given a deletion date at the end of the analyst's study.
Worked example
Ravi pseudonymises tenant identifiers with keyed hashes, keeps three tenants as three pseudonyms with T1001's three jobs linked, protects the key in a vault per project and explains that the extract remains personal data.
Synthetic case. The extract and key are synthetic; outputs come from lab_mask.py; the GDPR and NIST SP 800-188 are real.
Errors and limits
Plain hashes without a key can be reversed by hashing known identifiers.
Exercises
- EX049 — T1001 pseudonym. What pseudonym did T1001 receive? Rubric: 3 = 083752155d81;; 2 = partly correct;; 1 = wrong;; 0 = none.
- EX050 — Key storage. Where is the key stored? Rubric: 3 = the data team's restricted vault;; 2 = partly correct;; 1 = script;; 0 = none.
- EX051 — Legal status. Are pseudonymised extracts still personal data? Rubric: 3 = yes;; 2 = partly correct;; 1 = no;; 0 = none.
Continue studying — lesson 17 (chapter 17 of 30)
← Chapter 16 · Course chapter index · Chapter 18 →
Continue to the next authored lesson.