Linkage attack

Anonymisation removes names. It does not remove distinctiveness. A person who appears in two different datasets, one with their name stripped and one without, can sometimes be re-identified by the combination of other attributes present in both: the city they live in, their age range, the time of day they tend to post, the topics they discuss. The linkage attack matches those quasi-identifiers across datasets to re-identify people in data released without direct identifiers.

Alone in a combination

The simplest attack uses two datasets with overlapping attributes. Neither need contain an explicit identifier. The quasi-identifiers do the work of matching.

released: anonymised records, names removed
    city | age_range | occupation | ... | condition

register: a list that carries names
    city | age_range | occupation | ... | full_name

# Attack:
for each record in released:
    candidates = register rows sharing every quasi-identifier

    if exactly one candidate:
        the record is identified, and the name is candidate.full_name
    otherwise:
        the record is hidden in a crowd of size len(candidates)

# The measure is the size of that crowd, not a confidence score.
# A record alone in its combination is identified; the rest are not.

Each sufficiently distinctive quasi-identifier can reduce the number of candidates. City and age range alone may leave dozens of possibilities. Add posting time patterns and the field narrows. Add topic preferences and it narrows further. The combination of individually unremarkable attributes converges on a specific person.

Eight variables

greenhouse-attacks linkage.py runs this on generated records shaped like an EU-SILC public use file: NUTS-1 regions, ISCED levels, ISCO-08 and NACE Rev. 2 at one digit, the four-way citizenship split. The records are generated, because that turns the population size and the cardinality of every variable into dials, and the curve can be watched as it climbs. 100,000 records:

Variables

Classes

Alone

In a group under five

sex

2

0.0%

0.0%

+ age band

14

0.0%

0.0%

+ NUTS-1 region

140

0.0%

0.0%

+ degree of urbanisation

420

0.0%

0.0%

+ citizenship

1,677

0.0%

0.3%

+ household type

10,593

2.3%

11.5%

+ education (ISCED)

36,993

17.9%

53.8%

+ occupation (ISCO)

77,515

62.6%

95.7%

+ industry (NACE)

95,562

91.6%

99.9%

+ activity status

98,852

97.7%

100.0%

+ tenure status

99,651

99.3%

100.0%

+ income quintile

99,930

99.9%

100.0%

+ work intensity

99,988

100.0%

100.0%

Nothing here is a name, an address or a date of birth. Five variables leave everybody in a crowd. Eight put most people alone.

That height is an upper bound, because the generator draws each variable independently and real ones are correlated: occupation, industry and activity status travel together. Those correlations cut the number of genuinely new combinations each column adds, so this curve climbs more steeply than a comparable real population can.

Two real files

Run through the same code, they land far apart.

The Office for National Statistics 2021 census teaching sample, 604,351 records for England and Wales under the Open Government Licence, reaches 44.67% alone across thirteen variables, with 64.1% in a group smaller than five. That is roughly half the generated height. The structure of a real population and the controls applied before release both account for the gap.

Alone there means unique inside the released file. It does not mean the person behind the record can be found. The file is a 1% sample, so being the only one of a kind in it says little about being the only one of a kind in England and Wales, and ONS swaps records between similar households before release, which it describes as small changes made to prevent the identification of individuals.

The Eurostat Labour Force Survey public use file for the Netherlands, 81,733 records for 2012, reaches 1.28%. Dutch people are no less distinctive than anybody else. The file has been protected past the point where the mechanism is visible. Region, marital status and income decile are suppressed outright, every value missing. Household type is a single constant. Citizenship is collapsed to three categories. Geography is not there at all, and what remains is post-randomised as well as recoded.

One variable escaped the banding. Usual hours worked is very nearly a number, and it survives with 69 distinct values; adding it back lifts the figure to 10.64%. In this run the whole climb comes from that one column.

File

Records

Alone

Generated, independent variables, no disclosure control

100,000

100.0%

ONS 2021 teaching sample

604,351

44.67%

Eurostat LFS 2012, Netherlands

81,733

1.28%

Statistical disclosure control is usually argued about rather than measured, and the contrast makes it visible. The Eurostat file has cut the precision and the variation out of several quasi-identifiers; the ONS sample keeps far more of both. The two are different countries, years and collection schemes, so the 43-point gap is not a clean measurement of what recoding and suppression buy, but the difference in what survives is there to be read in the files themselves.

Both are derived from data about real people and both are published openly. Under this linkage procedure, the Eurostat file leaves almost nobody unique.

Neither number is a claim about a population. The swapping described above means a record singled out in the ONS file is not reliably the person it names, and Eurostat states plainly that its public files cannot support inference to a wider population. The disclosure control is visible in the data being attacked, and that is the more useful half of the exercise.

The Eurostat SILC public files look like an obvious European comparison until the documentation is read: they are fully synthetic, simulated from the distributions of the real data, so a uniqueness measured on them describes the simulator.

Reasonably likely

Recital 26 makes identifiability turn on the means reasonably likely to be used, by the controller or by anyone else. What settles it is what other data can reasonably be brought to the match. The Article 29 Working Party named the three risks an anonymisation has to defeat in Opinion 05/2014: singling out, linkability, and inference. A share of records alone in their combination is a direct measure of the first, and puts a number where the argument usually goes.

Latanya Sweeney gave the problem one of its most influential early measurements in 2000, finding that 87% of the United States population, 216 million people of 248 million, was unique on zip code, date of birth and sex. The proportions travel badly, because they depend on how finely a country codes its geography and how many people live in it, but the shape does not: a handful of coarse attributes, none of them an identifier, converge on one person.

Releasing a medical dataset with names removed, ages and regions intact, alongside any register that lists ages and regions, creates the conditions for singling out without either file being sensitive on its own. The risk lives in the combination.

The same principle joins records from different sources. Purchases, location history, browsing data and public records each say little alone, and say a great deal more once they share attributes to match on.

The size of the crowd decides

Differential privacy adds calibrated randomness to a statistical release, which bounds how much the published result can depend on any one person’s data. Data minimisation, collecting and publishing only the attributes necessary for the stated purpose, leaves fewer attributes for anybody to match on. Generalising attributes before release reduces quasi-identifier precision. How far that has to go depends on the population, the variables and what else is available to match against. It is a disclosure-risk calculation and not a fixed setting.

Last reviewed: 2026-09-19.