Added screnn protection

First draft for #220
This commit is contained in:
kkapsner 2019-11-11 23:00:39 +01:00
parent cc776b48de
commit d42a4d2372
14 changed files with 532 additions and 2 deletions

View File

@ -38,6 +38,7 @@ Beschützte "Fingerprinting"-APIs:
<li>window (standardmäßig deaktiviert)</li>
<li>DOMRect</li>
<li>navigator (standardmäßig deaktiviert)</li>
<li>screen</li>
</ul>
Falls Sie Fehler finden oder Verbesserungsvorschläge haben, teilen Sie mir das bitte auf https://github.com/kkapsner/CanvasBlocker/issues mit.

View File

@ -39,6 +39,7 @@ Protected "fingerprinting" APIs:
<li>window (disabled by default)</li>
<li>DOMRect</li>
<li>navigator (disabled by default)</li>
<li>screen</li>
</ul>
Please report issues and feature requests at https://github.com/kkapsner/CanvasBlocker/issues

View File

@ -29,6 +29,7 @@ Protected "fingerprinting" APIs:
* window (disabled by default)
* DOMRect
* navigator (disabled by default)
* screen
Special thanks to:
* spodermenpls for finding all the typos

View File

@ -147,6 +147,10 @@
"message": "Navigator-API",
"description": ""
},
"section_Screen-api": {
"message": "Screen-API",
"description": ""
},
"displayAdvancedSettings_title": {
"message": "Expertenmodus",
"description": ""
@ -271,6 +275,18 @@
"message": "Wollen Sie das Auslesen über die Navigator-API erlauben?",
"description": ""
},
"askForScreenPermission": {
"message": "Wollen Sie die Screen-API erlauben?",
"description": ""
},
"askForScreenInputPermission": {
"message": "Wollen Sie das Schreiben über die Screen-API erlauben?",
"description": ""
},
"askForScreenReadoutPermission": {
"message": "Wollen Sie das Auslesen über die Screen-API erlauben?",
"description": ""
},
"askOnlyOnce_title": {
"message": "Nur einmal nachfragen",
"description": ""
@ -655,6 +671,10 @@
"message": "Navigator-Auslese vorgetäuscht auf {url}",
"description": ""
},
"fakedScreenReadout": {
"message": "Screen-Auslese vorgetäuscht auf {url}",
"description": ""
},
"fakedInput": {
"message": "Bei Ausgabe vorgetäuscht auf {url}",
"description": ""
@ -1123,6 +1143,34 @@
"message": "Zurücksetzen",
"description": ""
},
"protectScreen_title": {
"message": "Screen-API beschützen",
"description": ""
},
"protectScreen_description": {
"message": "Dies schützt vor Fingerprinting, das die Bildschirmgröße einbezieht.",
"description": ""
},
"protectScreen_urlSpecific": {
"message": "Um bestimmte Seiten von diesem Schutz auszuschließen, klicken Sie auf den schwarzen Pfeil um das Menü zu öffnen, fügen Sie die gewünschte Domain oder URL mit einem Klick auf \"+\" hinzu und entfernen Sie das zugehörige Häkchen.",
"description": ""
},
"screenSize_title": {
"message": "Bildschirmgröße",
"description": ""
},
"screenSize_description": {
"message": "Wenn dies auf einen Wert \"...x...\" gesetzt wird, werden diese Größen als Bildschirmgröße verwendet.",
"description": ""
},
"fakeMinimalScreenSize_title": {
"message": "Minimale Bildschirmgröße vortäuschen",
"description": ""
},
"fakeMinimalScreenSize_description": {
"message": "Verwende die minimale Bildschirmgröße aus der folgenden Liste, die zur inneren Browserfenstergröße passt. Bildschirmgrößen: 1366x768, 1440x900, 1600x900, 1920x1080, 4096x2160, 8192x4320",
"description": ""
},
"theme_title": {
"message": "Theme",
"description": ""
@ -1407,6 +1455,10 @@
"message": "Teilen Sie die persistenten Zufallszahlen nicht zwischen Domains, da dies den Browser 100% eindeutig identifizierbar macht.",
"description": ""
},
"sanitation_error.customScreenSize": {
"message": "Verwenden Sie keine benutzerdefinierte Bildschirmgröße, da sie den Browser verfolgbarer macht.",
"description": ""
},
"whitelist_inspection_title": {
"message": "CanvasBlocker Erlaubnisse ansehen",
"description": ""

View File

@ -154,6 +154,10 @@
"message": "Navigator API",
"description": ""
},
"section_Screen-api":{
"message": "Screen API",
"description": ""
},
"displayAdvancedSettings_title": {
"message": "Expert mode",
@ -283,6 +287,18 @@
"message": "Do you want to allow navigator API readout?",
"description": ""
},
"askForScreenPermission": {
"message": "Do you want to allow the screen API?",
"description": ""
},
"askForScreenInputPermission": {
"message": "Do you want to allow screen API input?",
"description": ""
},
"askForScreenReadoutPermission": {
"message": "Do you want to allow screen API readout?",
"description": ""
},
"askOnlyOnce_title": {
"message": "Ask only once",
"description": ""
@ -689,6 +705,10 @@
"message": "Faked navigator readout on {url}",
"description": ""
},
"fakedScreenReadout": {
"message": "Faked screen readout on {url}",
"description": ""
},
"fakedInput": {
"message": "Faked at input on {url}",
"description": ""
@ -1172,6 +1192,35 @@
"description": ""
},
"protectScreen_title": {
"message": "Protect screen API",
"description": ""
},
"protectScreen_description": {
"message": "This protects against fingerprinting attempts including the screen size.",
"description": ""
},
"protectScreen_urlSpecific": {
"message": "To exclude specific websites from this protection, click on the black arrow to open the menu, add the domain or URL by clicking on \"+\" and remove its checkmark.",
"description": ""
},
"screenSize_title": {
"message": "Screen size",
"description": ""
},
"screenSize_description": {
"message": "If this is set with a value \"...x...\" the specified dimensions will be reported as the screen size.",
"description": ""
},
"fakeMinimalScreenSize_title": {
"message": "Fake minimal screen size",
"description": ""
},
"fakeMinimalScreenSize_description": {
"message": "Use a minimal screen size from the following set that can fit the inner window dimensions. Screen sizes: 1366x768, 1440x900, 1600x900, 1920x1080, 4096x2160, 8192x4320",
"description": ""
},
"theme_title": {
"message": "Theme",
"description": ""
@ -1466,6 +1515,10 @@
"message": "Do not share persistent randomness between domains because this makes the browser 100% trackable.",
"description": ""
},
"sanitation_error.customScreenSize": {
"message": "Do not use a custom screen size as it makes the browser more trackable.",
"description": ""
},
"whitelist_inspection_title": {
"message": "CanvasBlocker whitelist inspection",

View File

@ -40,4 +40,5 @@
appendModified(require("./modifiedWindowAPI"));
appendModified(require("./modifiedDOMRectAPI"));
appendModified(require("./modifiedNavigatorAPI"));
appendModified(require("./modifiedScreenAPI"));
}());

338
lib/modifiedScreenAPI.js Normal file
View File

@ -0,0 +1,338 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
(function(){
"use strict";
var scope;
if ((typeof exports) !== "undefined"){
scope = exports;
}
else {
scope = require.register("./modifiedScreenAPI", {});
}
const {checkerWrapper} = require("./modifiedAPIFunctions");
const physical = {
width: Math.round(window.screen.width * window.devicePixelRatio),
height: Math.round(window.screen.height * window.devicePixelRatio)
};
if (!window.matchMedia(`(device-width: ${physical.width / window.devicePixelRatio}px`).matches){
let minWidth = Math.ceil((window.screen.width - 0.5) * window.devicePixelRatio);
let maxWidth = Math.floor((window.screen.width + 0.5) * window.devicePixelRatio);
for (let width = minWidth; width <= maxWidth; width += 1){
if (window.matchMedia(`(device-width: ${width / window.devicePixelRatio}px`).matches){
physical.width = width;
break;
}
}
}
if (!window.matchMedia(`(device-height: ${physical.height / window.devicePixelRatio}px`).matches){
let minHeight = Math.ceil((window.screen.height - 0.5) * window.devicePixelRatio);
let maxHeight = Math.floor((window.screen.height + 0.5) * window.devicePixelRatio);
for (let height = minHeight; height <= maxHeight; height += 1){
if (window.matchMedia(`(device-height: ${height / window.devicePixelRatio}px`).matches){
physical.height = height;
break;
}
}
}
const resolutions = {
portrait: [
{height: 1366, width: 768},
{height: 1440, width: 900},
{height: 1600, width: 900},
{height: 1920, width: 1080},
{height: 4096, width: 2160},
{height: 8192, width: 4320},
],
landscape: [
{width: 1366, height: 768},
{width: 1440, height: 900},
{width: 1600, height: 900},
{width: 1920, height: 1080},
{width: 4096, height: 2160},
{width: 8192, height: 4320},
]
};
function getScreenDimensions(prefs, window){
const screenSize = prefs("screenSize", window.location);
if (screenSize.match(/\s*\d+\s*x\s*\d+\s*$/)){
const [width, height] = screenSize.split("x").map(function(value){
return Math.round(parseFloat(value.trim()));
});
return {
width: width / window.devicePixelRatio,
height: height / window.devicePixelRatio
};
}
if (!prefs("fakeMinimalScreenSize", window.location)){
return window.screen;
}
const isLandscape = window.screen.width > window.screen.height;
// subtract 0.5 to adjust for potential rounding errors
const innerWidth = (window.innerWidth - 0.5) * window.devicePixelRatio;
const innerHeight = (window.innerHeight - 0.5) * window.devicePixelRatio;
for (let resolution of resolutions[isLandscape? "landscape": "portrait"]){
if (resolution.width >= innerWidth && resolution.height >= innerHeight){
return {
width: resolution.width / window.devicePixelRatio,
height: resolution.height / window.devicePixelRatio
};
}
}
return window.screen;
}
scope.changedGetters = [
{
objectGetters: [function(window){return window.Screen && window.Screen.prototype;}],
name: "width",
getterGenerator: function(checker){
const temp = {
get width(){
return checkerWrapper(checker, this, arguments, function(args, check){
const {prefs, notify, window, original} = check;
const originalValue = original.apply(this, window.Array.from(args));
const returnValue = Math.round(getScreenDimensions(prefs, window).width);
if (originalValue !== returnValue){
notify("fakedScreenReadout");
}
return returnValue;
});
}
};
return Object.getOwnPropertyDescriptor(temp, "width").get;
}
},
{
objectGetters: [function(window){return window.Screen && window.Screen.prototype;}],
name: "height",
getterGenerator: function(checker){
const temp = {
get height(){
return checkerWrapper(checker, this, arguments, function(args, check){
const {prefs, notify, window, original} = check;
const originalValue = original.apply(this, window.Array.from(args));
const returnValue = Math.round(getScreenDimensions(prefs, window).height);
if (originalValue !== returnValue){
notify("fakedScreenReadout");
}
return returnValue;
});
}
};
return Object.getOwnPropertyDescriptor(temp, "height").get;
}
},
{
objectGetters: [function(window){return window.Screen && window.Screen.prototype;}],
name: "availWidth",
getterGenerator: function(checker){
const temp = {
get availWidth(){
return checkerWrapper(checker, this, arguments, function(args, check){
const {prefs, notify, window, original} = check;
const originalValue = original.apply(this, window.Array.from(args));
const returnValue = Math.round(getScreenDimensions(prefs, window).width);
if (originalValue !== returnValue){
notify("fakedScreenReadout");
}
return returnValue;
});
}
};
return Object.getOwnPropertyDescriptor(temp, "availWidth").get;
}
},
{
objectGetters: [function(window){return window.Screen && window.Screen.prototype;}],
name: "availHeight",
getterGenerator: function(checker){
const temp = {
get availHeight(){
return checkerWrapper(checker, this, arguments, function(args, check){
const {prefs, notify, window, original} = check;
const originalValue = original.apply(this, window.Array.from(args));
const returnValue = Math.round(getScreenDimensions(prefs, window).height);
if (originalValue !== returnValue){
notify("fakedScreenReadout");
}
return returnValue;
});
}
};
return Object.getOwnPropertyDescriptor(temp, "availHeight").get;
}
},
{
objectGetters: [function(window){return window.Screen && window.Screen.prototype;}],
name: "availLeft",
getterGenerator: function(checker){
const temp = {
get availLeft(){
return checkerWrapper(checker, this, arguments, function(args, check){
const {prefs, notify, window, original} = check;
const originalValue = original.apply(this, window.Array.from(args));
if (originalValue !== 0){
notify("fakedScreenReadout");
}
return 0;
});
}
};
return Object.getOwnPropertyDescriptor(temp, "availLeft").get;
}
},
{
objectGetters: [function(window){return window.Screen && window.Screen.prototype;}],
name: "availTop",
getterGenerator: function(checker){
const temp = {
get availTop(){
return checkerWrapper(checker, this, arguments, function(args, check){
const {prefs, notify, window, original} = check;
const originalValue = original.apply(this, window.Array.from(args));
if (originalValue !== 0){
notify("fakedScreenReadout");
}
return 0;
});
}
};
return Object.getOwnPropertyDescriptor(temp, "availTop").get;
}
},
{
objectGetters: [function(window){return window;}],
name: "outerWidth",
getterGenerator: function(checker){
const temp = {
get outerWidth(){
return checkerWrapper(checker, this, arguments, function(args, check){
const {prefs, notify, window, original} = check;
const originalValue = original.apply(this, window.Array.from(args));
const returnValue = window.innerWidth;
if (originalValue !== returnValue){
notify("fakedScreenReadout");
}
return returnValue;
});
}
};
return Object.getOwnPropertyDescriptor(temp, "outerWidth").get;
}
},
{
objectGetters: [function(window){return window;}],
name: "outerHeight",
getterGenerator: function(checker){
const temp = {
get outerHeight(){
return checkerWrapper(checker, this, arguments, function(args, check){
const {prefs, notify, window, original} = check;
const originalValue = original.apply(this, window.Array.from(args));
const returnValue = window.innerHeight;
if (originalValue !== returnValue){
notify("fakedScreenReadout");
}
return returnValue;
});
}
};
return Object.getOwnPropertyDescriptor(temp, "outerHeight").get;
}
},
{
objectGetters: [function(window){return window.MediaQueryList && window.MediaQueryList.prototype;}],
name: "matches",
getterGenerator: function(checker){
const temp = {
get matches(){
return checkerWrapper(checker, this, arguments, function(args, check){
const {prefs, notify, window, original} = check;
const originalValue = original.apply(this, window.Array.from(args));
const screenSize = prefs("screenSize", window.location);
if (
(
screenSize.match(/\s*\d+\s*x\s*\d+\s*$/) ||
prefs("fakeMinimalScreenSize", window.location)
) &&
this.media.match(/device-(width|height)/)
){
const dimensions = getScreenDimensions(prefs, window);
const originalMedia = this.media;
const alteredMedia = this.media.replace(
/\(\s*(?:(min|max)-)?device-(width|height):\s+(\d+\.?\d*)px\s*\)/,
function(m, type, dimension, value){
value = parseFloat(value);
let newCompareValue = value;
switch (type){
case "min":
if (value <= dimensions[dimension]){
newCompareValue = 0;
}
else {
newCompareValue = 2 * physical[dimension];
}
break;
case "max":
if (value >= dimensions[dimension]){
newCompareValue = 2 * physical[dimension];
}
else {
newCompareValue = 0;
}
break;
default:
if (
Math.round(value * 100) ===
Math.round(dimensions[dimension] * 100)
){
newCompareValue = physical[dimension];
}
else {
newCompareValue = 0;
}
}
return "(" + (type? type + "-": "") +
"device-" + dimension + ": " +
(
newCompareValue /
window.devicePixelRatio
) + "px)";
}
);
if (alteredMedia !== originalMedia){
const alteredQuery = window.matchMedia(alteredMedia);
const fakedValue = original.call(alteredQuery);
if (originalValue !== fakedValue){
notify("fakedScreenReadout");
}
return fakedValue;
}
}
return originalValue;
});
}
};
return Object.getOwnPropertyDescriptor(temp, "matches").get;
}
},
];
function getStatus(obj, status, prefs){
status = Object.create(status);
status.active = prefs("protectScreen", status.url);
return status;
}
scope.changedGetters.forEach(function(changedGetter){
changedGetter.type = "readout";
changedGetter.getStatus = getStatus;
changedGetter.api = "screen";
});
}());

View File

@ -130,6 +130,16 @@
"userAgent @ navigator",
"vendor @ navigator",
"vendorSub @ navigator",
{name: "Screen-API", level: 1},
"width @ screen",
"height @ screen",
"availWidth @ screen",
"availHeight @ screen",
"availTop @ screen",
"availLeft @ screen",
"matches @ screen",
"outerWidth @ screen",
"outerHeight @ screen",
],
defaultKeyValue: true
},
@ -334,6 +344,21 @@
name: "navigatorDetails",
defaultValue: {},
},
{
name: "protectScreen",
defaultValue: true,
urlSpecific: true
},
{
name: "screenSize",
defaultValue: "",
urlSpecific: true
},
{
name: "fakeMinimalScreenSize",
defaultValue: true,
urlSpecific: true
},
{
name: "displayAdvancedSettings",
defaultValue: false

View File

@ -51,6 +51,7 @@
"lib/modifiedDOMRectAPI.js",
"lib/navigator.js",
"lib/modifiedNavigatorAPI.js",
"lib/modifiedScreenAPI.js",
"lib/modifiedAPI.js",
"lib/randomSupplies.js",
"lib/intercept.js",

View File

@ -11,7 +11,8 @@
"rng": "persistent",
"ignoreFrequentColors": 3,
"minColors": 3,
"storePersistentRnd": true
"storePersistentRnd": true,
"fakeMinimalScreenSize": false
},
"max_protection": {
"minFakeSize": 0,

View File

@ -81,6 +81,7 @@
{mainFlag: "protectWindow", section: "Window-API"},
{mainFlag: "protectDOMRect", section: "DOMRect-API"},
{mainFlag: "protectNavigator", section: "Navigator-API"},
{mainFlag: "protectScreen", section: "Screen-API"},
].forEach(function(api){
if (settings.get(api.mainFlag) !== (api.mainFlagDisabledValue || false)){
let inSection = false;
@ -297,6 +298,19 @@
}]
});
}
if (settings.protectScreen && settings.screenSize){
errorCallback({
message: extension.getTranslation("sanitation_error.customScreenSize"),
severity: "medium",
resolutions: [{
label: extension.getTranslation("sanitation_resolution.setTo")
.replace(/{value}/g, "\"\""),
callback: function(){
settings.screenSize = "";
}
}]
});
}
}
},
];

View File

@ -632,6 +632,44 @@
},
]
},
{
name: "Screen-API",
settings: [
{
"name": "protectScreen"
},
{
"name": "protectedAPIFeatures",
"replaceKeyPattern": / @ .+$/,
"displayedSection": "Screen-API",
"displayDependencies": [
{
"protectScreen": [true],
"displayAdvancedSettings": [true]
}
]
},
{
"name": "screenSize",
"displayDependencies": [
{
"protectScreen": [true],
"fakeMinimalScreenSize": [false],
"displayAdvancedSettings": [true]
}
]
},
{
"name": "fakeMinimalScreenSize",
"displayDependencies": [
{
"protectScreen": [true],
"screenSize": [""]
}
]
},
]
},
]
},
{

View File

@ -3,7 +3,7 @@ Version 0.5.15:
- improved storage of protected API features
new features:
-
- added screen protection
fixes:
- background color of the textarea in the settings export was not readable in the dark theme when the value was invalid

View File

@ -77,6 +77,10 @@
{
"version": "0.5.15Alpha20190924",
"update_link": "https://canvasblocker.kkapsner.de/versions/canvasblocker_beta-0.5.15Alpha20190924-an+fx.xpi"
},
{
"version": "0.5.15Alpha20191111",
"update_link": "https://canvasblocker.kkapsner.de/versions/canvasblocker_beta-0.5.15Alpha20191111-an+fx.xpi"
}
]
}