company_banner

Эффективный фингерпринтинг через кэш фавиконов в браузере


    Демо

    Фавикон сайта — маленький значок .ico размером 16*16 или 32*32 пикселей на вкладке браузера. Помогает ориентироваться в сотнях вкладок. У твиттера синяя птичка, у Gmail красный символ почты, у Википедии жирное W.

    Но оказывается, что эти значки представляют собой уязвимость, через которую можно выполнять фингерпринтинг — идентифицировать юзера даже через VPN и режим инкогнито в браузере (см. демо).

    Немецкий программист Йонас Штреле (Jonas Strehle) в репозитории на Github описывает метод установки неудаляемого суперкуки через фавиконы: «Суперкуки через фавиконы присваивают посетителям сайта уникальные ID. В отличие от традиционных методов отслеживания, этот ID сохраняется практически навсегда и не может быть удалён пользователем с помощью простых методов. Метод отслеживания работает даже в режиме инкогнито. Суперкуки не удаляются при очистке кэша, закрытии браузера или перезапуске системы, использовании VPN или блокировщиков рекламы».

    Как работает фингерпринтинг


    Чтобы отобразить иконку, в код страницы вставляется такой атрибут:

    <link rel="icon" href="/favicon.ico" type="image/x-icon">

    Фавиконы должны быть очень легко доступны через браузер. Поэтому они кэшируются в отдельной локальной базе данных, так называемом кэше фавиконов (F-Cache), где хранится URL, идентификатор фавикона и время жизни.

    Когда пользователь заходит на сайт, браузер проверяет локальный F-Cache на наличие записи, содержащей URL-адрес активного веб-сайта. Если запись найдена, значок загружается из кэша. Если запись отсутствует, браузер отправляет GET-запрос, чтобы загрузить фавикон с сервера.

    Такой механизм позволяет серверу довольно много узнать о посетителе. Комбинируя статусы доставленных и не доставленных фавиконов для определённых URL, клиенту присваивается уникальный шаблон (идентификационный номер). Затем ID сохраняется:

    const N = 4;
    const ROUTES = ["/a", "/b", "/c", "/d"];
    const ID = generateNewID(); // -> 1010 • (select unassigned decimal number, here ten: 10 -> 1010b in binary)

    const vector = generateVectorFromID(ID); // -> ["/a", "/c"] • (because [a, b, c, d] where [1, 0, 1, 0] is 1 -> a, c)

    После повторной загрузки сайта этот ID можно восстановить по списку сетевых запросов, отправленных клиентом для недостающих фавиконов — и таким образом идентифицировать браузер.

    const visitedRoutes = [];
    Webserver.onvisit = (route) => visitedRoutes.push(route);  // -> ["/b", "/d"]
    Webserver.ondone = () => { const ID = getIDFromVector(visitedRoutes) }; // -> 10 • (because "/a" and "/b" are missing -> 1010b)

    Автор запустил сайт для демонстрации фингерпринтинга с помощью фавиконов. Опубликован исходный код и детальное описание механизма.

    В этой уязвимости самое неприятное — это то, насколько легко можно обойти традиционные методы, которые люди используют для защиты своей приватности. По словам Штреле, фингерпринтинг пробивает «приватный» режим Chrome, Safari, Edge и Firefox. Очистка кэша, VPN или блокировщик рекламы — ничто не помешает вредоносным фавиконам.

    К таким же выводам пришли исследователи из университета Иллинойса в опубликованной недавно научной работе "Tales of Favicons and Caches: Persistent Tracking in Modern Browsers": «Мы обнаружили, что сочетание нашей техники отслеживания на фавиконах с фингерпринтингом через неизменяемые атрибуты браузера позволяет сайту восстановить 32-битный идентификатор трекинга за две секунды», — говорится в исследовании. — В связи с серьёзностью уязвимости мы предлагаем внести изменения в кэширование фавиконов браузерами, чтобы предотвратить эту форму трекинга. Мы передали наши результаты разработчикам поставщикам браузеров, которые в настоящее время изучают варианты смягчения последствий».

    На данный момент фингерпринтинг через фавиконы работает во всех крупнейших браузерах, в том числе в мобильных (значок плюса):

    Браузер

    Windows

    MacOS

    Linux

    iOS

    Android

    Примечания
    Chrome (v 87.0) + + + + + ?
    Safari (v 14.0) ? + ? + ? ?
    Edge (v 87.0) + + ? ? + ?
    Firefox (v 85.0) + + ? ? ? Другой фингерпринтинг в режиме инкогнито
    Brave (v 1.19.92) + + + ? ? ?

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

    Редиректов
    (N бит)
    Кол-во различимых клиентов Время записи Время чтения Масштаб атаки
    2 4 < 300 мс < 300 мс Один юзер с четырьмя браузерами
    3 8 < 300 мс ~ 300 мс Примерная численность Кардашьянов
    4 16 < 1 с ~ 1 с Кучка ваших соседей
    8 256 < 1 с ~ 1 с Все ваши френды на Facebook
    10 1024 < 1,2 с ~ 1 с Очень маленькая деревня
    20 1 048 576 < 1,8 с < 1,5 с Небольшой город (Сан-Хосе)
    24 16 777 216 < 2,4 с < 2 с Все Нидерланды
    32 4 294 967 296 ~ 3 с < 3 с Все люди с доступом в интернет
    34 17 179 869 184 ~ 4 с ~ 4 с Все люди с доступом в интернет и 4 браузерами у каждого
    ITSumma
    Собираем безумных людей и вместе спасаем интернет

    Comments 45

      +1

      Что-то из примера непонятно — этот код выполняется на сервере или в браузере?


      Если я правильно понял, то атака основана на том, что браузер часть favicon-ов закеширует, а часть нет при создании отпечатка. Но при повторном визите он закеширует все остальные — т.е. эта атака одноразовая?

        0

        Нет, ведь url favicon можно генерировать скриптом

          0

          Ну и что? При первой же проверке вы на все юрл-ы сходите и закешируете их и придется еще один идентификатор генерировать. Так у вас быстро закончатся
          1) идентификаторы
          2) место в базе

            0

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

              +3

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

                0

                Я всегда думал, когда глядел на окончание путей у CSS, js в виде ?random=27474838, что для браузера это каждый раз новый путь и новый файл. С favicon не проверял.

                  +1

                  С фавиконами — то же самое. Просто у сайта может быть, например, 32 фавикона (фавикон привязан к странице, а не домену). И факт их закешированности (читай, отсутствия запроса к серверу) — это признак конкретного браузера.

                  +1

                  Если сервер отвечает ошибкой, то браузер ошибку не кеширует, а пробует каждый раз заново. И именно этот признак используется в режиме "чтения".

                    0
                    Так сервер должен заранее знать ответить 200 или ошибкой на данный конкретный бит-фавиконку. Откуда сервер это знает?
                      +1
                      Сначала мы пробуем прочитать идентификатор, если его — тогда выполняем запись. Сервер может знать о том, какой сейчас этап.
                        +2
                        В режиме чтения сервер, грубо говоря, отвечает всегда ошибками, но при этом ещё косвенно передаёт на клиент за какими картинками по факту ходил браузер (фавикон загружен успешно из кеша). Если клиент видит, что браузер ходил за всеми картинками, то клиент понимает, что куки ещё нет и её надо проставить. В режиме записи сервер отдаёт 200 коды на 1 биты и 404 для 0 и браузер кеширует единички.
              +9
              В переводе опущен важный механизм собственно как это все работает :) Но есть ссылка на оригинал.
              Там есть два режима — чтения (проверка идентификатора) и записи (запись идентификатора пользователя).
              Идентификатор записывается редиректом по разным урлам. Это вот /a,/b,/c из статьи. По каким-то из них сервер на запрос фавикона отдает 200-ОК, по каким-то 404 (по сгенерированному заранее ID).
              Соответственно чтение — прогнать пользователя по всем урлам и узнать какие фавиконы у него загружены, но при этом отправляя 404 в ответ. Т.е. при чтении браузер фавиконы и не получит (а сервер инфу получит). Если чтение показало, что фавиконов вообще нету, значит записи не было — прогоняем режим записи.
              Как-то так.
                +1
                Видно что это перевод, но статья нигде не помечена как перевод, это уже норма на хабре?
            0
            Chromium 88.0.4324.150 (Official Build) (64-bit)
            В режиме инкогнито не сработало.
            В обычном режиме по-умолчанию работает, но перестает, если принудительно обновить по Ctrl+F5.

            Очень странные результаты. Если бы вообще починили, то и трекинг сломался бы во всех режимах, а так не пойми что.
              +1
              Приватное окно FF 85.0.2 (win, x64) полностью сменило ИД — по несколько раз одновременно обновлял в обоих.
              В обычном режиме по-умолчанию работает, но перестает, если принудительно обновить по Ctrl+F5.

              Ctrl+F5 — тоже меняет, да.
                0

                Та же версия Firefox, но трекинг не работает и в обычном режиме. На гитхабе есть информация, что как раз в 85 версии это исправили.


                Firefox 85.0.2 (x64)
                Windows 10 Home 1909 (build 18363.1379)

                0
                Чего только не придумают. Скоро нужно будет каждый раз перед запуском браузера создавать чистый профиль. Хотя почему скоро? Уже сейчас.
                  +2
                  запускать чистую систему в виртуалке)
                    0
                    Перед входом в инет делать снэпшот, а потом по итогу работы удалять его
                      0
                      Да, есть такая мысль. Ещё можно скрипт прикрутить для автоматизации.
                  +10

                  Вы скопировали чужую статью и выдаёте за свою. Ах, ну да, это называется «вольный перевод». А я называю это мусором.

                    +2
                    Только собрался писать подобное, искал плашку перевод — не нашёл, подумал, что наверное что-то дописали своё, но когда увидел таблицу, понял, что нового не увижу, но хотелось бы узнать по теме чуточку побольше
                    0
                    Яндекс браузер
                    обычный режим а потом инкогнито выдал одинаковые идентификаторы
                      +1
                      Блокировщик рекламы, настроенный на перехват запросов к фавиконам, спасёт отца русской демократии от потери приватности?
                        0
                        Как он отличит что там именно favicon?

                        <link rel="icon" href="/image.jpg" type="image/jpg">
                          0
                          Ну это вы какой-то некорректный пример привели
                          Изначально было href="/favicon.ico"
                            0
                            Именно, что изначально был — пример. То что я привёл — это будет практика. Никто не запретит трекающим компаниям так делать.
                            0
                            Так, rel="icon" и говорит, что там указан favicon. Иногда пишут и так: rel="shortcut icon".
                              0
                              rel="icon" фигурирует в запросе? — Речь шла про «перехват запросов к фавиконам».
                                0

                                А в рекламных ссылках разве фигурирует какой-нибудь rel="ads"? Вот точно также и будет перехватывать.

                                  0
                                  Т.е. по базе доменов, IP-адресов и имён файлов. Никакой настройки на перехват именно фавиконов, в этом случае, — нет.
                          0

                          Что-то я не пойму, в чем уязвимость?
                          Ну, допустим, используя эту "уязвимость" сайт А знает, что на него заходят с конкретного ПК не единожды… и что с того?
                          Сайт Б ведь никогда не узнает, что я был на сайте А. Подозреваю, что такое возможно только если оба сайта будут использовать один и тот же favicon, который лежит на другом домене, либо на домене одного из сайтов? Но такой сценарий крайне маловероятен, кто может этим воспользоваться и с какой целью?

                            +2
                            «кто может этим воспользоваться и с какой целью?» — например имеется трекинговая компания Z предоставляющая услуги по антифрод защите сайтам A, B, C… сайты устанавливают на своих страничках(например регистрации) скрипт компании Z генерирующий фавиконы. Если пользователь зарегистрируется на сайте A, то сайты B,C уже будут знать что этот конкретный браузер связан с таким то клиентом сайта А. И допустим бан на одном из сайтов влечет за собой бан на всех других сайтах, даже если использовались разные айпи/емайлы/данные. Много сценариев, по пресечению мульти-регистрации, мошеннических действий. Возьмем пример с рекламной кампанией. То же самое, контора Z и услуга трекинга через фавикон позволит хранить отпечаток браузера и метрики пользователя о том, что он покупает или ищет в поиске на сайте C, и предоставлять информацию сайту D, когда пользователь зайдет туда, и сразу уже пойдет релевантный контент.
                              0

                              Правильно же я понимаю, что достаточно заблокировать домены этой ненужной прослойки в лице компании Z и вся эта схема перестанет работать?
                              Я о чем и говорю: если это делается через "третьих лиц", то как это можно называть уязвимостью? Некрасиво, да. Но даже в винде можно вылечить банально через hosts. Фингерпринтинг через пиксели мы же не называем уязвимостью, помоему принцип один в один.

                                0
                                Следящий пиксель не поможет, если использовать инкогнито. Перезапустил браузер — и ты новый пользователь для пикселя.
                                Favicon supercookie «пробивает» инкогнито за счет отдельного кэша для фавиконок — т.е. даже если пользователь перезапустил браузер, его supercookie останется тем же.

                                Если вы про фингерпринтинг на основе canvas/WebGl/шрифтов — то там проблема в том, что браузеру разрешено читать то, что отрисовано в canvas. Не знаю, что мешает производителям браузеров запретить это (или хотя бы запрашивать permission).

                                Если заблокировать домены — перестанет. Но это же технические домены, их можно выпускать тысячами, и это может делать и «третья» компания, и «первая» — замучаешься блокировать.
                                  +1
                                  Именно. А еще — обычный рядовой пользователь не будет это всё блокировать. Можно все что угодно заблокировать или подменять, канвас тот же, вебгл хеши. Но каждое противодействие фингерпринту увеличивает ваши фрод-риск очки. И при очередной блокировке система не даст вам, скажем, совершить оплату на сайте, потому что ваш браузер уже попадает под шаблон мошеннической операции. Технологии ФП широко применяются в платежных системах, банках, магазинах, везде где есть деньги. И этим сайтам как раз нужно что бы у клиентов все цифровые следы были включены и отрабатывали именно так как это обеспечивают браузеры по-умолчанию, а не заблокированы или подменены.
                            +2

                            Я правильно понимаю, что https://demo.supercookie.me/identity должен выдавать каждый раз одно значение?


                            Если так, то в обычном режиме, обычный F5 даёт разный результат на Linux/Firefox 85.0.1 вне режима Приватного просмотра.

                            0
                            Firefox: если обновить страницу через Ctrl+Shift+R, то отпечаток получается другим. И это с учётом того, что не установлены расширения, блокирующие получение fingerprint. Обычный режим. В Chrome не смотрел.

                            В 85 версии, вроде как, встраивали защиту от supercookie. Хотя, есть просто нажать на «Try Again», то отпечаток не поменялся. В принципе, это результат обычного кэширования.
                              0
                              Очень слабая эффективность в Chromium Version 88.0.4324.150 (Official Build) snap (64-bit)
                              В обычно окне только изредка поле рефреша (по F5) выдается тот же ID.
                              В окне инкогнито та же картина.
                              Но ID из обычного окна и из инкогнито — ни разу не совпали.
                                0
                                В Safari и FireFox не удалось воспросизвести — крутится в бесконечном цикле.
                                В Opera удалось.
                                  0
                                  Между прочим, от одного из авторов статьи поступил багрепорт в багтрекер Firefox о том, что кэш favicon в Firefox работает не так, как у других браузеров (и делает невозможным применение такого способа фингерпринтинга, но об этом в багрепорте умолчали).
                                    0
                                    Версия 88.0.4324.96 (Официальная сборка), for Linux Mint (64 бит)

                                    Инкогнито пробито.
                                      0
                                      Само по себе давний способ отслеживания (хотя прежде может и не применялся). В Firefox можно отключить иконки полностью (параметр browser.chrome.site_icons).
                                      После этого новые иконки перестают сохраняться в БД в профиле (но уже существовавшие остаются, нужно удалить файл базы favicons вручную).

                                      Only users with full accounts can post comments. Log in, please.