Skip to content

Commit bd3c720

Browse files
committed
[selectors-4] Tighten :visited rules, add appendix with example algo. #11151
1 parent d1ea96f commit bd3c720

File tree

1 file changed

+148
-16
lines changed

1 file changed

+148
-16
lines changed

selectors-4/Overview.bs

Lines changed: 148 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ Ignored Vars: identifier, i
3232
<pre class=link-defaults>
3333
spec:css-values-4; type:dfn; text:identifier
3434
spec:css-display-3; type:property; text:display
35+
spec:css-display-4; type:property; text:visibility
3536
spec:css-pseudo-4; type:selector;
3637
text: ::before
3738
text: ::after
@@ -2136,10 +2137,11 @@ The Hyperlink Pseudo-class: '':any-link''</h3>
21362137
<h3 id="link">
21372138
The Link History Pseudo-classes: '':link'' and '':visited''</h3>
21382139

2139-
User agents commonly display unvisited <a href="#the-any-link-pseudo">hyperlinks</a> differently from
2140-
previously visited ones. Selectors
2141-
provides the pseudo-classes <dfn id='link-pseudo'>:link</dfn> and
2142-
<dfn id='visited-pseudo'>:visited</dfn> to distinguish them:
2140+
User agents commonly display unvisited [[#the-any-link-pseudo|hyperlinks]]
2141+
differently from previously visited ones.
2142+
Selectors provides the pseudo-classes
2143+
<dfn id='link-pseudo'>:link</dfn> and <dfn id='visited-pseudo'>:visited</dfn>
2144+
to distinguish them:
21432145

21442146
<ul>
21452147
<li>The '':link'' pseudo-class applies to links that have
@@ -2148,23 +2150,53 @@ The Link History Pseudo-classes: '':link'' and '':visited''</h3>
21482150
been visited by the user.
21492151
</ul>
21502152

2151-
After some amount of time, user agents may choose to return a
2152-
visited link to the (unvisited) '':link'' state.
2153-
21542153
The two states are mutually exclusive.
21552154

2156-
<div class="example">
2157-
The following selector represents links carrying class
2158-
<code>footnote</code> and already visited:
2155+
After some amount of time,
2156+
user agents may choose to return a visited link
2157+
to the (unvisited) '':link'' state.
2158+
2159+
The '':visited'' pseudo-class comes with obvious privacy implications--
2160+
letting random websites know what <em>other</em> websites you've visited
2161+
can be problematic for a number of reasons--
2162+
and so user agents <em>must</em> preserve user privacy
2163+
in their implementation of '':visited''.
2164+
2165+
<div class=note>
2166+
This specification intentionally does not specify
2167+
exactly how to preserve user privacy in this regard,
2168+
to allow for user agents to innovate in this space.
2169+
The following methods are suggested, however:
2170+
2171+
* Have '':visited'' never match,
2172+
so all links match '':link'' instead.
2173+
* Carefully track what history entries
2174+
could have been observed by a given origin on their own,
2175+
and only have links match '':visited''
2176+
if that visit would have been observable from the site's origin.
2177+
A possible specific approach for this
2178+
is described in [[#visited-privacy]].
2179+
* Allow links to match '':visited'' on any origin,
2180+
but carefully restrict what styles they can apply
2181+
and what information is returned by style-querying APIs
2182+
like {{getComputedStyle()}},
2183+
to prevent sites from observing
2184+
whether a link is styled with '':link'' or '':visited''.
2185+
(This is documented at <a href="https://developer.mozilla.org/en-US/docs/Web/CSS/Privacy_and_the_:visited_selector">MDN</a>,
2186+
and was the historical approach browsers took,
2187+
but is not perfect;
2188+
there are several ways for a hostile page
2189+
to still extract history information.)
2190+
</div>
21592191

2160-
<pre>.footnote:visited </pre>
2192+
<div class="example">
2193+
For example, the selector ''.footnote:visited''
2194+
would allow styling footnote links differently
2195+
if they've been previously followed,
2196+
allowing users of the page to know
2197+
they might not need to click the footnote again.
21612198
</div>
21622199

2163-
Since it is possible for style sheet authors to abuse the :link and :visited pseudo-classes
2164-
to determine which sites a user has visited without the user's consent,
2165-
UAs may treat all links as unvisited links
2166-
or implement other measures to preserve the user's privacy
2167-
while rendering visited and unvisited links differently.
21682200

21692201
<h3 id="the-local-link-pseudo">
21702202
The Local Link Pseudo-class: '':local-link''</h3>
@@ -4199,6 +4231,106 @@ Appendix B: Obsolete but Required <code>-webkit-</code> Parsing Quirks for Web C
41994231
and all right-thinking web developers.
42004232
</details>
42014233

4234+
<h2 id="visited-privacy">
4235+
Appendix C: Example Privacy-Preserving '':visited'' Restrictions</h2>
4236+
4237+
Previous attempts to protect user privacy in '':visited''
4238+
involved <a href="https://developer.mozilla.org/en-US/docs/Web/CSS/Privacy_and_the_:visited_selector">complex restrictions and behaviors</a>
4239+
to "lie" about whether the link match '':visited'' or '':link'',
4240+
to reduce the chance that a hostile site
4241+
could observe what unrelated sites a user had visited
4242+
while still allowing '':visited'' to work in all cases
4243+
and help the user know what links they'd already clicked.
4244+
This is ultimately an arms race that can't be won;
4245+
there are multiple documented ways to still extract a user's browsing history
4246+
even with these mitigations.
4247+
4248+
This section describes an approach first developed and documented at
4249+
<a href="https://github.com/explainers-by-googlers/Partitioning-visited-links-history">https://github.com/explainers-by-googlers/Partitioning-visited-links-history</a>,
4250+
that partitions a user's browsing history information,
4251+
to allow '':visited'' to only match links
4252+
corresponding to navigations that the site's origin could have observed on its own.
4253+
With this, '':visited'' can be treated as a normal pseudo-class,
4254+
without any of the complex mitigations described above,
4255+
as it doesn't expose any information not already theoretically available to the site,
4256+
while still preserving as much of the <em>usefulness</em> of '':visited'' as possible for the user.
4257+
4258+
1. Let |visited history| be a [=/set=]
4259+
containing [=tuples=] of three pieces of information:
4260+
* a visited [=/URL=]
4261+
* an [=/origin=] for the site that started a navigation
4262+
* an [=/origin=] for the top-level site containing the frame that started the navigation.
4263+
(This will often be the same as the previous,
4264+
but can differ if the user clicks a link in a iframe, for example.)
4265+
4266+
2. Whenever a navigation is triggered <em>from within a page</em>--
4267+
e.g.,
4268+
from the user clicking a link,
4269+
or a script on the page initiating a navigation--
4270+
add an entry to |visited history|
4271+
recording the navigation's destination URL,
4272+
the origin of the page containing the link or script,
4273+
and the origin of the top-level site containing that page
4274+
(which might be the same as the previous origin).
4275+
4276+
Note: This allows a site to see '':visited'' information
4277+
for links that the user has clicked
4278+
from anywhere in that site's origin.
4279+
In other words, any <code>A -> B</code> navigation
4280+
where the site is A.
4281+
4282+
Additionally, add an entry to |visited history|
4283+
recording the destination's URL,
4284+
and the <em>destination's</em> origin
4285+
for both origin values.
4286+
4287+
Note: This allows for a site to see '':visited'' information about its own pages
4288+
(which is already observable by the site)
4289+
regardless of what site initiated the navigation to that page.
4290+
In other words, any <code>A -> B</code> navigation
4291+
where the site is B.
4292+
4293+
Note: Notably, direct navigations triggered by the <em>user agent's</em> UI,
4294+
such as typing into the address bar,
4295+
clicking on bookmarks,
4296+
or dragging a link from another program into the page,
4297+
<em>do not</em> add a |visited history| entry.
4298+
These can, of course,
4299+
still add to the browser's record of visited sites
4300+
that it uses for other purposes,
4301+
such as suggesting URLs as the user types into the URL bar.
4302+
4303+
3. When determining if a link element should match '':link'' or '':visited'',
4304+
only allow it to match '':visited'' if
4305+
the link's destination,
4306+
the origin of the page containing the link,
4307+
and the origin of the top-level site containing the link
4308+
match a tuple in |visited history|.
4309+
4310+
<div class=note>
4311+
The inclusion of both page origin and top-level site origin
4312+
prevents several possible privacy attacks,
4313+
such as:
4314+
4315+
* If history entries were <em>only</em> keyed by the starting site's URL,
4316+
a tracking site could be embedded in a hidden iframe on multiple sites
4317+
which triggers a navigation to a unique URL for a user on the first visit,
4318+
and then uses many such links on subsequent visits
4319+
to see which one had been visited,
4320+
effectively becoming a new "third-party cookie"
4321+
identifying the user across the web.
4322+
By keying the history entry with the top-level site,
4323+
this information can't be shared across different sites.
4324+
4325+
* If history entires were <em>only</em> keyed by the top-level site's URL,
4326+
a hostile iframe,
4327+
perhaps included in a page as part of an advertisement,
4328+
could observe what sites were visited from the top-level site.
4329+
By keying the history entry with the link's own site,
4330+
the top-level site's information can't "leak" into cross-origin iframes.
4331+
</div>
4332+
4333+
42024334

42034335
<h2 id="changes">
42044336
Changes</h2>

0 commit comments

Comments
 (0)