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

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

Содержание начального пакета данных должно быть вычислительно неотличимо от случайных данных (от шума) для внешнего наблюдателя. Это как раз и достигается тем, что данные зашифрованы. Но зашифрованы должны быть буквально все данные. То есть, тут не может быть некоторой структуры приветствия, как, например, в TLS: если пакет содержит некоторые заданные спецификацией поля (версию, идентификатор, таймстемп и пр.), то пассивный наблюдатель легко узнает протокол, к которому такой пакет относится. Поэтому видимой структуры быть не должно, но она может быть скрыта в зашифрованном блоке. (Тут еще есть транспортный аспект, типа TCP, об этом отдельно рассказано ниже.)

Но если пакет выглядит как шум, то что делать серверу? Тут как раз и начинается пробное расшифрование. Сервер пытается расшифровать полученные данные всеми известными ему клиентскими симметричными ключами. Внутри зашифрованного блока данных, – то есть, в открытом тексте, – уже содержится структура, которая и позволяет серверу понять, что расшифровано правильно – то есть, ключ найден верный. Это довольно хорошо отработанный подход, поэтому есть типовое решение: начальная часть открытого текста должна содержать достаточно длинный идентификатор клиента (например, 128 бит), этот же идентификатор сохраняется в таблице на стороне сервера, вместе с соотвествующими секретными ключами. Сервер перебирает эти ключи, расшифровывая пакет каждым.

Как только сервер, в результате расшифрования, получил идентификатор, совпавший с данными в таблице – сервер считает, что это начальное сообщение именно от того клиента, который указан в таблице. Это пока что предварительное решение, по схеме “в лоб”. С сетевой точки зрения, у данного решения есть сразу несколько больших недостатков, прежде всего – повторная отправка записанных пакетов. Мы недостатки сейчас рассмотрим и исправим. Но прежде всего важно заметить, что найден способ определить, что это за клиент стучится, приняв пакет, содержание которого выглядит для третьей стороны как поток байтов со случайными значениями.

Вероятность, что какой-то подставной пакет расшифруется так, что совпадёт и ключ, и идентификатор – настолько мала, что её можно не рассматривать. Тем более, что если так получится, то, для добротного шифра, это будет означать, что подставной клиент угадал ключ. Если же после всех вариантов расшифрования идентификатор клиента не найден, сервер отбрасывает пакет и не отвечает ничего. Да, обработка каждого входящего пакета требует выполнения многих операций расшифрования на сервере. Это неустранимая особенность: серверу придётся проверить данные по каждому клиенту. Если клиентов тысячи, то “невалидный” пакет сервер будет расшифровывать тысячи раз. Но, как отмечено выше, шифр AES нынче быстрый, а отправка наружу ответа о том, что пакет неверный – не требуется.

Теперь о других недостатках. Прежде всего, наивная схема зашифрования означает, что один и тот же клиент будет присылать одинаковый начальный пакет, потому что у клиента зафиксированы и значение ключа, и содержание пакета-приветствия. Это очень плохо, но легко исправляется, да ещё и сразу несколькими способами: например, в начало пакета в открытом виде записывается случайный вектор инициализации, который сервер использует при расшифровании (а клиент – при зашифровании). Этот вектор имеет фиксированную длину, но, поскольку значение вектора инициализации случайное, это не даёт никакой дополнительной различительной информации: к случайным данным прицепили случайные данные. Естественно, можно дописать изменяемый счётчик или слйчайный вектор внутри зашифрованных данных (см. ниже про таймстемп).

Следующий момент: если пакет имеет фиксированную длину, то при пассивном анализе можно зацепиться за эту длину. Решается тоже просто: к данным дописывается некий хвост-дополнение из случайных байтов, длина которого варьируется. Тогда у пакета образуется минимальная допустимая длина (поскольку должны войти данные приветствия), а практическая длина – изменяется от пакета к пакету. (Тут есть тонкий момент с контролем длины дополнения и дуплексным режимом для успешного соединения: дополнение нужно отбрасывать корректно, потому что иначе оно может попасть в начало следующего клиентского пакета. Но, опять же, оставим этот момент за пределами записки.)

Сервер знает длину полей в расшифрованных данных, знает, что поля идут одно за другим, может знать длину дополнения, поэтому просто отбрасывает это дополнение (где, после расшифрования, образуется некий мусор). Это всё самые простые способы, без аутентификации данных. Раз нет аутентификации, то кто-то может взять валидный пакет и дописать к нему что угодно в качестве дополнения, сделав некоторую метку. Сервер это “что угодно” вынужден будет расшифровать и если прочие данные сойдутся к валидным, то пакет будет принят в качестве приветствия. Это, на самом деле, не такая большая проблема: подобное тегирование мало что даёт, потому что для эффективного использования потребуется повторная отправка пакетов. И, опять же, есть несложное решение: используем код аутентификации сообщения, построенный на другом секретном ключе, а значение кода аутентификации дописываем в пакет. Сам код аутентификации тоже вычислительно неотличим от случайного (у нас случайный вектор инициализации), поэтому новых меток в пакете не появляется. Если аутентификация не сошлась для всех ключей, то пакет отбрасывается без дальнейшей обработки. В общем, это дополнительный затратный шаг на стороне сервера, который, в принципе, не требуется (внутри должна быть отдельная аутентификация – это другой аспект, там требуются асимметричные криптосистемы).

Только что была упомянута проблема повторной отправки валидных записанных приветствий. Третья сторона записывает трафик, выделяет валидные приветствия (это те, на которые сервер отправил ответ), а потом проигрывает эти приветствия в сторону сервера. В наивной схеме, действительно, такое сработает: сервер ответит. Однако, так как третья сторона не знает секретов, то она даже не сможет расшифровать ответ сервера. Впрочем, повторное проигрывание пакетов даёт некоторые новые направления для атак: во-первых, можно устроить зонд (connection probe), выявляющий действующие серверы – отправляем валидный пакет-приветствие на IP-адрес, если есть ответ, то там есть работающий по данному протоколу сервер; во-вторых, так как сервер вынужден на своей стороне открыть сессию, можно исчерпать доступные сессии на сервере; в-третьих, повторная отправка чужого приветствия может приводить к тому, что будет разорвано легитимное соединение того же клиента – а это уже неприятно, поскольку позволяет просто блокировать возможность использования протокола: фиксируем успешный обмен по ответу сервера – тут же отправляем ранее записанный пакет-приветствие в сторону сервера (это вообще один из хитрых моментов, который, впрочем, за пределами данной записки, посвящённой пробному расшифрованию).

Как бороться с вмешательством, котрое реализуется повторной отправкой начальных пакетов? К сожалению, эффективное противодействие – потребует некоторых дополнительных ресурсов: нужно на сервере вести таблицу состояний, – некий буфер, если точнее, – и отбрасывать все повторные пакеты по их значению (можно брать только начальные байты). А внутрь пакета-приветствия, в зашифрованную часть, нужно встроить таймстемп, и потребовать синхронного времени на клиенте и сервере. Тогда сервер ведёт запись о поступлении и успешной обработке недавних приветствий, прямые быстрые повторы – сразу отбрасывает (игнорирует), а если получено старое приветствие, то его придётся расшифровать, чтобы обнаружить, что оно устарело (по таймстемпу – тут речь о том, что допускается только расхождение на сколько-то секунд с сервером). Таким образом, проблема повторной отправки решается тоже.

Итак, в добротной реализации сервер ведёт себя тихо, с сетевой точки зрения, пока не получит валидный свежий пакет-приветствие, на который уже отвечает своим приветствием и далее может начаться сессия. Тут необходимо оговорить траспортный момент. Предположим, используется TCP. А в TCP есть собственная сессия. Соответственно, невозможно отказаться от установления TCP-сессии для того, чтобы получить начальный пакет на сервере. Это означает, что зонд может проверить доступность TCP-соединения. Конечно, само по себе TCP-соединение ещё не означает, что на сервере установлен искомый сервис, но, всё же, “наводит на мысли”. В принципе, если третья сторона анализирует трафик, то она так или иначе увидит, что к сервису кто-то подключается, поэтому само по себе TCP-зондирование ничего нового о работающем сервисе не расскажет, но вот найти подозрительный сервер-кандидат, который пока не работает, позволит. К сожалению, этот момент довольно трудно устранить. Есть способы типа port knocking (“секретный стук”), кроме того, можно ограничить доступ на уровне ядра ОС по IP-адресам источника. При этом TCP вообще позволяет несколько более эффективно ограничивать попытки повторных ложных подключений с одного IP-адреса. Но это всё полумеры.

Есть вариант с UDP. Поскольку этот протокол без сессий, то ложный зондирующий пакет будет оставлен сервером без ответа, а зонд вряд ли сможет отличить эту ситуацию от отсутствия сервиса (конечно, и тут есть свои оговорки, но от TCP – ситуация точно отличается очень существенно). UDP быстрее TCP, но могут быть проблемы с преодолением пограничных узлов, транслирующих адреса, и с тем, что в каких-то сетях UDP просто зафильтрован.



Комментировать »

Обычный вариант атаки MITM для TLS подразумевает, что у перехватывающего узла (прокси) есть секретный ключ от валидного серверного сертификата (ну и сам сертификат, естественно). Тогда перехватывающий узел выдаёт себя за настоящий TLS-сервер в сторону клиента, и за клиента – в сторону настоящего TLS-сервера.

В IP-сети всякий промежуточный узел может ответить вместо настоящего, так что в сетевом смысле атака не самая трудная. Получается, что TLS-сессия установлена не между клиентом и сервером, а превратилась в две TLS-сессии: одна – от клиента до перехватывающего узла, вторая – от перехватывающего узла до настоящего сервера. Промежуточный узел принимает трафик в обе стороны и видит его в открытом виде, так как TLS-соединение между клиентом и настоящим сервером не установлено, трафик никто не защищает.

На что тут влияет клиентская аутентификация в TLS? Вот на что: штатный способ аутентификации TLS-клиента сервером, – на этапе установления соединения, – подразумевает, что клиент подписывает все TLS-сообщения, которые были переданы до настоящего момента, а это означает, что перехватывающий узел не сможет получить корректную подпись для своей сессии в сторону подлинного сервера. Чтобы это сработало, сервер должен поддерживать клиентскую аутентификацию, конечно.

То есть, буквально, TLS-клиент при помощи секретного ключа подтверждает, что получил некоторые TLS-сообщения с конкретным содержанием (там побайтово все сообщения подаются на вход хеш-функции). В случае MITM, клиент подпишет те сообщения, которыми обменивался с перехватывающим узлом, с прокси. Но состав этих сообщений отличается от сообщений, которыми перехватывающий узел обменялся с подлинным сервером (см. ниже объяснения). Подлинный сервер же – видит лишь сообщения от перехватывающего узла (и свои собственные), соответственно, проверять клиентскую подпись он будет по этим сообщениям. Подпись, присланную клиентом, перехватывающий узел передаёт без изменений. Так как сообщения различаются, подпись не сойдётся. Перехватывающему узлу не известен секретный ключ клиента, поэтому перехватывающий узел не может подписать за клиента сообщения сессии в сторону подлинного сервера. Если же сообщения прокси передавал без изменений, то это означает, что перехвата TLS не случилось, а случилось обычное TLS-соединение и прокси не сможет раскрыть трафик.

Важная оговорка: это всё верно, но лишь без учёта возможности принудительного понижения стойкости – в принципе, перехватывающий узел, активно вмешиваясь в соединение, может вынудить стороны TLS согласовать заведомо нестойкие ключи и параметры протокола. Это позволит расшифровать трафик в пассивном режиме. Однако это совсем другая схема перехвата, она гораздо сложнее, клиенты типа современного браузера лучше защищены от применения подобных атак, а в TLS давно есть дополнительные индикаторы, помогающие такую атаку обнаружить, и т.д. В общем, в записке речь идёт о максимально простой схеме перехвата – с полной подменой узла.

А что если у перехватывающего узла есть подходящий секретный ключ и дверенный клиентский сертификат? Тогда – да, перехватывающий узел сможет выдать себя за аутентифицированного клиента в сторону подлинного сервера и продолжить прозрачный перехват, просто пригнорировав аутентификационный ответ прослушиваемого клиента.

Тут есть тонкий момент: нельзя забывать, что в TLS проверить корректность подписи и валидность клиентского сертификата должен подлинный сервер. Если сервер реально использует результат аутентификации клиента, то сессия с MITM – развалится (если у перехватывающего прокси нет клиентского секретного ключа). Но это именно особенность TLS. То есть, сервер должен всё проверять, но может и не проверить. Если сервер реально не проверяет значение клиентской подписи, то перехватывающий прокси может прислать что угодно похожее на подпись и TLS-соединение продолжит работать как обычно. Ну и тем более так будет, если сервер вообще не использует клиентской аутентификации в TLS (обычное дело).

И это приводит к следующим особенностям трансляции доверия через TLS. Предположим, что есть дополнительный прикладной протокол аутентификации, работающий через TLS, но не использующий встроенную в TLS клиентскую аутентификацию. Простейший вариант: клиент вводит пароль. Понятно, что тут MITM-прокси просто пересылает запрос на пароль клиенту, а пароль от клиента – пересылает серверу. Всё прозрачно работает, прокси узанёт пароль.

Более сложный вариант: у клиента есть секретный ключ для цифровой подписи, сервер верит в открытый ключ, соответствующий этому секретному. Сервер присылает запрос на подпись некоторых данных – клиент подписывает – сервер проверяет и убеждается, что у клиента есть секретный ключ, если всё сошлось – клиент аутентифицирован. Помешает ли это MITM-прокси? Нет, не помешает. Прокси, в рамках штатного перехвата трафика, просто копирует запрос сервера, пересылает его клиенту, получает от клиента подпись, передаёт её серверу – сработало, сервер аутентифицировал клиента и незаметил вмешательство прокси. Почему это работает? Потому, что тут аутентификация не привязана к транспортной сессии: сервер проверяет, что запросы поступают от стороны, имеющей доступ к секретному ключу, парному к доверенному открытому, но не проверяет, что транспортная сессия установлена с той же самой стороной.

Что это всё означает? Вот что: представьте, что клиент авторизуется через тот или иной механизм, использующий цифровую подпись, выполняемую на стороне клиента; эта схема, с точки зрения простого MITM-перехвата TLS, не даёт дополнительной защиты сессии. То есть, всё происходит на пару уровней выше TLS: клиент что-то подписал, сервер выдал клиенту авторизационный токен, этот токен виден на стороне MITM-прокси, теперь сторона с прокси может выдать себя за аутентифицированного клиента, подпись не требуется. Если подпись потребуется, то прокси перенаправит запрос клиенту. Да, по сравнению с паролем, тут есть некоторое преимущество: секретный ключ подписи не утекает наружу. Но, с другой стороны, выгода в плане защиты информации не так уж велика: ведь MITM-прокси может попросить клиента подписать что угодно, прикинувшись подлинным TLS-сервером при помощи перехватывающего TLS-сертификата.



Комментировать »

Ещё из серии “Забавные истории с LLM”. Снова Claude. Так как на пути программного кода возводятся неожиданные “кибербезопасные препоны” (cybersecurity guardrails), я тут на днях решил попробовать задачи из области теоретической математики: там есть Lean (это инструмент описания и проверки “компьютерных доказательств”), но не должно быть “кибербезопасных препон для безопасной безопасности”. На роль примера я наобум выбрал задачу 11.115 из “Коуровской тетради” (этот широко известный в узких кругах канонический список я упоминал ранее; как оказалось, не зря упоминал). Я набросал общее описание контрпримера к задаче, в меру собственного понимания, и попросил Claude найти конкретный контрпример и подготовить соответствующее доказательство на Lean.

Система Claude существует в нескольких LLM-воплощениях, доступны мне не все, но основные, как я понимаю, доступны. Сперва я задал запрос в Fable 5. Оно почему-то зациклилось и кружилось внутри себя, наступая периодически на “лимит вызова команд”, очень долго. Наверное, часа два. Никакого результата не выдало, но “кредиты” съело (неплохая “бизнес-модель”, кстати: показываем “юзеру” какие-то меняющиеся текстовые строки в браузере через Javascript и списываем за это “кредиты” в огромных количествах). Но не будем торопиться с выводами.

Потом я решил запустить в той же ветке Opus 5 Max. Как ни странно, Opus 5 Max довольно быстро нашло контрпример (да! и очень даже похожий на правду – см. ниже), но совсем не осилило Lean. Код Lean содержал какие-то вымышленные имена теорем, которых нет в библиотеках (я не очень хорошо понимаю Lean, но, думаю, тут всё именно так). Claude Opus 5 Max – объяснило, что на своей стороне не может компилировать Lean-код: нет среды и не хватает места, чтобы развернуть из пакетов. Но без машинного доказательства все рассуждения LLM, даже такой сверхмощной, о контрпримере, даже для такой простой задачи, – мне, к сожалению, не очень-то полезны (мой план-то был другим: попытаться вручную из корректного Lean-кода восстановить привычное математическое описание контрпримера).

Казалось бы, всё опять застопорилось.

Но нет. Внезапно Anthropic выпустили Fable 5.1. Буквально, вчера. Якобы, Fable 5.1 “сильно лучше в исследовательских задачах”, чем предыдущая Fable 5. Без особой надежды на успех, попробовал я в том же чате, со сломанным Lean, запустить Fable 5.1. Как ни странно, но оно бодро заявило, что сейчас в коде Lean оставит только те библиотеки (import), которые реально нужны для этого доказательства. Это логично. И, – возможно, – среда Lean c этими библиотеками влезет в доступные гигабайты контейнера на стороне Claude, так предположило Fable 5.1. После чего, как ни странно, действительно “урезало библиотеки” и действительно исправило ошибки в Lean-коде, так что стало понятно, что имеется в виду. В итоге, выдан файл с Lean-кодом, который уже компилируется и корректен со всех точек зрения, кроме того, что там могут быть неверные исходные допущения, но это я планирую проверить позже. Так вот.

(На всякий случай: если код и контрпример окажутся правильными, то, да, это будет решение для 11.115, которая пока что отмечена в тетради как открытая. Задачу я выбрал случайно, а критерием было то, что я сам смогу быстро написать условия на контрпример – поэтому-то и задача выбрана очень простая по формулировке и составу используемых объектов. Да, LLM-системы развиваются. Но тут и контрпример – какой-то подозрительно очевидный; если кому-то интересны супертехнические подробности, то вот: построим подгруппу на словах с чётными и нечётными степенями (это отображение в Z/2Z), и подгруппу на соотношениях с a^2. Код Lean я пока не публикую, поскольку не проверил, да и это всё может оказаться “подтягиванием” другого результата, который просто забыли упомянуть составители сборника.)



Комментировать »

Я иногда говорю, что к JWT (веб-токену) нужно относиться так, как если бы это был “публичный документ”, что JWT мог быть “написан на заборе”. Речь тут всегда идёт о точке зрения сервера, использующего “внешние” JWT для внутренней аутентификации пользователей. Но всё равно этот момент вызывает у слушателей удивление: как так? ведь это же токен аутентификации, он позволяет получить доступ к сервису, его же нужно держать в секрете! Конечно, нужно держать в секрете, если вы клиент-пользователь и только что этот токен получили. Но если вы реализуете внешний, по отношению к провайдеру токенов, сервер, то можно ли расчитывать на то, что токен клиент “держал в секрете”? Можно ли рассчитывать на то, что получал токен именно тот клиент, который сейчас его предъявил? Правильный ответ: нет и нет, нельзя. Так что нужна совсем другая интерпретация, если вы на стороне сервера, а токен получили даже не от того, кто его выдал – токен вам принёс клиент, который, предположим, и “прочитал его на заборе”.

“Что мы знаем о лисе? Ничего. И то – не все”. Вспомним про JWT. JWT – это токены доступа (JSON Web Token), содержащие различные данные, имеющие цифровую подпись, и представленные JSON-структурой. В JWT может быть записано имя пользователя и его роль, задающая уровень доступа. Данные токена нередко не зашифрованы, а занесены в открытом виде, поэтому их легко прочитать, получив токен. Банальный способ применения JWT – носитель данных авторизации. Есть разновдности токенов по назначению, а перехват того или иного токена, в типовом режиме использования, становится равносилен перехвату роли, записанной в токене. Но эффект перехвата не всегда прямой и ограниченный. Например, при “удачном” стечении обстоятельств, если перехвачен токен “обновления” (refresh JWT), то перехвативший его может “обновить” чужую сессию и получить новые параметры доступа для чужого аккаунта. JWT сейчас едва ли не повсеместно используют в качестве носителя данных в процессах авторизации/аутентификации. Несмотря на то, что сам механизм допускает дополнительные каналы подтверждения подлинности токена сервером, его выдавшим, в подавляющем большинстве случаев – JWT используют напрямую: верят самому токену и только, и хорошо, если корректно проверяют подпись и срок действия.

Пусть токен подписан цифровой подписью. Предполагается, что секретный ключ от данной подписи принадлежит некоторому доверенному провайдеру токенов. Однако, чтобы сформировать доверие технически, можно начать верить в открытый ключ, связанный с провайдером токенов. Сам по себе JWT, что бы в нём ни было написано, доверия вызывать не должен. Правильно: ведь клиент-пользователь мог “прочитать его на заборе”.

Это как раз и означает, что для системы, которая проверяет доступы по токену, используя указанные внутри токена параметры (scope) и значение подписи, всё равно, как этот токен получен. Нельзя строить доверие только на способе получения токена, а тем более, на том, что токен принёс клиент, назвавшийся каким-то там именем.

То есть, буквально, доверие внутри системы, которая проверяет данный токен, образуется потому, что токен содержит валидную подпись от дверенного ключа, а не потому, что токен был получен каким-то “защищённым” образом. Например, с точки зрения этой системы – данный токен вообще можно считать публичным документом, сколь бы парадоксальным это ни казалось. (Ещё раз, данные внутри полей токена могут быть дополнительно зашифрованы (JWE), а чтобы их прочитать – нужно знать подходящий ключ. Но это дополнительная мера.)

Почему это всё может казаться странным? Прежде всего потому, что, в массовых сценариях веба, JWT разрешает доступ некоторому пользователю к некоторой онлайн-системе и разрешает использование некоторых функций. Соответственно, если пользователь свой авторизационный токен потерял, а кто-то этот токен – нашёл (“прочитал на заборе”), то этот “кто-то” может получить и доступ пользователя к целевой системе (полный аналог “авторизационной куки”). От действий пользователя этот доступ уже не зависит. Поэтому, конечно, пользователь должен держать свой токен в секрете. Это очевидно. Возникает ли тут противоречие? “Публичный документ”, который нужно держать в секрете? Как это? Но обратите внимание, что тут нет никаких механизмов дополнительного подтверждения для пользователя: пользователь где-то получил токен; вероятно, там он вводил пароль или ещё как-то авторизовался, но теперь токен действует непосредственно – если внутри токена, в открытом виде, написано, что это пользователь Имярек, значит, в доверяющей системе пользователь с данным токеном станет Имяреком. То есть, противоречия нет. А вся хитрость в том, что это просто так принято: считать JWT секретным и привязанным к пользователю, но без реальных проверок. Проверки возможны – об этом в конце записки.

Важный момент, про который нельзя забывать: от пользователя, авторизующегося при помощи JWT в базовой схеме, даже не требуется доказательство владения секретным ключом от подписи в токене. Считается, что ключ есть у сервиса, который выпускает токен. Соответственно, если подходить строго формально, то и авторизация тут предоставляется ключу сервиса, не пользователю. То есть, всякий может прочитать значения внутри токена (если он не зашифрован) и внешний сервис (не пользователь!) имеет полный контроль над ключом, позволяющим создавать доверенные токены, в том числе, с заданным значением полей. Это ли не “публичный документ”, при прочих равных?

Заметьте, что так как обычный JWT самодостаточен, утащить его у пользователя может и тот сервис, на котором пользователь пытается JWT применить. Другими словами: пользователь получает JWT на одном сервисе, но сам протокол подразумевает, что пользователь потом предъявляет этот токен произвольным другим сервисам, которые токен видят и могут использовать в произвольных целях. То есть, опять возникают признаки “публичного документа”.

Но если интерепртировать JWT как публичный, то, получается, и доступ предоставить можно только тот же, что получает произвольный, неавторизованный пользователь? В идеале – именно так и должно быть (и см. про расширения протокола ниже). Однако на практике – это радикально уменьшает степень полезности JWT. Да, такие “определённо публичные” JWT всё ещё можно использовать как некий инструмент подтверждения происхождения данных, например, в качестве доверенного файла конфигурации: ведь изменить параметры, не зная секретного ключа, не получится. Но от JWT ожидают некоторого прозрачного способа аутентификации “по цепочке”: то есть, какой-то другой сервис пользователя проверил, выпустил подтверждение, отдал его пользователю, и теперь это подтверждение принимается на другом сервисе как эквивалент локальной аутентификации. Удобно. Но не всегда правильно.

Вообще, история с дополнительным ограничением применимости JWT весьма важная. Например, RFC 9449 как раз описывает дополнительный механизм, который позволяет привязать JWT, выданный внешним сервисом, к открытому ключу, секретную часть которого контролирует пользователь, предъявивший JWT. При использовании JWT пользователь предъявляет и доказательство владения собственным секретным ключом. В такой схеме уже не получится использовать только лишь подписанный внешним сервисом JWT. Нужно ещё знать пользовательский секретный ключ. И это как раз полный и верный ответ на трактовку JWT как публичного документа: да, JWT – публичный, но чтобы применить его на сервисе – нужно доказать знание пользовательсткого секретного ключа, поэтому-то в этой схеме “просто прочитать JWT на заборе” уже не достаточно.



Комментировать »

Вновь пишут про успехи ИИ/LLM-систем “олимпиадного уровня”, на этот раз – про набор из задач Международной лингвистической олимпиады (IOL), где LLM-система Claude Opus 4.8 показывает “результат уровня золотой медали”.

Странно всё это.

Я некоторое время использую Claude Opus 5 Max, которая, вроде как, должна быть получше. Cправедливости ради, необходимо отметить, что вот программный код, – при компактной задаче и наличии подробнейшего пошагового описания того, что и как должна делать программа, – оно генерирует неплохой (пусть и не всегда, но – почти всегда неплохой; возможно, если доступ не отберут, я как-нибудь поделюсь впечатлениями). Однако вот языковая логика (не языка программирования, а естественная) – нередко хромает. На все три ноги из двух.

Так, недавно эта система выдала мне заведомую “дихотомию”, но с третьим вариантом. Да. Буквально:

если вы знаете пароль – тогда, [blah blah blah, текст про этот вариант];
если вы не знаете пароль – тогда, [blah blah blah, текст про вариант, когда пароль не знаем];
если ни то, ни другое (neither) – [blah blah blah, произвольный текст, который, понятно, не может относиться к поставленной задаче, где мы либо знаем пароль, либо не знаем пароль].

Но, оказывается, уровень “золотой медали” по языковым задачам был у прошлой модели. Как говорится: “что-то тут не так”. Продолжаем наблюдение.



Комментарии (2) »

Сейчас веб-сайтах крупнейших российских банков в TLS появился “сертификат Минцифры”, в том числе, на веб-интерфейсах систем “Личного кабинета”. “Сертификат Минцифры” – это сертификат корневого ключа Национального Удостоверяющего Центра, “сертификат НУЦ”. То есть, для штатного соединения – нужно доверие соответствующим корневым сертификатам (от НУЦ). Из коробки – доверие есть только в “Яндекс.Браузере”. В остальных случаях – нужно добавлять сертификат вручную.

С доверием тут проблема не только в браузерах. Сертификатам этим вообще доверяют не все пользователи. В связи с этим сейчас много где рекомендуется некоторый весьма технический метод установки “сертификатов Минцифры”, но с ограничением по допустимым именам хостов, чтобы сертификаты от данного корня считались валидными только для некоторых имён, предположим, для имён только в зоне .RU, или для имён только в DNS-зоне банковсого сайта. Это боязнь атаки типа MITM: технически, всякий УЦ может выпустить сертификат для любого имени.

Речь не про механизмы управления доверием по именам в конкретных браузерах, а именно про уровень TLS-сертификатов и цифровой подписи. Чтобы реализовать ограничения, предлагается “переподписывание” при помощи собственного, локального доверенного корневого ключа, с установлением ограничений в nameConstraints. Схема многим кажется хитрой. И надо заметить, что техническая хитрость в схеме действительно есть. Я не считаю, что это хорошая схема для продвинутого пользователя. Для такого пользователя гораздо лучше – аккуратно создать отдельную копию браузера, со своим списком корней. Поэтому детальных команд я не привожу, но если захотите, то сгенерировать всё нетрудно при помощи OpenSSL (не забудте только ключи удалить).

Вообще, цель этой записки в другом: объяснить, что это всё означает, почему и как работает. Речь здесь пойдёт про веб-браузеры и TLS в HTTPS.

Итак, упомянутая выше схема “переподписывания” давно известна среди специалистов, она вполне штатная, и называется “кросс-подпись”. Логика алгоритма в том, что открытый ключ из целевого “корневого сертификата”, – обозначим его литерой “Б”, – подписывается ключом доверенного УЦ, а получившийся новый сертификат содержит такой же открытый ключ, как сертификат “Б”, и такое же имя в поле Subject. По такой схеме долгое время в браузерах работал УЦ Let’s Encrypt (да и сейчас есть сертификаты для корневых ключей Let’s Encrypt, выпущенные от других УЦ, это улучшает совместимость).

Почему кросс-подпись работает? Потому что в ходе валидации серверного сертификата цепочка строится по именам (Issuer <===> Subject), а подписи проверяются по открытым ключам из сертификатов цепочки. Часть цепочки – присылает сервер, а доверенный корень – встроен в браузер. Чтобы сертификат был признан валидным, необходимо (но не достаточно), чтобы цепочка пришла к доверенному ключу. Но нет разницы, что это за ключ и откуда он – главное, чтобы он был доверенным. Поэтому прочие данные из TLS-сертификата всегда играют вспомогательную роль, главное, что есть в сертификате – открытый ключ. Поэтому и сертификаты строго называют “сертификатами ключей”.

Предположим, что сервер вернул собственный, оконечный сертификат, а подпись на этом сертификате стоит от ключа промежуточного удостоверяющего центра (УЦ), сертификат данного ключа тоже прислал сервер. Валидирующий сертификаты браузер выбирает сертификат подписывающего ключа, прежде всего, по имени Issuer из подписанного сертификата, потом производится попытка проверки подписи ключом из выбранного сертификата. Если вдруг оказалось несколько сертификатов, подходящих по имени, но с разными ключами, то процедура проверки подписи даст валидный результат только для правильного ключа. Обратите на этот момент внимание – он главный: проверка подписи выполняется при помощи ключа, и доверие, в конечном итоге, выстраивается по ключам. Фокус схемы с кросс-подписью в том, что ваш браузер начинает верить в ключ потому, что доверие этому ключу прямо установлено. И установлено доверие может быть другим корневым сертификатом, а не тем, который предлагается “скачать и установить”. Главное, чтобы открытый ключ совпадал.

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

Рассмотрим очень подробный пример.

Пусть сервер адресуется именем example.com. Для example.com выпущен оконечный (серверный) сертификат от ключа промежуточного УЦ под названием Interm-CA-1. Для открытого ключа этого УЦ Interm-CA-1 выпущен сертификат “корневым УЦ” Root-CA-1. И открытый ключ Root-CA-1 встроен в браузер. Браузер доверяет открытому ключу Root-CA-1. Этот открытый ключ непосредственно указан в сертификате Root-CA-1. Тогда браузер выстраивает цепочку example.com <- Interm-CA-1 <- Root-CA-1, проверяет подписи от Root-CA-1 на Interm-CA-1, а от Interm-CA-1 на example.com и, если всё сошлось, распространяет доверие по цепочке до сертификата example.com. Это обычный способ, без кросс-подписи.

Теперь представьте, что в браузере есть и Root-CA-2, тоже доверенный. Пусть Root-CA-2 выпустил и подписал сертификат для того же открытого ключа, который указан в сертификате Root-CA-1. “Обычный” сертификат корневого ключа Root-CA-1, который упоминался выше, – это самоподписанный сертификат, то есть, подпись в нём от того же ключа, открытая часть которого указана в сертификате. Новый сертификат, выпущенный для ключа Root-CA-1 не является самоподписанным – его подписал Root-CA-2, используя другой ключ.

Обратите внимание: открытый ключ в этом новом сертификате – тот же, что и в самоподписанном сертификате корневого ключа Root-CA-1. Это подпись другая. Соответственно, этот открытый ключ позволит успешно проверить подпись, поставленную Root-CA-1 на сертификате Interm-CA-1 из цепочки, описанной выше. Более того, когда УЦ Root-CA-2 генерировал сертификат для ключа Root-CA-1, то этот УЦ и в качестве имени Subject сертификата указал Root-CA-1. Но Issuer – отличается: здесь стоит Root-CA-2 (в самоподписанном исходном и Issuer, и Subject – были Root-CA-1). Браузер верит ключу Root-CA-2. Теперь возможна другая цепочка: example.com <- Interm-CA-1 <- {Root-CA-1} <- Root-CA-2. Цепочка ведёт к Root-CA-2, он доверенный. Сертификат в фигурных скобках {Root-CA-1} – это сертификат для того же открытого ключа, от Root-CA-1, но выпущен он Root-CA-2. А раз это тот же ключ, то и для сертификатов, которые в цепочке находятся левее {Root-CA-1} ничего не поменялось – доверие транслируется точно так же. Это и есть кросс-подпись.

В схеме кросс-подписи больше не нужен доверенный корневой самоподписанный сертификат Root-CA-1. Его не нужно ставить в браузер вообще, потому что доверие транслируется от Root-CA-2 через кросс-подпись, поставленную на ключе, а не “на исходном сертификате”, как почему-то иногда пишут. Нет, исходный корневой сертификат не “переподписывается” – из него просто берётся открытый ключ, а далее доверие этому ключу образуется при помощи его подписывания собственным, локальным корневым ключом. И в браузер добавляется доверие этому, корневому ключу. Естестсвенно, необходим {Root-CA-1} (в фигурных скобках), выпущенный Root-CA-2. Этот сертификат (в фигурных скобках) локальный, его не пришлёт сервер. Поэтому сертификат придётся добавить в браузер (если для этого сертификата установить флаг доверия, то не нужен уже Root-CA-2, кстати. Но см. ниже про целевые ограничения – их нельзя утрачивать.)

Где же здесь ограничения? Они в nameConstraints. В TLS-сертификатах есть штатный способ, который позволяет описать ограничения по именам, для которых применим ключ из сертификата. Обратите внимание на важный момент: это механизм описания, он вовсе никак не позволяет “реализовать ограничение”. Реализация – остаётся на стороне валидатора сертификатов. То есть, если браузер применяет ограничения из блока сертификата nameConstraints, то сертификат не будет считаться валидным для имён, за пределами списка, если не применяет – будет. Современные браузеры nameConstraints обрабатывают и ограничения применяют. В частности, Firefox действует даже строже, чем предписывают RFC, касающиеся этих ограничений.

Механизм ограничения валидности довольно прост: в поле nameConstraints перечисляются наборы DNS-имён для которых разрешено или запрещено применять ключ при проверке подписи. Прежде всего, данное поле предназначено для сертификатов ключей УЦ. То есть, для ключей, от которых выпускаются другие сертификаты. При корректном применении ограничения должны действовать по всей цепочке вниз. Например, если в корневом сертификате указано ограничение “разрешено только для example.com”, то валидатор, следующий ограничениям, не примет сертификат от этого корня для example.net. Цепочка проверки при этом может включать несколько промежуточных сертификатов. Ограничения по именам применяются и тогда, когда в сертификате этих имён много: все имена должны быть разрешены, иначе валидация не пройдёт. То есть, при правильной реализации, метод весьма действенный.

Теперь, если мы подписываем кросс-подписью ключ другого УЦ, то мы можем указать нужные ограничения в сертификате, который подписываем. Ограничения перечисляются в nameConstraints. В принципе, ограничения можно указать и сразу в локальном сертификате корневого ключа. В качестве состава ограничений может быть как только конкретный домен верхнего уровня, так и конкретные DNS-имена веб-сайтов. Тогда браузер посчитает невалидными оконечные сертификаты, выпущенные от исходного корневого ключа, но для имён, которые не входят в разрешённый список. Предполагается, что это противодействует потенциальным MITM-атакам: “ограниченный” корневой сертификат уже не позволит браузеру посчитать валидным оконечный сертификат, выпущенный для, условно, google.com. Заметьте, впрочем, что у Chrome и Google есть специальные меры, отслеживающие подобную подмену без всяких nameConstarints и собственного локального корня. При этом локальный корень, налаженный по описанной схеме в том же Chrome, данные меры поломает, поскольку всё будет выглядеть так, как если бы в систему установлен “сертификат корпоративного УЦ”, а для таких случаев многие способы детектирования подмены серверных ключей работают не так строго. Это один из неочевидных аспектов использования данного решения.

Итак, схема сводится к следующим шагам: 1) генерируем собственный корневой секретный ключ и сертификат для этого ключа с ограничениями по именам в nameConstraints; 2) встраиваем в браузер открытую часть данного ключа в качестве доверенного; 3) выпускаем от этого корневого ключа сертификат кросс-подписи для ключа из “сертификата Минцифры”, совпадающий по имени Issuer с “оригинальным”, возможно, повторяем тут ограничения по именам, добавляем этот сертификат тоже в доверенные. “Сертификат Минцифры” – добавлять не нужно, уже добавлен ключ из него. Всё. Теперь браузером начинают считаться доверенными сертификаты только на сайтах, подходящих по именам.

Какие есть подводные камни на этом направлении? Самые разные. Форма и размер этих камней – определяются теми рисками, с которыми вы попытались бороться. Прежде всего: наличие локального “ограниченного” корня вовсе не отменяет установления TLS-соединения. Нет. Браузер всё равно будет подключаться к произвольным сайтам, выполнять начальную стадию TLS-соединения, и лишь получив в ответ сертификат, ключ из которого сходится к нашему “ограниченному корню”, применять ограничения по именам и выдавать ошибку, если ограничения не позволяют принять сертификат. Про это нельзя забывать: собственный корень не ограничивает подключение и обработку сертификатов, он управляет только итогом валидации.

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

Добавление локального корневого сертификата в доверенные означает, что если соответствующий секретный ключ утечёт, то тот, кто его получил, сможет подменять сертификаты для всех вариантов имён, подходящих под ограничение nameConstaraints. Если это сайты банков, то сразу возникает весьма неприятный риск. Поэтому, как минимум, следует защитить (лучше – уничтожить) такой “подменный” секретный ключ. Этот момент тоже постоянно упускают из виду.

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



Комментировать »

Ближайшее будущее в области TLS-сертификатов для веба – это сертификаты на хеш-деревьях. Я писал об этом неоднократно. Ещё одно подтверждение: черновик политики Google по включению в список доверенных удостоверяющих центров (УЦ) веб-браузера Chrome. Этот черновик касается постквантовых криптосистем цифровой подписи (или, как их ещё обозначают, “квантовостойких” криптосистем). В контексте данной программы доверенных УЦ Chrome – это криптосистема ML-DSA (и только). Но главное тут не тип криптосистемы, а то, что политика допускает исключительно схему с хеш-деревьями, с MTC (Merkle Tree Certificates). Соответственно, УЦ, не поддерживающие MTC – в программу даже податься не смогут, вне зависимости от других параметров. MTC, с точки зрения УЦ, совсем другая история, если сранивать с имеющимся сейчас вариантом.

Этот вариант, – хеш-деревья, – алгоритмически близок с реализации Certificate Transparency (CT). Концептуально, MTC – это перенос CT на сторону УЦ. Поэтому в черновике политики прямо сказано, что на начальном этапе включение в “квантовостойкие корни браузера” будет доступно только для тех организаций, которые уже поддерживают работоспособный лог CT (то есть, запустили такой лог до 1 февраля 2026 года). К таким организация, естественно, относятся Let’s Encrypt, Cloudflare и Google (но не только эти).

Основные особенности MTC в TLS: “бесподписные” сертификаты – то есть, буквально, серверные TLS-сертификаты в которых вместо подписи – доказательство включения в хеш-дерево; очень короткий срок действия сертификатов – предпочтение будет отдаваться “шестидневным” сертификтам (и более “коротким”). “Бесподписная” часть – экономит трафик, поскольку сильно уменьшает размер сертификата в байтах, но также привязывает клиента к сервису, с которого нужно скачивать обновления дерева. Короткий срок действия – привязывает к точке выдачи сертификатов (и, таким образом, к провайдеру данных дерева) ещё и операторов TLS-серверов.



Комментировать »

Нередко я называю современные LLM-системы “синонимайзерами”. Термин, возможно, не самый точный. Однако, как ни странно, его применимость теперь полностью подтверждается свежей официальной публикацией Anthropic, про проставление меток (“водяных знаков”) на выдаче системы Claude. Там уже самое начало текста полностью раскрывает связь с “синонимизацией”. Буквально утверждается, что нет разницы между двумя словами (синонимами) в заданном предложении, поэтому конкретное слово выбирается случайным образом. (На задании псевдослучайной последовательности, управляющей работой такого синонимайзера, как раз построен механизм простановки метки, но сейчас не об этом.)

Цитата (и перевод):

Take the sentence “The weather today was cold and…”. The next word is very unlikely to be “sugary.” But it is quite likely to be “overcast” or “grey.” Under most circumstances, it doesn’t matter much to the reader which of these latter two words the model ultimately chooses—the meaning of the sentence is largely the same either way. In cases like this, the choice is settled by a random number.

/Возьмём предложение “Погода сегодня была холодной и…”. Вряд ли следующее слово будет “сахарной”. Но вполне вероятно, что следующее слово это “пасмурной” или “серой”. В большинстве случаев, для читателя не имеет значения, какое из двух слов модель в конце концов выберет: значение предложения, в общем-то, одинаковое, так или иначе. В случаях, похожих на этот, выбор осуществляется при помощи случайного числа./

Обратите внимание: “синонимы” вне контекста и “случайное число”. Всё сходится. Это описание синонимайзера. Между тем, с литературной точки зрения, два английских предложения “the weather today was cold and overcast” и “the weather today was cold and grey”, конечно, имеют разную коннотацию. Особенно, если предположить, что вокруг есть некий больший контекст. Английский вариант тут работает почти так же, как и русский: “погода сегодня была холодной и пасмурной” и “погода сегодня была холодной и серой”. Но для синонимайзера – здесь главное не коннотация, а то, что синонимы есть и их возможно переставить, а значит, можно и приспособить образовавшуюся вариабельность в качестве носителя метки текста! Далее там как раз объясняется, что если возможности для синонимизации нет, – как в конкретных декларациях фактов, например, – то и метку не применить. Конкретные декларации здесь, это о том, что добротная система не может “синонимизировать” слово “братья” в сообщении о том, что роман “Братья Карамазовы” написал Достоевский.

К сожалению, такой подход начисто удаляет и “интеллект” (в кавычках) и литературную основу, заменяя процесс на механическую “простановку меток”.



Комментировать »

В продолжение записки про метки на выдаче LLM-систем, которые должны позволять атрибутировать текст (или другой тип контента), как сгенерированный ИИ/LLM. Тут есть несколько нехороших моментов.

Момент первый. Если определять такие метки будет тот же провайдер, который обеспечивает LLM, то получается существенное усиление позиции этого провайдера: он сможет по собственному разумению приписывать своей системе произвольные тексты.

То есть, LLM-провайдер захватил и использовал в своих целях практически весь контент, до которого смог дотянуться (в том числе, страницы веб-сайтов), а теперь ещё и сможет маркировать контент как “свой”, придавая, тем самым, дополнительную окраску различным публикациям. Тут важно понимать, что это только кажется, будто тут борбьба идёт за то, что некие пользователи распространяют “сгенерированный контент” как “подлинный”. Нет. Вопрос подлинности контента остаётся в стороне. Выдача LLM тоже может быть подлинной. Банальный пример – это, например, публикация, основная часть которой – разбор особенностей этой самой выдачи. Есть и небанальные примеры: цитирование и синонимизирование текстов LLM-генератором.

Так что речь не про произвольную атрибуцию, а про навязываемую окраску: и контент был, буквально, отобран и захвачен через синонимизацию, и результат захвата теперь использовать не позволяется без обязательного окрашивания тем же провайдером того же самого сервиса, в который этот контент утёк. И это всё в ситуации, когда без крупнейших LLM-систем уже практически не обойтись, поскольку и интернет-поиск сломали, и всё вокруг заливается выдачей этих систем. Ещё раз: это, конечно, работает только в том случае, если обнаружение меток и аутентификацию таки оставят за провайдерами LLM-сервисов, а сам метод детектирования не будет подразумевать непрерывного независимого аудита (хотя, кого такой аудит пугает? и где же его “независимость”?).

Момент второй. Что делать, если вполне себе авторский текст, написанный человеком, а не сгенерированный LLM, тем не менее, детектор меток записывает за LLM? То есть, автору говорят – нет, это всё сгенерировано.

– Вы просто переписываете “Википедию”!
– Нет, это не так, это редакторы “Википедии” берут мои статьи за основу.

Ошибки атрибуции, понятно, не то что возможны, они – обязательно будут. Про эти самые ошибки до сих пор написано в тех же самых интерфейсах лидирующих LLM-сервисов. Более того, если метка не является криптографической, то подготовленному человеку не составит особого труда написать текст, который будет определяться как сгенерированный данной LLM. То есть, тут уже получается такое “навязывание метки”. Можно подумать, что это ерунда. Но нет – кто там знает, что “такое сомнительное” могут “навесить” на сервис? Тоже интересный момент.

– Можно ли подзарядить мой смартфон от твоего портативного аккумулятора?
– Конечно. Подключай.
– Спасибо! Ого, а что это там замигало?
– Это данные передаются.
– Да? Ну, ладно, мне нечего скрывать из того, что есть в смартфоне.
– А кто сказал, что данные считываются из смартфона? Напротив, они сейчас туда загружаются. И, кстати, теперь тебе есть, что скрывать.

Момент третий. Что считать достаточной “степенью генерирования”? Подходит ли дорисованная на ночную фотографию пейзажа Луна? Это достаточное вмешательство, чтобы стать “генерированием”? А если Луну дорисовало ПО фотокамеры в смартфоне? А если это сработал “фильтр” в программе для обработки цифровых фотографий, и Луна не дорисована полностью, но “улучшена”? Считается ли исправление орфографических ошибок в тексте достаточным признаком? Сколько ошибок можно исправить автоматом, чтобы деятельность автомата посчиталась как генерирование LLM? Есть ли разница между “орфографией” на буквах и “рисованием” на пикселах? Сколько можно исправить пикселов? Заметьте, что практически всякое установление границ и лимитов на этом направлении тут же приводит к размытию и лимитов, и границ.

Но, конечно, хуже всего выглядит то, что LLM-првайдеры получают возможность помечать как “свой” контент, полученный на основе творчества человеческих авторов, а позже лишь синонимизированный. И на основании такого окрашивания будет выстраиваться отношение к исходным идеям и публикациям. Максима “Компьютер не может ошибаться” приведёт к тому, что LLM-сервис, на основании значения метки, станут считать первоисточником. Причём, вот тут уже и не требуется, чтобы метки обрабатывал тот же провайдер, который предоставляет LLM-сервис.



Comments Off on Метки и окрашивание контента от LLM-систем

Между тем, под видом борьбы с AI-ботами уже предлагают полностью сломать веб. Вот до чего уже дошло. Проект ShieldFont при помощи шрифтовой подстановки заменяет одни слова на другие.

То есть, буквально, в тексте страницы написано одно слово, но процессор шрифтов визуализирует совершенно другое. Естественно, это делается через подменный, заведомо кривой, шрифт! Предполагается, что AI-боты споткнутся на исходном тексте, так как в нём “не те слова”. Я недавно объяснял на конкретных примерах с подменой букв, что это вообще не так – разбор подобного не представляет никакой проблемы для современных LLM-систем: достаточно взять подменяющий шрифт и построить таблицу замены, можно сразу “в токенах”. Более того, продвинутая LLM-система ещё и сама сможет код сгенерировать (написать), реализующий такую обработку.

Зато для обычного пользователя – веб будет полностью сломан, на самом низком уровне этого веба: различные языки и системы письма, поиск по странице, речевой браузер, собственный набор шрифтов, просмотр в консольном браузере – всё это сломается, но не только это. Что уж там говорить о следовании веб-стандартам. И только для “AI-скрейперов” – никакой заметной проблемы. Возможно, впрочем, в этом и смысл?

Забавно, что в описании такая “замена слов” называется заменой на “лигатуру”, но, естественно, это не лигатура никакая: конечно, лигатура может соответствовать короткому слову, но не наоборот – произвольное слово не может стать лигатурой, чтобы “помешать боту”, потому что лигатура – это про буквы. И ещё более забавно, что, если верить репозиторию с кодом проекта на Github, в разработке использовалась LLM-система Claude.



Комментировать »

В декабре 2024 года я писал на dxdt, что эффективным направлением применения ИИ-LLM к математическим задачам был бы поиск контрпримеров к более или менее известным утверждениям, в формате компьютерного доказательства. В качестве примера сборников подходящих задач я приводил “Коуровскую тетрадь” (это широко известный в узких кругах канонический список задач теории групп). Цитата из той записки:

Для заметной части из этих проблем и связанных задач можно было бы отыскать контрпримеры (и даже просто – примеры), используя современные возможности по оптимизированному перебору текстов компьютерных доказательств.
[…]
То есть, это самое реальное применение для знаменитых “LLM с нейросетками” на ближайшее время, при котором они могли бы оказаться очень эффективными для математических исследований.

Ну, идея довольно очевидная, поэтому, кто бы сомневался, что именно так и вышло: на днях OpenAI опубликовали список из десяти достаточно известных математических проблем, к которым предложены решения или контрпримеры, найденные “методами ИИ”, с использованием Lean.

Интересно, что одна из решённых в OpenAI задач – прямо указана и в “Коуровской тетради”, но лишь в самой свежей, 21-й редакции (я проверил только для англоязычной версии, понятно). В публикации OpenAI – это третья глава с утверждением non-sofic groups exist и контрпримером – с построением такой группы. В англоязычной “Коуровской тетради” это задача 21.86. Да, там обратная формулировка: “всякая ли группа является софической (sofic)?”. Но это как раз то, что нужно для поиска контрпримера: покажите одну группу, которая non-sofic, и это даст ответ – нет, не всякая. (“Софической”, конечно, не лучший перевод – должно быть “терминальной” или “терминируемой”, но данный вариант уже занят.)

Но что особенно занятно, так это то, что решённая ИИ OpenAI задача 21.86 в тетрадь добавлена в этом же, 2026 году! Удивительное совпадение. Например, препринт на Arxiv с 21-м изданием, в котором появляется данная задача, датирован январём 2026 года (версия 39, кому интересно). Естественно, сама исходная гипотеза про “софичность” всех групп – сильно старше, она, примерно, 2000 года. Однако в более старых версиях “Коуровской тетради” она не указана, а, похоже, появляется только в 2026 году.



Комментировать »