5

For added security our server keeps track of the browser fingerprint. At the moment we use the following headers:

  • HTTP_CLIENT_IP, HTTP_X_FORWARDED_FOR, HTTP_X_FORWARDED, HTTP_X_CLUSTER_CLIENT_IP, HTTP_FORWARDED_FOR, HTTP_FORWARDED, REMOTE_ADDR (take the first non-empty as the client-IP)
  • HTTP_ACCEPT_* headers
  • HTTP_USER_AGENT

Are there any more (optional) headers that can be used?

What in general is the best 'algorithm' to calculate the client fingerprint?

Trece
  • 37
  • 6
Patrick Savalle
  • 4,068
  • 3
  • 22
  • 24
  • just for your information, all these information can be spoofed, however, USER_AGENT and IP address can be used... –  Jul 24 '13 at 08:30
  • 2
    You are enabling anyone to spoof anyone else's fingerprint here, congratulations. Only `REMOTE_ADDR` is guaranteed to be correct, anything else is arbitrary user supplied information. You should never ever use any alternative \*_IP headers, unless you know exactly that a proxy under your control has set them. That's pretty much the problem with any sort of fingerprinting by HTTP headers in a nutshell as well. – deceze Jul 24 '13 at 08:31
  • @deceze: for IP, not all users has public IP, many users might use one public IP (in my country at least 10,000 users are behind one IP) –  Jul 24 '13 at 08:34
  • Deceze: note the word 'added' before 'security'. – Patrick Savalle Jul 24 '13 at 08:46
  • @Akam Exactly, which makes IPs even less useful for fingerprinting. I'd guess a large number of those 10,000 people are running the same version of Windows and IE, so the other headers will be identical too. – deceze Jul 24 '13 at 08:46
  • @deceze; yes exactly you get the point, I am working at that ISP and I can see many similar users... –  Jul 24 '13 at 08:48
  • 1
    @Patrick Sure, I wasn't expecting this to be your only security. It's still problematic due to the above mentioned issues to the point where it makes little sense to add it. Further, IPs can change for perfectly valid reasons at any time, so requiring them to stay constant can be quite annoying to the user. – deceze Jul 24 '13 at 08:49
  • Yep, I just reached the same conclusion :) – Patrick Savalle Jul 24 '13 at 09:12

2 Answers2

3

you can use a unique browser fingerprint (user agent, web browser, canvas, etc) and after get the hash.

/* Generate a fingerprint string for the browser */
function generateFingerprint(){
//Generate a string based on "stable" information taken from the browser
//We call here "stable information", information that normally don't   change during the user
//browse the application just after authentication
var fingerprint = [];

//Take plugins
for(var i = 0; i < navigator.plugins.length; i++){
   fingerprint.push(navigator.plugins[i].name);
   fingerprint.push(navigator.plugins[i].filename);
   fingerprint.push(navigator.plugins[i].description);
   fingerprint.push(navigator.plugins[i].version);
}

//Take User Agent
fingerprint.push(navigator.userAgent);

//Take Screen resolution
fingerprint.push(screen.availHeight);
fingerprint.push(screen.availWidth);
fingerprint.push(screen.colorDepth);
fingerprint.push(screen.height);
fingerprint.push(screen.pixelDepth);
fingerprint.push(screen.width);

//Take Graphical card info
//See http://output.jsbin.com/ovekor/3/
try {
    //Add a Canvas element if the body do not contains one
    if ( $("#glcanvas").length == 0 ){
        $(document.body).append("<canvas id='glcanvas'></canvas>");
    }
    //Get ref on Canvas
    var canvas = document.getElementById("glcanvas");
    //Retrieve Canvas properties
    gl = canvas.getContext("experimental-webgl");
    gl.viewportWidth = canvas.width;
    gl.viewportHeight = canvas.height;
    fingerprint.push(gl.getParameter(gl.VERSION));
    fingerprint.push(gl.getParameter(gl.SHADING_LANGUAGE_VERSION));
    fingerprint.push(gl.getParameter(gl.VENDOR));
    fingerprint.push(gl.getParameter(gl.RENDERER));
    fingerprint.push(gl.getSupportedExtensions().join());
} catch (e) {
    //Get also error because it's will be stable too..
    fingerprint.push(e);
}

//Last and, in order to made this browser unique, generate a random ID that we will store
//in local storage (in order to be persistent after browser close/reopen)
//Add this ID because, in Enterprise, most of the time browser have the same configuration
var browserUniqueID = localStorage.getItem("browserUniqueID");
if (browserUniqueID === null) {
  localStorage.setItem("browserUniqueID", CryptoJS.lib.WordArray.random(80));
  browserUniqueID = localStorage.getItem("browserUniqueID");
}
fingerprint.push(browserUniqueID);

return fingerprint.join();
}

And finally get the hash and sent to the server.

//Call the fingerprint dedicated function
var fingerprint = generateFingerprint();
//Use CryptoJS library ot generate a hex encoded string of the hash of the fingerprint
var fingerprintHash = CryptoJS.SHA256(fingerprint);

Source: https://www.owasp.org/index.php/JSON_Web_Token_(JWT)_Cheat_Sheet_for_Java#Token_sidejacking https://browserleaks.com/canvas

Mr SC
  • 162
  • 2
  • 11
0

To answer the question of which naming for the header:

I used on my side X-Fingerprint but I didn't find any standard way. You can make it more "obscure" so people don't guess that's a field to tamper.

At the end from what I saw headers like X-... are supposed to be free of use (less chance to conflict), but the last years more and more tools use them, and this kind of naming appears to become "standard" or a "convention" (like x-request-id for example).

Thomas Ramé
  • 438
  • 4
  • 10