Detect trackers without a list
Why it matters A list only knows trackers someone has already found and written down. New trackers appear every week, and some change their names on purpose. Spotting a tracker by how it behaves catches the ones no list has heard of yet.
Spotting a shoplifter without a photo
A shop guard with a list of known shoplifters is useful. But a good guard also watches behaviour: someone who visits every shop on the street and writes down who goes in. You do not need their name to see what they are doing.
Trackers have tell-tale behaviour too. In this lesson you look for three clues in a browser's request log:
- Everywhere at once. The same third-party site shows up on two or more different sites. That is what lets it join your visits together.
- A unique ID in the address. A query value like
cid=df0641532d7fthat looks random: that is a name tag for you. - A pixel. An "image" of 100 bytes or less, too small to show anything. Its only job is to make your browser send a request.
Heuristic: a rule of thumb that is usually right but not always, like "dark clouds mean rain". Heuristics can catch new things, and they can also be fooled.
Our rule: a site is a tracker if clue 1 is true and at least one of clues 2 or 3 is true. Requiring clue 1 keeps the harmless cdn.example safe: it is on every shop, but it sends no ID and its logo is a real 2 KB picture.
How random is random? Entropy, briefly
To tell an ID from a normal word, we measure how mixed-up its characters are. aaaa is not mixed at all. abcd uses four different characters equally.
Shannon entropy: a number for how unpredictable something is, in bits. For a text: for each different character, take its share p of the text, and add up -p * log2(p). More variety gives more bits.
char_entropy("aaaa") # 0.0 every character is the same
char_entropy("abab") # 1.0 two characters, half each
char_entropy("abcd") # 2.0 four characters, a quarter eachmath.log2(p) is "how many times do I halve 1 to get p", given as a negative number, which is why there is a minus sign. collections.Counter(text) counts each character for you. You will meet this same formula again, applied to whole crowds of browsers, in the next module.
Entropy alone is not enough: trail-shoes is quite mixed too. So looks_like_id also asks for at least 8 characters, only letters, digits, - or _, and at least one letter and one digit. Words fail the digit test; dates and URLs fail the letter or character test.
Reading the log
Each record in browser.log has what you need:
site_of(record.url)andrecord.top_site: if they differ, it is third party.record.request.params: the query as a dict, already decoded.record.response:Noneif blocked; otherwise.header("Content-Type")and.sizein bytes.
Honest limits
A tracker on only one site in your log passes unnoticed, even if it is on thousands of sites you have not visited yet. A tracker that hides IDs in a cookie and sends a normal-sized image also slips through. And a first-party analytics script that sends IDs to its own site is not third party at all. That is why real blockers use lists and heuristics together.
Try it Open DevTools (F12, or Cmd+Option+I on a Mac), go to the Network tab, filter by Img, and load a news site. Sort by Size. Any image of a few dozen bytes from another site is very likely a pixel. Click one and look at its query string for a long random value.
In other languages
JavaScript:
function charEntropy(text) {
if (!text) return 0;
const counts = {};
for (const ch of text) counts[ch] = (counts[ch] || 0) + 1;
let bits = 0;
for (const n of Object.values(counts)) {
const p = n / text.length;
bits -= p * Math.log2(p);
}
return bits;
}
const looksLikeId = v => v.length >= 8 && /^[A-Za-z0-9_-]+$/.test(v)
&& /[0-9]/.test(v) && /[A-Za-z]/.test(v) && charEntropy(v) >= 2;C#:
static double CharEntropy(string text) =>
text.Length == 0 ? 0 :
-text.GroupBy(c => c)
.Select(g => (double)g.Count() / text.Length)
.Sum(p => p * Math.Log2(p));Recap
- Behaviour gives trackers away: present on many sites, sending random IDs, fetching pixels.
- Shannon entropy measures how mixed a text is; random IDs score high.
- Requiring "on 2 or more sites" protects harmless helpers like a CDN.
- Heuristics catch new trackers but miss some; real blockers combine them with lists.
Write char_entropy(text) (Shannon entropy in bits per character, 0.0 for empty text), looks_like_id(value) (at least 8 characters, all in ID_CHARS, at least one digit and one letter, entropy at least 2.0), is_pixel(record) (a response with a Content-Type starting image/ and size 100 bytes or less) and find_trackers(log), returning the set of sites that appear as third party on at least 2 different top sites and sent an ID-like query value or received a pixel. Do not use watched.reference.
This lesson is locked
Lessons open one at a time. Finish the previous lesson to unlock this one.