Spoof/Disable performance API when 'privacy.resistFingerprinting' is true

VERIFIED FIXED in Firefox 56

Status

()

Core
DOM
P1
normal
VERIFIED FIXED
a year ago
3 months ago

People

(Reporter: timhuang, Assigned: timhuang)

Tracking

(Blocks: 1 bug)

unspecified
mozilla56
Points:
---
Dependency tree / graph

Firefox Tracking Flags

(firefox56 fixed)

Details

(Whiteboard: [fingerprinting][tor][fp:m2])

MozReview Requests

()

Submitter Diff Changes Open Issues Last Updated
Loading...
Error loading review requests:

Attachments

(4 attachments)

(Assignee)

Description

a year ago
For fingerprinting resistance, we'd like to spoof/disable performance API when 'privacy.resistFingerprinting' is true.
* Spoof performance timing API
* Disable resource timing
* Disable user timing API

Some reference prefs 
* dom.enable_performance
* dom.enable_resource_timing
* dom.enable_user_timing, this was removed, but we want to add a similar feature back.

Updated

a year ago
Priority: -- → P1
(Assignee)

Comment 1

a year ago
The browser timing attacks can be easily deployed with these APIs. Therefore, there APIs should be spoofed or disabled when fingerprinting resistance is enabled. Following things will happen after 'privacy.resistFingerprinting' been set to true.

1. Every item of performance timing should be spoofed to zero.
2. Disable resource timing API
3. Disable user timing API
Comment hidden (mozreview-request)
Comment hidden (mozreview-request)
Comment hidden (mozreview-request)
Comment on attachment 8874818 [details]
Bug 1369303 - Part 1: Adding 'privacy.resistFingerprinting' into worker prefs.

https://reviewboard.mozilla.org/r/146208/#review150284
Attachment #8874818 - Flags: review?(arthuredelstein) → review+
Comment on attachment 8874819 [details]
Bug 1369303 - Part 2: Marking the performance timing API always reports 0 and the access of resource timing and user timing becomes NOP when 'privacy.resistFingerprinting' is true.

https://reviewboard.mozilla.org/r/146210/#review150288
Attachment #8874819 - Flags: review?(arthuredelstein) → review+
Comment on attachment 8874820 [details]
Bug 1369303 - Part 3: Add a test case for making sure that performance APIs have been correctly spoofed.

https://reviewboard.mozilla.org/r/146212/#review150290

::: browser/components/resistfingerprinting/test/browser/browser_performanceAPI.js:33
(Diff revision 1)
> +  "loadEventStart",
> +  "loadEventEnd",
> +];
> +
> +add_task(async function setup() {
> +  await SpecialPowers.pushPrefEnv({"set":

I'm not sure you need a separate `setup` function here. Maybe merge with `run`?

::: browser/components/resistfingerprinting/test/browser/browser_performanceAPI.js:49
(Diff revision 1)
> +    for (let time of list) {
> +      is(content.performance.timing[time], 0, `The timing(${time}) is correctly spoofed.`);
> +    }
> +
> +    // Check that whether the resource timing API is disabled.
> +    ok(!content.performance.getEntries, "The performance.getEntries is disabled.");

Maybe we should be testing here explicitly for the value expected for `content.performance.getEntriesByType`. Is it `undefined`, `null`, or an empty array? (Unfortunately the expression `!0` returns `true`, though I don't expect that to occur here.)
Attachment #8874820 - Flags: review?(arthuredelstein) → review+
(Assignee)

Updated

a year ago
Blocks: 1333651
(In reply to Tim Huang[:timhuang] from comment #1)
> The browser timing attacks can be easily deployed with these APIs.

I think some citation is needed for this.  What are you trying to prevent exactly?  I don't immediately understand what these APIs have to do with fingerprinting?
Flags: needinfo?(tihuang)
(Assignee)

Comment 9

a year ago
There are several fingerprinting concerns around these APIs.

a) For performance timing API, it can be used to measure the network latency [1]. The network latency is distinct between different browsers which depends on where and how a browser connects to Web.

b) For resource timing API, it is an attack surface of cache-timing attacks [2]. By observing the timing of hits and misses of a given *third-party* resource, an attacker can extract browsing history. However, this requires third party websites have 'Timing-Allow-Origin' in the response header. Given that, this still has a fingerprinting concern.

c) For user timing API, attackers can use this to benchmark a given script for measuring a browser's performance pattern. Since the timing of executing the same script could be different depending on the hardware, so this can be used to generate a fingerprinting. [3] shows a similar idea. 





[1] http://www.darkwavetech.com/fingerprint/fingerprint_latency.html
[2] http://sip.cs.princeton.edu/pub/webtiming.pdf
[3] http://w2spconf.com/2011/papers/jspriv.pdf
Flags: needinfo?(tihuang)

Updated

a year ago
Whiteboard: [fingerprinting][tor][fp:m1] → [fingerprinting][tor][fp:m2]

Comment 10

a year ago
mozreview-review
Comment on attachment 8874818 [details]
Bug 1369303 - Part 1: Adding 'privacy.resistFingerprinting' into worker prefs.

https://reviewboard.mozilla.org/r/146208/#review152326
Attachment #8874818 - Flags: review?(amarchesini) → review+

Comment 11

a year ago
mozreview-review
Comment on attachment 8874819 [details]
Bug 1369303 - Part 2: Marking the performance timing API always reports 0 and the access of resource timing and user timing becomes NOP when 'privacy.resistFingerprinting' is true.

https://reviewboard.mozilla.org/r/146210/#review152328

I don't like the disabling of methods in Performance interfaces.
I suggest to make those methods NOP.
Attachment #8874819 - Flags: review?(amarchesini) → review-

Comment 12

a year ago
mozreview-review
Comment on attachment 8874820 [details]
Bug 1369303 - Part 3: Add a test case for making sure that performance APIs have been correctly spoofed.

https://reviewboard.mozilla.org/r/146212/#review152334

Canceling review request
Attachment #8874820 - Flags: review?(amarchesini)
Comment hidden (mozreview-request)
Comment hidden (mozreview-request)
Comment hidden (mozreview-request)
(Assignee)

Comment 16

a year ago
Comment on attachment 8874819 [details]
Bug 1369303 - Part 2: Marking the performance timing API always reports 0 and the access of resource timing and user timing becomes NOP when 'privacy.resistFingerprinting' is true.

Arthur, please review these patches again, especially for part 2 and 3.
Attachment #8874819 - Flags: review+ → review?(arthuredelstein)
(Assignee)

Updated

a year ago
Attachment #8874820 - Flags: review+ → review?(arthuredelstein)

Comment 17

a year ago
mozreview-review
Comment on attachment 8874819 [details]
Bug 1369303 - Part 2: Marking the performance timing API always reports 0 and the access of resource timing and user timing becomes NOP when 'privacy.resistFingerprinting' is true.

https://reviewboard.mozilla.org/r/146210/#review152934

you cannot use nsContentUtils::ShouldResistFingerprinting() in workers as it is. You must have a worker-version of it.

::: dom/performance/Performance.cpp:175
(Diff revision 2)
>  
>  void
>  Performance::GetEntries(nsTArray<RefPtr<PerformanceEntry>>& aRetval)
>  {
> +  // We return an empty list when 'privacy.resistFingerprinting' is on.
> +  if (nsContentUtils::ShouldResistFingerprinting()) {

Same issue here.

::: dom/performance/Performance.cpp:190
(Diff revision 2)
>  void
>  Performance::GetEntriesByType(const nsAString& aEntryType,
>                                nsTArray<RefPtr<PerformanceEntry>>& aRetval)
>  {
> +  // We return an empty list when 'privacy.resistFingerprinting' is on.
> +  if (nsContentUtils::ShouldResistFingerprinting()) {

Same issue here.

::: dom/performance/Performance.cpp:220
(Diff revision 2)
>                                nsTArray<RefPtr<PerformanceEntry>>& aRetval)
>  {
>    aRetval.Clear();
>  
> +  // We return an empty list when 'privacy.resistFingerprinting' is on.
> +  if (nsContentUtils::ShouldResistFingerprinting()) {

this is wrong.. getEntriesByName() is exposed to workers.

::: dom/performance/Performance.cpp:282
(Diff revision 2)
>  
>  void
>  Performance::Mark(const nsAString& aName, ErrorResult& aRv)
>  {
> +  // We add nothing when 'privacy.resistFingerprinting' is on.
> +  if (nsContentUtils::ShouldResistFingerprinting()) {

This is wrong. Mark() is exposed to workers.
Attachment #8874819 - Flags: review?(amarchesini) → review-

Comment 18

a year ago
mozreview-review
Comment on attachment 8874820 [details]
Bug 1369303 - Part 3: Add a test case for making sure that performance APIs have been correctly spoofed.

https://reviewboard.mozilla.org/r/146212/#review152936
Attachment #8874820 - Flags: review?(amarchesini) → review+
Comment on attachment 8874820 [details]
Bug 1369303 - Part 3: Add a test case for making sure that performance APIs have been correctly spoofed.

https://reviewboard.mozilla.org/r/146212/#review153174
Attachment #8874820 - Flags: review?(arthuredelstein) → review+
Comment hidden (mozreview-request)
Comment hidden (mozreview-request)
Comment hidden (mozreview-request)
Comment on attachment 8877460 [details]
Bug 1369303 - Part 4: Add a test case for workers to check whether performance API has been correctly spoofed when 'privacy.resistFingerprinting' is true.

https://reviewboard.mozilla.org/r/148886/#review153718
Attachment #8877460 - Flags: review?(arthuredelstein) → review+

Comment 24

a year ago
mozreview-review
Comment on attachment 8874819 [details]
Bug 1369303 - Part 2: Marking the performance timing API always reports 0 and the access of resource timing and user timing becomes NOP when 'privacy.resistFingerprinting' is true.

https://reviewboard.mozilla.org/r/146210/#review153828

::: dom/base/nsContentUtils.cpp:2335
(Diff revision 3)
>    return !isChrome && nsRFPService::IsResistFingerprintingEnabled();
>  }
>  
>  /* static */
> +bool
> +nsContentUtils::ThreadsafeShouldResistFingerprinting() {

What about if you merge this function with 
ShouldResistFingerprinting() ?

Just move this code into that function.
And add a comment in nsContentUtils.h saying that that function can be used in workers and on main-thread as well.

::: dom/base/nsContentUtils.cpp:2340
(Diff revision 3)
> +nsContentUtils::ThreadsafeShouldResistFingerprinting() {
> +  if (NS_IsMainThread()) {
> +    return ShouldResistFingerprinting();
> +  }
> +
> +  using namespace workers;

remove this and simply do:

workers::WorkerPrivate* workerPrivate = workers::GetCurrentThreadWorkerPrivate();
...

or add it at the beginning of the file: using namespace mozilla::dom::workers;

::: dom/base/nsContentUtils.cpp:2343
(Diff revision 3)
> +  }
> +
> +  using namespace workers;
> +
> +  WorkerPrivate* workerPrivate = GetCurrentThreadWorkerPrivate();
> +  MOZ_ASSERT(workerPrivate);

I prefer:

if (NS_WARN_IF(!workerPrivate)) {
  return false;
}

...
Attachment #8874819 - Flags: review?(amarchesini) → review+

Comment 25

a year ago
mozreview-review
Comment on attachment 8877460 [details]
Bug 1369303 - Part 4: Add a test case for workers to check whether performance API has been correctly spoofed when 'privacy.resistFingerprinting' is true.

https://reviewboard.mozilla.org/r/148886/#review153830
Attachment #8877460 - Flags: review?(amarchesini) → review+
Comment hidden (mozreview-request)
Comment hidden (mozreview-request)
Comment hidden (mozreview-request)
Comment hidden (mozreview-request)
Comment hidden (mozreview-request)
Comment hidden (mozreview-request)
Comment hidden (mozreview-request)
Comment hidden (mozreview-request)
Comment hidden (mozreview-request)
Comment hidden (mozreview-request)
(Assignee)

Comment 36

a year ago
Try looks good.

https://treeherder.mozilla.org/#/jobs?repo=try&revision=04b9fee409f2
Keywords: checkin-needed

Comment 37

a year ago
Pushed by ryanvm@gmail.com:
https://hg.mozilla.org/integration/autoland/rev/f01f4f94f912
Part 1: Adding 'privacy.resistFingerprinting' into worker prefs. r=arthuredelstein,baku
https://hg.mozilla.org/integration/autoland/rev/61e7a2279d81
Part 2: Marking the performance timing API always reports 0 and the access of resource timing and user timing becomes NOP when 'privacy.resistFingerprinting' is true. r=arthuredelstein,baku
https://hg.mozilla.org/integration/autoland/rev/9c0424ddae1b
Part 3: Add a test case for making sure that performance APIs have been correctly spoofed. r=arthuredelstein,baku
https://hg.mozilla.org/integration/autoland/rev/2af3f05ec41b
Part 4: Add a test case for workers to check whether performance API has been correctly spoofed when 'privacy.resistFingerprinting' is true. r=arthuredelstein,baku
Keywords: checkin-needed

Comment 38

a year ago
bugherder
https://hg.mozilla.org/mozilla-central/rev/f01f4f94f912
https://hg.mozilla.org/mozilla-central/rev/61e7a2279d81
https://hg.mozilla.org/mozilla-central/rev/9c0424ddae1b
https://hg.mozilla.org/mozilla-central/rev/2af3f05ec41b
Status: ASSIGNED → RESOLVED
Last Resolved: a year ago
status-firefox56: --- → fixed
Resolution: --- → FIXED
Target Milestone: --- → mozilla56
Verified on Mac OS 10.12.6 with Nightly 58.0a1 (2017-10-25) (64-bit)

Verification steps:
1. Open a new tab, and navigate to http://cnn.com
2. Set parameter privacy.resistFingerprinting to true
3. Open Console, and type the following commands
3a. performance.timing
3b. performance.getEntriesByType('resource').filter(item => item.name.includes("index.html"))
3c. window.performance.getEntries()
3d. performance.getEntries({name : "Begin", entryType: "mark"})
3e. performance.getEntriesByType("mark")
3f. performance.getEntriesByName("domReady")
3g. 
performance.mark("mySetTimeout-start");
setTimeout(function() {
  performance.mark("mySetTimeout-end");
  performance.measure(
    "mySetTimeout",
    "mySetTimeout-start",
    "mySetTimeout-end"
  );
  var measures = performance.getEntriesByName("mySetTimeout");
  var measure = measures[0];
  console.log("setTimeout milliseconds:", measure.duration)

  performance.clearMarks();
  performance.clearMeasures();
}, 1000);

Expected results:
All the output should be either zero, or empty

Actual results:
There is one unexpected issue:
performance.timing returns this:
PerformanceTiming
connectEnd: 1509063389918
connectStart: 1509063389918
domComplete: 0
domContentLoadedEventEnd: 0
domContentLoadedEventStart: 0
domInteractive: 0
domLoading: 0
domainLookupEnd: 1509063389918
domainLookupStart: 1509063389918
fetchStart: 1509063390083
loadEventEnd: 0
loadEventStart: 0
navigationStart: 0
redirectEnd: 0
redirectStart: 0
requestStart: 1509063389918
responseEnd: 1509063389918
responseStart: 1509063389918
secureConnectionStart: 1509063389918
unloadEventEnd: 0
unloadEventStart: 0

All these items in the array should equal zero.
Status: RESOLVED → REOPENED
Flags: needinfo?(tihuang)
Resolution: FIXED → ---
(Assignee)

Comment 40

8 months ago
The reason why these values do not show a zero is that performance.timing seems to be cached after it has been accessed. The cnn.com will access some fields of performance.timing by itself. So, if you turn 'privacy,resistFingerprinting' after the cnn.com is loaded, it will show cached value, before fingerprinting resistance is on, instead of a zero.

Ideally, we should make the 'privacy.resistFingerprinting' works perfectly in every case. However, it is hard to cover them all. So, the ideal behavior for enabling fingerprinting resistance is that turning 'privacy.resistFingerprinting' on before loading any page. 

What do you think, Ethan and Tom? Should testing follow this principle or we need to fix this?
Flags: needinfo?(tom)
Flags: needinfo?(tihuang)
Flags: needinfo?(ettseng)

Comment 41

8 months ago
(In reply to Tim Huang[:timhuang] from comment #40)
> The reason why these values do not show a zero is that performance.timing
> seems to be cached after it has been accessed. The cnn.com will access some
> fields of performance.timing by itself. So, if you turn
> 'privacy,resistFingerprinting' after the cnn.com is loaded, it will show
> cached value, before fingerprinting resistance is on, instead of a zero.
> 
> Ideally, we should make the 'privacy.resistFingerprinting' works perfectly
> in every case. However, it is hard to cover them all. So, the ideal behavior
> for enabling fingerprinting resistance is that turning
> 'privacy.resistFingerprinting' on before loading any page. 
> 
> What do you think, Ethan and Tom? Should testing follow this principle or we
> need to fix this?

I think requiring it to be on before a page is loaded is reasonable. I presume the caching happens at the page level though, so if I enable the pref, and then navigate the page (either through an address bar navigation or a link click) the new page will work as intended, right?

Adding Arthur for visibility.

How does this interact with things that survive page loads? *Workers (Shared/Background whatever other types of Workers there are...)
Flags: needinfo?(tom) → needinfo?(arthuredelstein)
I agree that it is reasonable to make sure the parameter is switched on *before* the page is loaded. I changed the steps and the results are as expected now.

Here are the updated steps and results:

Verification steps:
1. Set parameter privacy.resistFingerprinting to true
2. Open a new tab, and navigate to http://cnn.com
3. Open Console, and type the following commands
3a. performance.timing
3b. performance.getEntriesByType('resource').filter(item => item.name.includes("index.html"))
3c. window.performance.getEntries()
3d. performance.getEntries({name : "Begin", entryType: "mark"})
3e. performance.getEntriesByType("mark")
3f. performance.getEntriesByName("domReady")
3g. 
performance.mark("mySetTimeout-start");
setTimeout(function() {
  performance.mark("mySetTimeout-end");
  performance.measure(
    "mySetTimeout",
    "mySetTimeout-start",
    "mySetTimeout-end"
  );
  var measures = performance.getEntriesByName("mySetTimeout");
  var measure = measures[0];
  console.log("setTimeout milliseconds:", measure.duration)

  performance.clearMarks();
  performance.clearMeasures();
}, 1000);

Expected results:
All the output should be either zero, or empty

Actual results:
Same as expected results


I will let Tim and Arthur answer the other questions from Tom Ritter.

Comment 43

8 months ago
(In reply to Tom Ritter [:tjr] from comment #41)
> How does this interact with things that survive page loads? *Workers
> (Shared/Background whatever other types of Workers there are...)

For the record.  We discussed this issue in a meeting.

The conclusion:
We are still concerned about caching in the event of background workers and other things that survive page loads.
We will file a new bug to track it.
Flags: needinfo?(ettseng)
Flags: needinfo?(arthuredelstein)

Comment 44

8 months ago
(In reply to Tom Grabowski [:TomGrab] from comment #42)
> I agree that it is reasonable to make sure the parameter is switched on
> *before* the page is loaded. I changed the steps and the results are as
> expected now.
> Expected results:
> All the output should be either zero, or empty
> 
> Actual results:
> Same as expected results

Thanks, Tom!
Then we can close this bug. :)
Status: REOPENED → RESOLVED
Last Resolved: a year ago8 months ago
Resolution: --- → FIXED
Status: RESOLVED → VERIFIED

Updated

4 months ago
See Also: → bug 1443701
You need to log in before you can comment on or make changes to this bug.