Compare commits
9 Commits
popup-appr
...
master
Author | SHA1 | Date | |
---|---|---|---|
1e00177aa1 | |||
|
abd9486a37 | ||
|
922a0bdbd1 | ||
|
e39251cafc | ||
|
866777686c | ||
|
b3c0097270 | ||
|
d70732d12b | ||
|
a63066edbd | ||
|
9dc7131f81 |
41
README.md
41
README.md
@ -12,14 +12,31 @@ There are spy libraries that can determine how long keys are held down and the t
|
||||
|
||||
This addon was created to skew the key press timing by limiting the speed of presses. The speed is changed randomly on each page load, with a min of 150ms and a max of 300ms.
|
||||
|
||||
It uses a less than ideal thread locking solution, as simply buffering text does not stop event listeners from spies. **Now it also has a non-default setting to use prompt() on non-password single line input elements. This reduces CPU usage and increase typing speed while offering better protection than the delay approach.**
|
||||
|
||||
You can whitelist domains that you trust on the addon settings page, but it was decided not to support changing the key speed manually as that would add another fingerprinting avenue.
|
||||
|
||||
Development Roadmap:
|
||||
# Usage
|
||||
|
||||
* Menu button to add whitelisted domain more quickly
|
||||
* Buffered window/menu/popup option to type quickly but prevent page surveillance
|
||||
This addon has two methods of protecting yourself.
|
||||
|
||||
## On-page protection
|
||||
|
||||
The easiest is to simply type on a page as normal. This method breaks the least amount of websites, but it can be frustrating to type a lot and it causes CPU spikes because of a limitation in javascript event processing.
|
||||
|
||||
You can whitelist sites by using the button in the addon's popup or by manually adding them on the addon settings page.
|
||||
|
||||
## Toolbar input
|
||||
|
||||
You can also enter text into the textarea in the toolbar button which will be instantly transfered to the page. This method allows you to type without lag, but it breaks on many websites and can still be tracked if a website is checking input changes as opposed to key events.
|
||||
|
||||
To avoid using the mouse to do this, you can press ctrl+1 (config [the same way as any addon](https://support.mozilla.org/en-US/kb/manage-extension-shortcuts-firefox)) to open the popup and press esc when done.
|
||||
|
||||
|
||||
|
||||
# Development Roadmap:
|
||||
|
||||
* ~~Menu button to add whitelisted domain more quickly~~
|
||||
* ~~Buffered window/menu/popup option to type quickly but prevent page surveillance~~
|
||||
* Identify websites that break on-page protection by lighting up the icon when on page protection is active
|
||||
* More throrough testing against different avenues of keyboard surveillance
|
||||
* Stylometry protection
|
||||
|
||||
@ -28,17 +45,23 @@ Development Roadmap:
|
||||
# Caveats
|
||||
|
||||
* **This addon may not defeat all types of keyboard biometric surveillance**, however it was tested against the Keytrac and TypingDNA demos and it worked well.
|
||||
* **It is recommended to turn on privacy.resistFingerprinting to avoid leaking your keyboard layout; however this addon protects against that as well if you strictly use the toolbar box**
|
||||
* **This addon does not yet deal with stylometry**
|
||||
* You may see higher CPU usage while typing. This is due do the unideal locking solution described above
|
||||
* Do not confuse this with spying keyboard apps on mobile devices, this cannot address that.
|
||||
* Some websites override all key events in which case only the toolbar button can protect you (but it likely doesn't function if this is the case)
|
||||
* Typing directly on pages will have CPU spikes due to a browser limitation
|
||||
* Of course, this cannot protect against malware on your device outside the webpage.
|
||||
* Not tested on Firefox Android
|
||||
-----
|
||||
|
||||
Sources:
|
||||
|
||||
www dot keytrac dot net/en/
|
||||
|
||||
www dot typingdna dot com/
|
||||
|
||||
(If you try the spy software demos, keep in mind that this addon changes your fingerprint for on-page typing on reload)
|
||||
|
||||
Keep in mind that scripts embeded on pages are not the only way to spy on you. [CSS can record keystrokes](https://css-tricks.com/css-keylogger/) and things such as autocomplete or typing notifications may measure your typing speed server side.
|
||||
|
||||
https://www.whonix.org/wiki/Stylometry
|
||||
[https://www.whonix.org/wiki/Keystroke_Deanonymization](https://www.whonix.org/wiki/Keystroke_Deanonymization)
|
||||
|
||||
(Spy companies not linked to avoid helping their SEO)
|
@ -16,9 +16,11 @@
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
*/
|
||||
let lastActive = null
|
||||
const defaultHosts = "<all_urls>";
|
||||
|
||||
let appCode = function (){
|
||||
let lastActive = document.createElement("p")
|
||||
let popupEnabled = false
|
||||
let popupGetter = browser.storage.sync.get("keyboardprivacyprompt")
|
||||
popupGetter.then(function(val){
|
||||
@ -34,8 +36,8 @@ let appCode = function (){
|
||||
document.selection.empty()
|
||||
}
|
||||
}
|
||||
let minValue = 75
|
||||
let maxValue = 150
|
||||
let minValue = 50
|
||||
let maxValue = 75
|
||||
let time = 0
|
||||
let last = null
|
||||
|
||||
@ -98,9 +100,8 @@ let appCode = function (){
|
||||
|
||||
function pausecomp(millis)
|
||||
{
|
||||
// Yes i know this wastes cpu. i don't like it either, but it seems a blocking
|
||||
// approach is needed to prevent spying event listeners from reading key events in *real time*
|
||||
// Might use an off-page buffer solution in the future
|
||||
// Yes i know this wastes cpu. i don't like it either, but it is the only way
|
||||
// i know of to have real page keypress events that aren't trackable
|
||||
var date = new Date();
|
||||
var curDate = null;
|
||||
do { curDate = new Date(); }
|
||||
@ -196,6 +197,38 @@ let appCode = function (){
|
||||
let whitelist = browser.storage.sync.get("keyboardprivacywhitelist");
|
||||
whitelist.then(shouldRunKeyboardPrivacy, noKeyboardPrivacySettings)
|
||||
|
||||
|
||||
document.addEventListener('focus', function(e){
|
||||
let active = document.activeElement;
|
||||
if (active.tagName == "INPUT" || active.tagName == "TEXTAREA") {
|
||||
lastActive = active
|
||||
}
|
||||
})
|
||||
|
||||
browser.runtime.onMessage.addListener(request => {
|
||||
let active = document.activeElement
|
||||
|
||||
if (request.getCurrent){
|
||||
if (active.tagName != "INPUT" && active.tagName != "TEXTAREA") {
|
||||
if (lastActive.tagName != "INPUT" && lastActive.tagName != "TEXTAREA"){
|
||||
console.debug("no current active or last active")
|
||||
return Promise.resolve({response: false});
|
||||
}
|
||||
return Promise.resolve({response: lastActive.value});
|
||||
}
|
||||
return Promise.resolve({response: active.value});
|
||||
}
|
||||
if (active.tagName != "INPUT" && active.tagName != "TEXTAREA"){
|
||||
|
||||
lastActive.value = request.keys
|
||||
}
|
||||
else{
|
||||
document.activeElement.value = request.keys
|
||||
lastActive = document.activeElement
|
||||
}
|
||||
return Promise.resolve({response: "ack"});
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
const dummyStr = ''
|
||||
|
@ -2,7 +2,7 @@
|
||||
|
||||
"manifest_version": 2,
|
||||
"name": "Private Keyboard",
|
||||
"version": "1.9",
|
||||
"version": "2.0",
|
||||
|
||||
|
||||
"description": "Protect against keyboard biometrics",
|
||||
@ -32,6 +32,15 @@
|
||||
"default_popup": "settings/button.html"
|
||||
},
|
||||
|
||||
"commands": {
|
||||
"_execute_browser_action": {
|
||||
"suggested_key": {
|
||||
"default": "Ctrl+1"
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
|
||||
|
||||
"browser_specific_settings": {
|
||||
"gecko": {
|
||||
|
@ -5,10 +5,15 @@
|
||||
<title>Private Keyboard</title>
|
||||
<link rel="stylesheet" href="./button.css">
|
||||
<script src="./button.js" defer></script>
|
||||
<script src="./keybuffer2.js" defer></script>
|
||||
</head>
|
||||
<body>
|
||||
<header id="siteDomain"></header>
|
||||
<button id="toggleSite">Disable Keyboard Privacy</button>
|
||||
<button id="toggleSite">Disable Slow Typing</button>
|
||||
<p id="reloadPage">Refresh the page to apply</p>
|
||||
|
||||
<br>
|
||||
<br>
|
||||
<textarea tabindex="0" id="keyBuffer" cols="20" rows="10" placeholder="Text typed here will be sent to active input/textarea elements. Breaks on many sites, but is faster than typing directly on the page."></textarea>
|
||||
</body>
|
||||
</html>
|
@ -33,7 +33,7 @@ browser.storage.sync.get("keyboardprivacywhitelist")
|
||||
let hostname = extractHostname(tabVal[0].url).replace('www.', '')
|
||||
document.getElementById('siteDomain').innerText = hostname
|
||||
if (val.keyboardprivacywhitelist.includes(hostname)) {
|
||||
document.getElementById('toggleSite').innerText = 'Enable Keyboard Privacy'
|
||||
document.getElementById('toggleSite').innerText = 'Enable Slow Typing'
|
||||
}
|
||||
})
|
||||
.catch(reportError);
|
||||
@ -104,11 +104,11 @@ document.getElementById('toggleSite').onclick = async function(){
|
||||
}
|
||||
document.getElementById('toggleSite').setAttribute('disabled', true)
|
||||
if (document.getElementById('toggleSite').innerText.startsWith('Disable')){
|
||||
document.getElementById('toggleSite').innerText = 'Enable Keyboard Privacy'
|
||||
document.getElementById('toggleSite').innerText = 'Enable Slow Typing'
|
||||
changeWhitelist(document.getElementById('siteDomain').innerText, true)
|
||||
}
|
||||
else if (document.getElementById('toggleSite').innerText.startsWith('Enable')){
|
||||
document.getElementById('toggleSite').innerText = 'Disable Keyboard Privacy'
|
||||
document.getElementById('toggleSite').innerText = 'Disable Slow Typing'
|
||||
changeWhitelist(document.getElementById('siteDomain').innerText, false)
|
||||
}
|
||||
setTimeout(function(){fixDuplicateCommas()}, 1000)
|
||||
@ -116,4 +116,4 @@ document.getElementById('toggleSite').onclick = async function(){
|
||||
document.getElementById('toggleSite').removeAttribute('disabled')
|
||||
}, 3000)
|
||||
document.getElementById('reloadPage').style.display = 'block'
|
||||
}
|
||||
}
|
||||
|
76
settings/keybuffer2.js
Normal file
76
settings/keybuffer2.js
Normal file
@ -0,0 +1,76 @@
|
||||
let started = false
|
||||
|
||||
function onError(error) {
|
||||
console.error(`Error: ${error}`);
|
||||
}
|
||||
|
||||
function doSendMsg(msg, tabs) {
|
||||
|
||||
for (let tab of tabs) {
|
||||
browser.tabs.sendMessage(
|
||||
tab.id,
|
||||
{keys: msg, getCurrent: false}
|
||||
).then(response => {
|
||||
console.log("Message from the content script:");
|
||||
console.log(response.response);
|
||||
}).catch(onError);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
let sender = async function(e){
|
||||
|
||||
if (e.key == 'Tab') {
|
||||
e.preventDefault();
|
||||
let start = this.selectionStart;
|
||||
let end = this.selectionEnd;
|
||||
|
||||
// set textarea value to: text before caret + tab + text after caret
|
||||
this.value = this.value.substring(0, start) +
|
||||
"\t" + this.value.substring(end);
|
||||
|
||||
// put caret at right position again
|
||||
this.selectionStart =
|
||||
this.selectionEnd = start + 1;
|
||||
}
|
||||
|
||||
let sendMessageToTabs = function(tabs){
|
||||
doSendMsg(document.getElementById('keyBuffer').value, tabs)
|
||||
}
|
||||
|
||||
browser.tabs.query({
|
||||
currentWindow: true,
|
||||
active: true
|
||||
}).then(sendMessageToTabs).catch(onError);
|
||||
|
||||
}
|
||||
|
||||
function getCurrent(){
|
||||
browser.tabs.query({
|
||||
currentWindow: true,
|
||||
active: true
|
||||
}).then(function(tabs){
|
||||
|
||||
for (let tab of tabs) {
|
||||
browser.tabs.sendMessage(
|
||||
tab.id,
|
||||
{keys: "", getCurrent: true}
|
||||
).then(response => {
|
||||
console.debug(response)
|
||||
if (response.response === undefined || response.response === false){
|
||||
//document.getElementById('keyBuffer').value = "No element in focus. Focus an element and reopen this."
|
||||
return
|
||||
}
|
||||
document.getElementById('keyBuffer').value = response.response
|
||||
}).catch(onError);
|
||||
}
|
||||
|
||||
}).catch(onError);
|
||||
}
|
||||
|
||||
getCurrent()
|
||||
|
||||
document.getElementById("keyBuffer").focus()
|
||||
|
||||
document.getElementById('keyBuffer').onkeydown = sender
|
||||
document.getElementById('keyBuffer').onpaste = sender
|
Loading…
Reference in New Issue
Block a user