Комментарии 3
Что-то здесь не так и чувствуется какая-то проблема в логике. Решали одну задачу, решили другую, при это проблем/рисков то ли столько же стало, то ли больше, первоначальная проблема усугубилась, и всё это под рассказы про безопасность.
Могу ошибаться, но прежде всего видна путаница в терминологии. Мне кажется, что под словом "клиент" у вас в разных местах разные сущности и разные риски. Где-то там пошли проблемы с логикой в расуждениях.
Во-вторых, вы много говорите о безопасности и угрозах, но у вас нет списка и приоритетов угроз - от чего именно и с какими приоритетами вы защищаетесь? Вы не можете защититься от всего - нужно выбирать, какие угрозы более приоритетны, какие менее. И дальше обязательно действовать в рамках это модели: каждый раз, когда у вас возникает вопрос о том, какое решение более безопасное/правильное, вы должны смотреть на свою модель и действовать в соответствии с ней. Если вы меняете модель, то пересматриваете все решения. А у вас вероятности и опасности угроз меняются прямо на ходу. И результат, честно говоря, очень сомнительный. Вы в какой-то момент начали защищаться от потери контроля на бекэндом, перелопатили совершенно всё, но в результате от заявленной угрозы не защитились и даже сделали хуже. Но зачем вы тогда это всё делали?
И хуже всего то, что, судя по выводам, вы из всей этой истории вынесли крайне отрицательный и вредный опыт по работе с безопасностью. Вместо последовательной работы по с оценкой угроз вы скатились в придумывание и переоценку этих угроз прямо на ходу. При этом вы неоднократно повышаете статус какой-то угрозы, на которой сконцентрированы прямо сейчас, но при это этом не пересматриваете статусы всех остальных угроз. В результате у вас то одна угроза в приоритете, то другая, то третья, но при этом непосредственно-связанные с ними угрозы вы не пересматриваете. Обратите внимание, что результат по защите у вас получился случайный и зависит от того, в каком порядке вы рассматривали угрозы. Так нельзя: вы составляете список угроз с приоритетами, и при необходимости правите всю модель, пересматривая все решения, конечный результат должен соответствовать модели.
Кроме того:
"Захваченный бэкенд может заранее собрать пачку валидных доказательств и держать их про запас, а потом использовать, когда пользователь давно закрыл ноутбук."
Это может быть очень серьёзная и очевидной угроза, но от этого можно защититься. Делайте одноразовые токены, делайте обмен токенов, делайте привязку ко времени - что-то из этого в зависимости от приоритетов ваших угроз.
"Общий вывод из всего этого получился короткий. Если страницу, в которой живёт твой второй фактор, отдаёт тот, от кого ты защищаешься, независимым фактором он не является."
Это очевидно-неверный вывод. Сам второй фактор при этом может быть защищен так, что контроль над страницей ничего не даст атакующему. Это ещё один признак того, что вы рассматривали угрозы фрагментарно и по ходу разработки, что-то там сами себе на ходу постулировали, без взгляда на всю ситуацию в целом. Даже просто прочитайте вот эту свою фразу - вы точно уверены, что она верная? Вы точно не можете придумать второго фактора, который не может быть скомпрометирован контролем над отдающей страницей? А если nouce, подписи и челендж со стороны клиента добавить? А потому подумайте, сколько неверных решений вы приняли из-за того, что в какой-то момент почему приняли вот эту фразу за постулат.
А ещё вы из-хз отсутствия модели угроз не рассмотрели случай, когда можно иметь ещё один сервер, находящийся в своём собственном изолированном контуре безопасности, и выполняющих какую-то очень простую изолированную задачу. Это решило бы практически все ваши проблемы. Но для этого нужно точно знать от каких именно угроз вы защищаетесь. От потери контроля над одним сервером? Без проблем. От потери контроля вообще над всем серверами? Так и вы сейчас от этого не защитились и нужно более тщательно сравнивать эти два варианта (ваш скорее всего проиграет). У вас сейчас потеря контроля над одним сервером/сервисом полностью рушить всю безопасность - зачем тогда вы всё это городили?
Спасибо, это самый содержательный разбор, который я получал. Три ваших пункта я принимаю целиком, по двум, кажется, статья вас подвела — отвечу по порядку.
Модель угроз. Вы правы: её не было в тексте, и без неё рассказ читается как импровизация. Добавил врезкой в начало статьи — пять пунктов «защищаем» по приоритету и три «не защищаем», ровно в том виде, в каком они лежат в README проекта. Там же теперь сказано главное: решение считается правильным, если оно закрывает пункт из первого списка и не притворяется, что закрывает пункт из второго.
Терминология. Согласен полностью. В статье «клиент» — это браузерная страница, «бэкенд» — сервер приложения, который эту страницу отдаёт и хранит документы, а key-service — отдельный сервис на отдельной машине, который хранит только ключи и документов не видит никогда. Три роли с разными правами, и названы они так, что их легко слить в одну.
Вывод про второй фактор. Здесь вы правы, и это не мелочь. Моя фраза шире того, что следует из фактов. WebAuthn с challenge от независимой стороны даёт вполне реальные вещи: заготовить подписи впрок нельзя, использовать фактор без физического присутствия человека нельзя. Чего он не даёт в вебе — привязки к намерению: устройство не показывает, что именно подписывает, а текст на экране рисует тот же бэкенд. Он покажет «доступ к документу А», а на подпись отправит challenge для документа Б.
Обиднее всего, что в моём же исходном разборе формулировка была точная: «доказательство присутствия не привязано к документу, который имел в виду пользователь». Обобщил я её уже в статье и потерял разницу между «фактор бесполезен» и «фактор подтверждает присутствие, но не выбор объекта». Поправлю.
Одноразовые токены и привязка ко времени — они есть. Разрешение на выдачу ключа: подпись Ed25519, одноразовый идентификатор, срок жизни не больше двух минут, привязка к конкретному документу, а контекст вшит в проверяемые данные шифра, так что подменить его нельзя. В статье про это сказано вскользь, отсюда и впечатление. Ваше замечание точно бьёт в WebAuthn-доказательство старой схемы, где challenge выдавал не key-service, — и это ровно то, что предписал разбор.
Отдельный сервис в изолированном контуре — это и есть моя архитектура. Раз из статьи это не считалось, написана она плохо. Что происходит при захвате каждого из двух:
Сервер с документами: данные зашифрованы, ключей на нём нет. Но он владеет ключом подписи разрешений, поэтому может законно запрашивать ключи по одному. Против этого работают лимиты (сто двадцать запросов в окно на пользователя, шестьдесят на документ), аварийная блокировка по порогу и приманки. Но фундаментально — да, он способен выкачивать в темпе лимита, и это принятый риск, а не недосмотр.
Key-service: документов там нет вовсе, мастер-ключ появляется в памяти только после ручного отпирания по локальному сокету.
Оба сразу: первый уровень падает, второй — нет, пока у атакующего нет парольной фразы, которой на серверах не существует.
То есть разделение поднимает цену атаки с «одна машина» до «две машины плюс активная малварь на устройстве пользователя». Не полная защита, но и не ноль.
И про то, зачем тогда убрали WebAuthn. Не из-за объёма работы. Я не мог поручиться, что у людей, для которых это делается, есть подходящее устройство, и не хотел, чтобы человеку со старым ноутбуком отказали в доступе к собственным записям. Это размен доступности на защиту от активной компрометации, и в решении он записан вместе с тем, что мы теряем. В статье я подал его как вывод, а не как размен, — и звучит хуже, чем есть.
qwe

Второй фактор, который жил в чужой странице: как я выбросил WebAuthn из своего сервиса ключей