Как определить OS клиента через JavaScript

Подходы по детектированию ОС сервера всем давно известны — отправляем n-ое количество сообщений, на основе полученных ответов проводим анализ с некой базой или выявляем ключевые слова, признаки содержания и структуры определенной инфы и уже пытаемся выявить наиболее подходящую ОС. Собственно, примерно так работает и знаменитый Nmap при детекте ОС.
Но что если мы перевернем вопрос, а именно зададимся целью со стороны сервера узнать ОС клиента, который предположительно пытается ее скрыть.

Конечно, можно это сделать подобным методом — отправкой и анализом TCP/UDP пакетов, но и на стороне самого клиента, например, через navigator.appVersion илиnavigator.userAgent, но цель данной статьи, найти какой-нибудь дополнительный метод, который будет идти в помощь остальным.

Во-первых, стоит обозначить что данная возможность может быть полезна для тех, кто, например, анализирует трафик и ему очень важны реальные данные, а не мнимые или погрешности или чтобы отличать ботов или сгенерированный трафик от реального. Взять хотя бы арендаторов или создателей связок експлоитов, которым порой критически важно бывает знать, какую ОС они заразили перед загрузкой очередной малвари под нужную платформу.

Во-вторых, надо заметить, что целью статьи стоит описание в своем роде универсального метода определения ОС, а не по косвенным признакам типа наличия ActiveX в JS и пр.

Теперь посмотрим на возможности, которые имеются для детекта:

  • HTTP-заголовок User-Agent, в котором не всегда бывает вставлена ОС, а тем более его легко подменить на дезинформирующие данные;
  • JS, Flash, Java, в которых мы можем методично проводить исследование в браузере юзера и скидывать результаты на сервер;

Очевидно, что второй метод единственный, с помощью которого можно задетектить ОС с наибольшей вероятностью.
Теперь разберемся с методами. Вероятно, что их тут может быть довольно много, просто они еще ждут своего часа, когда их откроют. В этой статье проблема будет решаться с помощью шрифтов и на JS.

Ни для кого не секрет, что в каждой ОС есть свой предустановленный набор шрифтов, который можно назвать «фирменным». Так, например, все шрифты с префиксом MS будут относиться к семейству винды. То есть основной метод состоял в том, чтобы определить какие шрифты стоят у юзера, сравнить их с некой загруженной базой и отправить на сервер результат.

Для начала надо разработать технику определение наличия шрифта через JS. В сети уже была наработка, суть которой заключалась в сравнении ширины текстового элемента с новым и старым шрифтом (более подробно интересующимся можно обратиться сюда).

Теперь надо составить базу шрифтов и проверить их наличие к различным ОС. Кстати, стоит упомянуть, что все ОС мы будем делить на 3 типа: Windows, Mac OS и Linux, это три основные системы, с которых вероятнее всего будет сидеть юзер.
У меня получилась такие результаты:

Mac OS:  +Impact, -Lucida Console, +Lucida Grande, +Lucida Sans Unicode, +Verdana, 
+Geneva, -Webdings, -MS Serif, +Tahoma, +Trebuchet MS, +Palatino Linotype, +Palatino, 
+Apple Symbols
Windows: +Impact, +Lucida Console, -Lucida Grande, +Lucida Sans Unicode, 
+Verdana, -Geneva, +Webdings, +MS Serif, +Tahoma, +Trebuchet MS, 
+Palatino Linotype, -Palatino, -Apple Symbols
Linux:   -Impact, -Lucida Console, -Lucida Grande, -Lucida Sans Unicode, -Verdana, 
-Geneva, -Webdings, -MS Serif, -Tahoma, -Trebuchet MS, -Palatino Linotype, -Palatino, 
-Apple Symbols

Видно, что ни один из этих экзотических шрифтов не поддерживается стандартными дистрами линукса, и есть шрифты, которые стоят и у мака и у винды.

Далее оставалось разработать саму систему подсчета вероятности, к какая ОС подходит к определенной конфигурации шрифтов.
Итак алгоритм был выбран следующий:

  • проходим по каждому шрифту из списка;
  • проверяем наличие шрифта в системе;
  • если шрифт существует и принадлежит к семейству виндовых, то увеличиваем счетчик вероятности, что ОС винда, если существует и маковский, то увеличиваем счетчик вероятности, что это мак ось, иначе если шрифт существует, то это какая-то иная ОС из детектящихся, если же шрифт не поддерживается, то уменьшаем счетчик (что ведет к увеличению вероятности, что ОС — линукс);
  • после прохода по всем шрифтам проверяем, счетчики и решаем на их основе, с какой ОС сидит юзер;

В результате на JS код вышел таким:

//функция детекта ОС по шрифтам
function getOSbyFonts() {
 var fonts = ["Impact", "Lucida Console", "Lucida Grande", "Lucida Sans Unicode", "Verdana", "Geneva", "Webdings", "MS Serif", "Tahoma", "Trebuchet MS", "Palatino Linotype", "Palatino", "Apple Symbols"];
 var macf = [true, false, true, true, true, true, false, false, true, true, true, true, true];
 var winf = [true, true, false, true, true, false, true, true, true, true, true, false, false];
 var count = 0;
 var mcount = 0;
 var wcount = 0;
 for (var i = 0; i < fonts.length; ++i) {
   if (macf[i] == doesFontExist(fonts[i])) mcount++;
   if (winf[i] == doesFontExist(fonts[i])) wcount++;
   if (doesFontExist(fonts[i])) {
     count++;
   } else {
     count--;
   }
 }
 var os_by_fonts = "Not detected";
 if (mcount > wcount) os_by_fonts = "Mac";
 else if (mcount < wcount) os_by_fonts = "Windows";
 if (count < 0) os_by_fonts = "Linux";
 return os_by_fonts;
}
//функция, определляющая наличие шрифта в системе
function doesFontExist(fontName) {
 var canvas = document.createElement("canvas");
 var context = canvas.getContext("2d");
 var text = "abcdefghijklmnopqrstuvwxyz0123456789";
 context.font = "72px monospace";
 var baselineSize = context.measureText(text).width;
 context.font = "72px '" + fontName + "', monospace";
 var newSize = context.measureText(text).width;
 delete canvas;
 if (newSize == baselineSize) {
     return false;
 } else {
     return true;
 }
}

Протестировать этот код на детект своей ОС можно здесь. Мне будет интересно узнать результаты теста других и ошибки или ложные срабатывания скрипта.

[ad name=»Responbl»]

Конечно, этот способ не дает 100% результата (но у меня еще ни разу не ошибался, но предположительно такое может произойти), может вообще не выдать результата, в случае если у юзера стоит очень много шрифтов из этого набора, так что скрипт просто запутается, но в большинстве случаев результат будет достаточно достоверным.

Click to rate this post!
[Total: 6 Average: 4]

Специалист в области кибер-безопасности. Работал в ведущих компаниях занимающихся защитой и аналитикой компьютерных угроз. Цель данного блога - простым языком рассказать о сложных моментах защиты IT инфраструктур и сетей.

1 comments On Как определить OS клиента через JavaScript

  • Очень интересно!
    Все больше прихожу к выводу, чтобы скрыть различного рода инфу нужно чуть ли не полностью блокировать JavaScript в браузере по типу NoScript в Firefox…

Leave a reply:

Your email address will not be published.