Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

get-browser-fingerprint

Package Overview
Dependencies
Maintainers
1
Versions
13
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

get-browser-fingerprint - npm Package Compare versions

Comparing version 2.0.1 to 2.1.1

.github/workflows/commit.yml

39

package.json
{
"name": "get-browser-fingerprint",
"version": "2.0.1",
"author": "Damiano Barbati <damiano.barbati@gmail.com> (http://github.com/damianobarbati)",
"repository": "https://github.com/damianobarbati/get-browser-fingerprint",
"license": "MIT",
"main": "src/index.js",
"type": "module",
"scripts": {
"eslint": "eslint --ignore-path .gitignore",
"prettier": "prettier --ignore-unknown",
"test": "node src/index.spec.js || echo 'test failed'"
},
"devDependencies": {
"@babel/eslint-parser": "^7.13.14",
"eslint": "^7.26.0",
"eslint-config-prettier": "^8.1.0",
"eslint-plugin-editorconfig": "^3.0.2",
"eslint-plugin-prettier": "^3.3.1",
"http-server": "^0.12.3",
"prettier": "^2.2.1",
"puppeteer": "^9.1.1"
}
"name": "get-browser-fingerprint",
"version": "2.1.1",
"author": "Damiano Barbati <damiano.barbati@gmail.com> (https://github.com/damianobarbati)",
"repository": "https://github.com/damianobarbati/get-browser-fingerprint",
"license": "MIT",
"main": "src/index.js",
"type": "module",
"scripts": {
"eslint": "eslint --ignore-path .gitignore --fix",
"test": "NODE_OPTIONS='--experimental-vm-modules' jest --runInBand --no-cache"
},
"devDependencies": {
"eslint-config-xs": "^1.3.0",
"http-server": "^14.1.1",
"jest": "^28.1.3",
"puppeteer": "^16.1.1"
}
}
# get-browser-fingerprint
Zero dependencies package exporting a single function which computes a browser fingerprint.
Zero dependencies package exporting a single, fast (<15ms) and synchronous function which computes a browser fingerprint, without requiring any permission to the user.

@@ -8,3 +8,3 @@ ## Usage

Get browser fingerprint:
```javascript
```js
import getBrowserFingerprint from 'get-browser-fingerprint';

@@ -16,8 +16,20 @@ const fingerprint = getBrowserFingerprint();

Options available:
- `enableWebgl`: enable webgl renderer, 5x times slower but deadly powerful (default `false`)
- `debug`: log data used to generate fingerprint to console (default `false`)
- `hardwareOnly` (default `false`): leverage only hardware info about device
- `enableWebgl` (default `false`): enable webgl renderer, ~4x times slower but adds another deadly powerful hardware detection layer on top of canvas
- `debug`: log data used to generate fingerprint to console and add canvas/webgl canvas to body to see rendered image (default `false`)
## Disclaimer
⚠️ Be careful: the strongest discriminating factor is canvas token which can't be computed on old devices (eg: iPhone 6), deal accordingly ⚠️
Be careful:
- strongest discriminating factor is canvas token which can't be computed on old devices (eg: iPhone 6)
## Development
To test locally:
```sh
nvm install
yarn install
yarn test
```
To run example locally:
```sh
yarn http-server src -o -c-1 -p 80
```

@@ -1,237 +0,248 @@

export default ({ enableWebgl = false, debug = false } = {}) => {
let { devicePixelRatio } = window;
// weird behaviour when getting value from localhost vs ip!!!
devicePixelRatio = +parseInt(devicePixelRatio);
const getBrowserFingerprint = ({ hardwareOnly = false, enableWebgl = false, debug = false } = {}) => {
const devicePixelRatio = +parseInt(window.devicePixelRatio);
const {
appName,
appCodeName,
appVersion,
cookieEnabled,
const {
appName,
appCodeName,
appVersion,
cookieEnabled,
deviceMemory,
doNotTrack,
hardwareConcurrency,
language,
languages,
maxTouchPoints,
platform,
product,
productSub,
userAgent,
vendor,
vendorSub,
} = window.navigator;
const { width, height, colorDepth, pixelDepth } = window.screen;
const timezoneOffset = new Date().getTimezoneOffset();
const timezone = Intl.DateTimeFormat().resolvedOptions().timeZone;
const touchSupport = 'ontouchstart' in window;
const canvas = getCanvasID(debug);
const webgl = enableWebgl ? getWebglID(debug) : undefined; // undefined will remove this from the stringify down here
const webglInfo = enableWebgl ? getWebglInfo(debug) : undefined; // undefined will remove this from the stringify down here
const data = hardwareOnly
? JSON.stringify({
canvas,
colorDepth,
deviceMemory,
doNotTrack,
devicePixelRatio,
hardwareConcurrency,
language,
languages,
height,
maxTouchPoints,
pixelDepth,
platform,
product,
productSub,
userAgent,
vendor,
vendorSub,
webdriver,
} = window.navigator;
const plugins = Object.entries(window.navigator.plugins).map(([, plugin]) => plugin.name);
const mimeTypes = Object.entries(window.navigator.mimeTypes).map(([, mimeType]) => mimeType.type);
const { width, height, colorDepth, pixelDepth } = window.screen;
const timezoneOffset = new Date().getTimezoneOffset();
const timezone = Intl.DateTimeFormat().resolvedOptions().timeZone;
const touchSupport = 'ontouchstart' in window;
const canvas = getCanvasID(debug);
const webgl = enableWebgl ? getWebglID(debug) : null;
const webglInfo = getWebglInfo(debug);
const data = {
devicePixelRatio,
touchSupport,
webgl,
webglInfo,
width,
})
: JSON.stringify({
appCodeName,
appName,
appCodeName,
appVersion,
canvas,
colorDepth,
cookieEnabled,
deviceMemory,
devicePixelRatio,
doNotTrack,
hardwareConcurrency,
height,
language,
languages,
maxTouchPoints,
mimeTypes,
pixelDepth,
platform,
plugins,
product,
productSub,
timezone,
timezoneOffset,
touchSupport,
userAgent,
vendor,
vendorSub,
webdriver,
width,
height,
colorDepth,
pixelDepth,
timezoneOffset,
timezone,
touchSupport,
canvas,
webgl,
webglInfo,
};
width,
});
const datastring = JSON.stringify(data, null, 4);
const datastring = JSON.stringify(data, null, 4);
if (debug) console.log('fingerprint data', datastring);
if (debug) console.log('fingerprint data', datastring);
const result = murmurhash3_32_gc(datastring);
return result;
const result = murmurhash3_32_gc(datastring);
return result;
};
export const getCanvasID = (debug) => {
try {
const canvas = document.createElement('canvas');
const ctx = canvas.getContext('2d');
const text = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ`~1!2@3#4$5%6^7&8*9(0)-_=+[{]}|;:',<.>/?";
ctx.textBaseline = 'top';
ctx.font = "14px 'Arial'";
ctx.textBaseline = 'alphabetic';
ctx.fillStyle = '#f60';
ctx.fillRect(125, 1, 62, 20);
ctx.fillStyle = '#069';
ctx.fillText(text, 2, 15);
ctx.fillStyle = 'rgba(102, 204, 0, 0.7)';
ctx.fillText(text, 4, 17);
try {
const canvas = document.createElement('canvas');
const ctx = canvas.getContext('2d');
const text = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ`~1!2@3#4$5%6^7&8*9(0)-_=+[{]}|;:',<.>/?";
ctx.textBaseline = 'top';
ctx.font = "14px 'Arial'";
ctx.textBaseline = 'alphabetic';
ctx.fillStyle = '#f60';
ctx.fillRect(125, 1, 62, 20);
ctx.fillStyle = '#069';
ctx.fillText(text, 2, 15);
ctx.fillStyle = 'rgba(102, 204, 0, 0.7)';
ctx.fillText(text, 4, 17);
const result = canvas.toDataURL();
const result = canvas.toDataURL();
if (debug) {
document.body.appendChild(canvas);
} else {
ctx.clearRect(0, 0, canvas.width, canvas.height);
}
if (debug) {
document.body.appendChild(canvas);
} else {
ctx.clearRect(0, 0, canvas.width, canvas.height);
}
return murmurhash3_32_gc(result);
} catch {
return null;
}
return murmurhash3_32_gc(result);
} catch {
return null;
}
};
export const getWebglID = (debug) => {
try {
const canvas = document.createElement('canvas');
const ctx = canvas.getContext('webgl');
canvas.width = 256;
canvas.height = 128;
try {
const canvas = document.createElement('canvas');
const ctx = canvas.getContext('webgl');
canvas.width = 256;
canvas.height = 128;
const f = 'attribute vec2 attrVertex;varying vec2 varyinTexCoordinate;uniform vec2 uniformOffset;void main(){varyinTexCoordinate=attrVertex+uniformOffset;gl_Position=vec4(attrVertex,0,1);}';
const g = 'precision mediump float;varying vec2 varyinTexCoordinate;void main() {gl_FragColor=vec4(varyinTexCoordinate,0,1);}';
const h = ctx.createBuffer();
const f =
'attribute vec2 attrVertex;varying vec2 varyinTexCoordinate;uniform vec2 uniformOffset;void main(){varyinTexCoordinate=attrVertex+uniformOffset;gl_Position=vec4(attrVertex,0,1);}';
const g = 'precision mediump float;varying vec2 varyinTexCoordinate;void main() {gl_FragColor=vec4(varyinTexCoordinate,0,1);}';
const h = ctx.createBuffer();
ctx.bindBuffer(ctx.ARRAY_BUFFER, h);
ctx.bindBuffer(ctx.ARRAY_BUFFER, h);
const i = new Float32Array([-0.2, -0.9, 0, 0.4, -0.26, 0, 0, 0.7321, 0]);
const i = new Float32Array([-0.2, -0.9, 0, 0.4, -0.26, 0, 0, 0.7321, 0]);
ctx.bufferData(ctx.ARRAY_BUFFER, i, ctx.STATIC_DRAW), (h.itemSize = 3), (h.numItems = 3);
ctx.bufferData(ctx.ARRAY_BUFFER, i, ctx.STATIC_DRAW), (h.itemSize = 3), (h.numItems = 3);
const j = ctx.createProgram();
const k = ctx.createShader(ctx.VERTEX_SHADER);
const j = ctx.createProgram();
const k = ctx.createShader(ctx.VERTEX_SHADER);
ctx.shaderSource(k, f);
ctx.compileShader(k);
ctx.shaderSource(k, f);
ctx.compileShader(k);
const l = ctx.createShader(ctx.FRAGMENT_SHADER);
const l = ctx.createShader(ctx.FRAGMENT_SHADER);
ctx.shaderSource(l, g);
ctx.compileShader(l);
ctx.attachShader(j, k);
ctx.attachShader(j, l);
ctx.linkProgram(j);
ctx.useProgram(j);
ctx.shaderSource(l, g);
ctx.compileShader(l);
ctx.attachShader(j, k);
ctx.attachShader(j, l);
ctx.linkProgram(j);
ctx.useProgram(j);
j.vertexPosAttrib = ctx.getAttribLocation(j, 'attrVertex');
j.offsetUniform = ctx.getUniformLocation(j, 'uniformOffset');
j.vertexPosAttrib = ctx.getAttribLocation(j, 'attrVertex');
j.offsetUniform = ctx.getUniformLocation(j, 'uniformOffset');
ctx.enableVertexAttribArray(j.vertexPosArray);
ctx.vertexAttribPointer(j.vertexPosAttrib, h.itemSize, ctx.FLOAT, !1, 0, 0);
ctx.uniform2f(j.offsetUniform, 1, 1);
ctx.drawArrays(ctx.TRIANGLE_STRIP, 0, h.numItems);
ctx.enableVertexAttribArray(j.vertexPosArray);
ctx.vertexAttribPointer(j.vertexPosAttrib, h.itemSize, ctx.FLOAT, !1, 0, 0);
ctx.uniform2f(j.offsetUniform, 1, 1);
ctx.drawArrays(ctx.TRIANGLE_STRIP, 0, h.numItems);
const n = new Uint8Array(canvas.width * canvas.height * 4);
ctx.readPixels(0, 0, canvas.width, canvas.height, ctx.RGBA, ctx.UNSIGNED_BYTE, n);
const n = new Uint8Array(canvas.width * canvas.height * 4);
ctx.readPixels(0, 0, canvas.width, canvas.height, ctx.RGBA, ctx.UNSIGNED_BYTE, n);
const result = JSON.stringify(n).replace(/,?"[0-9]+":/g, '');
const result = JSON.stringify(n).replace(/,?"[0-9]+":/g, '');
if (debug) {
document.body.appendChild(canvas);
} else {
ctx.clear(ctx.COLOR_BUFFER_BIT | ctx.DEPTH_BUFFER_BIT | ctx.STENCIL_BUFFER_BIT);
}
if (debug) {
document.body.appendChild(canvas);
} else {
ctx.clear(ctx.COLOR_BUFFER_BIT | ctx.DEPTH_BUFFER_BIT | ctx.STENCIL_BUFFER_BIT);
}
return murmurhash3_32_gc(result);
} catch {
return null;
}
return murmurhash3_32_gc(result);
} catch {
return null;
}
};
export const getWebglInfo = () => {
try {
const ctx = document.createElement('canvas').getContext('webgl');
try {
const ctx = document.createElement('canvas').getContext('webgl');
const result = {
VERSION: ctx.getParameter(ctx.VERSION),
SHADING_LANGUAGE_VERSION: ctx.getParameter(ctx.SHADING_LANGUAGE_VERSION),
VENDOR: ctx.getParameter(ctx.VENDOR),
SUPORTED_EXTENSIONS: ctx.getSupportedExtensions(),
};
const result = {
VERSION: ctx.getParameter(ctx.VERSION),
SHADING_LANGUAGE_VERSION: ctx.getParameter(ctx.SHADING_LANGUAGE_VERSION),
VENDOR: ctx.getParameter(ctx.VENDOR),
SUPORTED_EXTENSIONS: ctx.getSupportedExtensions(),
};
return result;
} catch {
return null;
}
return result;
} catch {
return null;
}
};
export const murmurhash3_32_gc = (key) => {
const remainder = key.length & 3; // key.length % 4
const bytes = key.length - remainder;
const c1 = 0xcc9e2d51;
const c2 = 0x1b873593;
const remainder = key.length & 3; // key.length % 4
const bytes = key.length - remainder;
const c1 = 0xcc9e2d51;
const c2 = 0x1b873593;
let h1, h1b, k1;
let h1, h1b, k1;
for (let i = 0; i < bytes; i++) {
k1 = (key.charCodeAt(i) & 0xff) | ((key.charCodeAt(++i) & 0xff) << 8) | ((key.charCodeAt(++i) & 0xff) << 16) | ((key.charCodeAt(++i) & 0xff) << 24);
++i;
for (let i = 0; i < bytes; i++) {
k1 = (key.charCodeAt(i) & 0xff) | ((key.charCodeAt(++i) & 0xff) << 8) | ((key.charCodeAt(++i) & 0xff) << 16) | ((key.charCodeAt(++i) & 0xff) << 24);
++i;
k1 = ((k1 & 0xffff) * c1 + ((((k1 >>> 16) * c1) & 0xffff) << 16)) & 0xffffffff;
k1 = (k1 << 15) | (k1 >>> 17);
k1 = ((k1 & 0xffff) * c2 + ((((k1 >>> 16) * c2) & 0xffff) << 16)) & 0xffffffff;
k1 = ((k1 & 0xffff) * c1 + ((((k1 >>> 16) * c1) & 0xffff) << 16)) & 0xffffffff;
k1 = (k1 << 15) | (k1 >>> 17);
k1 = ((k1 & 0xffff) * c2 + ((((k1 >>> 16) * c2) & 0xffff) << 16)) & 0xffffffff;
h1 ^= k1;
h1 = (h1 << 13) | (h1 >>> 19);
h1b = ((h1 & 0xffff) * 5 + ((((h1 >>> 16) * 5) & 0xffff) << 16)) & 0xffffffff;
h1 = (h1b & 0xffff) + 0x6b64 + ((((h1b >>> 16) + 0xe654) & 0xffff) << 16);
}
h1 ^= k1;
h1 = (h1 << 13) | (h1 >>> 19);
h1b = ((h1 & 0xffff) * 5 + ((((h1 >>> 16) * 5) & 0xffff) << 16)) & 0xffffffff;
h1 = (h1b & 0xffff) + 0x6b64 + ((((h1b >>> 16) + 0xe654) & 0xffff) << 16);
}
const i = bytes - 1;
const i = bytes - 1;
k1 = 0;
k1 = 0;
switch (remainder) {
case 3: {
k1 ^= (key.charCodeAt(i + 2) & 0xff) << 16;
break;
}
case 2: {
k1 ^= (key.charCodeAt(i + 1) & 0xff) << 8;
break;
}
case 1: {
k1 ^= key.charCodeAt(i) & 0xff;
break;
}
switch (remainder) {
case 3: {
k1 ^= (key.charCodeAt(i + 2) & 0xff) << 16;
break;
}
case 2: {
k1 ^= (key.charCodeAt(i + 1) & 0xff) << 8;
break;
}
case 1: {
k1 ^= key.charCodeAt(i) & 0xff;
break;
}
}
k1 = ((k1 & 0xffff) * c1 + ((((k1 >>> 16) * c1) & 0xffff) << 16)) & 0xffffffff;
k1 = (k1 << 15) | (k1 >>> 17);
k1 = ((k1 & 0xffff) * c2 + ((((k1 >>> 16) * c2) & 0xffff) << 16)) & 0xffffffff;
h1 ^= k1;
k1 = ((k1 & 0xffff) * c1 + ((((k1 >>> 16) * c1) & 0xffff) << 16)) & 0xffffffff;
k1 = (k1 << 15) | (k1 >>> 17);
k1 = ((k1 & 0xffff) * c2 + ((((k1 >>> 16) * c2) & 0xffff) << 16)) & 0xffffffff;
h1 ^= k1;
h1 ^= key.length;
h1 ^= key.length;
h1 ^= h1 >>> 16;
h1 = ((h1 & 0xffff) * 0x85ebca6b + ((((h1 >>> 16) * 0x85ebca6b) & 0xffff) << 16)) & 0xffffffff;
h1 ^= h1 >>> 13;
h1 = ((h1 & 0xffff) * 0xc2b2ae35 + ((((h1 >>> 16) * 0xc2b2ae35) & 0xffff) << 16)) & 0xffffffff;
h1 ^= h1 >>> 16;
h1 ^= h1 >>> 16;
h1 = ((h1 & 0xffff) * 0x85ebca6b + ((((h1 >>> 16) * 0x85ebca6b) & 0xffff) << 16)) & 0xffffffff;
h1 ^= h1 >>> 13;
h1 = ((h1 & 0xffff) * 0xc2b2ae35 + ((((h1 >>> 16) * 0xc2b2ae35) & 0xffff) << 16)) & 0xffffffff;
h1 ^= h1 >>> 16;
return h1 >>> 0;
return h1 >>> 0;
};
window.getBrowserFingerprint = getBrowserFingerprint;
export default getBrowserFingerprint;

@@ -1,28 +0,52 @@

import { strict as assert } from 'assert';
import puppeteer from 'puppeteer';
import getBrowserFingerprint from './index.js';
(async () => {
await (async () => {
const browser = await puppeteer.launch();
const page = await browser.newPage();
const result = await page.evaluate(getBrowserFingerprint);
await browser.close();
describe('getBrowserFingerprint', () => {
let browser, page;
assert.deepStrictEqual(Number.isInteger(result), true, 'fingerprint is not an integer');
assert.deepStrictEqual(String(result).length > 7, true, 'fingerprint is not long enough');
})();
beforeAll(async () => {
browser = await puppeteer.launch({
// headless: false,
// devtools: true,
});
page = await browser.newPage();
await (async () => {
const browser = await puppeteer.launch();
const page = await browser.newPage();
const result = await page.evaluate(getBrowserFingerprint, true);
await browser.close();
await page.addScriptTag({
type: 'module',
path: './src/index.js',
});
});
assert.deepStrictEqual(Number.isInteger(result), true, 'fingerprint is not an integer');
assert.deepStrictEqual(String(result).length > 7, true, 'fingerprint is not long enough');
})();
})()
.then(console.log)
.catch(console.error)
.finally(process.exit);
afterAll(async () => {
await browser.close();
});
it('works without args', async () => {
const result = await page.evaluate(() => {
const result = window.getBrowserFingerprint();
return result;
});
expect(typeof result).toBe('number');
expect(String(result).length).toBeGreaterThanOrEqual(7);
});
it('works without hardwareOnly=true', async () => {
const result = await page.evaluate(() => {
const result = window.getBrowserFingerprint();
return result;
});
expect(typeof result).toBe('number');
expect(String(result).length).toBeGreaterThanOrEqual(7);
});
it('works with enableWebgl=true', async () => {
const result = await page.evaluate(() => {
const result = window.getBrowserFingerprint({ enableWebgl: true });
return result;
});
expect(typeof result).toBe('number');
expect(String(result).length).toBeGreaterThanOrEqual(7);
});
});

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc