Skip to content
Permalink
Browse files

Bug 15646: Prevent keyboard layout fingerprinting in KeyboardEvent

  • Loading branch information...
arthuredelstein committed Jun 14, 2015
1 parent daa6d8f commit 9b4f66455fe8aa6c6cf5a13907e720a83d80dade
Showing with 261 additions and 11 deletions.
  1. +4 −0 dom/base/nsContentUtils.cpp
  2. +11 −0 dom/base/nsContentUtils.h
  3. +189 −0 dom/events/KeyCodeConsensus.h
  4. +57 −11 dom/events/KeyboardEvent.cpp
@@ -247,6 +247,7 @@ bool nsContentUtils::sIsResourceTimingEnabled = false;
bool nsContentUtils::sIsUserTimingLoggingEnabled = false;
bool nsContentUtils::sIsExperimentalAutocompleteEnabled = false;
bool nsContentUtils::sEncodeDecodeURLHash = false;
bool nsContentUtils::sPrivacyResistFingerprinting = false;

uint32_t nsContentUtils::sHandlingInputTimeout = 1000;

@@ -525,6 +526,9 @@ nsContentUtils::Init()
Preferences::AddBoolVarCache(&sEncodeDecodeURLHash,
"dom.url.encode_decode_hash", false);

Preferences::AddBoolVarCache(&sPrivacyResistFingerprinting,
"privacy.resistFingerprinting", false);

Preferences::AddUintVarCache(&sHandlingInputTimeout,
"dom.event.handling-user-input-time-limit",
1000);
@@ -1911,6 +1911,16 @@ class nsContentUtils
return sEncodeDecodeURLHash;
}

/*
* Returns true if the browser should attempt to prevent content scripts
* from collecting distinctive information about the browser that could
* be used to "fingerprint" and track the user across websites.
*/
static bool ResistFingerprinting()
{
return sPrivacyResistFingerprinting;
}

/**
* Returns true if the doc tree branch which contains aDoc contains any
* plugins which we don't control event dispatch for, i.e. do any plugins
@@ -2385,6 +2395,7 @@ class nsContentUtils
static bool sIsUserTimingLoggingEnabled;
static bool sIsExperimentalAutocompleteEnabled;
static bool sEncodeDecodeURLHash;
static bool sPrivacyResistFingerprinting;

static nsHtml5StringParser* sHTMLFragmentParser;
static nsIParser* sXMLFragmentParser;
@@ -0,0 +1,189 @@
// See https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent/keyCode

// When privacy.resistFingerprinting is active, we hide the user's use of
// the numpad, right modifer keys, and any non-QWERTY US English keyboard.

#include "nsString.h"
#include "nsDataHashtable.h"

// KEY_INTERNAL is called by KEY or SHIFT.
#define KEY_INTERNAL(key, code, keyCode, shift) \
gCodes->Put(NS_LITERAL_STRING(key), NS_LITERAL_STRING(#code)); \
gKeyCodes->Put(NS_LITERAL_STRING(key), keyCode); \
gShiftStates->Put(NS_LITERAL_STRING(key), shift);

// KEY and SHIFT Assign a consensus codeName and keyCode for the given keyName.
// KEY indicates that shift is off.
#define KEY(key, code, keyCode) KEY_INTERNAL(key, code, keyCode, false)
// SHIFT indicates that shift is on.
#define SHIFT(key, code, keyCode) KEY_INTERNAL(key, code, keyCode, true)

// Three global constant static maps.
// gCodes provides a codeName for each keyName.
static nsDataHashtable<nsStringHashKey, nsString>* gCodes;
// gKeyCodes provides a keyCode for each keyName.
static nsDataHashtable<nsStringHashKey, uint32_t>* gKeyCodes;
// gShiftStates provides a shift value for each keyName.
static nsDataHashtable<nsStringHashKey, bool>* gShiftStates;

// Populate the global static maps gCodes, gKeCodes, gShiftStates
// with their constant values.
static void createKeyCodes()
{
if (gCodes) return;

gCodes = new nsDataHashtable<nsStringHashKey, nsString>();
gKeyCodes = new nsDataHashtable<nsStringHashKey, uint32_t>();
gShiftStates = new nsDataHashtable<nsStringHashKey, bool>();

KEY("AltLeft", AltLeft, 18)
KEY("AltRight", AltRight, 18)
KEY("CapsLock", CapsLock, 20)
KEY("Control", ControlLeft, 17)
KEY("Meta", OSLeft, 91)
KEY("Shift", ShiftLeft, 16)
KEY("ContextMenu", ContextMenu, 93)
KEY("Enter", Enter, 13)
KEY(" ", Space, 32)
KEY("Tab", Tab, 9)
KEY("Delete", Delete, 46)
KEY("End", End, 35)
KEY("Help", Help, 6)
KEY("Home", Home, 36)
KEY("Insert", Insert, 45)
KEY("PageDown", PageDown, 34)
KEY("PageUp", PageUp, 33)
KEY("ArrowDown", ArrowDown, 40)
KEY("ArrowLeft", ArrowLeft, 37)
KEY("ArrowRight", ArrowRight, 39)
KEY("ArrowUp", ArrowUp, 38)
KEY("Escape", Escape, 27)
KEY("PrintScreen", PrintScreen, 44)
KEY("ScrollLock", ScrollLock, 145)
KEY("Pause", Pause, 19)

KEY(",", Comma, 188)
SHIFT("<", Comma, 188)
KEY(".", Period, 190)
SHIFT(">", Period, 190)
KEY("/", Slash, 191)
SHIFT("?", Slash, 191)
KEY(";", Semicolon, 59)
SHIFT(":", Semicolon, 59)
KEY("'", Quote, 222)
SHIFT("\"", Quote, 222)
KEY("[", BracketLeft, 219)
SHIFT("{", BracketLeft, 219)
KEY("]", BracketRight, 221)
SHIFT("}", BracketRight, 221)
KEY("`", Backquote, 192)
SHIFT("~", Backquote, 192)
KEY("\\", Backslash, 220)
SHIFT("|", Backslash, 220)
KEY("-", Minus, 173)
SHIFT("_", Minus, 173)
KEY("=", Equal, 61)
SHIFT("+", Equal, 61)

SHIFT("A", KeyA, 65)
SHIFT("B", KeyB, 66)
SHIFT("C", KeyC, 67)
SHIFT("D", KeyD, 68)
SHIFT("E", KeyE, 69)
SHIFT("F", KeyF, 70)
SHIFT("G", KeyG, 71)
SHIFT("H", KeyH, 72)
SHIFT("I", KeyI, 73)
SHIFT("J", KeyJ, 74)
SHIFT("K", KeyK, 75)
SHIFT("L", KeyL, 76)
SHIFT("M", KeyM, 77)
SHIFT("N", KeyN, 78)
SHIFT("O", KeyO, 79)
SHIFT("P", KeyP, 80)
SHIFT("Q", KeyQ, 81)
SHIFT("R", KeyR, 82)
SHIFT("S", KeyS, 83)
SHIFT("T", KeyT, 84)
SHIFT("U", KeyU, 85)
SHIFT("V", KeyV, 86)
SHIFT("W", KeyW, 87)
SHIFT("X", KeyX, 88)
SHIFT("Y", KeyY, 89)
SHIFT("Z", KeyZ, 90)

KEY("a", KeyA, 65)
KEY("b", KeyB, 66)
KEY("c", KeyC, 67)
KEY("d", KeyD, 68)
KEY("e", KeyE, 69)
KEY("f", KeyF, 70)
KEY("g", KeyG, 71)
KEY("h", KeyH, 72)
KEY("i", KeyI, 73)
KEY("j", KeyJ, 74)
KEY("k", KeyK, 75)
KEY("l", KeyL, 76)
KEY("m", KeyM, 77)
KEY("n", KeyN, 78)
KEY("o", KeyO, 79)
KEY("p", KeyP, 80)
KEY("q", KeyQ, 81)
KEY("r", KeyR, 82)
KEY("s", KeyS, 83)
KEY("t", KeyT, 84)
KEY("u", KeyU, 85)
KEY("v", KeyV, 86)
KEY("w", KeyW, 87)
KEY("x", KeyX, 88)
KEY("y", KeyY, 89)
KEY("z", KeyZ, 90)

KEY("F1", F1, 112)
KEY("F2", F2, 113)
KEY("F3", F3, 114)
KEY("F4", F4, 115)
KEY("F5", F5, 116)
KEY("F6", F6, 117)
KEY("F7", F7, 118)
KEY("F8", F8, 119)
KEY("F9", F9, 120)
KEY("F10", F10, 121)
KEY("F11", F11, 122)
KEY("F12", F12, 123)
KEY("F13", F13, 124)
KEY("F14", F14, 125)
KEY("F15", F15, 126)
KEY("F16", F16, 127)
KEY("F17", F17, 128)
KEY("F18", F18, 129)
KEY("F19", F19, 130)
KEY("F20", F20, 131)
KEY("F21", F21, 132)
KEY("F22", F22, 133)
KEY("F23", F23, 134)
KEY("F24", F24, 135)

KEY("0", Digit0, 48)
KEY("1", Digit1, 49)
KEY("2", Digit2, 50)
KEY("3", Digit3, 51)
KEY("4", Digit4, 52)
KEY("5", Digit5, 53)
KEY("6", Digit6, 54)
KEY("7", Digit7, 55)
KEY("8", Digit8, 56)
KEY("9", Digit9, 57)

SHIFT("!", Digit0, 48)
SHIFT("@", Digit1, 49)
SHIFT("#", Digit2, 50)
SHIFT("$", Digit3, 51)
SHIFT("%", Digit4, 52)
SHIFT("^", Digit5, 53)
SHIFT("&", Digit6, 54)
SHIFT("*", Digit7, 55)
SHIFT("(", Digit8, 56)
SHIFT(")", Digit9, 57)

}
@@ -6,10 +6,17 @@
#include "mozilla/dom/KeyboardEvent.h"
#include "mozilla/TextEvents.h"
#include "prtime.h"
#include "KeyCodeConsensus.h"
#include "nsContentUtils.h"

namespace mozilla {
namespace dom {

static bool ResistFingerprinting() {
return nsContentUtils::ResistFingerprinting() &&
!nsContentUtils::IsCallerChrome();
}

KeyboardEvent::KeyboardEvent(EventTarget* aOwner,
nsPresContext* aPresContext,
WidgetKeyboardEvent* aEvent)
@@ -26,6 +33,7 @@ KeyboardEvent::KeyboardEvent(EventTarget* aOwner,
mEvent->time = PR_Now();
mEvent->AsKeyboardEvent()->mKeyNameIndex = KEY_NAME_INDEX_USE_STRING;
}
createKeyCodes();
}

NS_IMPL_ADDREF_INHERITED(KeyboardEvent, UIEvent)
@@ -66,7 +74,16 @@ KeyboardEvent::GetCtrlKey(bool* aIsDown)
bool
KeyboardEvent::ShiftKey()
{
return mEvent->AsKeyboardEvent()->IsShift();
bool shiftState = mEvent->AsKeyboardEvent()->IsShift();
if (!ResistFingerprinting()) {
return shiftState;
}
// Find a consensus fake shift state for the given key name.
bool fakeShiftState;
nsString keyName;
GetKey(keyName);
bool exists = gShiftStates->Get(keyName, &fakeShiftState);
return exists ? fakeShiftState : shiftState;
}

NS_IMETHODIMP
@@ -131,7 +148,17 @@ KeyboardEvent::GetKey(nsAString& aKeyName)
void
KeyboardEvent::GetCode(nsAString& aCodeName)
{
mEvent->AsKeyboardEvent()->GetDOMCodeName(aCodeName);
if (!ResistFingerprinting()) {
mEvent->AsKeyboardEvent()->GetDOMCodeName(aCodeName);
} else {
// Use a consensus code name corresponding to the
// key name.
nsString keyName, codeNameTemp;
GetKey(keyName);
if (gCodes->Get(keyName, &codeNameTemp)) {
aCodeName = codeNameTemp;
}
}
}

NS_IMETHODIMP
@@ -157,7 +184,7 @@ KeyboardEvent::CharCode()
case NS_KEY_BEFORE_UP:
case NS_KEY_UP:
case NS_KEY_AFTER_UP:
return 0;
// return 0;
case NS_KEY_PRESS:
return mEvent->AsKeyboardEvent()->charCode;
}
@@ -176,12 +203,19 @@ uint32_t
KeyboardEvent::KeyCode()
{
// If this event is initialized with ctor, we shouldn't check event type.
if (mInitializedByCtor) {
return mEvent->AsKeyboardEvent()->keyCode;
}

if (mEvent->HasKeyEventMessage()) {
return mEvent->AsKeyboardEvent()->keyCode;
if (mInitializedByCtor || mEvent->HasKeyEventMessage()) {
if (!ResistFingerprinting()) {
return mEvent->AsKeyboardEvent()->keyCode;
} else {
if (CharCode() != 0) {
return 0;
}
// Find a consensus key code for the given key name.
nsString keyName;
GetKey(keyName);
uint32_t keyCode;
return gKeyCodes->Get(keyName, &keyCode) ? keyCode : 0;
}
}
return 0;
}
@@ -206,7 +240,7 @@ KeyboardEvent::Which()
//Special case for 4xp bug 62878. Try to make value of which
//more closely mirror the values that 4.x gave for RETURN and BACKSPACE
{
uint32_t keyCode = mEvent->AsKeyboardEvent()->keyCode;
uint32_t keyCode = KeyCode();
if (keyCode == NS_VK_RETURN || keyCode == NS_VK_BACK) {
return keyCode;
}
@@ -229,7 +263,19 @@ KeyboardEvent::GetLocation(uint32_t* aLocation)
uint32_t
KeyboardEvent::Location()
{
return mEvent->AsKeyboardEvent()->location;
uint32_t location = mEvent->AsKeyboardEvent()->location;
if (!ResistFingerprinting()) {
return location;
}
// To resist fingerprinting, hide right modifier keys, as
// well as the numpad.
switch (location) {
case 0 : return 0;
case 1 : return 1;
case 2 : return 1;
case 3 : return 0;
default: return 0;
}
}

// static

0 comments on commit 9b4f664

Please sign in to comment.
You can’t perform that action at this time.