| 1 | = What is Fingerprinting? = |
| 2 | [[PageOutline]] |
| 3 | |
| 4 | This page describes the mechanisms WebKit offers to ports and clients |
| 5 | interested in reducing the ability of websites to identify users and track |
| 6 | their behaviour without obtaining consent. |
| 7 | |
| 8 | Use cases for fingerprinting include: |
| 9 | * Sites attempting to identify users on devices previously used for fraud |
| 10 | * Sites attempting to establish a unique visitor count |
| 11 | * Advertising networks attempting to establish a unique click-through count |
| 12 | * Advertising networks attempting to profile users to increase ad relevance |
| 13 | * Sites attempting to profile the behaviour of unregistered users |
| 14 | * Sites attempting to link the visits of users when they are both registered |
| 15 | and unregistered and identify the user when visiting the site without |
| 16 | authenticating. |
| 17 | |
| 18 | In order to evade fingerprinting users will often disable/clear HTTP Cookies |
| 19 | or change their IP address between visits. Research has demonstrated that |
| 20 | these measures are not sufficient and that in order to evade tracking by |
| 21 | third parties a wide variety of technologies implemented in the browser must |
| 22 | be considered. |
| 23 | |
| 24 | |
| 25 | == Entropy and Fingerprinting == |
| 26 | Entropy defines the amount of uniqueness a specific property exposed by the |
| 27 | browser (such as the User-Agent header) introduces into a browser fingerprint. |
| 28 | It's usually expressed in bits. For example, Peter Eckersley's |
| 29 | ['Panopticlick' study for the EFF |
| 30 | http://panopticlick.eff.org/browser-uniqueness.pdf] finds that the User-Agent |
| 31 | header provide 10.0 bits of entropy. Since 2^10^ == 1024 that means only 1 in |
| 32 | 1024 random browsers visiting a site are expected to share the same |
| 33 | user-agent header. |
| 34 | |
| 35 | == Why this isn't about 'Private Browsing' == |
| 36 | There are a number of things WebCore could do differently to reduce the user's |
| 37 | exposure to remote tracking mechanisms but which it is not necessarily |
| 38 | desirable to implement as default behaviour. At first glance, some of these |
| 39 | appear a good fit with 'Private Browsing' but really they're addressing a |
| 40 | different problem. Private browsing's overriding objective is to ensure no |
| 41 | trace of a browsing session is left on your disk. |
| 42 | Fingerprinting a user's browser and tracking that browser's visits presents a |
| 43 | different set of challenges. |
| 44 | |
| 45 | == Private Browsing in WebKit == |
| 46 | |
| 47 | WebKit defines private browsing as: |
| 48 | {{{ |
| 49 | // When this option is set, WebCore will avoid storing any record of browsing |
| 50 | activity |
| 51 | // that may persist on disk or remain displayed when the option is reset. |
| 52 | // This option does not affect the storage of such information in RAM. |
| 53 | // The following functions respect this setting: |
| 54 | // - HTML5/DOM Storage |
| 55 | // - Icon Database |
| 56 | // - Console Messages |
| 57 | // - Cache |
| 58 | // - Application Cache |
| 59 | // - Back/Forward Page History |
| 60 | // - Page Search Results |
| 61 | }}} |
| 62 | |
| 63 | Some of the items discussed in this page are already taken care of by private |
| 64 | browsing |
| 65 | in webkit - so if you are trying to defend your webkit browser against |
| 66 | fingerprinting you will at the very least need to enable private browsing |
| 67 | through your WebKit port's API. |
| 68 | |
| 69 | == Private Browsing in WebKit and your browser == |
| 70 | |
| 71 | Be aware that not all aspects of 'private browsing' are implemented within |
| 72 | WebKit. |
| 73 | WebKit ports offer different APIs to clients. Some functionality relevant to |
| 74 | mitigating browser fingerprinting is left entirely to the client. For example, |
| 75 | QtWebKit does not implement a HTTP Cookie store or Page cache: the |
| 76 | client is responsible for managing both. |
| 77 | |
| 78 | You need to pay close attention to the limitations and capabilities of your |
| 79 | chosen port's WebKit API - in particular the aspects of cookie and cache |
| 80 | management you are expected to implement yourself. |
| 81 | |
| 82 | == Things that aren't 'Private Browsing' but are 'Fingerprinting' == |
| 83 | |
| 84 | === 1. Session Isolation === |
| 85 | If you have implemented an anti-fingerprinting mode you don't want a website |
| 86 | to access information from the browser's normal mode - doing so might reveal |
| 87 | information that allows the site to fingerprint your user or browser. This is |
| 88 | not a property of private browsing - which is only interested in preventing |
| 89 | data from the private session leaving any traces on the user's disk. |
| 90 | |
| 91 | === 2. Session Persistence === |
| 92 | An accidental property of private browsing is that HTTP Cookies and |
| 93 | page caches generated in one private browsing session are not available in any |
| 94 | subsequent or simultaneous browsing sessions because they are not stored to |
| 95 | disk. This property of non-persistence is an important counter-measure against |
| 96 | fingerprinting, however where sites use a browser feature as a side channel |
| 97 | for simulating the behaviour of HTTP cookies you may not be able to rely on |
| 98 | private browsing to take care of this for you. |
| 99 | |
| 100 | === 3. Long-Running Sessions === |
| 101 | At the moment private browsing happens to take care of some aspects of session |
| 102 | isolation and persistence for you. But a browser session that is never closed, |
| 103 | on a machine that is never turned off, will become trackable over time. This |
| 104 | means you will need to decide on a way of managing long-running |
| 105 | browser sessions and cannot rely on any side-effect benefits from private |
| 106 | browsing in webkit. The obvious way of managing this problem is to implement a |
| 107 | periodic cleardown of cookies and page cache, as well as any other |
| 108 | sidechannel-cookies such as 'window.name'. |
| 109 | |
| 110 | == Creating a Common Fingerprint == |
| 111 | This page is premised on the notion that the best way to mitigate against |
| 112 | browser tracking is to implement a fingerprint for your browser that all users |
| 113 | of your browser will share. The larger your user base the more rewarding this |
| 114 | policy becomes. Even with a relatively small userbase this approach is still |
| 115 | useful as long as you can close off as many sources of client entropy as |
| 116 | possible. A browser with a small userbase is inherently more vulnerable to |
| 117 | tracking if it leaks entropy than a browser with a large userbase suffering |
| 118 | from a similar problem. |
| 119 | |
| 120 | == Creating a Dynamic Fingerprint == |
| 121 | Not much attention has been paid to approaches that suggest creating a |
| 122 | constantly mutating browser fingerprint. Common sense suggests it would be |
| 123 | hard to implement and even harder to get right. |
| 124 | |
| 125 | = Creating a Static and Common Fingerprint for your WebKit Browser = |
| 126 | |
| 127 | == 1. Javascript Objects == |
| 128 | |
| 129 | Javascript exposes a lot of entropy-rich information to websites through the |
| 130 | Screen object, the Window object, the Navigator object, the Document object, |
| 131 | and even the Date Object. |
| 132 | |
| 133 | WebKit-based browsers need to decide on a strategy for reducing the variety of |
| 134 | information these objects leak to websites. The most common approach is to |
| 135 | decide on a pre-determined set of values that the browser will always use in |
| 136 | fingerprint-resistant mode - this allows users of the browser in that mode to |
| 137 | be indisinguishable from each other based on information from the JS objects |
| 138 | alone. |
| 139 | |
| 140 | If you are building a tracking-resistant mode for your WebKit-based browser |
| 141 | you will need to consider managing at least the following values: |
| 142 | |
| 143 | === i. Document Object === |
| 144 | |
| 145 | The document.referrer property needs to be managed in the same way as |
| 146 | [Referer Header and Origin Header] below. |
| 147 | |
| 148 | === ii. History Object === |
| 149 | |
| 150 | history.length:: |
| 151 | This value has potential, in cases where it is unusually high due to prolonged |
| 152 | use of a single browser/tab session, to assist sites in tracking the |
| 153 | user. That said, such users already have a pretty revealing cache and |
| 154 | coookie data set. |
| 155 | |
| 156 | === iii. Window object === |
| 157 | |
| 158 | You have two choices here: |
| 159 | - restrict the layout of the actual browser window to three or four |
| 160 | predetermined-sizes, and return those. |
| 161 | - return values that do not reflect the real size of the browser window. |
| 162 | |
| 163 | The properties you need to override are at least: |
| 164 | {{{ |
| 165 | outerHeight() |
| 166 | outerWidth() |
| 167 | innerHeight() |
| 168 | innerWidth() |
| 169 | screenX() |
| 170 | screenY() |
| 171 | scrollX() |
| 172 | scrollY() |
| 173 | }}} |
| 174 | |
| 175 | === iv. Window.name === |
| 176 | |
| 177 | This deserves a special mention. Window.name is cross-domain and persists |
| 178 | across page loads. Not surprisingly, many sites use it as a cookie. Since it |
| 179 | does not persist across sessions it's not as much of a problem as a HTTP |
| 180 | cookie but it does allow tracking within sessions - and this is a worry if the |
| 181 | session is long-running. |
| 182 | |
| 183 | There is room here for WebKit to restrict cross-domain access to window.name |
| 184 | but you are probably better off managing the value in this field yourself with |
| 185 | the JS API. The chances are you will break the user's experience on at least |
| 186 | some websites. |
| 187 | |
| 188 | === v. Screen object === |
| 189 | |
| 190 | Torbutton and Torora use the following values for the Screen object's |
| 191 | properties: |
| 192 | |
| 193 | {{{ |
| 194 | height() = window innerHeight() |
| 195 | width() = width() rounded to the nearest 50 px |
| 196 | colorDepth() = 24 |
| 197 | pixelDepth() = 24 |
| 198 | availLeft() = 0 |
| 199 | availTop() = 0 |
| 200 | availHeight() = window innerHeight() |
| 201 | availWidth() = window innerWidth() |
| 202 | }}} |
| 203 | |
| 204 | Entropy for the values provided here is as much as 4.83 bits. |
| 205 | |
| 206 | === vi. Navigator Object === |
| 207 | You will need to look closely at all the values exposed by the Navigator |
| 208 | object and decide on a set of values that can remain static across many |
| 209 | releases as long as possible. You will also need to ensure that the values |
| 210 | decided upon here are also presented in the user-agent HTTP header by your |
| 211 | browser. |
| 212 | |
| 213 | You will need to decide what to do with navigator.plugins. Internet Explorer |
| 214 | does not return anything to navigator.plugins, so websites tend not to rely on |
| 215 | it - making it a relatively safe option to follow IE's suit. If you do expose |
| 216 | a list of plugins through this property you will need to ensure your decision |
| 217 | is consistent with the behaviour you have implemented under Plugins below. |
| 218 | |
| 219 | === vii. Date Object === |
| 220 | |
| 221 | ==== a. Timezone ==== |
| 222 | |
| 223 | You need to decide what timezone your browser is in when in |
| 224 | fingerprinting-resistance mode - a common choice is UTC. Rather than attempt |
| 225 | to override the Date object you should set the local timezone to UTC by setting |
| 226 | the environment variable TZ as follows: |
| 227 | {{{ |
| 228 | setenv("TZ",":UTC",1); |
| 229 | }}} |
| 230 | This will ensure that WebKit always returns the local time as UTC when the |
| 231 | Date object is queried. |
| 232 | |
| 233 | ==== b. Timing Users ==== |
| 234 | Believe it or not, there is evidence that some companies have tracked users |
| 235 | based on the typing cadence gleaned from querying the Date object: |
| 236 | |
| 237 | http://arstechnica.com/tech-policy/news/2010/02/firm-uses-typing-cadence-to-finger-unauthorized-users.ars |
| 238 | |
| 239 | Currently, WebKit does not offer any means of countering this. |
| 240 | |
| 241 | === viii. Language Object === |
| 242 | |
| 243 | You need to decide on a consistent set of values for your browser in |
| 244 | fingeprinting-resistance mode. For example: |
| 245 | |
| 246 | {{{ |
| 247 | charset = 'iso-8859-1,*,utf-8' |
| 248 | language = 'en-us, en' |
| 249 | locale = 'en-US' |
| 250 | }}} |
| 251 | |
| 252 | Your decision should be consistent with your implementation of the |
| 253 | Accept-Language header. |
| 254 | |
| 255 | |
| 256 | == 2. Overriding Javascript Objects == |
| 257 | |
| 258 | WebCore does allow WebKit ports to overload the values of JS objects. Most |
| 259 | WebKit ports expose this API to browser clients. |
| 260 | |
| 261 | === i. Overriding Javascript Objects - Qt === |
| 262 | |
| 263 | You can use the function below to overload existing JS objects with a Qt |
| 264 | object. Any functions called on the JS object will call the equivalently named |
| 265 | function in your Qt object: |
| 266 | {{{ |
| 267 | void QWebFrame::addToJavaScriptWindowObject ( const QString & name, QObject *object ) |
| 268 | }}} |
| 269 | For an example of this in practice see: |
| 270 | |
| 271 | * https://github.com/mwenge/torora/commit/5191dca4d5df08514f21e68472cacb6cacd2eb06 |
| 272 | |
| 273 | In order to override specific properties of a JS object in Qt see: |
| 274 | |
| 275 | * https://bugs.webkit.org/show_bug.cgi?id=46566 |
| 276 | |
| 277 | == 3. CSS == |
| 278 | |
| 279 | === i. CSS Media Queries === |
| 280 | |
| 281 | The same information that can be collected from the Screen and Window object |
| 282 | can also be collected via CSS Media Queries. |
| 283 | |
| 284 | WebKit currently does not offer a means of countering this. |
| 285 | |
| 286 | * https://bugs.webkit.org/show_bug.cgi?id=50895 |
| 287 | |
| 288 | === ii. CSS Fonts === |
| 289 | |
| 290 | CSS rules may be used to inspect locally available fonts. A working example of |
| 291 | this 'font introspection' using simple CSS rules can be found at |
| 292 | http://flippingtypical.org. |
| 293 | |
| 294 | WebKit currently does not offer a means of countering this. |
| 295 | |
| 296 | There needs to be a mechanism for allowing WebKit clients to decide which |
| 297 | fonts are locally available when CSS rules are evaluated. |
| 298 | |
| 299 | See also Fonts. |
| 300 | |
| 301 | === iii. Querying Page History with CSS === |
| 302 | |
| 303 | There is a well-known attack on the CSS 'visited:' rule that allows a CSS |
| 304 | stylesheet to determine by brute-force which websites the user has visited. |
| 305 | A good examples of this in action is available at: |
| 306 | * http://ha.ckers.org/weird/CSS-history.cgi |
| 307 | |
| 308 | This method can inspect user history across sessions and is deployed by live |
| 309 | websites. |
| 310 | |
| 311 | WebKit has mitigated against this attack since the implementation of |
| 312 | https://bugs.webkit.org/show_bug.cgi?id=24300, based on the approach outlined |
| 313 | in http://dbaron.org/mozilla/visited-privacy. |
| 314 | |
| 315 | |
| 316 | == 4. Plugins and Java Applets == |
| 317 | |
| 318 | If you want complete control over the information your browser reveals to |
| 319 | websites then you can't let your browser run someone else's |
| 320 | executable code. That means plugins and Java applets. That also means a pretty |
| 321 | unusable browser by most people's lights. |
| 322 | |
| 323 | WebKit does not offer you very much in this category. There is no sandboxing |
| 324 | of NPAPI plugins - all WebKit ports currently dlopen() the binary blob and let |
| 325 | it run with the user's privileges. In the case of a malicious plugin this |
| 326 | means relatively unfettered OS access. In the case of well-defined plugins such |
| 327 | as Flash there is |
| 328 | still scope for collecting a lot of information. |
| 329 | |
| 330 | === i. Using the List of Installed Plugins To Build Up A Fingerprint === |
| 331 | |
| 332 | As well as isolating users who have an exotic set of installed plugins, a major |
| 333 | source of entropy is found in the micro-version information provided |
| 334 | by navigator.plugins. There is scope here for WebKit to limit the version |
| 335 | information at the request of the client. |
| 336 | |
| 337 | Apart from querying navigator.plugins, a page can attempt to instantiate as |
| 338 | many plugins as possible and inspect the page's layout to see what the user |
| 339 | has installed. There are a few possible countermeasures here of varying |
| 340 | efficacy, though none have been tried in practice: |
| 341 | |
| 342 | * Limit the number of plugins that can be loaded on a single page |
| 343 | * Prevent zero-size plugins from loading |
| 344 | * Disable plugins completely |
| 345 | * Only load individual plugins at the user's request - e.g. 'Click to Flash' |
| 346 | |
| 347 | Note that blocking plugins can actually improve the quality of fingerprint: |
| 348 | http://panopticlick.eff.org/browser-uniqueness.pdf finds that the uniqueness |
| 349 | of flash-blocking browsers is over 1 in 400,000. |
| 350 | |
| 351 | === ii. Collecting System Fonts via Flash Plugins === |
| 352 | |
| 353 | The Adobe Flash API allows flash applications to obtain a list of all the |
| 354 | fonts installed on a system in unsorted order. This is a very rich source of |
| 355 | entropy for anyone trying to fingerprint a user. Worse, it is completely |
| 356 | outside the control of WebKit and the client browser. |
| 357 | |
| 358 | Short of running your browser in a chroot'ed jail there is nothing you can do |
| 359 | to prevent Flash inspecting your file system for the fonts folder and |
| 360 | supplying the list of fonts installed there to swf objects that request |
| 361 | it. |
| 362 | |
| 363 | This is a problem you will have to address at application-level for now. There |
| 364 | are no open bugs for introducing plugin-sandboxing (i.e. intercepting plugin's |
| 365 | system calls) in WebKit at the time of writing. |
| 366 | |
| 367 | === iii. Flash Cookies (Local Shared Objects) === |
| 368 | |
| 369 | You should read (it's |
| 370 | short): http://papers.ssrn.com/sol3/papers.cfm?abstract_id=1446862 |
| 371 | |
| 372 | From the paper: |
| 373 | "Flash data is stored in a different folder on different computing platforms. |
| 374 | For instance, on an Apple, Flash local shared objects (labeled .sol) are |
| 375 | stored at: /users/[username]/Library/Preferences/Macromedia/Flash Player/ |
| 376 | On a Windows computer, they are stored at: |
| 377 | \Documents and Settings\[username]\Application Data\Macromedia |
| 378 | \Flash Player |
| 379 | Several subdirectories may reside at that location: |
| 380 | “#SharedObjects” contains the actual Flash cookies and subdirectories under |
| 381 | “Macromedia.com” contains persistent global and domain-specific settings for |
| 382 | how the Flash player operates. As such, there will be a subdirectory for each |
| 383 | Flash-enabled domain a user visits under the “Macromedia.com” settings |
| 384 | folder. |
| 385 | This has privacy implications .." |
| 386 | |
| 387 | So Flash LSOs are used by a number of well-known sites to regenerate or respawn |
| 388 | HTTP cookies the user has already deleted. |
| 389 | |
| 390 | Fortunately, Adobe Flash has supported private browsing since Flash |
| 391 | Player 10.1 and does not store flash cookies when in private browsing mode. |
| 392 | |
| 393 | |
| 394 | == 5. SilverLight And ActiveX == |
| 395 | TBC |
| 396 | |
| 397 | == 6. Fonts == |
| 398 | |
| 399 | A site may render a page in a number of different fonts and then use |
| 400 | getComputedSize() to determine which were rendered correctly. |
| 401 | |
| 402 | WebKit currently does not offer a means of countering this. |
| 403 | |
| 404 | There needs to be a mechanism for allowing WebKit clients to decide which |
| 405 | fonts are locally available when the page is rendered. |
| 406 | |
| 407 | |
| 408 | == 7. Cookies == |
| 409 | |
| 410 | You either have these (i) disabled completely, (ii) clear them every time a new |
| 411 | browsing session starts, or (iii) clear them every N minutes/hours. Since |
| 412 | Private Browsing already takes care of (ii), you may decide that (i) and (iii) |
| 413 | are not worth the trouble. Whatever you decide, you need to be consistent with |
| 414 | your implementation of the Page Cache. |
| 415 | |
| 416 | == 8. Third Party Cookies == |
| 417 | |
| 418 | If you are clearing all cookies periodically then third-party cookies are not |
| 419 | something you need to worry about any more than their first-party cousin. |
| 420 | |
| 421 | The default behaviour of third-party cookies is the subject of a lot of |
| 422 | interoperability issues between browsers. Firefox has a proposal in active |
| 423 | development and there has been some good discussion in at least one WebKit |
| 424 | bug. |
| 425 | |
| 426 | Most WebKit ports offer you the possibility of managin third-party cookies |
| 427 | however you choose, and the default behaviour between WebKit ports often |
| 428 | differs - Safari is the most restrictive as it does not allow 3rd parties to |
| 429 | set new cookies, though they can update existing ones. |
| 430 | |
| 431 | * https://bugs.webkit.org/show_bug.cgi?id=35824 |
| 432 | * https://wiki.mozilla.org/Thirdparty |
| 433 | * https://bugzilla.mozilla.org/show_bug.cgi?id=565965 |
| 434 | |
| 435 | Qt does not offer a means of identifying third party cookies yet: |
| 436 | * https://bugs.webkit.org/show_bug.cgi?id=45455 |
| 437 | |
| 438 | == 9. Page Cache == |
| 439 | |
| 440 | The [http://samy.pl/evercookie/ evercookie] is an excellent practical |
| 441 | demonstration of how a website can |
| 442 | inspect the browser's cache to determine if the user has visited the site |
| 443 | before. So if your browser has disabled cookies completely or disables cookie |
| 444 | storage across sessions or over long periods of time, you will need to treat |
| 445 | the page cache in the same way. |
| 446 | |
| 447 | == 10. HTTP Headers == |
| 448 | |
| 449 | You will need to decide what to do with the Referer header, the Origin header, |
| 450 | the Accept header, and the Accept-Language header. |
| 451 | |
| 452 | === i. User-Agent Header === |
| 453 | |
| 454 | Whatever decision you make about the User-Agent header, be prepared to stick |
| 455 | with the values you set initially for as long as possible. |
| 456 | |
| 457 | === ii. Referer Header and Origin Header === |
| 458 | |
| 459 | Manipulating these headers isn't strictly a fingerprinting-resistance |
| 460 | requirement, however they do leak information about the user's browsing |
| 461 | history. Manipulating them can break website behaviour and may even get your |
| 462 | browser blacklisted by certain sites. |
| 463 | |
| 464 | Possible countermeasures in the case of FireFox are discussed at: |
| 465 | https://bugzilla.mozilla.org/show_bug.cgi?id=587523. The best suggestion |
| 466 | there is to scrub the path but not the domain from the referrer header. |
| 467 | |
| 468 | * https://bugs.webkit.org/show_bug.cgi?id=51638 |
| 469 | |
| 470 | === iii. Accept-Language Header === |
| 471 | |
| 472 | This should be consistent with the value you choose to return from |
| 473 | the Javascript Language object, e.g.'en-us'. |
| 474 | |
| 475 | === iv. Accept Header === |
| 476 | |
| 477 | The entropy provided by an Accept header will depend largely on the language |
| 478 | and charsets your browser has decided to support or permit to the user while |
| 479 | in fingerprinting-resistance mode. |
| 480 | |
| 481 | === v. HTTP ETags === |
| 482 | [http://en.wikipedia.org/wiki/HTTP_ETag ETags] can be used as a [http://en.wikipedia.org/wiki/HTTP_ETag#Tracking_using_ETags substitute for HTTP cookies] and |
| 483 | this use has been [http://samy.pl/evercookie/ demonstrated in practice]. |
| 484 | |
| 485 | WebKit supports ETags. Implementing the recommendations in Page Cache will |
| 486 | mitigate against their use by preventing them from persisting across sessions |
| 487 | and even long-running browser sessions if you're prepared to implement an |
| 488 | aggressive cache-clearing policy. |
| 489 | |
| 490 | == 11. DOM LocalStorage/DOM SessionStorage/DOM GlobalStorage == |
| 491 | Private browsing in WebKit denies read and write access to DOM storage since |
| 492 | https://bugs.webkit.org/show_bug.cgi?id=49329. |
| 493 | |
| 494 | == 12. GeoLocation == |
| 495 | |
| 496 | You will, um, need to ensure you disable geolocation if it supported by your |
| 497 | chosen WebKit port. |
| 498 | |
| 499 | = Further Reading = |
| 500 | |
| 501 | * http://panopticlick.eff.org/browser-uniqueness.pdf |
| 502 | * https://www.torproject.org/torbutton/design/#FirefoxBugs |
| 503 | * http://browserspy.dk |
| 504 | *http://blog.torproject.org/blog/firefox-private-browsing-mode-torbutton-and- |
| 505 | fingerprinting |
| 506 | * http://www.collinjackson.com/research/private-browsing.pdf |
| 507 | * https://wiki.mozilla.org/Security/Anonymous_Browsing |
| 508 | * https://wiki.mozilla.org/Security/Fingerprinting |
| 509 | * http://samy.pl/evercookie/ |
| 510 | * http://dbaron.org/mozilla/visited-privacy |
| 511 | * https://wiki.mozilla.org/Thirdparty |
| 512 | * http://lists.macosforge.org/pipermail/webkit-dev/2009-May/007788.html |
| 513 | * http://flippingtypical.org |