
Существуют встроенные решения (например, виртуальная клавиатура в Windows), однако, как мы с тобой подозреваем, определить какие именно значения вводятся при помощи такого инструмента теоретически можно с помощью получения (X,Y) нашей драгоценной мышки. Чтобы выйти из ситуации просто перемешаем раскладку виртуальной клавиатуры! Что? Перемешать раскладку? Это же дико неудобно! — воскликнешь ты. И будешь прав, однако, параноим до упора…
Если быть честным, идея перемешать раскладку подсмотрена в одном крупном банке — на сайте отсутствует поле для ввода реквизитов банковской карты и предложено вводить номер через виртуальную клавиатуру, причем цифры в панели «калькулятора» переставляются местами. Сделаем же нечто подобное для любого сайта => мастерим свое расширение для браузера! #В качестве примера используем Safari.
Чтобы не особо мучаться — возьмем готовую виртуальную клавиатуру на javascript/css отсюда и слегка доработаем ее.
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
<head>
	<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
 
	<title>Online Keyboard</title>
	<link rel="stylesheet" type="text/css" href="css/style.css" />
</head>
<body>
 
<div id="container">
	<textarea id="write" rows="6" cols="60"></textarea>
	<ul id="keyboard">
		<li class="symbol"><span class="off">`</span><span class="on">~</span></li>
		<li class="symbol"><span class="off">1</span><span class="on">!</span></li>
		<li class="symbol"><span class="off">2</span><span class="on">@</span></li>
		<li class="symbol"><span class="off">3</span><span class="on">#</span></li>
		<li class="symbol"><span class="off">4</span><span class="on">$</span></li>
		<li class="symbol"><span class="off">5</span><span class="on">%</span></li>
		<li class="symbol"><span class="off">6</span><span class="on">^</span></li>
		<li class="symbol"><span class="off">7</span><span class="on">&</span></li>
		<li class="symbol"><span class="off">8</span><span class="on">*</span></li>
		<li class="symbol"><span class="off">9</span><span class="on">(</span></li>
		<li class="symbol"><span class="off">0</span><span class="on">)</span></li>
		<li class="symbol"><span class="off">-</span><span class="on">_</span></li>
		<li class="symbol"><span class="off">=</span><span class="on">+</span></li>
		<li class="delete lastitem">delete</li>
		<li class="tab">tab</li>
		<li class="letter">q</li>
		<li class="letter">w</li>
		<li class="letter">e</li>
		<li class="letter">r</li>
		<li class="letter">t</li>
		<li class="letter">y</li>
		<li class="letter">u</li>
		<li class="letter">i</li>
		<li class="letter">o</li>
		<li class="letter">p</li>
		<li class="symbol"><span class="off">[</span><span class="on">{</span></li>
		<li class="symbol"><span class="off">]</span><span class="on">}</span></li>
		<li class="symbol lastitem"><span class="off"></span><span class="on">|</span></li>
		<li class="capslock">caps lock</li>
		<li class="letter">a</li>
		<li class="letter">s</li>
		<li class="letter">d</li>
		<li class="letter">f</li>
		<li class="letter">g</li>
		<li class="letter">h</li>
		<li class="letter">j</li>
		<li class="letter">k</li>
		<li class="letter">l</li>
		<li class="symbol"><span class="off">;</span><span class="on">:</span></li>
		<li class="symbol"><span class="off">'</span><span class="on">"</span></li>
		<li class="return lastitem">return</li>
		<li class="left-shift">shift</li>
		<li class="letter">z</li>
		<li class="letter">x</li>
		<li class="letter">c</li>
		<li class="letter">v</li>
		<li class="letter">b</li>
		<li class="letter">n</li>
		<li class="letter">m</li>
		<li class="symbol"><span class="off">,</span><span class="on"><</span></li>
		<li class="symbol"><span class="off">.</span><span class="on">></span></li>
		<li class="symbol"><span class="off">/</span><span class="on">?</span></li>
		<li class="right-shift lastitem">shift</li>
		<li class="space lastitem"> </li>
	</ul>
</div>
 
<script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.3.2/jquery.min.js"></script>
<script type="text/javascript" src="js/keyboard.js"></script>
</body>
</html>
* {
margin: 0;
padding: 0;
}
body {
font: 71%/1.5 Verdana, Sans-Serif;
background: #eee;
}
#container {
margin: 100px auto;
width: 688px;
}
#write {
margin: 0 0 5px;
padding: 5px;
width: 671px;
height: 200px;
font: 1em/1.5 Verdana, Sans-Serif;
background: #fff;
border: 1px solid #f9f9f9;
-moz-border-radius: 5px;
-webkit-border-radius: 5px;
}
#keyboard {
margin: 0;
padding: 0;
list-style: none;
}
	#keyboard li {
	float: left;
	margin: 0 5px 5px 0;
	width: 40px;
	height: 40px;
	line-height: 40px;
	text-align: center;
	background: #fff;
	border: 1px solid #f9f9f9;
	-moz-border-radius: 5px;
	-webkit-border-radius: 5px;
	}
		.capslock, .tab, .left-shift {
		clear: left;
		}
			#keyboard .tab, #keyboard .delete {
			width: 70px;
			}
			#keyboard .capslock {
			width: 80px;
			}
			#keyboard .return {
			width: 77px;
			}
			#keyboard .left-shift {
			width: 95px;
			}
			#keyboard .right-shift {
			width: 109px;
			}
		.lastitem {
		margin-right: 0;
		}
		.uppercase {
		text-transform: uppercase;
		}
		#keyboard .space {
		clear: left;
		width: 681px;
		}
		.on {
		display: none;
		}
		#keyboard li:hover {
		position: relative;
		top: 1px;
		left: 1px;
		border-color: #e5e5e5;
		cursor: pointer;
		}
$(function(){
	var $write = $('#write'),
		shift = false,
		capslock = false;
 
	$('#keyboard li').click(function(){
		var $this = $(this),
			character = $this.html(); // If it's a lowercase letter, nothing happens to this variable
 
		// Shift keys
		if ($this.hasClass('left-shift') || $this.hasClass('right-shift')) {
			$('.letter').toggleClass('uppercase');
			$('.symbol span').toggle();
 
			shift = (shift === true) ? false : true;
			capslock = false;
			return false;
		}
 
		// Caps lock
		if ($this.hasClass('capslock')) {
			$('.letter').toggleClass('uppercase');
			capslock = true;
			return false;
		}
 
		// Delete
		if ($this.hasClass('delete')) {
			var html = $write.html();
 
			$write.html(html.substr(0, html.length - 1));
			return false;
		}
 
		// Special characters
		if ($this.hasClass('symbol')) character = $('span:visible', $this).html();
		if ($this.hasClass('space')) character = ' ';
		if ($this.hasClass('tab')) character = "	";
		if ($this.hasClass('return')) character = "
";
 
		// Uppercase letter
		if ($this.hasClass('uppercase')) character = character.toUpperCase();
 
		// Remove shift once a key is clicked.
		if (shift === true) {
			$('.symbol span').toggle();
			if (capslock === false) $('.letter').toggleClass('uppercase');
 
			shift = false;
		}
 
		// Add the character
		$write.html($write.html() + character);
	});
});
Как видно из «основной формы» наша виртуальная клавиатура представляет собой обычный структурированный список ul-li, его превращает в виртуальную клавиатуру прилагающийся css файл, а обработкой нажатий занят js-скрипт, изменяющий соответствующие характеристики кнопок (видимость, регистр и прочее).
Сперва внесем некоторые модификации в стили, добавив #keycont зависимость, тем самым изолировав стили нашей клавиатуры от стилей страницы, на которой мы будем ее использовать следующим образом:
#keycont {
display: none;
position: fixed !important;
top: 50% !important;
left: 50% !important;
font: normal 14px/1.5 Verdana, Sans-Serif !important;
color: #444 !important;
text-shadow: none !important;
background-color: #eee !important;
padding: 5px 0 0 5px !important;
margin: 0;
-moz-border-radius: 5px !important;
-webkit-border-radius: 5px !important;
-webkit-user-select: none !important;
z-index:999999999 !important;
}
Модификации дают нам возможность видеть клавиатуру адекватно, поверх страницы, строго по центру + добавлена некоторая кросс-браузерность для сглаживания кнопочек. Прочие модификации опустим, они не столь важны.
Далее доработаем на js-скрипт. Добавляем функцию перемешивания клавиш:
Array.prototype.shuffle = function( b )
{
 var i = this.length, j, t;
 while( i ) 
 {
  j = Math.floor( ( i-- ) * Math.random() );
  t = b && typeof this[i].shuffle!=='undefined' ? this[i].shuffle() : this[i];
  this[i] = this[j];
  this[j] = t;
 }
 return this;
};
Она-то и будет менять положение кнопочек на клавиатуре. Следующим шагом добавим элементы «кнопочки» для перемешивания. Для простоты — будем перемешивать клавиши в рамках горизонтали расположения (не совсем чисто, но вполне достаточно для наших нужд):
	//Constructing keyboard
	var keyboard = '<div id="keycont"><ul id="keyboard">';
	var line0 = new Array('<li class="symbol"> <span class="off">`</span> <span class="on">~</span> </li>', '<li class="symbol"> <span class="off">1</span> <span class="on">!</span> </li>', '<li class="symbol"> <span class="off">2</span> <span class="on">@</span> </li>', '<li class="symbol"> <span class="off">3</span> <span class="on">#</span> </li>', '<li class="symbol"> <span class="off">4</span> <span class="on">$</span> </li>', '<li class="symbol"> <span class="off">5</span> <span class="on">%</span> </li>', '<li class="symbol"> <span class="off">6</span> <span class="on">^</span> </li>', '<li class="symbol"> <span class="off">7</span> <span class="on">&</span> </li>', '<li class="symbol"> <span class="off">8</span> <span class="on">*</span> </li>', '<li class="symbol"> <span class="off">9</span> <span class="on">(</span> </li>', '<li class="symbol"> <span class="off">0</span> <span class="on">)</span> </li>', '<li class="symbol"> <span class="off">-</span> <span class="on">_</span> </li>', '<li class="symbol"> <span class="off">=</span> <span class="on">+</span> </li>');
	var line1 = new Array('<li class="letter">q</li>', '<li class="letter">w</li>', '<li class="letter">e</li>', '<li class="letter">r</li>', '<li class="letter">t</li>', '<li class="letter">y</li>', '<li class="letter">u</li>', '<li class="letter">i</li>', '<li class="letter">o</li>', '<li class="letter">p</li>', '<li class="symbol"> <span class="off">[</span> <span class="on">{</span> </li>', '<li class="symbol"> <span class="off">]</span> <span class="on">}</span> </li>', '<li class="symbol lastitem"> <span class="off">\</span> <span class="on">|</span> </li>');
	var line2 = new Array('<li class="letter">a</li>', '<li class="letter">s</li>', '<li class="letter">d</li>', '<li class="letter">f</li>', '<li class="letter">g</li>', '<li class="letter">h</li>', '<li class="letter">j</li>', '<li class="letter">k</li>', '<li class="letter">l</li>', '<li class="symbol"> <span class="off">;</span> <span class="on">:</span> </li>', '<li class="symbol"> <span class="off">'</span> <span class="on">"</span> </li>');
	var line3 = new Array('<li class="letter">z</li>', '<li class="letter">x</li>', '<li class="letter">c</li>', '<li class="letter">v</li>', '<li class="letter">b</li>', '<li class="letter">n</li>', '<li class="letter">m</li>', '<li class="symbol"> <span class="off">,</span> <span class="on"><</span> </li>', '<li class="symbol"> <span class="off">.</span> <span class="on">></span> </li>', '<li class="symbol"> <span class="off">/</span> <span class="on">?</span> </li>');
	line0.shuffle();
	line1.shuffle();
	line2.shuffle();
	line3.shuffle();
	keyboard += line0.join("");
	keyboard += '<li class="delete lastitem">delete</li>';
	keyboard += '<li class="tab">tab</li>';
	keyboard += line1.join("");
	keyboard += '<li class="capslock">caps lock</li>';
	keyboard += line2.join("");
	keyboard += '<li class="return lastitem">return</li>';
	keyboard += '<li class="left-shift">shift</li>';
	keyboard += line3.join("");
	keyboard += '<li class="right-shift lastitem">shift</li>';
	keyboard += '<li class="space lastitem"> </li>';
	keyboard+='</ul></div>';
	$('body').append(keyboard);
Клавиатура собрана!
Осталось дело за малым — на страничке клавиатура нужна далеко не всегда, а только в тех случаях, когда мы собираемся что-то вводить в «секретные» поля, причем этих полей на странице может быть несколько (в данном релизе будем отлавливать поля ввода пароля по известному свойству). Поэтому добавляем пару-тройку новых функций по активации/деактивации (скрытию/показу) клавиатуры:
	$write.focus(function(){
		$write = $(this);
		$keycont.fadeIn();
		wrfocus = true;
	});
	$write.focusout(function(){
		if (focusss == false) {wrfocus=false; $keycont.fadeOut();}
	});
	$keycont.hover(
	function(){
		if (wrfocus == true) focusss = true;
	},
	function(){
		focusss = false;
	}
	);
	$keycont.focusout(function(){
		if (focusss == false) {wrfocus=false; $keycont.fadeOut();}
	});
	$keycont.click(function(){
		focusss = true;
		if (wrfocus == true) $write.focus();
	});
Вот и вся магия, если не углубляться дальше.
Особенности клавиатуры: относительная кроссбраузерность, перемешивание клавиш при перезагрузке страницы (для разных вкладок клавиатура перемешивается по независимо), требуется включение javascript, могут быть неявные подводные камни.
Протестировано на ряде популярных ресурсов:




К сожалению, не заработало в Google и iCloud, скорее всего есть специфические моменты на данных ресурсах.
Делал клавиатуру давно, поэтому извиняюсь за не слишком подробное описание.
Сборку расширения для Safari можно скачать по этой ссылке. Устанавливается и активируется в Safari через вкладку «Разработчик», о том как это сделать — спросить в любимом поисковике.
P.S. можно настроить клавиатуру по своему вкусу (отлавливать поля ввода банковских карт и прочих специфических полей любимых сайтов), перемешивание кнопочек же отключается удалением соответствующей строчки в коде.
Пользуемся на здоровье, удачи!
Комментарии (32)
 - extempl14.10.2016 07:57+1- Array.prototype.shuffle = function( b )
 
 - var line0 = new Array('<li class="symbol">…
 - var line1 = new Array('<li class="letter">…
 - var line2 = new Array('<li class="letter">…
 
 - if (focusss == false)- Ужас, ужас, ужас. Приведите в порядок код и форматирование.  - Palich23914.10.2016 13:21-5- Подскажите тогда, что в js считается красивым кодом. Мне, как специалисту широкого профиля важно, чтобы работало… остальное за борт))  - extempl14.10.2016 13:37+1- Да в общем-то тут к JS применим только манкипатчинг как антипаттерн. Всё остальное — глобально. 
 - Переменные вида "focusss" и вид самого метода shuffle сравните с тем что на SO: http://stackoverflow.com/a/2450976/506954. Вроде и делают одно и то же, однако их код более читаем, что не менее важно чем работоспособность, если вы решили поделиться своим кодом с сообществом. 
 - Пробелы с отступами у вас отличаются от примера к примеру — если и копипастите код откуда-то — форматируйте его, приводите в единый вид. 
 - Перечисление кнопок просто кошмарно, и JS тут вообще ни при чём. Используйте отдельный метод — генератор кнопки и исходный массив символов — это убережёт от возможных опечаток и сделает код более читабельным и поддерживаемым, складывайте его в коллекцию по символу или просто по индексу. 
 - Сортируйте массив индексов, а не сами кнопки. Да, + массив, однако всё стаёт гораздо читабельнее, гораздо легче поддерживать и вообще - душа радуется.
 - if (variable == false)абсолютно равноценно- if (!variable), либо проверяйте на строгое соответствие- ===если это важно.
 - Кроме всего прочего, неплохо бы выложить куда-нибудь на Github. 
 - И почему, собственно, пост про Safari? Какое он отношение имеет конкретно к этому браузеру?  - Palich23914.10.2016 14:00- Не мелковато для GitHub? С замечаниями согласен. Расширение (ссылка на исходник в конце статьи) собрано доя Safari, да и стиль клавиатуры похож на «яблочный» 
 
 
 
- Veliant14.10.2016 09:41+5- На моей памяти есть банковские трояны, которые не только сохраняют данные набираемые с клавиатуры, но и сохраняют небольшую область экранного буфера под курсором в момент клика мышкой. Так что это не панацея  - EminH14.10.2016 10:06- есть даже трояны с «прямым эфиром» — streaming screencast. Единственная разница- это легко заметить по нагрузке сетевой карты. 
  - third11214.10.2016 11:42+1- Да. Не панацея. Но в статье хорошая идея: 
 - Чтобы выйти из ситуации просто перемешаем раскладку виртуальной клавиатуры! 
 Конечно, если злоумышленник будет получать скрины в момент клика, то это не поможет. Но здесь возможна хитрость: обозначим буквы-цифры пиктограммами: 1-Яблоко,..., 9-Молоток,..., а-Персик, и т.д. Распечатаем на принтере эту табличку и будем выводить на клавиатуре со случайной раскладкой пиктограммы. Пользователь, набирает пароль по бумажной табличке, которую злоумышленник не видит (не забыть отключить web-камеру, перед вводом пароля!). Злоумышленник получит скрины, где были нажаты: Молоток, Персик, Яблоко, Молоток, Яблоко,… и далее ему остается делать перебор, который будет очень долгим при достаточной длине пароля.
 
 Можно модифицировать этот метод и использовать обычную клавиатуру, если ПО будет ее перекодировать при вводе пароля: пусть 1 -> q, 2 -> x, ..., a -> t, b -> 8,… Тогда, чтобы набрать пароль x8q, пользователь должен набрать 2b1. Понятно, что таблица перекодировки должна быть надежно зашифрована, и должны быть приняты меры на случай дампа участков памяти — в этом плане предложено много эффективных подходов.
 
 Еще можно зашумлять набор. После каждого нажатия/клика пользователем ПО случайным образом эмулирует несколько нажатий/кликов, запоминает их и удаляет из общей последовательности нажатий/кликов, получая таким образом пароль, введенный пользователем. А злоумышленнику придется гадать, что в общей последовательности исходит от пользователя. - Palich23914.10.2016 13:16+1- Это хороший комментарий. Над этим подумаю, у меня не было мысли развивать клавиатуру, писал как баловство, если честно, но на досуге можно сделать и новую версию)  - third11214.10.2016 13:47- Несколько лет назад у меня была небольшая переписка с Dominik Reichl (автор KeePass), где обсудили эти идеи. Ему в принципе понравилось, но, кажется, он так ничего и не попробовал. Напишите ЛС, если попробуете, что получилось. 
 
  - mamiamradio14.10.2016 15:34+1- Яблочки это хорошо, но уж слишком много мороки. Первое что приходит в голову, это скрывать символ при наведении на него мышкой (для пущего эффекта можно скрывать рядом находящиеся символы). 
 
  - Palich23914.10.2016 13:00- Как вариант можно случайно менять стили: шрифты, размеры, цвет, фон кнопки… капча?)) 
 
 - Anthrax_Beta14.10.2016 13:30- Можно еще перетасовывать буквы после каждого нажатия.  - Palich23914.10.2016 13:34+2- Пробовал. Это дико неудобно. К случайной раскладе в процессе ввода пароля еще можно приноровиться, но чтобы всякий раз — просто фигурный онанизм)) 
 
 - vozalel199114.10.2016 13:35- вполне можно наклеить на стандартную клавиатуру буквы вперемешку (или как удобно), и написать приложение по замене нажатой буквы/цифры на то что наклеено на клаве. (горе тем кто учил слепой 10 пальцевый метод печати)  - dartraiden16.10.2016 01:29- Или воспользоваться программной реализацией. В Windows это утилиты наподобие KeyScrambler, Ghostpress, которые работают через собственный драйвер… возможно что-то подобное есть и для MacOS? 
 
- punkkk14.10.2016 14:32-2- Господи, я думал что я один такой параноик, я когда на учебе за компьбтерами сидел, если приходилось заходить куда-то открывал виртуальную клавиатуру, и перед каждым нажатием меня лее положение, плюс положение курсора в воде и прочее. :D 
 
 Единственное что интересно, нельзя ли вытащить значение прямиком из поля ввода? Или из «канала» к полю ввода? - Palich23914.10.2016 14:37- Я так понимаю сложность доступа такого рода — дело уязвимостей самого браузера 
 
 - mamiamradio14.10.2016 15:26+1- В osx (mac os) нажатия клавиш в полях ввода типа «password» не отлавливаются с помощью CGEventTap по умолчанию (что конечно не отменяет возможности логировать пароли из других типов инпутов). Но вполне вероятно что есть и другие, недокументированные возможности. 
 - adgery15.10.2016 03:40- Сомнительная штука. По скольку это может быть актуальным до первого внимания тех, кто не получил валидных логов — далее будет легко найти заковырки, чтобы получить актуальную инфу с различных уровней. И сразу после этого данный метод станет опаснейшим заблуждением безопасности. 
 //держите в чистоте операционку… ubuntu или mac
 
           
 
shifttstas
Я сам дико удивился этому, что я подчеркиваю как важную особенность
sebres
Да не сочтёт хабрасообщество за рекламу и звиняюсь за небольшой оффтоп (ибо тема, точнее подход, не совсем про-то, но с кейлоггерами, включая скрин-троянов и т.п. нечисть, справляется на ура, плюс запоминать пароли не надо) — сделал как-то расширение для ffox-а — пользуйтесь на здоровье... Вдруг кому пригодится.
third112
sebres
секретная фраза вводится один раз (запоминается в профиле), вытащить её оттуда сродни вытаскиванию пароля из поля формы, даже хуже ибо расширения в фоксе подписываются (т.е. неизменяемы). Максимум что может быть украдено кейлоггером — это нестойкий, не замешанный, пароль.
От keepass это отличается тем что пароли не хранятся нигде (имхо это моветон), они "вычисляются* алгоритмически
third112
Одного раза достаточно, чтобы украсть секретную фразу при вводе. Или ее нужно вводить на свеже поставленных ОС и фоксе офф-лайн, т.е. с гарантией, что не сядет троян? А возможно украсть профиль?
sebres
Т.е. другими словами, вы знаете что у вас кейлоггер сидит и живете с этим? Т.е. случайное, до времени незамеченное, заражение — это одно, но это ж не норма...
Ну тогда вводите секрет как-нибудь "гарантированно" секьюрно (по частям, с копи-пастой, драг-дропом с яндекс клавиатуры и т.п.), т.е. максимально сложно — обфусцировано (один раз же)...
third112
В этом и проблема, как вводить «как-нибудь „гарантированно“ секьюрно». Подумал, Вы знаете, раз этим занимались. Я не про себя, но среднему юзеру откуда знать сидит ли у него кейлоггер? Подозреваю, что для многих заражение не замечено.