Звуковой отпечаток компьютера через AudioContext API



    Компании, отслеживающие действия пользователей в интернете, нуждаются в надёжной идентификации каждого человека без его ведома. Фингерпринтинг через браузер подходит идеально. Никто не заметит, если веб-страница попросит отрисовать фрагмент графики через canvas или сгенерирует звуковой сигнал нулевой громкости, замеряя параметры ответа.

    Метод работает по умолчанию во всех браузерах, кроме Tor. Он не требует получения никаких разрешений пользователя.

    Тотальный трекинг


    Недавно журналистка NY Times Кашмир Хилл обнаружила, что некая малоизвестная компания Sift накопила на неё досье объёмом 400 страниц. Там список покупок за несколько лет, все сообщения хостам на Airbnb, лог запусков кошелька Coinbase на мобильном телефоне, IP-адреса, заказы пиццы с iPhone и многое другое. Аналогичный сбор ведут несколько скоринговых фирм. Они учитывают до 16 000 факторов при составлении «рейтинга доверия» каждого пользователя. Трекеры Sift установлены на 34 000 сайтов и мобильных приложений.

    Поскольку следящие куки и скрипты не всегда хорошо работают или отключены у клиента, трекинг пользователей дополняют фингерпринтингом — это набор методов для получения уникального «отпечатка» браузера/системы. Список установленных шрифтов, плагинов, разрешение экрана и другие параметры в сумме дают достаточно бит информации для получения уникального ID. Хорошо работает фингерпринтинг через canvas.

    Фингерпринтинг через Canvas API


    Веб-страница отправляет браузеру команду отрисовать графический объект из нескольких элементов.

    <canvas class="canvas"></canvas>

     const canvas = document.querySelector('.canvas');
    const ctx = canvas.getContext('2d');
    // Maximize performance effect by
    // changing blending/composition effect
    ctx.globalCompositeOperation = 'lighter';
    
    // Render a blue rectangle
    ctx.fillStyle = "rgb(0, 0, 255)";
    ctx.fillRect(25,65,100,20);
    
    // Render a black text: "Hello, OpenGenus"
    var txt = "Hello, OpenGenus";
    ctx.font = "14px 'Arial'";
    ctx.fillStyle = "rgb(0, 0, 0)";
    ctx.fillText(txt, 25, 110);
    
    // Render arcs: red circle & green half-circle
    ctx.fillStyle = 'rgb(0,255,0)';
    ctx.beginPath();
    ctx.arc(50, 50, 50, 0, Math.PI*3, true);
    ctx.closePath();
    ctx.fill();
    ctx.fillStyle = 'rgb(255,0,0)';
    ctx.beginPath();
    ctx.arc(100, 50, 50, 0, Math.PI*2, true);
    ctx.closePath();
    ctx.fill();

    Результат выглядит примерно так:



    Функция Canvas API под названием toDataURL() возвращает URI с данными, которые соответствуют этому результату:

     console.log(canvas.toDataURL());
    /*
    Ouputs something like:
    "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAUAAAAFCAYAAACNby
    mblAAAWDElEQVQImWNgoBMAAABpAAFEI8ARexAAAElFTkSuQmCC"
    */

    Этот URI отличается на разных системах. Затем он хэшируется и используется вместе с другими битами данных, которые составляют уникальный отпечаток системы. Среди прочего:

    • установленные шрифты (около 4,37 бита идентифицирующей информации);
    • установленные плагины в браузере (3,08 бита);
    • заголовки HTTP_ACCEPT (16,85 бита);
    • user-agent;
    • язык;
    • часовой пояс;
    • размера экрана;
    • камера и микрофон;
    • версия ОС;
    • и др.

    Хэш отпечатка canvas добавляет ещё 4,76 бита идентифицирующей информации. Хэш отпечатка WebGL — 4,36 бита.

    Тест фингерпринтинга

    Недавно в дополнение к набору параметров добавился ещё один: звуковой отпечаток через AudioContext API.

    Ещё в 2016 году этот метод идентификации уже использовали сотни сайтов, такие как Expedia, Hotels.com и др.

    Фингерпринтинг через AudioContext API


    Алгоритм действий такой же: браузер выполняет задачу, а мы записываем результат выполнения и вычисляем уникальный хэш (отпечаток), только в данном случае данные извлекаются из аудиостека. Вместо Canvas API идёт обращение к AudioContext API, это интерфейс Web Audio API, который поддерживают все современные браузеры.

    Браузер генерирует низкочастотный аудиосигнал, который обрабатывается с учётом звуковых настроек и оборудования, установленного на устройстве. При этом никакой звук не записывается и не воспроизводится. Колонки и микрофон не задействуются.

    Преимущество этого метода фингерпринтинга в том, что он независим от браузера, так что позволяет отслеживать пользователя даже после перехода с Chrome в Firefox, затем в Opera и так далее.

    Тест отпечатка через AudioContext API



    Как получить отпечаток, пошаговое руководство:

    1. Во-первых, нужно создать массив для хранения значений частоты.

       let freq_data = [];

    2. Затем создаётся объект AudioContext и различные узлы для генерации сигнала и сбора информации, используя встроенные методы объекта AudioContext.

       // Create nodes
      const ctx = new AudioContext(); // AudioContext Object
      const oscillator = ctx.createOscillator(); // OscillatorNode
      const analyser = ctx.createAnalyser(); // AnalyserNode
      const gain = ctx.createGain(); // GainNode
      const scriptProcessor = ctx.createScriptProcessor(4096, 1, 1); // ScriptProcessorNode

    3. Отключаем громкость и соединяем узлы друг с другом.

       // Disable volume
      gain.gain.value = 0;
      // Connect oscillator output (OscillatorNode) to analyser input
      oscillator.connect(analyser);
      // Connect analyser output (AnalyserNode) to scriptProcessor input
      analyser.connect(scriptProcessor);
      // Connect scriptProcessor output (ScriptProcessorNode) to gain input
      scriptProcessor.connect(gain);
      // Connect gain output (GainNode) to AudioContext destination
      gain.connect(ctx.destination);

    4. Используя ScriptProcessorNode, создаём функцию, которая собирает частотные данные во время обработки звука.
      • Функция создаёт типизированный массив Float32Array с длиной, равной числу (частоте) значений данных в AnalyserNode, а затем заполняет его значениями.
      • Эти значения затем копируются в массив, который мы создали ранее (freq_data), чтобы мы могли легко записывать их в выходные данные.
      • Отключаем узлы и выводим результат.

         scriptProcessor.onaudioprocess = function(bins) {
        // The number of (frequency) data values
        bins = new Float32Array(analyser.frequencyBinCount);
        // Fill the Float32Array array of these based on values
        analyser.getFloatFrequencyData(bins);
        // Copy frequency data to 'freq_data' array
        for (var i = 0; i < bins.length; i = i + 1) {
        freq_data.push(bins[i]);
        }
        // Disconnect the nodes from each other
        analyser.disconnect();
        scriptProcessor.disconnect();
        gain.disconnect();
        // Log output of frequency data
        console.log(freq_data);
        }; 

    5. Начинаем воспроизводить тон, так что звук генерируется и обрабатывается в соответствии с функцией.

       // Start playing tone
      oscillator.start(0);
    Результат получается примерно такой:

     /*
    Output:
    [
    -119.79788967947266, -119.29875891113281, -118.90072674835938,
    -118.08164726269531, -117.02244567871094, -115.73435120521094,
    -114.24555969238281, -112.56678771972656, -110.70404089034375,
    -108.64968109130886, ...
    ]
    */

    Эта комбинация значений хэшируется для создания отпечатка, который затем используется с другими битами идентификации.

    Для защиты от такого трекинга можно использовать расширения вроде AudioContext Fingerprint Defender, которые подмешивают случайный шум в отпечаток.

    NY Times приводит адреса email, по которым можно обратиться в трекинговые фирмы и попросить показать собранную на вас информацию.

    • Zeta Global: онлайн-форма
    • Retail Equation: returnactivityreport@theretailequation.com
    • Riskified: privacy@riskified.com
    • Kustomer: privacy@kustomer.com
    • Sift: privacy@sift.com, онлайн-форма деактивирована после публикации статьи




    GlobalSign
    181,69
    Компания
    Поделиться публикацией

    Комментарии 10

      +3
      Еще одно обязательно расширение в копилку, спасибо. В рунете, оказывается, тоже многие этим балуются. Например, уведомление на авито сработало.
        +6
        А потом у меня люди спрашивают зачем я отключаю JS на некоторых сайтах
          +1
          Большинство сайтов с выключенным JS просто не будут работать.
          Выключать JS не обязательно, достаточно установить расширения Adblock Plus, uBlock Origin или Ghostery, которые блокируют отслеживающие скрипты на сайтах.
            +2
            uBlock Origin, конечно, и так стоит. Но отключение JS на сайтах со статьями обычно ломает как раз только самые раздражающие их части — например, видео с автоплеем и попапы про рассылку.

            p.s. те, кто считает, что lazy loading картинок — хорошая идея, должны гореть в аду
          0
          Тест фингерпринта на сайте из статьи неидеален. У меня стоит ru, en, ja, он посчитал только ru и вывел всего лишь 5.44 бита. Думаю, что в такой комбинации будет около 17+ бит (en не учитывал, т. к. он итак обычно стоит по умолчанию). Хотя посчитан HTTP_ACCEPT Headers, в который языки входят, правда насчитал всего 17 бит.

          Также не указывается пересечение, разве что додумывать самому. Например, на ru пишет 5.44 бита, на UTC+3 — 4.54. Но у очень многих с языком ru часовой пояс будет +3, поэтому сумма будет лишь немного выше 5.44 бит.

          Не посчитан fresh rate. Хотя он может меняться (на FreeSync при воспроизведении видео ещё пока не знаю, вероятно, там необязательно менять частоту монитора, а можно сразу выбросить кадры в нужный момент без изменения частоты).

          Не посчитана высота и ширина страницы, но она может часто меняться.

          В остальном тест прикольный.
            0
            Windows используют 70% пользователей и Google Chrome 60%. Размер экрана у 20% пользователей Full HD, в user-agent уже включена ОС и у большинства пользователей user-agent будет одинаков. В территориально маленьких странах часовой пояс будет совпадать с языком в системе.
            В Chrome и Firefox установлено от трёх до пяти стандартных плагинов, такие как Chrome PDF Plugin и Native Client. Доступ к установленным расширениям получить нельзя.
            Если вы проверите аудио отпечаток на нескольких одинаковых серверах, то он будет одинаковым, по крайней мере на windows 10. Шрифты в ОС и браузерах одинаковые.
            Из всех параметров которые мы написали можно идентифицировать пользователя только по canvas, и то затруднительно.
              +1
              > Шрифты в ОС и браузерах одинаковые.
              а если ставил кастомные?
              0
              Протестировал на двух ноутбуках одинаковой модели — результаты идентичные, пользы мало от этого отпечатка.
                0

                Крутяк.

                  +2
                  В Firefox сейчас расширение CanvasBlocker стало универсальным инструментом: искажает отпечатки Canvas, WebGL, Audio, DOMRect API…

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

                  Самое читаемое