Socket
Socket
Sign inDemoInstall

inject-fingerprint

Package Overview
Dependencies
347
Maintainers
4
Versions
12
Alerts
File Explorer

Advanced tools

Install Socket

Detect and block malicious and high-risk dependencies

Install

Comparing version 1.0.7 to 1.1.0

utils-finger-printer.js

223

finger-print-builder.js

@@ -49,20 +49,20 @@ const { join } = require('path');

return `
Object.defineProperty(window.navigator, 'plugins', {
get: function () {
const pluginData = [
{ name: "Chrome PDF Plugin", filename: "internal-pdf-viewer", description: "Portable Document Format" },
{ name: "Chrome PDF Viewer", filename: "mhjfbmdgcfjbbpaeojofohoefgiehjai", description: "" },
{ name: "Native Client", filename: "internal-nacl-plugin", description: "" },
]
const pluginArray = []
pluginData.forEach(p => {
function FakePlugin() { return p }
const plugin = new FakePlugin()
Object.setPrototypeOf(plugin, Plugin.prototype);
pluginArray.push(plugin)
})
Object.setPrototypeOf(pluginArray, PluginArray.prototype);
return pluginArray
},
});`;
Object.defineProperty(window.navigator, 'plugins', {
get: function () {
const pluginData = [
{ name: "Chrome PDF Plugin", filename: "internal-pdf-viewer", description: "Portable Document Format" },
{ name: "Chrome PDF Viewer", filename: "mhjfbmdgcfjbbpaeojofohoefgiehjai", description: "" },
{ name: "Native Client", filename: "internal-nacl-plugin", description: "" },
]
const pluginArray = []
pluginData.forEach(p => {
function FakePlugin() { return p }
const plugin = new FakePlugin()
Object.setPrototypeOf(plugin, Plugin.prototype);
pluginArray.push(plugin)
})
Object.setPrototypeOf(pluginArray, PluginArray.prototype);
return pluginArray
},
});`;
};

@@ -72,15 +72,15 @@

return `
const getParameter = WebGLRenderingContext.getParameter;
WebGLRenderingContext.prototype.getParameter = function (parameter) {
// UNMASKED_VENDOR_WEBGL
if (parameter === 37445) {
return 'Intel Open Source Technology Center';
}
// UNMASKED_RENDERER_WEBGL
if (parameter === 37446) {
return 'Mesa DRI Intel(R) Ivybridge Mobile ';
}
const getParameter = WebGLRenderingContext.getParameter;
WebGLRenderingContext.prototype.getParameter = function (parameter) {
// UNMASKED_VENDOR_WEBGL
if (parameter === 37445) {
return 'Intel Open Source Technology Center';
}
// UNMASKED_RENDERER_WEBGL
if (parameter === 37446) {
return 'Mesa DRI Intel(R) Ivybridge Mobile ';
}
return getParameter(parameter);
};`;
return getParameter(parameter);
};`;
};

@@ -90,6 +90,6 @@

return `
const originalQuery = window.navigator.permissions.query;
window.navigator.permissions.query = (parameters) => (parameters.name === 'notifications'
? Promise.resolve({ state: Notification.permission })
: originalQuery(parameters));`;
const originalQuery = window.navigator.permissions.query;
window.navigator.permissions.query = (parameters) => (parameters.name === 'notifications'
? Promise.resolve({ state: Notification.permission })
: originalQuery(parameters));`;
};

@@ -100,31 +100,150 @@

return `
function changeProperty(parent, attribute, values) {
Object.defineProperty(window[parent], attribute, {
function changeProperty(parent, attribute, values) {
Object.defineProperty(window[parent], attribute, {
get: function () {
return values
}
});
}
this.changeProperty('navigator', 'languages', ${JSON.stringify(languages)})
this.changeProperty('navigator', 'deviceMemory', ${getDeviceMemory()})
this.changeProperty('navigator', 'hardwareConcurrency', ${getHardwareConcurrency()})
this.changeProperty('navigator', 'chrome', { runtime: {}, });
this.changeProperty('navigator', 'appCodeName', 'Mozilla');
this.changeProperty('navigator', 'platform', 'Linux x86_64');
this.changeProperty('navigator', 'vendor', 'Google Inc.');
this.changeProperty('navigator', 'appName', 'Netscape');
this.changeProperty('window', 'chrome', { runtime: {}, });
this.changeProperty('screen', 'colorDepth', 24);`;
};
const bypassWebdriver = () => {
return 'delete navigator.__proto__?.webdriver;';
};
const bypassIframe = () => {
return `
try {
// Adds a contentWindow proxy to the provided iframe element
const addContentWindowProxy = iframe => {
const contentWindowProxy = {
get(target, key) {
// Now to the interesting part:
// We actually make this thing behave like a regular iframe window,
// by intercepting calls to e.g. .self and redirect it to the correct thing. :)
// That makes it possible for these assertions to be correct:
// iframe.contentWindow.self === window.top // must be false
if (key === 'self') {
return this
}
// iframe.contentWindow.frameElement === iframe // must be true
if (key === 'frameElement') {
return iframe
}
return Reflect.get(target, key)
}
}
if (!iframe.contentWindow) {
const proxy = new Proxy(window, contentWindowProxy)
Object.defineProperty(iframe, 'contentWindow', {
get() {
return proxy
},
set(newValue) {
return newValue // contentWindow is immutable
},
enumerable: true,
configurable: false
})
}
}
// Handles iframe element creation, augments srcdoc property so we can intercept further
const handleIframeCreation = (target, thisArg, args) => {
const iframe = target.apply(thisArg, args)
// We need to keep the originals around
const _iframe = iframe
const _srcdoc = _iframe.srcdoc
// Add hook for the srcdoc property
// We need to be very surgical here to not break other iframes by accident
Object.defineProperty(iframe, 'srcdoc', {
configurable: true, // Important, so we can reset this later
get: function () {
return values
return _iframe.srcdoc
},
set: function (newValue) {
addContentWindowProxy(this)
// Reset property, the hook is only needed once
Object.defineProperty(iframe, 'srcdoc', {
configurable: false,
writable: false,
value: _srcdoc
})
_iframe.srcdoc = newValue
}
});
})
return iframe
}
this.changeProperty('navigator', 'languages', ${JSON.stringify(languages)})
this.changeProperty('navigator', 'deviceMemory', ${getDeviceMemory()})
this.changeProperty('navigator', 'hardwareConcurrency', ${getHardwareConcurrency()})
this.changeProperty('navigator', 'chrome', { runtime: {}, });
this.changeProperty('navigator', 'appCodeName', 'Mozilla');
this.changeProperty('navigator', 'platform', 'Linux x86_64');
this.changeProperty('navigator', 'vendor', 'Google Inc.');
this.changeProperty('navigator', 'appName', 'Netscape');
this.changeProperty('window', 'chrome', { runtime: {}, });
this.changeProperty('screen', 'colorDepth', 24);`;
// Adds a hook to intercept iframe creation events
const addIframeCreationSniffer = () => {
/* global document */
const createElementHandler = {
// Make toString() native
get(target, key) {
return Reflect.get(target, key)
},
apply: function (target, thisArg, args) {
const isIframe =
args && args.length && String(args[0]).toLowerCase() === 'iframe'
if (!isIframe) {
// Everything as usual
return target.apply(thisArg, args)
} else {
return handleIframeCreation(target, thisArg, args)
}
}
}
// All this just due to iframes with srcdoc bug
utils.replaceWithProxy(
document,
'createElement',
createElementHandler
)
}
// Let's go
addIframeCreationSniffer()
} catch (err) {
// console.warn(err)
}`;
};
const bypassWebdriver = () => {
return 'delete navigator.__proto__?.webdriver;';
const bypassHairlineFeature = () => {
return `// store the existing descriptor
const elementDescriptor = Object.getOwnPropertyDescriptor(HTMLElement.prototype, 'offsetHeight');
// redefine the property with a patched descriptor
Object.defineProperty(HTMLDivElement.prototype, 'offsetHeight', {
...elementDescriptor,
get: function () {
if (this.id === 'modernizr') {
return 1;
}
return elementDescriptor.get.apply(this);
},
});`;
};
const createFingerPrintScript = () => {
return `${bypassPlugins()}
return `${readFileSync('./utils-finger-printer.js')}
${bypassPlugins()}
${bypassChangeProperties()}
${bypassPermissions()}
${bypassWebGL()}
${bypassWebdriver()}`;
${bypassIframe()}
${bypassWebdriver()}
${bypassHairlineFeature()}`;
};

@@ -131,0 +250,0 @@

{
"name": "inject-fingerprint",
"version": "1.0.7",
"version": "1.1.0",
"description": "Create Internal proxy to inject fingerprint",

@@ -5,0 +5,0 @@ "main": "index.js",

@@ -1,14 +0,17 @@

const { writeFileSync, existsSync } = require('fs');
const { join } = require('path');
const { existsSync } = require('fs');
const { expect } = require('chai');
const { By, until } = require('selenium-webdriver');
const ProxyServer = new (require('../index'))({ logLevel: 'silly' });
let driver;
let driver, tables;
describe('Page fingerprint application validation test', async () => {
before(() => {
before(async () => {
ProxyServer.start();
driver = ProxyServer.DriverBuilder(ProxyServer.options).build();
await driver.get('https://bot.sannysoft.com/');
await driver.wait(until.elementLocated(By.xpath('//*[@id="fp2"]')), 10000);
tables = await driver.findElements(By.css('table'));
});
after(() => {

@@ -19,8 +22,7 @@ ProxyServer.close();

it('Should run fingerprint tests on test page and validate results', async () => {
await driver.get('https://bot.sannysoft.com/');
await driver.wait(until.elementLocated(By.xpath('//*[@id="fp2"]')), 10000);
await new Screenshot(driver).take('test');
const tables = await driver.findElements(By.css('table'));
it('Should run old fingerprint tests on test page and validate results', async () => {
await oldFingerPrintValidate(tables);
});
it('Should run new fingerprint tests on test page and validate results', async () => {
await newFingerPrintValidate(tables);

@@ -51,3 +53,3 @@ });

const failedEls = await line.findElements(By.className('failed'));
if (failedEls.length != 0 && !name.includes('Hairline Feature'))//Bypass desnessário
if (failedEls.length != 0)
failedTests.push({ name, result });

@@ -57,3 +59,3 @@ }

expect(failedTests, `[FAIL] Old FingerPrint: ${JSON.stringify(failedTests)}`).to.be.empty;
expect(passedTests.length, '[SUCCESS] Old FingerPrint').to.be.equal(11);
expect(passedTests.length, '[SUCCESS] Old FingerPrint').to.be.equal(12);
}

@@ -63,7 +65,6 @@

const newFingerprintLines = await tables[1].findElements(By.css('tr'));
const passedTests = [], failedTests = [];
const passedTests = [], warnTests = [], failedTests = [];
for (const line of newFingerprintLines) {
const tds = await line.findElements(By.css('td'));
const key = await tds[0].getText();
if (key === 'HEADCHR_IFRAME') continue;//Sem bypass por enquanto
const status = await tds[1].getText();

@@ -75,3 +76,4 @@ const value = await tds[2].getText();

passedTests.push(fingerResult);
else if (status == 'WARN')
warnTests.push(fingerResult);
else

@@ -81,39 +83,29 @@ failedTests.push(fingerResult);

expect(failedTests, `[FAIL] New FingerPrint: ${JSON.stringify(failedTests)}`).to.be.empty;
expect(passedTests.length, '[SUCCESS] New FingerPrint').to.be.equal(20);
}
const explanation = `
PHANTOM_UA: Detect PhantomJS user agent
PHANTOM_PROPERTIES: Test the presence of properties introduced by PhantomJS
PHANTOM_ETSL: Runtime verification for PhantomJS
PHANTOM_LANGUAGE: Use navigator.languages to detect PhantomJS
PHANTOM_WEBSOCKET: Analyze the error thrown when creating a websocket
MQ_SCREEN: Use media query related to the screen
PHANTOM_OVERFLOW: Analyze error thrown when a stack overflow occurs
PHANTOM_WINDOW_HEIGHT: Analyze window screen dimension
HEADCHR_UA: Detect Chrome Headless user agent
WEBDRIVER: Test the presence of webriver attributes
HEADCHR_CHROME_OBJ: Test the presence of the window.chrome object
HEADCHR_PERMISSIONS: Test permissions management
HEADCHR_PLUGINS: Verify the number of plugins
HEADCHR_IFRAME: Test presence of Chrome Headless using an iframe
CHR_DEBUG_TOOLS: Test if debug tools are opened
SELENIUM_DRIVER: Test the presence of Selenium drivers
CHR_BATTERY: Test the presence of battery
CHR_MEMORY: Verify if navigator.deviceMemory is consistent
TRANSPARENT_PIXEL: Verify if a canvas pixel is transparent`;
class Screenshot {
__LOGGER_FINGERPRINT && __LOGGER_FINGERPRINT.info(`[proxy-server] Explanation test new fingerprint: '${explanation}'`);
constructor(driver) {
this.driver = driver;
}
if (warnTests) __LOGGER_FINGERPRINT.warn(`[WARN] New FingerPrint: ${JSON.stringify(warnTests)}`);
/**
* @param {String} path Path to save photo
* @returns {Promise}
*/
async take(path = './') {
try {
await this._setBackground();
const image = await this.driver.takeScreenshot();
const fileName = Date.now() + '.png';
writeFileSync(join(path, fileName), image.replace(/^data:image\/png;base64,/, ''), 'base64');
__LOGGER_FINGERPRINT.info(`[screenshot] Screenshot => '${fileName}'`);
return fileName;
} catch (err) {
__LOGGER_FINGERPRINT.warn('[screenshot] Não foi possível salvar screenshot: ' + err.message);
}
}
async _setBackground() {
/* istanbul ignore next */
await this.driver.executeScript(function () {
const style = document.createElement('style'),
text = document.createTextNode('body { background: #fff }');
style.setAttribute('type', 'text/css');
style.appendChild(text);
document.head.insertBefore(style, document.head.firstChild);
});
}
expect(failedTests, `[FAIL] New FingerPrint: ${JSON.stringify(failedTests)}`).to.be.empty;
expect(passedTests.length, '[SUCCESS] New FingerPrint').to.be.equal(20);
}
SocketSocket SOC 2 Logo

Product

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

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc