A platform that wants to detect a known image cannot compare files. Re-saving a JPEG at a different quality setting changes millions of bytes and produces a completely different cryptographic hash, and a user who crops fifteen pixels off the bottom defeats byte comparison entirely. Storing the images themselves to compare against is worse: it means keeping a library of exactly the material that must not be kept.
Perceptual hashing is the answer the industry arrived at. Reduce an image to a short fingerprint that captures its visual structure and discards everything else, then compare fingerprints. Two images that look the same to a person produce fingerprints a small distance apart, even when no two bytes of the files agree.
How a basic perceptual hash is computed
The classic constructions are simple enough to implement in an afternoon, which is a good way to build intuition for the whole family.
Average hash is the simplest. Convert to greyscale, resize to 8 by 8 pixels, compute the mean brightness of those 64 values, then emit one bit per pixel: 1 if the pixel is above the mean, 0 if below. The result is a 64-bit fingerprint. Resizing throws away resolution, detail, and compression artefacts. What survives is the coarse light and dark structure of the picture.
Difference hash is a variation with better behaviour. Resize to 9 by 8, then emit a bit for each adjacent horizontal pair: 1 if the left pixel is brighter than the right. It encodes gradients rather than absolute levels, so it is unbothered by a global brightness or contrast change.
pHash applies a discrete cosine transform to a larger greyscale version and keeps only the low-frequency coefficients, discarding the top-left DC term. Low frequencies carry overall composition; high frequencies carry noise, sharpening and compression artefacts. Keeping the former and dropping the latter is exactly the robustness property wanted.
Comparison is Hamming distance, the count of differing bits. Identical images give 0. Re-encoded and slightly resized versions of the same image typically land within a handful of bits. Unrelated images cluster near 32 out of 64, which is what you expect from two roughly random bit strings.
Everything a matching system will ever do right or wrong is decided by one number: the Hamming distance below which two fingerprints are declared the same image. Set it low and edited copies escape. Set it high and unrelated pictures start colliding. There is no setting that avoids both, only a choice about which error you would rather have.
What gets deployed in practice
Production systems are considerably more sophisticated than a DCT over an 8 by 8 thumbnail, and they diverge in one important way.
PhotoDNA, developed at Microsoft with Dartmouth researcher Hany Farid around 2009, is the long-standing tool for detecting known child sexual abuse material. It converts an image to greyscale, divides it into a grid, and derives a signature from intensity gradients in each cell. It is robust to resizing, re-encoding and moderate alteration. It is licensed rather than published, and the algorithm is not public, which is a deliberate choice: an attacker who knows the transform can design around it.
NeuralHash was Apple's 2021 proposal, and it took the opposite structural approach. A convolutional neural network mapped an image to a descriptor, which was then quantised via a hashing step into a short code. The intent was greater robustness to transformations than a hand-designed transform could achieve.
Video and audio use the same idea on different signals. Video systems typically hash sampled frames and match on sequences; audio fingerprinting extracts time and frequency peaks, which is how a phone identifies a song from ten seconds of a noisy recording.
The two attacks, and why both are inherent
A perceptual hash is a lossy, continuous-ish function whose whole purpose is to be smooth. Smooth functions can be optimised against. That gives an adversary two directions, and they are opposites.
| Attack | Goal | Who benefits |
|---|---|---|
| Evasion | Alter a known image so its hash moves past the threshold, while it still looks the same to a human. | Whoever is distributing the material the system exists to catch. |
| Collision | Alter an innocuous image so its hash matches a known-bad entry, while it still looks innocuous. | Whoever wants a target flagged, investigated, or their account frozen. |
Within days of NeuralHash being found in shipping iOS builds in August 2021, researchers extracted the model, published it, and demonstrated both. Collisions were generated between visually unrelated images, and near-imperceptible perturbations moved a matching image out of range. None of this was surprising to anyone who works on adversarial examples. It is the standard result for any differentiable function with a public model: gradients point at the answer.
That is the case against publishing the algorithm. The case for publishing is that a system nobody can test is a system whose error rate is whatever its operator says it is. Apple shelved the client-side scanning plan in December 2022, and the demonstrations were part of why.
Keeping the transform secret does not remove the vulnerability. It removes the ability of anyone outside the operator to measure it.
The false positive problem is arithmetic
Suppose a matcher is genuinely accurate: a one in ten million chance that an unrelated image is flagged. That sounds close enough to zero to ignore.
A large platform processes several billion image uploads per day. At one in ten million, that is hundreds of false matches every day, each of which is a real person whose private photograph has been routed to a human reviewer, and possibly to a report. The rate is excellent. The absolute number is a queue with people in it.
This is base-rate arithmetic and it is not a criticism of any particular algorithm. It is the reason well-designed deployments require multiple independent matches before escalating, and the reason human review sits between a match and any consequence. It is also the reason "the false positive rate is negligible" is an incomplete sentence. Negligible against what denominator, and what happens to the person on the wrong side of it?
The privacy question the primitive raises
Perceptual hashing is often introduced as privacy-preserving, on the grounds that a hash is not an image. Two things complicate that.
First, a perceptual hash is a lossy but real description of the picture. It is a low-resolution structural summary, and research has shown that approximate images can be reconstructed from some perceptual hash schemes. A cryptographic hash reveals nothing about its input by design; a perceptual hash reveals visual structure by design. Treating the two as equivalent for disclosure purposes is a category error.
Second, and more consequentially, the matching database is a list of hashes and nothing more. Whether an entry corresponds to abuse material, a leaked document, a protest banner, or a meme critical of a government is not visible from the entry, and not visible to the client doing the matching. The technical mechanism is indifferent to what is on the list. Every property that makes it good at one task makes it equally good at the others, and the only thing standing between the two is governance.
That is the argument that has driven a decade of debate about scanning on the device rather than on the server, and it is not a technical dispute. Everyone involved agrees about how the matching works.
What to take from this
- A perceptual hash is not a cryptographic hash and the two have opposite design goals. Do not reason about one using intuitions from the other, particularly around collisions, which are a defect in one and an unavoidable feature of the other.
- The threshold is a policy decision wearing a technical costume. Ask what it is set to and who chose it.
- Both attack directions are inherent to the primitive. Secrecy about the algorithm changes who can find them first, not whether they exist.
- A tiny error rate at planetary scale is a large number of people. Ask about the denominator and about what a match triggers.
- The list is the system. The matcher enforces whatever it is given, and cannot tell you what that is.
Haven does not perform content matching on messages, which is a consequence of the architecture rather than a policy we could revise: end-to-end encrypted content is not available to the server to match against. That places the whole class of questions above outside what we can do, which is the point of the design and also a real cost of it. A system that cannot scan for the worst material also cannot be quietly repurposed to scan for anything else, and both halves of that sentence are true at once.