Пользователь
0,0
рейтинг
25 июля 2014 в 18:10

Разработка → Много анонимности не бывает — скрываем User-Agent

Очередной приступ паранойи был вполне обоснован — он наступил после прочтения статьи о методах анонимности в сети, где автор на примере браузера FireFox рассказывал о потенциальных утечках идентификационной информации. И стало интересно — а на сколько озвученные решения применимы, скажем, к браузеру Google Chrome?
Скрыть реальный IP — используем разные VPN сервера, отключить львиную долю отслеживающих скриптов — Adblock Plus и Ghostery, убрать Referer — не вопрос, что то ещё забыли… Ах да — User-Agent — своеобразный «отпечаток», по которому (в связке, скажем, с IP) легко идентифицировать пользователя. И с этим надо было что-то делать. Найденные решения лишь статично изменяли значение User-Agent, чего было явно недостаточно. Тогда и было решено написать плагин для скрытия реального User-Agent'a, а если быть точнее — подменять его на рандомный. Или почти рандомный.

Для нетерпеливых сразу: исходники на GitHub и расширение в Google Webstore.



Немного теории


Вообще, User-Agent (далее по тексту — UA) — штука нужная. Нужная в первую очередь для корректного отображения страниц, ведь нам всем известно — разные версии разных браузеров по разному рендерят странички, и заботливые web-программисты учитывают этот факт, выдавая нужным браузерам нужным скрипты и стили. Разнится поддержка доступных технологий «движками». Отсюда вытекает первое требование к итогу — возможность «имитировать» различные браузеры, и что самое важное — иметь возможность выбора между ними.
UA — это в первую очередь набор. Набор различных идентификаторов, по которым и происходит определение — какой браузер, какая операционная система, какой версии, и какое специфичное ПО (привет, IE) стоит у пользователя.
Почему именно IP и UA надо скрывать в первую очередь? А давайте посмотрим на лог пустого сайта-заглушки, на котором вообще ничего нет:

[meow@hosting /var/log]$ cat somesite.org.access_log | tail -3
10.12.11.254 - - [25/Jul/2014:15:51:16 +0700] "GET / HTTP/1.0" 200 5768 "-" "Mozilla/5.0 (compatible; MJ12bot/v1.4.5; http://www.majestic12.co.uk/bot.php?+)"
10.12.11.254 - - [25/Jul/2014:15:57:38 +0700] "GET / HTTP/1.0" 200 5768 "-" "Mozilla/5.0 (compatible; YandexBot/3.0; +http://yandex.com/bots)"
10.12.11.254 - - [25/Jul/2014:19:19:25 +0700] "GET / HTTP/1.0" 200 5768 "-" "Mozilla/5.0 (Windows NT 6.1; WOW64; rv:30.0) Gecko/20100101 Firefox/30.0"


На сайте ничего нет, а я знаю о посетителе более чем достаточно. Всё потому что «логи знают всё» ©.

Немного практики


Итак, решено — подставляем фейковый UA. Но как его сформировать? Я пошел по пути собирания с десятка UA каждого интересующего браузера, и написания регулярки для каждого, которая будет генерировать максимально правдоподобный и в то же время а какой-то мере уникальный отпечаток. Хотите пример? Вот вам 10 UA браузера «IE 9», и среди них пять настоящих. Сможете отличить?

Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; WOW64; Trident/5.0; chromeframe/12.0.742.112)
Mozilla/5.0 (Windows; U; MSIE 9.0; Windows NT 6.0; Win64; x64; Trident/5.0; .NET CLR 3.8.50799; Media Center PC 6.0; .NET4.0E)
Mozilla/5.0 (Windows; U; MSIE 9.0; Windows NT 8.1; Trident/5.0; .NET4.0E; en-AU)
Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; WOW64; Trident/5.0; .NET CLR 3.5.30729; .NET CLR 3.0.30729; .NET CLR 2.0.50727; Media Center PC 6.0)
Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 8.0; WOW64; Trident/5.0; .NET CLR 2.7.40781; .NET4.0E; en-SG)
Mozilla/5.0 (Windows; U; MSIE 9.0; Windows NT 8.0; Win64; x64; Trident/5.0; .NET4.0E; en)
Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; Win64; x64; Trident/5.0; .NET CLR 3.5.30729; .NET CLR 3.0.30729; .NET CLR 2.0.50727; Media Center PC 6.0)
Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; Win64; x64; Trident/5.0; .NET CLR 2.0.50727; SLCC2; .NET CLR 3.5.30729; .NET CLR 3.0.30729; Media Center PC 6.0; Zune 4.0; Tablet PC 2.0; InfoPath.3; .NET4.0C; .NET4.0E)
Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 7.0; Trident/5.0; .NET CLR 2.2.50767; Zune 4.2; .NET4.0E)
Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; Win64; x64; Trident/5.0

Да, это возможно, но для это нужно анализировать. Анализировать, например, какие .net могут стоять на каких ОС, анализировать версии и сочетания, нюансы. Когда они теряются в куче — задача становиться мало тривиальной. Кому интересно как выглядят регулярки — добро пожаловать по этой ссылке. Дня генерации использовал randexp.js (за подсказку спасибо хабрачуваку под ником barkalov).

Вообще расширение успешно выдает себя за следующие браузеры:
  • IE с 6 по 10;
  • Chrome (Win / Mac / Linux);
  • Firefox (Win / Mac / Linux);
  • Safari (Win / Mac / Linux);
  • Opera (Win / Mac / Linux);
  • iPad и iPhone.

Что ещё интересного? Автоматизация. Отмечаешь галочками какие браузеры мы имитируем, ставишь галочку «Обновлять автоматически», указываешь интервал времени, и забываешь. Ничего лишнего. Для любопытных — посмотрите в консоли «фоновую страницу» — там всё не плохо залогировано.

Открытые исходники. Если есть желание допилить под себя, всё что необходимо, это:
  1. Открыть ссылку расширения на гитхабе;
  2. Нажать «Download Zip» или склонировать;
  3. На странице расширений поставить чекбокс «Режим разработчика»;
  4. Нажать «Загрузить распакованное расширение...» и указать путь к распакованному архиву или клону;

Буду очень признателен конструктивной критике и предложениям.

Лучше один раз увидеть: Google Webstore

История изменений

  • 1.5 — Добавлены исключения, теперь модно некоторые URL-ы добавить в «белый список», и для них ничего не будет подменяться


Хорошие ссылки:

Paramtamtam @cmepthuk
карма
27,5
рейтинг 0,0
Пользователь
Реклама помогает поддерживать и развивать наши сервисы

Подробнее
Спецпроект

Самое читаемое Разработка

Комментарии (39)

  • +2
    Я бы менял юзерагент только в рамках текущего браузера, т.е. не показывал бы, к примеру, юзерагент Google Chrome, используя при этом Internet Explorer. Иначе возможна ситуация, что некоторые сайты будут работать некорректно. У браузеров отличается поведение в некоторых ситуациях и некоторые сайты учитывают эти нюансы в своей работе.
    • +1
      Так настраиваемо же.

      Спасибо за расширение!
    • +4
      Также существуют сайты, которые привязывают сессию пользователя к юзерагенту. Переавторизовываться каждые n минут не очень-то весело. Поддерживают ли подобные расширения для браузеров список сайтов-исключений?
  • –12
    Народ, а объясните мне пожалуйста, зачем это нужно делать? У нас в России же скрывай или не скрывай, если сильно захотят посадить — посадят. И более того, я не понимаю что и зачем скрывать? Все занимаются какой то преступной деятельностью?
    • +25
      Где-то на хабре встретил фразу: Мне нечего скрывать, но это не ваше дело.
    • +2
      Скрывать не от спецслужб, а от всяких рекламных сервисов и прочих трекингов, которые используют в том числе и UA+IP+еще кучу нетрадиционных методов, типа проверки закешированности файла, localStorage и прочая…
  • +8
    Угу, скрыл)) А через javascript мы выявляем несоответсвие UA и к этому пользователю приглядываемся более внимательно))
    • +5
      Да, "navigator.userAgent is a read-only property". Можно делать inject для подмены, но этот метод не очень нравится, я ещё подумаю как это можно обойти
      • +1
        console.log(navigator.userAgent);
        navigator.__defineGetter__('userAgent', function(){return 'bla bla bla';})
        console.log(navigator.userAgent);
        


        п.с. аналогично можно и document.referrer подменять и т.п.
    • 0
      Спустя довольно значительное время на изучение темы, пришел к выводу — заменить UA на фейковый, попутно подменив «navigator.plugins» и прочее можно, выполнив одну единственную инъекцию js-кода до обработчика window.onload. То есть, если мы в манифесте укажем конструкцию вида:

      manifest.json
      /* ... */
        "content_scripts":[  
          {  
            "all_frames":true,
            "js":["content.js"],
            "matches":[  
              "http://*/*",
              "https://*/*",
              "file:///*" /* для теста на локальных файлах */
            ],
            "run_at":"document_start"
          }
        ]
      /* ... */
      

      А в «content.js» вставим нашу инъекцию:

      content.js
      /* original source from : http://goo.gl/LWICQR */
      /* idea author: http://stackoverflow.com/users/938089/rob-w */
      function setupUserAgentHook(UserAgent){
          if(typeof UserAgent !== 'string' && UserAgent == '')
              return false;
      
          function addslashes(str) {	// Quote string with slashes
          	return str.replace(/([\"\'])/g, "\\$1");
          }
      
          var actualCode = '(' + function(newUserAgent){
              'use strict';
              var navigator = Object.create(window.navigator);
              Object.defineProperties(navigator, {
                  userAgent: {
                      value: newUserAgent,
                      configurable: false,
                      enumerable: true,
                      writable: false
                  },
                  appVersion: {
                      value: newUserAgent,
                      configurable: false,
                      enumerable: true,
                      writable: false
                  },
                  platform: {
                      value: 'Win32',
                      configurable: false,
                      enumerable: true,
                      writable: false
                  },
              });
              Object.defineProperty(window, 'navigator', {
                  value: navigator,
                  configurable: false,
                  enumerable: false,
                  writable: false
              });
          } + ')("'+addslashes(UserAgent)+'");';
          
          //console.log(actualCode);
          
          document.documentElement.setAttribute('onreset', actualCode);
          document.documentElement.dispatchEvent(new CustomEvent('reset'));
          document.documentElement.removeAttribute('onreset');
      }
      
      setupUserAgentHook('Hell Yeah!');
      


      То всё замечательно и все довольны. Но. Есть одно но. Нужно получить от «background.js» данные — какой UA ставить. Это можно сделать через «chrome.runtime.onMessage» — передать сообщение, можно через «chrome.tabs.executeScript» — выполнить что то в духе «var UA='Bla-Bla';», но эти функции работают в потоке, и результат приходит после window.onload. Замечательная многопоточность яваскиптов тут стала как кость в горле, как решить эту задачу — ума не приложу. Если есть идеи — буду чертовски признателен.
  • +4
    Владельцам Firefox рекомендую обратить внимание на Random Agent Spoofer. Помимо смены юзер-агента он умеет анонимизировать пользователя по другим признакам.
    Скрытый текст
    • +1
      Интересно. Не замечал ещё один канал утечки — WebGL, а через него можно вытащить некоторую инфу о видеокарте.
  • +9
    В яндексе вас забанят за странное поведение
    • +2
      На самом деле, не только в яндексе. Доблестные защитники сайтов от DDoS часто привязываются к ip + UA (покуда зачастую больше информации об одном пользователе для его идентификации не получить), и ip, с которого каждый раз ходит уникальный UA попадет под подозрение. В штатном режиме ничего не будет, однако при обнаружении атаки определенного типа можете попасть под нож (хотя это все зависит от конкретных алгоритмов отсева).
      Т.е. лучше будет, если на один сайт вы будете ходить с одним UA, на другие — с случайным, но тоже фиксированным в рамках сессии.
  • 0
    Если на то пошло, то из объекта navigator надо подменять инфу об установленных плагинах. Комбинация их версий + ip даст не худший уникальный слепок, чем c UA. Хотя, идея рандомизации мне нравится, спасибо за плагин.
  • +1
    А что делать с navigator.plugins?
    • +1
      Отлично, спасибо за наводку, я поработаю над этим
  • +1
    Интересный плагин для Chrome. А вот для Firefox знаю штуку помощнее, называется Secret Agent: www.dephormation.org.uk/index.php?page=81
    Позволяет не только подменять UA, но и также настраивать accept headers и некоторые другие параметры для усложнения снятия fingerprint-a, настраивать уровень энтропии и как бонус — выявлять попытки хайджека HTTP-запросов с редиректом на другой ресурс. И всё это очень гибко конфигурируется, в т.ч. наличиствует белый список ресурсов.
  • +2
    Хотите пример? Вот вам 10 UA браузера «IE 9», и среди них пять настоящих. Сможете отличить?

    Легко!

    Фейковые UA

    Mozilla/5.0 (Windows; U; MSIE 9.0; Windows NT 6.0; Win64; x64; Trident/5.0; .NET CLR 3.8.50799; Media Center PC 6.0; .NET4.0E)
    Mozilla/5.0 (Windows; U; MSIE 9.0; Windows NT 8.1; Trident/5.0; .NET4.0E; en-AU)
    Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 8.0; WOW64; Trident/5.0; .NET CLR 2.7.40781; .NET4.0E; en-SG)
    Mozilla/5.0 (Windows; U; MSIE 9.0; Windows NT 8.0; Win64; x64; Trident/5.0; .NET4.0E; en)
    Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 7.0; Trident/5.0; .NET CLR 2.2.50767; Zune 4.2; .NET4.0E)

    Реальные UA

    Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; WOW64; Trident/5.0; chromeframe/12.0.742.112)
    Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; WOW64; Trident/5.0; .NET CLR 3.5.30729; .NET CLR 3.0.30729; .NET CLR 2.0.50727; Media Center PC 6.0)
    Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; Win64; x64; Trident/5.0; .NET CLR 3.5.30729; .NET CLR 3.0.30729; .NET CLR 2.0.50727; Media Center PC 6.0)
    Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; Win64; x64; Trident/5.0; .NET CLR 2.0.50727; SLCC2; .NET CLR 3.5.30729; .NET CLR 3.0.30729; Media Center PC 6.0; Zune 4.0; Tablet PC 2.0; InfoPath.3; .NET4.0C; .NET4.0E)
    Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; Win64; x64; Trident/5.0
    • 0
      Зачем вообще генерить УА, которые однозначно определяются как фейк? Даже человек отличает, а робота и подавно научат.
      • 0
        «Определяется как фейк» !== «Раскрыть настоящий»
        «Если человек отличает» !== «Обучить робота тривиальная задача»
    • 0
      Хм, а так сможете отличить? Не подглядывая в исходники:

      Mozilla/5.0 (Windows NT 8.1) AppleWebKit/536.21 (KHTML, like Gecko) Chrome/32.0.2018.39 Safari/536.21
      Mozilla/5.0 (Windows NT 7.1; WOW64) AppleWebKit/536.12 (KHTML, like Gecko) Chrome/31.0.2043.42 Safari/536.12
      Mozilla/5.0 (Windows NT 7.0; Win64; x64) AppleWebKit/536.25 (KHTML, like Gecko) Chrome/37.0.2035.63 Safari/536.25
      Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/536.34 (KHTML, like Gecko) Chrome/33.0.2004.50 Safari/536.34
      Mozilla/5.0 (Windows NT 7.0) AppleWebKit/537.33 (KHTML, like Gecko) Chrome/34.0.2045.76 Safari/537.33
      Mozilla/5.0 (Windows NT 7.0; Win64; x64) AppleWebKit/537.15 (KHTML, like Gecko) Chrome/32.0.2005.16 Safari/537.15
      Mozilla/5.0 (Windows NT 6.0; WOW64) AppleWebKit/537.16 (KHTML, like Gecko) Chrome/37.0.2031.16 Safari/537.16
      Mozilla/5.0 (Windows NT 8.1) AppleWebKit/537.17 (KHTML, like Gecko) Chrome/34.0.2025.99 Safari/537.17
      Mozilla/5.0 (Windows NT 7.1; Win64; x64) AppleWebKit/537.22 (KHTML, like Gecko) Chrome/33.0.2010.94 Safari/537.22
      Mozilla/5.0 (Windows NT 8.1; WOW64) AppleWebKit/537.37 (KHTML, like Gecko) Chrome/37.0.2039.96 Safari/537.37

      (напомню, у нас на данный момент 19 различных браузеров имитируется, суммарно выбор довольно большой получается)
      • +1
        у chrome слишком много версий, чтобы мой метод сработал также хорошо как на определении фейковых UA для IE.

        Дело в том, что у меня есть доступ к архивам логов одного достаточно посещаегого сайта, в логах есть информация про UA пользователя, я просто прогнал грепом ваши UA по записям в логах. Если совпадений не находилось то я считал такие UA фейковыми.

        «Если человек отличает» !== «Обучить робота тривиальная задача»


        Для chrome это просто так не сработает, так как у них часто меняются версии. Но если есть доступ в большому объему логов, то вычислить фейковый UA также не составит проблем.
        • +1
          А где гарантия, что на сайт не заходили пользователи c расширением, генерирующим фейк-UA? Их UA запишется в лог и станет для вас настоящим.
          • 0
            hint: А каков процент посетителей с фейковым UA и с настоящим UA?
            • 0
              Методика же — просто сделать grep и проверить, что такой UA есть в логах. О подсчёте количеств ничего не говорилось ))
      • 0
        Хм, а так сможете отличить? Не подглядывая в исходники:
        (напомню, у нас на данный момент 19 различных браузеров имитируется, суммарно выбор довольно большой получается)


        Кажется вы решили дать мне задачку в которой нет правильного ответа, сдается мне что все UA фейковые, нет?
        • 0
          Это и есть правильный ответ, а метод грепа каждого UA по значительному объему логов — метод конечно мощный, но требует ресурсов. Если сайт обрабатывает, скажем, 100000 запросов в час (напомню, считается любой запрос в этом случае) — это 100000 грепов в час по большим логам. Можно конечно все повторяющиеся UA грохнуть и кешить запросы — но это уже отдельная история.
          Есть желание написать пост про «Долой анонимность — вычисляем фейковый UA?» :)
          • 0
            :) чукча не писатель, чукча читатель, статьи не будет.

            а метод грепа каждого UA по значительному объему логов — метод конечно мощный, но требует ресурсов


            100 тыс запросов в час это чуть больше 1го запроса в секунду. предположим что валидных UA около 10 тыс, поиск по 10 тыс строк занимает гораздо меньше секунды. Поэтому если кому-то это нужно, то это можно реализовать за нескольков часов на простеньком VPS.

            как я уже сказал UA chrome детектировать по логам мне было сложно, но я поступил проще, я проверял только версии chrome (31.0.2043.42) в логах :)

        • 0
          UPD. Да, чуть не забыл — что делать с тем, что фейковый UA будет идентифицирован? И через 5 минут (по дефолту) он будет уже сменен, а если есть несколько посетителей «под подозорением» — как идентифицировать каждого?
          • +1
            Set-Cookie: suspicious_ua=1 expires=Fri, 31 Dec 2099 23:59:59 GMT; path=/; domain=.example.net
  • +1
    Надеюсь, что соответствие браузеров стандартам шагнуло настолько далеко, что веб-приложение в Google Chrome не сглючит, если ему сказать, что это Firefox.
  • 0
    Кстати, почему бы просто не обрезать UA до идентификатора appName и минимально совместимой версии? В таком случае у сотни пользователей различных хромов после подмены окажется 3-4 уникальных UA, по которым отличить одного пользователя из сотни от другого станет крайне затруднительно.
    • 0
      Это было бы актуально, если за 100 юзверей 90 пользовались бы этим плагином с указанной вами конфигурацией.
      Разные алгоритмы используют разные методики определения, и дабы большинство «прокатило» — необходимо делать максимально подобный.
  • 0
    У chromium (и chrome тоже, я думаю) есть опция командной строки: --user-agent
    $ chromium --user-agent='Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; Win64; x64; Trident/5.0'
    
    Без = не работает.

    Можно засунуть желаемый список агентов в текстовый файл и выбирать случайный при запуске:
    $ chromium --user-agent="`shuf user-agents.txt | shuf | head --lines=1`"
    

    К сожалению, у Firefox такой опции не нашел.
  • +3
    См. статью Немножко анонимен.
    Скрыть реальный IP — используем разные VPN сервера, отключить львиную долю отслеживающих скриптов — Adblock Plus и Ghostery, убрать Referer — не вопрос, что то ещё забыли… Ах да
    … размер и глубину цвета экрана, шрифты (отпечаток по Canvas), временную зону и язык, отклонение времени от точного времени, пинг, локальный адрес и адреса других устройств (WebRTC) и так далее.

    Проверьтесь на panopticlick.eff.org Набрали 20 бит? А ведь там зверства вроде WebRTC- и Canvas-фингерпринтинга не используются.

    Но это всё полумеры, на самом деле нужно как минимум Tor Browser использовать, там многое уже исправлено. Если ресурсы позволяют, то внутри Whonix.

Только зарегистрированные пользователи могут оставлять комментарии. Войдите, пожалуйста.