How CSS can leak your browser history
Despite the huge effort that goes into making browsers secure, there are still a few ways they can expose your private data to attackers if you aren't careful. Most involve some kind of complex fingerprinting using JavaScript, but there is a way to trick users into giving up their browsing history using only CSS.
The :visited pseudo-class
In addition to regular selectors for HTML tag name, ID, and class, CSS
also provides a few special selectors callde pseudo-classes. For
example, the :hover
pseudo-class represents an element that
the user is hovering over with their cursor.
This privacy-invading trick involves the :visited
pseudo-class. It's a way to style links that the user has already
visited, making long lists of links easier to read.
The fact that the styling is based on your browser's history makes this pseudo-class an obvious security issue. Because of this, browsers implement a number of measures to make it difficult to exploit.
The only rules you can apply to visited links are color
,
background-color
, border-color
,
column-rule-color
, and outline-color
. Further,
it's normally impossible to detect whether the rule is being applied
with JavaScript because window.getComputedStyle
pretends as
if all links are always unvisted.
If it weren't for these security measures, you could just include a
hidden link to www.shady-site.com
and then automatically
test whether the link has the visited style applied to it to figure out
if a user has gone to the shady site before.
So how can you exploit the :visited
pseudo-class when you
can't check if the style is being applied? Just trick the user into
telling you.
Proof of concept
It's surprisingly easy to get people to give up their history with a little bit of trickery. I made a simple proof of concept that you can try out now. Don't worry, it only reports your history to you, check the source if you don't believe me.
The page simply assigns a common English word for each of the top 50
most visited sites in the US, and colors them black if the link is
:visited
. Unvisited links are white and therefore
invisible, and a few decoy words are thrown in to make it less obvious
how it works. Then we just put an invisible div
over the
whole thing to prevent the user from clicking or selecting the links,
which would give away the trick.
The user types in all the words that are visible to them, and some JavaScript reports all the matching sites.
This example is kind of silly, but it's not too hard to imagine implementing a CAPTCHA service that only shows certain letters if you've visited a particular site. In fact, Fräntz Miccoli has already implemented a demo that does exactly that.
How to protect yourself
These types of tricks are difficult to detect, so the best thing to do is just disable styling visited links entirely. You lose the benefit of seeing what links you've already clicked on some sites, but it makes you more secure.
In Firefox, just add this line to your user.js
file to
disable the :visited
pseudo-class:
user_pref("layout.css.visited_links_enabled", false)
If there is a way to disable it in Chrome I couldn't find it, but you shouldn't really be using Chrome for security anyway.