Opened 4 years ago

Closed 7 months ago

#18279 closed defect (fixed)

Javascript setTimeout can be used for high resolution clock

Reported by: cypherpunks Owned by: tbb-team
Priority: Medium Milestone:
Component: Applications/Tor Browser Version:
Severity: Normal Keywords:
Cc: gk Actual Points:
Parent ID: #16110 Points:
Reviewer: Sponsor:

Description

Using Javascript setTimeout, around 20ms timer resolution can be achieved. This is likely sufficient for input fingerprinting.

Simple demo that logs a counter to the console.

<html>
<body onload="logTime()">
<p>Test</p>

<script>
var counter = 0;

function logTime() {
  counter += 1;
  var today = new Date();
  console.log(today.getSeconds() + "." + today.getMilliseconds() + " " + counter);
  var t = setTimeout(logTime, 1);
}
</script>

</body>
</html>

Child Tickets

Attachments (2)

timing.png (28.5 KB) - added by Thorin 7 months ago.
sample of code run
timing=noRFP.png (34.1 KB) - added by Thorin 7 months ago.

Download all attachments as: .zip

Change History (12)

comment:1 Changed 4 years ago by gk

Cc: gk added

comment:2 Changed 4 years ago by cypherpunks

How about high resolution clock based on while loops to make delays instead of setTimeout?

comment:3 Changed 4 years ago by arthuredelstein

A similar thing can be done with window.setInterval.

Changed 7 months ago by Thorin

Attachment: timing.png added

sample of code run

comment:4 Changed 7 months ago by Thorin

This can be closed as resolved fixed: unless you wanted to test them over workers, service workers (which are disabled in Private Mode), iframes. Timing is clamped by RFP

setTimeout

<html><title>setTimeout</title><body onload="run()"><html>
<body>
<p>setTimeout</p>
<button id="run" onClick="run()">run again</button><br><br>
<textarea rows="50" id="r"></textarea>
<script>
var counter=1; var r="";
function logTime() {
  if (counter < 201) {
		var today = new Date();
		r = r+(today.getSeconds() +"."+ today.getMilliseconds() +" "+ counter +"\n");
		var t = setTimeout(logTime, 1);
		counter += 1;
		if (counter == 201) {document.getElementById("r").innerHTML=r}
	};
};
function run() {
	counter=1; r="";
	document.getElementById("r").innerHTML=r;
	logTime();
};
</script>
</body>
</html>

setInterval

<html><title>setInterval</title><body onload="run()"><html>
<body>
<p>setInterval</p>
<button id="run" onClick="run()">run again</button><br><br>
<textarea rows="50" id="r"></textarea>
<script>
var counter = 1; var r="";
function logTime() {
	setInterval(function(){
		if (counter < 201) {
			var today = new Date();
			r = r+(today.getSeconds() +"."+ today.getMilliseconds() +" "+ counter +"\n");
			counter += 1;
			if (counter == 201) {document.getElementById("r").innerHTML=r;}
		};
	}, 10);
};
function run() {
	counter = 1; r="";
	document.getElementById("r").innerHTML=r;
	logTime();
};
</script>
</body>
</html>

Changed 7 months ago by Thorin

Attachment: timing=noRFP.png added

comment:5 Changed 7 months ago by Thorin

Added another image to illustrate what happens when RFP is disabled

comment:6 Changed 7 months ago by Thorin

FWIW I think you should lock RFP except in alpha

comment:7 in reply to:  4 Changed 7 months ago by gk

Replying to Thorin:

This can be closed as resolved fixed: unless you wanted to test them over workers, service workers (which are disabled in Private Mode), iframes. Timing is clamped by RFP

Hrm, you mean timing is not clamped in those cases?

comment:8 Changed 7 months ago by Thorin

Tom did all the timing into the privacy.resistFingerprinting pref (that's what I call RFP). He put them behind two prefs (see at least https://bugzilla.mozilla.org/show_bug.cgi?id=1217238 & https://bugzilla.mozilla.org/show_bug.cgi?id=1369303 - in FF55/56)

  • privacy.resistFingerprinting.reduceTimerPrecision.jitter
  • privacy.resistFingerprinting.reduceTimerPrecision.microseconds

dom.enable_resource_timing & dom.enable_performance are two prefs I can think of that no longer make a difference, when RFP = true. And dom.event.highrestimestamp.enabled must be true - that pref has just been removed anyway (https://bugzilla.mozilla.org/show_bug.cgi?id=1485264).

Hrm, you mean timing is not clamped in those cases

Absolutely. What I'm saying is that that without RFP=true (and there is more tied behind it than just timing as you know), then you lose everything. Example: disable RFP, run the two timing tests, you leak high precision timing. Hence I think you should lock RFP :) Just saying

Might pay to ask tom, because I'm not an expert

comment:9 Changed 7 months ago by Thorin

Sorry, you meant are they clamped over workers, service workers, iframes: no idea. I would think they are fine over workers: see

.timeStamp:

comment:10 Changed 7 months ago by gk

Resolution: fixed
Status: newclosed

Okay, thanks. I think we are good here to close this ticket.

Note: See TracTickets for help on using tickets.