![]() |
https://forum.antichat.xyz/attachmen...2055606136.png
Сегодня у нас время. Нет, не то время, которое показывают часы на стене офиса, где ты сидишь в позе креветки, ожидая окончания рабочего дня. А время как канал утечки данных. Самый незаметный, самый сладкий и, блин, самый нелюбимый программистами, которые пишут код на коленке. Глава 1. Почему "Безопасно" - это не "Работает" Скажи мне, вэбэ, как ты проверяешь, правильный ли пароль ввел юзер? Ну, кроме тех случаев, когда у тебя там SELECT * FROM users WHERE password = '".$_POST['pass']."' (за такое по голове лопатой, но мы знаем, ты так не делаешь... или делаешь?). Ты берешь строку из базы, хеш пароля, и сравниваешь. Чаще всего ты юзаешь что-то вроде strcmp() в PHP, == в Python или equals() в Java. И ты думашь: "Ну, я сравнил строки. Если они одинаковые - пускаю, если нет - шлю лесом. Безопасно же?". А вот хер там. С этого момента я прошу тебя включить воображение. Представь, что у тебя есть сейф с кодом 1234. Ты подходишь и начинаешь вводить цифры. Если ты ввел первую цифру неправильно, замок щелкает сразу и говорит "нет". А если ты угадал первую цифру, то механизм переходит ко второй, и ему нужно чуть больше времени, чтобы проверить вторую и понять, что ты лох. Теперь представь, что у тебя есть супер-микрофон, который слышит разницу между щелчком "неправильно сразу" и паузой перед вторым щелчком. Вот это и есть timing attack. Мы не ломаем шифр математически (как эти задроты из академий). Мы не тыкаем миллиард паролей в секунду (нас заблочит Fail2Ban). Мы просто замеряем, сколько времени сервер мучается, прежде чем послать нас. И знаешь, что в этом самое смешное? Это не баг в коде. Это фича физики. Это фича материи, из которой сделан твой процессор. Ты не можешь отмазаться, сказав "это глюк фреймворка". Это глюк реальности. И сегодня мы научимся его эксплуатировать. 1.1 Абстракции - враг правды Знаешь, в чем главная проблема современного веба? В том, что среднестатистический вэбэ (давай уже назовем вещи своими именами - кодер) искренне верит в магию. Он пишет if (userInput === secretKey) { grantAccess(); } и считает, что компьютер просто мистическим образом понимает - равны строки или нет. Компьютер берет эти две последовательности байтов, смотрит на них своей электронной душой и выносит вердикт. Но, твою мать, компьютер - это не шаман с бубном. Это просто гора песка (кремния), которую научились травить кислотой, чтобы получились транзисторы. И единственное, что умеют делать эти транзисторы - это переключаться между состояниями "открыт" и "закрыт", пропуская ток или не пропуская. Весь твой Python, JavaScript, PHP - это просто тоненькая пленка абстракции, которая скрывает от тебя кровавую баню, происходящую внутри. Когда ты пишешь strcmp(), на самом деле процессор выполняет сотни микро-действий. Он грузит данные из памяти (медленно), он сравнивает их по байтам (быстро, но с нюансами), он принимает решения (переходы) и пишет результат обратно. И каждое из этих действий оставляет след. След во времени. След в потреблении энергии. След в электромагнитном излучении. Большинство разработчиков - как слепые котята в этом мире сигналов. Они видят только результат функции. А хакер смотрит на осциллограф. Или на точный таймер. И видит всю подноготную. Практический инструмент: Перцептрон для программиста Прежде чем мы пойдем дальше, давай проведем простейший эксперимент у тебя на локальной машине, чтоб ты прочувствовал момент. Забудь про сеть, просто скомпилируй и запусти. Это наш первый инструмент -микроскоп для кода. Возьмем две простые функции на C. Не пугайся, это базово. Файл: timing_test.c C: Код:
#include Минусы: нужно писать на C/asm, не переносимо на ARM (там другие инструкции). В Python можно использовать библиотеку percpu или встроенный time.perf_counter(), который на большинстве платформ использует RDTSC или его аналоги.[/I] 2.4.2 Системный вызов, возвращающий время в наносекундах. Точность зависит от реализации, обычно несколько десятков наносекунд. В Python time.perf_counter() использует именно его. Python: Код:
importМинусы: накладные расходы самого вызова (может быть несколько сотен наносекунд), что может быть сопоставимо с измеряемой величиной. 2.4.3 Для серьёзного профилирования используется perf, который может считать не только время, но и количество кэш‑промахов, ошибочных предсказаний и т.д. Это не для онлайн‑атаки, а для исследования. 2.4.4 Калибровка и учёт накладных расходов При измерении очень маленьких интервалов нужно вычитать время самого измерительного кода. Например, измерить пустой цикл с вызовом __rdtsc и вычесть его из результатов. 2.5 Шум и статистика: как отсеивать выбросы Ты никогда не получишь идеально чистое время. На результат влияют:
Базовые приёмы:
Python: Код:
import5.4 Шум от Garbage Collector и фоновых процессов Сервер - это не изолированная среда. Там крутится куча всего: cron, логи, мониторинг, другие клиенты. И всё это влияет на время ответа. 5.4.1 Garbage Collector (Java, C#, Go, Python...) В языках с автоматическим управлением памятью сборщик мусора может запуститься в любой момент и на несколько миллисекунд (или даже секунд) заморозить выполнение программы. Для timing-атаки это катастрофа: один такой выброс может испортить всю выборку. Как бороться:
Виртуальные машины типа Java HotSpot или V8 компилируют часто выполняемый код на лету. Первый вызов функции может быть медленным (интерпретация), потом - быстрым (скомпилированный код). Это тоже создаёт неоднородность. Совет: перед началом атаки сделать много "прогревочных" запросов, чтобы код скомпилировался и вошёл в установившийся режим. 5.4.3 Турбо-буст и энергосбережение Современные процессоры меняют частоту в зависимости от нагрузки. Если сервер простаивает, частота может упасть, и время выполнения увеличится. Если ты долбишь сервер запросами, частота поднимется. Это не страшно, если все измерения делаются в одинаковых условиях. Но если атака длится долго, а сервер то нагружается, то простаивает из-за других клиентов, частота будет плавать. Решение: мониторить частоту (через cpufreq или rdtsc), и если замечаешь скачки, делать перерывы или нормализовать время по текущей частоте. 5.4.4 Прерывания и планировщик Каждые несколько миллисекунд происходит прерывание таймера, переключение контекста, обработка сетевых прерываний. Это добавляет случайный шум. Но он обычно невелик (микросекунды) и при большом количестве замеров усредняется. Заключение Мы копаемся в кишках процессов, мы - археологи цифрового мира. Мы видим, как на самом деле работает эта хитрая штука под названием "компьютер". Он не идеален. Он не безопасен. Он просто выполняет инструкции, даже если эти инструкции выдают наши секреты через чёрный ход времени. Я не призываю тебя валить сайты. Я призываю тебя думать. Когда ты в следующий раз сядешь писать свой "супер-пупер защищенный" код на Laravel или Django, вспомни про эту статью. Вспомни, что твой hash_equals() в PHP (кстати, он как раз constant-time) появился не просто так. Его добавили, потому что умные дяди поняли: сравнение строк через == - это пуля в ногу. Будь честен с кодом. Код - это отражение твоей мысли. Если твоя мысль кривая, код будет течь. Атаки по времени - это не магия. Это просто разговор с машиной на её языке. Она шепчет тебе: "Ты угадал первый символ... ну давай, пробуй дальше...".[/I][/I] |
| Время: 00:55 |