Атаки на реализацию: timing attacks, side-channel

Природа атак по времени выполнения

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

В JavaScript эта проблема усиливается особенностями среды выполнения:

  • JIT-компиляция (V8, SpiderMonkey, JavaScriptCore)
  • оптимизации горячих веток кода
  • динамическая типизация
  • кэширование объектов и массивов
  • непредсказуемость работы сборщика мусора

Даже при отсутствии явных ошибок логики, реализация может утечь через косвенные признаки.


Криптографические библиотеки и модель постоянного времени

Библиотеки вроде TweetNaCl.js и nacl.js основаны на оригинальной NaCl (Networking and Cryptography library), разработанной с жёстким требованием:

все операции над секретными данными должны выполняться в постоянное время (constant-time).

Это означает:

  • отсутствие ветвлений, зависящих от секретных данных
  • отсутствие ранних выходов (early return)
  • одинаковый паттерн доступа к памяти независимо от ключа или сообщения
  • использование битовых операций вместо условных переходов

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


Особенности JavaScript-реализаций и границы защиты

TweetNaCl.js — порт оригинальной C-реализации, транслированный в JavaScript с сохранением логики constant-time. При этом возникает ряд проблем:

1. JIT-оптимизации могут разрушать предполагаемую модель времени

JavaScript-движок может:

  • инлайнить функции
  • изменять порядок инструкций
  • оптимизировать условные выражения
  • заменять циклы на более эффективные конструкции

Даже если исходный код написан как constant-time, итоговое поведение может зависеть от движка.


2. Неравномерное время операций над массивами

Работа с Uint8Array и ArrayBuffer обычно более предсказуема, чем с обычными массивами, но:

  • доступ к памяти может кэшироваться
  • выход за пределы может приводить к деградации производительности
  • разные CPU и браузеры имеют различную латентность операций

3. Различия в обработке чисел

JavaScript использует IEEE-754 double precision, что приводит к:

  • дополнительным преобразованиям типов
  • различной стоимости арифметических операций
  • возможным ветвлениям внутри движка при оптимизации

Классические источники утечек в криптографическом JS-коде

Условные конструкции на основе секретов

Наиболее опасный класс ошибок:

if (secretByte === 0) {
  // выполнение зависит от секретного значения
}

Даже если условие кажется безобидным, различие во времени исполнения фиксируется внешним наблюдателем.


Сравнение строк и массивов

Типичная уязвимость:

function insecureCompare(a, b) {
  if (a.length !== b.length) return false;

  for (let i = 0; i < a.length; i++) {
    if (a[i] !== b[i]) return false;
  }

  return true;
}

Проблема: ранний выход создаёт корреляцию между временем выполнения и позицией первого несовпадения.

В криптографии используется:

  • побитовое сравнение без ветвлений
  • аккумуляция результата через XOR

Побочные каналы через память

Даже при constant-time логике возможны утечки через:

  • кэш процессора (cache timing)
  • различия в доступе к памяти (cache hits/misses)
  • предсказатель ветвлений CPU
  • garbage collector паузы

Подход NaCl: дисциплина constant-time

NaCl-подход включает несколько принципов:

Арифметика вместо ветвлений

Условные операции заменяются на:

  • маски
  • побитовые операции AND/OR/XOR
  • арифметические комбинации

Пример идеи:

  • вместо if (x) y = a else y = b
  • используется y = (mask & a) | (~mask & b)

Равномерный доступ к памяти

Алгоритмы проектируются так, чтобы:

  • каждый шаг цикла обращался к памяти одинаково
  • отсутствовали данные-зависимые индексы массивов

Отказ от секретных условий

Любые проверки:

  • длины ключа
  • корректности подписи
  • валидности сообщения

выполняются так, чтобы не раскрывать информацию о структуре входа через время выполнения.


TweetNaCl.js и реализация в JavaScript

TweetNaCl.js стремится сохранить свойства оригинальной библиотеки:

  • использование Uint8Array
  • реализация операций над 32-битными словами через битовую арифметику
  • отсутствие условных переходов на основе секретных данных

Однако существуют ограничения JS-уровня:

1. Невозможность полного контроля над движком

Даже идеально написанный код не гарантирует:

  • одинаковую машинную оптимизацию на всех платформах
  • стабильное время выполнения на разных браузерах

2. Абстракция памяти

В отличие от C:

  • нет прямого управления кэшем
  • отсутствует контроль над выравниванием памяти
  • JIT может реорганизовать доступ к данным

3. Влияние окружения

На timing могут влиять:

  • загрузка CPU
  • работа других вкладок браузера
  • энергосберегающие режимы
  • особенности мобильных процессоров

Side-channel beyond timing

Timing attacks — лишь один класс побочных каналов. В криптографических реализациях также учитываются:

Cache-based attacks

Различия в:

  • попадании в L1/L2/L3 кэш
  • промахах кэша при доступе к данным

Branch prediction leakage

CPU предсказывает ветвления, и:

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

Memory access patterns

Даже при одинаковом времени исполнения:

  • разные адреса памяти могут оставлять следы в подсистеме кэша
  • доступ к различным областям массива может быть измерим

Практика защиты в JavaScript-криптографии

Использование Typed Arrays

Uint8Array, Uint32Array:

  • уменьшают накладные расходы
  • обеспечивают более предсказуемый доступ к памяти

Constant-time primitives

Критические операции должны:

  • избегать if на основе секретов
  • использовать побитовые маски
  • всегда выполнять одинаковое количество операций

Изоляция секретов

Секретные данные:

  • не должны покидать область криптографических функций
  • не должны использоваться в UI-логике
  • не должны участвовать в форматировании или логировании

Минимизация взаимодействия с JS-рантаймом

Опасные источники утечек:

  • console.log с секретами
  • конкатенация строк с ключами
  • сериализация объектов
  • неявные преобразования типов

Ограниченность гарантий в браузере

Даже при использовании TweetNaCl.js:

  • браузер не является безопасной моделью против продвинутых side-channel атак
  • постоянное время в JS — это приближение, а не строгая гарантия
  • атаки уровня Spectre/Meltdown концептуально обходят многие абстракции

Криптография в JavaScript рассматривается как:

  • практическая защита от сетевых атак
  • но не как защита против локального высокоточного наблюдателя выполнения

Контроль поведения оптимизатора

Некоторые реализации применяют техники:

  • «разогрева» функций (warm-up) для стабилизации JIT
  • предотвращения деоптимизации горячих путей
  • принудительного использования Uint8Array вместо generic arrays
  • минимизации динамических свойств объектов

Итоговая модель угроз для TweetNaCl.js

В контексте side-channel атак модель угроз включает:

  • наблюдение за временем выполнения функций
  • анализ различий между корректными и некорректными входами
  • статистическую корреляцию задержек
  • эксплуатацию особенностей JS-движка

Основной принцип остаётся неизменным:

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