Вы правы. В этом сценарии проблема не в bcrypt, а в самом корпоративном шаблоне: общий префикс не добавляет стойкости, хвост легко перебрать, и менеджер паролей здесь — правильное решение.
Статья приводит этот пример не как основную угрозу, а как иллюстрацию того, что неожиданное поведение bcrypt может накладываться на уже плохие пользовательские практики и создавать ложное ощущение уникальности.
72 байта — это достаточно для защиты от взлома. Статья не утверждает обратного.
Проблема в другом: bcrypt тихо игнорирует всё, что длиннее 72 байт. Если пользователь ввёл пароль из 100 символов, система запомнит только первые 72. Пользователь думает, что его пароль уникален, а на самом деле последняя часть пароля не имеет значения.
Главная опасность — не взлом, а сбой при обновлении библиотек: новые версии Python-модуля bcrypt не обрезают пароль, а падают с ошибкой, если он длиннее 72 байт. Если у вас в форме нет ограничения длины — пользователи с длинными паролями не смогут войти после обновления.
Сертификат здесь — всего лишь следствие, а не причина. Если вы управляете сертификатами сами, но оставляете «висячий» CNAME, злоумышленник всё равно получит ваш домен и автоматически выпустит на него свой сертификат (через Let's Encrypt или другого провайдера).
Ваше собственное управление сертификатами эту атаку не закрывает, потому что вы не контролируете, кто запрашивает сертификат для вашего поддомена, пока DNS указывает на чужой IP.
Да, справедливо. Флага «длиннее лимита» тут и правда достаточно, само число ничего не добавляет.
У себя мы просто не принимаем такие пароли: форма отказывает сразу, и слишком длинная строка до хеширования не доходит. Тогда и в лог писать почти нечего.
Спотыкались мы там на другом: длину надо мерить в байтах, а не в символах. 30 кириллических символов — это уже 60 байт, так что на форме человек видит одно число, а упирается совсем в другое.
Спасибо за комментарий! Здесь не говорится, что это самый популярный вектор, он скорее подан с посылом для тех, кто сейчас или когда-то в будущем будет строить процессы безопасной работы с кодом (чтобы учитывали, что такие кейсы бывают). Я вижу это как интеграцию в CI, когда при пролитии кода запускается статический анализатор, который помимо проблемных моментов (наличие уязвимостей, алерты линтеров и пр.) подсвечивает ещё подобные кейсы. Кмк проверка может оказаться весьма полезной
Спасибо, что проверили. Пустой каталог означает, что подключением управляет не keyfile-плагин NetworkManager, и пароль надо искать в другом месте.
Самый короткий способ узнать, где именно:
nmcli -f NAME,TYPE,FILENAME connection show
Команда печатает путь к файлу для каждого профиля. Если список пуст, NetworkManager этим подключением не занимается вовсе.
Дальше по частым вариантам:
— Ubuntu внутри WSL. Своего Wi-Fi-интерфейса в Linux тогда нет, сеть проброшена из Windows, и пароль лежит в профиле Windows. Проверяется по uname -r: в WSL в версии ядра есть слово microsoft.
— netplan, обычная история для серверной установки: файлы .yaml в /etc/netplan, пароль там открытым текстом или хешем.
— wpa_supplicant напрямую: конфиги в /etc/wpa_supplicant/.
— iwd вместо wpa_supplicant: файлы .psk в /var/lib/iwd/.
— подключение не сохранено, а поднято на время сессии: /run/NetworkManager/system-connections/.
Отдельно про сам grep: если файлы профилей в каталоге есть, а psk= в них не находится, значит пароль сохранён в связке ключей. В профиле тогда стоит psk-flags=1, а само значение хранит gnome-keyring. В статье этот случай оговорён, но по выводу grep он выглядит так же, как отсутствие пароля.
Пароль от WIFI - это легкозаменяемая и технически решаемая мелочь. Она приведена здесь не потому, что это главная угроза, а потому что это быстрый и дешёвый выигрыш в безопасности, которым многие пренебрегают: перейти на WPA2-Enterprise или привязать MAC-адреса — проще, чем менять графики работы сотрудников после каждого увольнения.
Также отмечу, что если точка доступа связана с внутренней инфраструктурой (или её частью), то атакующему это дает большой простор для атак. И подключиться к WIFI в этом случае намного проще, чем искать нужные розетки, проникать в офис, либо искать фото с телефонов сотрудников)
Но согласен, что деталей, которые могут быть использованы при атаках много, но в одной статье их не перечислить
Минуса по сути нет, вариант рабочий — это как раз одна из тех развязок, о которых речь в статье («развести слушателей»). Вы вынесли разбор обоих стеков на nginx, а бэкенд общается с ним по чистому IPv4 через 127.0.0.1 и mapped-адрес не увидит вовсе. Хороший подход, мне нравится.
Проблема при этом никуда не девается, просто стягивается в одну точку — в сам nginx. Пока listen раздельные, как у вас (443 и конкретный [2001:…]), всё чисто. Но стоит где-то появиться listen [::]:443 без ipv6only=on — и в логах самого nginx $remote_addr поедет в форме ::ffff:…, теперь уже на уровне прокси.
И второе: реальный адрес клиента теперь приезжает в приложение не из сокета, а из X-Forwarded-For, который вы прокидываете. Если перед nginx окажется ещё один прокси на дуал-стек сокете, mapped-форма может прилететь и в этот заголовок — а его обычно разбирают руками. Так что приём отличный, просто точка, где держать нормализацию адреса, смещается с сокета приложения на границу прокси.
Отличный разбор, редко кто доводит цепочку рассуждений оптимизатора до конца так наглядно. Вдобавок к -fwrapv и санитайзерам хочу подкинуть ещё один удобный инструмент из этой же оперы — checked-арифметику, она хорошо ложится рядом.
В GCC и Clang есть __builtin_add_overflow(a, b, &res) и его собратья __builtin_sub_overflow и __builtin_mul_overflow. Каждый выполняет операцию, кладёт результат по указателю и возвращает флаг переполнения, а компилируется в аппаратную проверку соответствующего флага процессора — быстро и без UB. Приятно тем, что классическое if (a + b < a) для знаковых, которое оптимизатор и выкидывает, тут просто не нужно: неопределённого поведения в builtin нет, проверять нечего.
В C23 это же завезли в стандарт — заголовок <stdckdint.h> и ckd_add(&res, a, b), уже переносимо, без привязки к конкретному компилятору.
Про -fwrapv стоит держать в голове его цену: он делает знаковое переполнение определённым для всей единицы трансляции, то есть заодно приглушает оптимизации везде, а не только там, где нужна проверка. Точечный builtin в этом смысле дешевле — ничего вокруг не трогает. В общем, есть из чего выбрать под задачу.
То, что вы описали, в ML называется concept drift, а «выключатель по трейлинг-Sharpe» — это детектор дрейфа по выходу. У него встроенный лаг: скользящий Sharpe на окне в тысячу баров реагирует, когда просадка уже случилась, — вы сами закрываете это гистерезисом против дёрганья. Размен фундаментальный: короткое окно ловит шум, длинное реагирует поздно, середины нет.
Рядом стоит поставить детекторы дрейфа не по результату, а по входу. Page-Hinkley, DDM, ADWIN следят за статистикой потока — распределением признаков и ошибкой предсказания — и сигналят о смене распределения раньше, чем она доедет до кривой капитала. Логика простая: режим меняется во входных данных, а P&L — уже следствие, с задержкой.
Оговорка, без которой это в трейдинге не работает: feature drift и P&L drift связаны не жёстко. Рынок может сменить распределение, а стратегия останется прибыльной, и наоборот. Так что входной детектор — это ранний сигнал «пора присмотреться и, может, ужать риск», а не готовая команда переключиться. Ваш выходной выключатель как финальный арбитр всё равно нужен, но входной даёт фору по времени, которой у чисто выходного нет.
К разделу про приоритизацию — CVSS отвечает на «насколько страшно, если проэксплуатируют», но не на «проэксплуатируют ли вообще». Для хранилища артефактов это важно: реальный риск сильно зависит от того, доступен сервис снаружи или живёт внутри периметра.
Рядом с CVSS стоит смотреть ещё две вещи.
EPSS от FIRST — оценка вероятности, что для уязвимости в ближайший месяц появится или уже есть работающий эксплойт. Она разводит критичные по CVSS, но «спящие» баги и те, по которым эксплойты уже летают. Нередко CVE с CVSS 9 и EPSS в пару процентов может подождать дольше, чем 8.6 с EPSS под полсотни.
CISA KEV — список того, что эксплуатируется в реальности прямо сейчас. Если CVE там, вопрос приоритета закрыт: патчить в первую очередь, независимо от баллов.
И третий фактор, которого в CVSS нет вообще, — экспозиция. Nexus, доступный только из внутренней сети, и Nexus, торчащий в интернет, — это разный риск при одной и той же уязвимости. Перед тем как гнать обновление по всему парку, установки стоит развести по этому признаку и начать с наружных.
Длина промпта тут вряд ли при чём — она влияет на время одного вызова модели, а это секунды, не часы. Многочасовые паузы у автономных агентов обычно от другого.
Самое частое — упор в rate limit того же API модели. Агент за прогон делает вызовов на порядок больше человека, выбирает квоту и дальше просто ждёт окна. Снаружи выглядит как «завис на полдня», хотя он всё это время стоит в очереди.
Второе — ожидание долгой внешней операции. Запустил скан, сборку или перебор, ждёт результата, прежде чем планировать следующий шаг — время идёт, видимой активности нет.
Третье — циклы перепланирования: перечитывает уже собранное, гоняет цепочки рассуждений, откатывается и пробует заново. Без внешнего таймбокса это тянется долго.
Так что сутки здесь — не обязательно след ручного вмешательства. У длинных агентных прогонов время нелинейное: большая часть уходит на ожидание, а не на сами действия.
Спасибо, что выложили список CT-логов открыто — теперь можно проверять, а не верить на слово.
Заметил вещь, которую в статье стоило бы проговорить. Логи, которым доверяет Яндекс Браузер, и логи из программы Chrome не пересекаются вообще: у Chrome это Google, Cloudflare, DigiCert, Sectigo, Let’s Encrypt и ещё пара операторов, у вас — Yandex, VK и Минцифры. Ни одного общего.
Для того, кто мониторит выпуск сертификатов на свои домены, это ловушка. Привычные crt.sh и Cert Spotter собирают данные с логов из экосистемы Chrome, а в ваш набор не заглядывают. То есть сертификат, выданный отечественным УЦ и попавший только в Agate или в лог Минцифры, ни в одном из этих мониторингов не всплывёт.
На практике это значит, что после перехода на отечественный УЦ следить за выпуском приходится уже по двум непересекающимся наборам логов вместо одного. Пропустишь второй — и фишинговый или ошибочно выданный на твой домен сертификат, залогированный только в российском CT, проедет мимо оповещений. Ровно то, ради чего Certificate Transparency и затевался, перестаёт работать в слепой зоне.
Отсюда вопрос: планируете ли публичный веб-поиск по вашим логам, как crt.sh? Через API проверять можно, но глазами по-быстрому посмотреть, что выпущено на домен, куда удобнее.
Я лично знаю один сервис для покупки билетов на рейсовые автобусы, который присылает пароль на почту при регистрации. Также ранее попадались такие кейсы в сервисах связанных с ЖКХ и сервисами по подключению к общедомовым камерам. Согласен, что крупные сервисы подобные практики не используют (что логично), но мелких частников с такими проблемами встретить вполне реально.
--
Исследование проведено у меня на ноутбуке)) Все полученные результаты взяты из реального вывода команд
Чего не хватает в выводах — взгляда со стороны тех, кто потом будет это ловить.
Проблема с агентом в том, что ни одно его действие по отдельности не аномально. Прочитать конфиг, перечислить бакет, склонировать репозиторий, запросить секрет из хранилища — штатные операции, каждая сама по себе повода для срабатывания не даёт. Вредоносной становится последовательность, а правила в большинстве команд написаны на отдельные события.
Второе: под агента нет базовой линии поведения. Профиль субъекта строится на его истории, а у агента истории нет, и его штатный режим — широкий разброс действий за короткое время, то есть ровно то, что у человека считается признаком компрометации. Агент под общим сервисным аккаунтом ломает профиль этого аккаунта и заодно прячется в нём.
Отсюда то, что имеет смысл сделать до появления агентов в инфраструктуре, а не после.
Отдельная учётная запись на каждого агента, а не общая с CI. Иначе в журналах его действия неотличимы от пайплайна, и ни разобрать инцидент, ни остановить точечно не выйдет.
Короткоживущие токены с отзывом на стороне выдающего. «Возможность оперативно остановить работу» на практике означает не «убить процесс» — к этому моменту агент мог положить полученные данные куда угодно, — а отозвать выданное.
Отдельный аудит, куда пишутся не только вызовы инструментов, но и то, что пришло агенту на вход. Значительная часть таких сценариев начинается с содержимого, которое агент прочитал и принял за указание — как в вашем же векторе через конфигурацию набора данных. Модель угроз для агента разумно строить из допущения, что любой прочитанный им текст является потенциальной командой.
Про арифметику в разделе о сборе: «с каждых 1920 байт картинки получаем 64 байта энтропии» — это размер хеша, а не количество энтропии. SHA-512 энтропию не создаёт, он её только перемешивает: на выходе её не может быть больше, чем было на входе. Если в куске кадра было двадцать бит непредсказуемости, то и в 512-битном хеше будет двадцать бит, просто размазанных по всей длине. Отсюда и «один бит энтропии на каждые 30 бит картинки» — это соотношение объёмов, а не оценка источника.
Второй момент — про проверку. Тест сжатием и статистические паки, применённые после хеширования, про источник не говорят ничего. Проверяется это за пару минут: берём счётчик 0, 1, 2, … — энтропия ровно ноль — и прогоняем через SHA-512 с накоплением, как у вас. Поток не сжимается, распределение байтов ровное, статистические тесты проходят. Хеш маскирует любую предсказуемость входа, поэтому по его выходу аквариум от счётчика не отличить в принципе.
Как это принято делать: NIST SP 800-90B требует оценивать min-entropy на сыром выходе шумового источника, до всякого кондиционирования, и именно min-entropy, а не Шеннона — интересен наихудший случай, а не средний. Для камеры это значит считать по несжатым сэмплам и с поправкой на то, что соседние пиксели и соседние кадры сильно скоррелированы: статичный фон, автоэкспозиция и внутрикамерный шумодав съедают заметную часть того, что на глаз выглядит хаосом.
Про Fortuna всё написано верно, и вывод «навредить не может» тоже верен — слабый источник в пуле действительно не портит остальные. Вопрос только в том, сколько бит аквариум добавляет на самом деле, и ответ даёт оценка входа, а не тесты выхода.
Действительно, согласен, профита в такой конфигурации будет немного, разве что номер версии будет занимать меньше места в БД, чем сама сессия как раньше, но это на небольших сервисах не особо профитно.
Зависит от конкретной реализации на сервере. Но как вариант:
Сервер хранит у себя счётчик версий (например, token_version) для каждого пользователя в базе данных.
При выдаче токена он записывает туда текущую версию (v1).
При нажатии «выйти везде» он просто увеличивает счётчик до v2.
При каждом запросе сервер делает одну очень лёгкую операцию: читает из БД (или кэша) текущую версию для этого пользователя и сравнивает с версией, зашитой в токен.
Вы правы. В этом сценарии проблема не в bcrypt, а в самом корпоративном шаблоне: общий префикс не добавляет стойкости, хвост легко перебрать, и менеджер паролей здесь — правильное решение.
Статья приводит этот пример не как основную угрозу, а как иллюстрацию того, что неожиданное поведение bcrypt может накладываться на уже плохие пользовательские практики и создавать ложное ощущение уникальности.
72 байта — это достаточно для защиты от взлома. Статья не утверждает обратного.
Проблема в другом: bcrypt тихо игнорирует всё, что длиннее 72 байт. Если пользователь ввёл пароль из 100 символов, система запомнит только первые 72. Пользователь думает, что его пароль уникален, а на самом деле последняя часть пароля не имеет значения.
Главная опасность — не взлом, а сбой при обновлении библиотек: новые версии Python-модуля bcrypt не обрезают пароль, а падают с ошибкой, если он длиннее 72 байт. Если у вас в форме нет ограничения длины — пользователи с длинными паролями не смогут войти после обновления.
Сертификат здесь — всего лишь следствие, а не причина. Если вы управляете сертификатами сами, но оставляете «висячий» CNAME, злоумышленник всё равно получит ваш домен и автоматически выпустит на него свой сертификат (через Let's Encrypt или другого провайдера).
Ваше собственное управление сертификатами эту атаку не закрывает, потому что вы не контролируете, кто запрашивает сертификат для вашего поддомена, пока DNS указывает на чужой IP.
Да, справедливо. Флага «длиннее лимита» тут и правда достаточно, само число ничего не добавляет.
У себя мы просто не принимаем такие пароли: форма отказывает сразу, и слишком длинная строка до хеширования не доходит. Тогда и в лог писать почти нечего.
Спотыкались мы там на другом: длину надо мерить в байтах, а не в символах. 30 кириллических символов — это уже 60 байт, так что на форме человек видит одно число, а упирается совсем в другое.
Спасибо за комментарий! Здесь не говорится, что это самый популярный вектор, он скорее подан с посылом для тех, кто сейчас или когда-то в будущем будет строить процессы безопасной работы с кодом (чтобы учитывали, что такие кейсы бывают). Я вижу это как интеграцию в CI, когда при пролитии кода запускается статический анализатор, который помимо проблемных моментов (наличие уязвимостей, алерты линтеров и пр.) подсвечивает ещё подобные кейсы. Кмк проверка может оказаться весьма полезной
Спасибо, что проверили. Пустой каталог означает, что подключением управляет не keyfile-плагин NetworkManager, и пароль надо искать в другом месте.
Самый короткий способ узнать, где именно:
nmcli -f NAME,TYPE,FILENAME connection show
Команда печатает путь к файлу для каждого профиля. Если список пуст, NetworkManager этим подключением не занимается вовсе.
Дальше по частым вариантам:
— Ubuntu внутри WSL. Своего Wi-Fi-интерфейса в Linux тогда нет, сеть проброшена из Windows, и пароль лежит в профиле Windows. Проверяется по uname -r: в WSL в версии ядра есть слово microsoft.
— netplan, обычная история для серверной установки: файлы .yaml в /etc/netplan, пароль там открытым текстом или хешем.
— wpa_supplicant напрямую: конфиги в /etc/wpa_supplicant/.
— iwd вместо wpa_supplicant: файлы .psk в /var/lib/iwd/.
— подключение не сохранено, а поднято на время сессии: /run/NetworkManager/system-connections/.
Отдельно про сам grep: если файлы профилей в каталоге есть, а psk= в них не находится, значит пароль сохранён в связке ключей. В профиле тогда стоит psk-flags=1, а само значение хранит gnome-keyring. В статье этот случай оговорён, но по выводу grep он выглядит так же, как отсутствие пароля.
Пароль от WIFI - это легкозаменяемая и технически решаемая мелочь. Она приведена здесь не потому, что это главная угроза, а потому что это быстрый и дешёвый выигрыш в безопасности, которым многие пренебрегают: перейти на WPA2-Enterprise или привязать MAC-адреса — проще, чем менять графики работы сотрудников после каждого увольнения.
Также отмечу, что если точка доступа связана с внутренней инфраструктурой (или её частью), то атакующему это дает большой простор для атак. И подключиться к WIFI в этом случае намного проще, чем искать нужные розетки, проникать в офис, либо искать фото с телефонов сотрудников)
Но согласен, что деталей, которые могут быть использованы при атаках много, но в одной статье их не перечислить
Большое спасибо за уточнение!
Минуса по сути нет, вариант рабочий — это как раз одна из тех развязок, о которых речь в статье («развести слушателей»). Вы вынесли разбор обоих стеков на nginx, а бэкенд общается с ним по чистому IPv4 через 127.0.0.1 и mapped-адрес не увидит вовсе. Хороший подход, мне нравится.
Проблема при этом никуда не девается, просто стягивается в одну точку — в сам nginx. Пока listen раздельные, как у вас (443 и конкретный [2001:…]), всё чисто. Но стоит где-то появиться listen [::]:443 без ipv6only=on — и в логах самого nginx $remote_addr поедет в форме ::ffff:…, теперь уже на уровне прокси.
И второе: реальный адрес клиента теперь приезжает в приложение не из сокета, а из X-Forwarded-For, который вы прокидываете. Если перед nginx окажется ещё один прокси на дуал-стек сокете, mapped-форма может прилететь и в этот заголовок — а его обычно разбирают руками. Так что приём отличный, просто точка, где держать нормализацию адреса, смещается с сокета приложения на границу прокси.
Отличный разбор, редко кто доводит цепочку рассуждений оптимизатора до конца так наглядно. Вдобавок к -fwrapv и санитайзерам хочу подкинуть ещё один удобный инструмент из этой же оперы — checked-арифметику, она хорошо ложится рядом.
В GCC и Clang есть __builtin_add_overflow(a, b, &res) и его собратья __builtin_sub_overflow и __builtin_mul_overflow. Каждый выполняет операцию, кладёт результат по указателю и возвращает флаг переполнения, а компилируется в аппаратную проверку соответствующего флага процессора — быстро и без UB. Приятно тем, что классическое if (a + b < a) для знаковых, которое оптимизатор и выкидывает, тут просто не нужно: неопределённого поведения в builtin нет, проверять нечего.
В C23 это же завезли в стандарт — заголовок <stdckdint.h> и ckd_add(&res, a, b), уже переносимо, без привязки к конкретному компилятору.
Про -fwrapv стоит держать в голове его цену: он делает знаковое переполнение определённым для всей единицы трансляции, то есть заодно приглушает оптимизации везде, а не только там, где нужна проверка. Точечный builtin в этом смысле дешевле — ничего вокруг не трогает. В общем, есть из чего выбрать под задачу.
То, что вы описали, в ML называется concept drift, а «выключатель по трейлинг-Sharpe» — это детектор дрейфа по выходу. У него встроенный лаг: скользящий Sharpe на окне в тысячу баров реагирует, когда просадка уже случилась, — вы сами закрываете это гистерезисом против дёрганья. Размен фундаментальный: короткое окно ловит шум, длинное реагирует поздно, середины нет.
Рядом стоит поставить детекторы дрейфа не по результату, а по входу. Page-Hinkley, DDM, ADWIN следят за статистикой потока — распределением признаков и ошибкой предсказания — и сигналят о смене распределения раньше, чем она доедет до кривой капитала. Логика простая: режим меняется во входных данных, а P&L — уже следствие, с задержкой.
Оговорка, без которой это в трейдинге не работает: feature drift и P&L drift связаны не жёстко. Рынок может сменить распределение, а стратегия останется прибыльной, и наоборот. Так что входной детектор — это ранний сигнал «пора присмотреться и, может, ужать риск», а не готовая команда переключиться. Ваш выходной выключатель как финальный арбитр всё равно нужен, но входной даёт фору по времени, которой у чисто выходного нет.
К разделу про приоритизацию — CVSS отвечает на «насколько страшно, если проэксплуатируют», но не на «проэксплуатируют ли вообще». Для хранилища артефактов это важно: реальный риск сильно зависит от того, доступен сервис снаружи или живёт внутри периметра.
Рядом с CVSS стоит смотреть ещё две вещи.
EPSS от FIRST — оценка вероятности, что для уязвимости в ближайший месяц появится или уже есть работающий эксплойт. Она разводит критичные по CVSS, но «спящие» баги и те, по которым эксплойты уже летают. Нередко CVE с CVSS 9 и EPSS в пару процентов может подождать дольше, чем 8.6 с EPSS под полсотни.
CISA KEV — список того, что эксплуатируется в реальности прямо сейчас. Если CVE там, вопрос приоритета закрыт: патчить в первую очередь, независимо от баллов.
И третий фактор, которого в CVSS нет вообще, — экспозиция. Nexus, доступный только из внутренней сети, и Nexus, торчащий в интернет, — это разный риск при одной и той же уязвимости. Перед тем как гнать обновление по всему парку, установки стоит развести по этому признаку и начать с наружных.
Длина промпта тут вряд ли при чём — она влияет на время одного вызова модели, а это секунды, не часы. Многочасовые паузы у автономных агентов обычно от другого.
Самое частое — упор в rate limit того же API модели. Агент за прогон делает вызовов на порядок больше человека, выбирает квоту и дальше просто ждёт окна. Снаружи выглядит как «завис на полдня», хотя он всё это время стоит в очереди.
Второе — ожидание долгой внешней операции. Запустил скан, сборку или перебор, ждёт результата, прежде чем планировать следующий шаг — время идёт, видимой активности нет.
Третье — циклы перепланирования: перечитывает уже собранное, гоняет цепочки рассуждений, откатывается и пробует заново. Без внешнего таймбокса это тянется долго.
Так что сутки здесь — не обязательно след ручного вмешательства. У длинных агентных прогонов время нелинейное: большая часть уходит на ожидание, а не на сами действия.
Спасибо, что выложили список CT-логов открыто — теперь можно проверять, а не верить на слово.
Заметил вещь, которую в статье стоило бы проговорить. Логи, которым доверяет Яндекс Браузер, и логи из программы Chrome не пересекаются вообще: у Chrome это Google, Cloudflare, DigiCert, Sectigo, Let’s Encrypt и ещё пара операторов, у вас — Yandex, VK и Минцифры. Ни одного общего.
Для того, кто мониторит выпуск сертификатов на свои домены, это ловушка. Привычные crt.sh и Cert Spotter собирают данные с логов из экосистемы Chrome, а в ваш набор не заглядывают. То есть сертификат, выданный отечественным УЦ и попавший только в Agate или в лог Минцифры, ни в одном из этих мониторингов не всплывёт.
На практике это значит, что после перехода на отечественный УЦ следить за выпуском приходится уже по двум непересекающимся наборам логов вместо одного. Пропустишь второй — и фишинговый или ошибочно выданный на твой домен сертификат, залогированный только в российском CT, проедет мимо оповещений. Ровно то, ради чего Certificate Transparency и затевался, перестаёт работать в слепой зоне.
Отсюда вопрос: планируете ли публичный веб-поиск по вашим логам, как crt.sh? Через API проверять можно, но глазами по-быстрому посмотреть, что выпущено на домен, куда удобнее.
Я лично знаю один сервис для покупки билетов на рейсовые автобусы, который присылает пароль на почту при регистрации. Также ранее попадались такие кейсы в сервисах связанных с ЖКХ и сервисами по подключению к общедомовым камерам. Согласен, что крупные сервисы подобные практики не используют (что логично), но мелких частников с такими проблемами встретить вполне реально.
--
Исследование проведено у меня на ноутбуке)) Все полученные результаты взяты из реального вывода команд
Чего не хватает в выводах — взгляда со стороны тех, кто потом будет это ловить.
Проблема с агентом в том, что ни одно его действие по отдельности не аномально. Прочитать конфиг, перечислить бакет, склонировать репозиторий, запросить секрет из хранилища — штатные операции, каждая сама по себе повода для срабатывания не даёт. Вредоносной становится последовательность, а правила в большинстве команд написаны на отдельные события.
Второе: под агента нет базовой линии поведения. Профиль субъекта строится на его истории, а у агента истории нет, и его штатный режим — широкий разброс действий за короткое время, то есть ровно то, что у человека считается признаком компрометации. Агент под общим сервисным аккаунтом ломает профиль этого аккаунта и заодно прячется в нём.
Отсюда то, что имеет смысл сделать до появления агентов в инфраструктуре, а не после.
Отдельная учётная запись на каждого агента, а не общая с CI. Иначе в журналах его действия неотличимы от пайплайна, и ни разобрать инцидент, ни остановить точечно не выйдет.
Короткоживущие токены с отзывом на стороне выдающего. «Возможность оперативно остановить работу» на практике означает не «убить процесс» — к этому моменту агент мог положить полученные данные куда угодно, — а отозвать выданное.
Отдельный аудит, куда пишутся не только вызовы инструментов, но и то, что пришло агенту на вход. Значительная часть таких сценариев начинается с содержимого, которое агент прочитал и принял за указание — как в вашем же векторе через конфигурацию набора данных. Модель угроз для агента разумно строить из допущения, что любой прочитанный им текст является потенциальной командой.
Про арифметику в разделе о сборе: «с каждых 1920 байт картинки получаем 64 байта энтропии» — это размер хеша, а не количество энтропии. SHA-512 энтропию не создаёт, он её только перемешивает: на выходе её не может быть больше, чем было на входе. Если в куске кадра было двадцать бит непредсказуемости, то и в 512-битном хеше будет двадцать бит, просто размазанных по всей длине. Отсюда и «один бит энтропии на каждые 30 бит картинки» — это соотношение объёмов, а не оценка источника.
Второй момент — про проверку. Тест сжатием и статистические паки, применённые после хеширования, про источник не говорят ничего. Проверяется это за пару минут: берём счётчик 0, 1, 2, … — энтропия ровно ноль — и прогоняем через SHA-512 с накоплением, как у вас. Поток не сжимается, распределение байтов ровное, статистические тесты проходят. Хеш маскирует любую предсказуемость входа, поэтому по его выходу аквариум от счётчика не отличить в принципе.
Как это принято делать: NIST SP 800-90B требует оценивать min-entropy на сыром выходе шумового источника, до всякого кондиционирования, и именно min-entropy, а не Шеннона — интересен наихудший случай, а не средний. Для камеры это значит считать по несжатым сэмплам и с поправкой на то, что соседние пиксели и соседние кадры сильно скоррелированы: статичный фон, автоэкспозиция и внутрикамерный шумодав съедают заметную часть того, что на глаз выглядит хаосом.
Про Fortuna всё написано верно, и вывод «навредить не может» тоже верен — слабый источник в пуле действительно не портит остальные. Вопрос только в том, сколько бит аквариум добавляет на самом деле, и ответ даёт оценка входа, а не тесты выхода.
Действительно, согласен, профита в такой конфигурации будет немного, разве что номер версии будет занимать меньше места в БД, чем сама сессия как раньше, но это на небольших сервисах не особо профитно.
Зависит от конкретной реализации на сервере. Но как вариант:
Сервер хранит у себя счётчик версий (например, token_version) для каждого пользователя в базе данных.
При выдаче токена он записывает туда текущую версию (v1).
При нажатии «выйти везде» он просто увеличивает счётчик до v2.
При каждом запросе сервер делает одну очень лёгкую операцию: читает из БД (или кэша) текущую версию для этого пользователя и сравнивает с версией, зашитой в токен.
Разница в том, что:
При обычной работе (когда кнопку не нажимают) нагрузка минимальна — сервер просто проверяет подпись, без обращения к БД.
При сбросе нагрузка вырастает, но это редкое событие (в этом случае, да, нагрузка как и в старых реализациях).