惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

Microsoft Azure Blog
Microsoft Azure Blog
H
Hacker News: Front Page
A
About on SuperTechFans
云风的 BLOG
云风的 BLOG
aimingoo的专栏
aimingoo的专栏
Martin Fowler
Martin Fowler
博客园 - 叶小钗
Last Week in AI
Last Week in AI
Recent Announcements
Recent Announcements
P
Palo Alto Networks Blog
Webroot Blog
Webroot Blog
Hacker News: Ask HN
Hacker News: Ask HN
IT之家
IT之家
K
KPMG report finds enterprise disconnect between AI and its ROI | CIO
T
Threat Research - Cisco Blogs
C
CERT Recently Published Vulnerability Notes
Google DeepMind News
Google DeepMind News
Hugging Face - Blog
Hugging Face - Blog
H
Help Net Security
P
Privacy & Cybersecurity Law Blog
C
Cisco Blogs
罗磊的独立博客
The GitHub Blog
The GitHub Blog
M
MIT News - Artificial intelligence
人人都是产品经理
人人都是产品经理
The Cloudflare Blog
Y
Y Combinator Blog
AWS News Blog
AWS News Blog
H
Hackread – Cybersecurity News, Data Breaches, AI and More
钛媒体:引领未来商业与生活新知
钛媒体:引领未来商业与生活新知
K
Kaspersky official blog
博客园 - 司徒正美
Threat Intelligence Blog | Flashpoint
Threat Intelligence Blog | Flashpoint
Security Archives - TechRepublic
Security Archives - TechRepublic
The Last Watchdog
The Last Watchdog
Jina AI
Jina AI
MyScale Blog
MyScale Blog
TaoSecurity Blog
TaoSecurity Blog
大猫的无限游戏
大猫的无限游戏
cs.CL updates on arXiv.org
cs.CL updates on arXiv.org
Cisco Talos Blog
Cisco Talos Blog
美团技术团队
T
Tor Project blog
cs.CV updates on arXiv.org
cs.CV updates on arXiv.org
博客园 - 【当耐特】
博客园 - 聂微东
V2EX - 技术
V2EX - 技术
I
Intezer
V
Visual Studio Blog
酷 壳 – CoolShell
酷 壳 – CoolShell

Все публикации подряд на Хабре

Ловим музу за клавиатуру: как айтишнику стать автором Что умеет Midjourney в 2026? Мой немного грустный разбор этого шикарного инструмента Никто не любит писать тесты, но ИИ может исправить это IPv8 выглядит как мечта. Поэтому почти наверняка не взлетит Производители вернули в продажу материнки с DDR3. Что происходит? Управление агентом с телефона через Telegram теперь в KodaCode От координации к лидерству: как меняется роль руководителя разработки Я сделала родителям бизнес вместо пенсии: зарабатываем 70 тысяч, мама не даёт продать В три раза быстрее приемка товара и оптимизация трудозатрат на 73%: как «РСТ-Инвент» помог Gulliver Group ИИ-шечный мир победил? О влиянии искусственного интеллекта на игропром Кремль снижает давление на Телеграмм пока Европа строит интернет по паспорту Как CEO, CTO и CIO за 8 часов собрали ИИ-директора, который умеет держать позицию под давлением Как (не) потерять домен за выходные Вместо 8 разных VPS: как я организовал практику студентам на одном сервере Почему твой Open Source проект не замечают? R&D: искусство управления неопределенностью в разработке AI-дефляция: вакансий для разработчиков больше, а рост зарплат — худший за 15 лет Мы отдали управление роботами OpenClaw. Что из этого вышло Галактический ID: система идентификации для всех форм разумной жизни Шесть основ бизнес-анализа: начинаем с вопроса «Кто в игре?» Код-ревью, в котором дело не в коде Данные переехали. Команда — нет Системной подход к сдаче OSWE в 2025 Почему комната управления реактором покрашена в цвет морской пены 4 YAML-файла вместо PySpark: как аналитикам строить пайплайны без разработчиков LLM-агент для поиска свободных доменов: автоматизируем подбор Когда, зачем и как правильно начинать новую сессию в Claude Code? Как я заставил нейросеть писать макросы для FreeCAD Анатомия ИИ‑агента для подбора персонала. От тысячи резюме к топ‑10 за минуты Опыт разработчика как экономика внимания Автономность как точка невозврата: кто будет субъектом в цифровом будущем Обучение ИИ в «диких» условиях: как рутинные действия превращаются в датасеты Как измерить LLM для задач кибербеза: обзор открытых бенчмарков Где хранить код? Сравнение GitHub, GitLab и Bitbucket Математика объясняет, почему нормальное распределение встречается повсюду Почему ваш FinOps не работает: 12 тезисов от практиков Как подписать проектную документацию УКЭП с использованием бесплатных лицензий Pilot Адаптивное администрирование Sigla Vision Я грузил уран в бочки, а потом 20 лет строил ИТ в атомной отрасли Чем позвонить с Эвереста? История и обзор спутниковой связи. Часть 2 Как языковая модель помогает контролировать качество инструктажей по охране труда в металлургии Как не передать на desktop свой IP в РКН Анатомия SAP Privileges: как устроено управление правами в macOS MoneyDev: Сказка про три главных слова Обновлённый токенизатор видео K-VAE 2.0 от Сбера Как сделать диспетчеризацию дома на 1284 квартиры почти бесплатно Как мы разогнали железную дорогу Мы дали агентам рутину. Теперь надо решить — что делать с освободившимся временем Токсичный контент, промпт-хакинг и защита ИИ — всё о Guardrails для LLM Умный город начинается с точного взгляда: как «Фалькон Тех» меняет пространство к лучшему Навайбкодил приложение для анализа графов Почему Дюну так интересно читать? Упрощаем работу с рутиной или как стать Гендальфом Белым Деконструкция Go: CPU, RAM и что там происходит. Go Assembler база. Часть 1.1 Какие профессии исчезнут из-за ИИ, а какие появятся? И что с этим делать Как мы построили IT-отдел, где хочется расти: архитектурные встречи, прозрачные метрики и книжные подарки Rufler: Делаем из Claude Code автономный рой через один YAML-конфиг Sing-box и белый список приложений Как построить надёжный обмен сообщениями в микросервисах: лучшие практики для enterprise OpenAI строит MLM-пирамиду, а McKinsey и Accenture помогают ей в этом Дом, который не построил Фишер (Часть 2) «Сверхзвуковой математик» против «Вдумчивого логиста»: битва алгоритмов 3D-упаковки Мультимодальные модели – грубый и дорогой инструмент Разговоры ничего не стоят. Код тоже Проверки физических лиц: с кого начнет ФНС Топ-10 бесплатных нейросетей для создания видео в 2026 году Первые слои кода: как наши решения сегодня определяют архитектуру ИИ на десятилетия Разработка нового статического анализатора: PVS-Studio JavaScript Поиск уязвимостей ПО: базовый минимум или роскошный максимум Почему оценка персонала не работает как инструмент управления Как мы разработали ИИ-ассистента и сократили рутину продуктовой команды на 50% Как я ушел из найма, нажарил косточек и продал на маркетплейсах на 168 млн в год Когда 1С:ERP уже внедрена, а нормального производственного плана всё ещё нет Как я сделал Claude мультимодальным, подключив к нему Qwen Omni Как приглашение на вакансию мечты превращается в атаку Infrastructure as Code: философия и лучшие практики IaC Тестируем Yandex Code Assistant на задаче, в которой нужно хранить секреты nxs-universal-chart v3.0: новое поколение универсального Helm-чарта Callback Injection: Техника, которая отправила Microsoft Defender в глухой нокаут «Все идеи на стол»: митап как способ вывести проект из тупика Сегодня я узнал нечто новое о GPU благодаря багу в своей игре Как заставить LLM ̶ ̶г̶а̶л̶л̶ю̶ ̶ эволюционировать Карта событий как фундамент аналитики: практический кейс для E-commerce Что выбрать для AI: x86, ARM или RISC-V? Дайджест железа за март Роль соматических мутаций в развитии аутоиммунных заболеваний: путь к избирательной терапии Mythos от Anthropic — тревожный сигнал для всех, а не только для банков Guardrails для LLM на Java: как приручить промпт‑инъекции и токсичные ответы Green-VLA: как мы собрали VLA-модель для реального антропоморфного робота и не потеряли обобщение Финансовая гонка вооружений: почему умные люди добровольно в ней участвуют Эра ИИ-агентов наступила: выбираем лучшего цифрового сотрудника # Практический опыт внедрения WinCC Redundancy на производственном предприятии Сделал MVP за 3 дня, а потом неделю прикручивал оплату. Оно того стоило? Физика против Маска: почему Starship V3 может оказаться ещё одной катастрофой Нефть Венесуэлы: крупнейшие запасы в мире, но не крупнейшая нефтяная держава JPA 4. Переосмысление Hibernate Почему зеркальная фотокамера Nikon D5 десятилетней давности идеально подошла для миссии «Артемида-2» Проект «Уровень-Спутник» или как мы сделали платформу для гидрологов «Замедлиться, чтобы ускориться»: почему ИИ повышает цену ошибок в требованиях и архитектуре Как с нуля поднять трафик IT-компании на 1657% при бюджете 55 тыс. и выжить Pixel-perfect Downsampling — идеальная отрисовка 50 миллионов точек без потерь
Как один комментарий на Хабре перевернул архитектуру моего мессенджера
Иванов Сергей · 2026-06-21 · via Все публикации подряд на Хабре

Как один комментарий на Хабре перевернул архитектуру моего мессенджера

Средний

8 мин

126

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

После моей первой статьи про Pulse я не ожидал, честно говоря, примерно ничего. Но к моему удивлению увидел реакцию: немного поддержки, немного критики, несколько советов по UX.

Pulse

Pulse

Я получил около 7 тысяч просмотров, десятки комментариев и, неожиданно для себя, полноценный аудит проекта. Особенно, просто золотой грааль - комментарий от пользователя domix32. Он не обсуждал идеи. Не спорил про дизайн. Не рассуждал о будущем мессенджеров или самого Пульса. Он просто взял и проверил его на прочность.

И нашёл проблемы.

На момент начала аудита в Pulse уже было:

— 172 зарегистрированных пользователя;
— более 1100 сообщений (и нет, не все мои, а всего лишь 20%);
— около 60 пользователей, реально отправлявших сообщения;
— Если кто-то вдруг еще не в курсе - то так же имеются группы, приглашения, восстановление доступа, вход по устройству и PWA.

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

Когда критика полезнее похвалы

В комментарии было несколько конкретных замечаний:

  • не работает связь с разработчиком;

  • странно работает валидация меток;

  • есть вопросы к юридическим страницам;

  • уведомления между сессиями синхронизируются не идеально;

  • безопасность вызывает вопросы.

Но одна фраза запомнилась больше остальных:

За прочую безопасность даже не пробовал тестировать.

На первый взгляд обычный комментарий. Мысленно я очень сильно зацепился за именно эту часть комментария. Потому что она с одной стороны - короткая, но чертовски емкая. И формулировка внутри меня звучала уже так:

«Я уже нашёл достаточно. Если копнуть глубже — найду ещё.»

Именно после этого я остановил работу над новыми фичами и принял решение вместо очередного релиза с красивыми возможностями и двигаться в сторону безопасности, потому что сейчас публикуясь уже на Хабре, да и вообще привлекая новых пользователей в Пульс - этот вопрос встает очень и очень остро. Я закинул снапшот проекта чату GPT, с просьбой провести полный аудит, опираясь на комментарий и дополнительно проверить возможные дыры, баги и прочие дефекты проекта. Сколько же хлама в нем оказалось, но увы это последствия ранней архитектуры и вайбкодинга, так как все писалось с нуля с бешенным рвением, пусть и старался вести политику "реализуем исключительно правильные архитектурные решения". Но... Меня это от рефакторинга увы не спасло. В целом я этого ожидал.

Неожиданный результат аудита

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

Например:

Сначала мы исправили доступ к диалогам. После этого стало сильно бросаться в глаза, что авторизация разбросана по нескольким обработчикам и требует отдельного AuthService. Когда начали выносить AuthService, выяснилось, что публичные и приватные DTO смешаны между собой. После разделения DTO стало заметно, что часть публичных endpoint вообще не ограничена по частоте запросов. После внедрения Rate Limiter обнаружились проблемы жизненного цикла WebSocket-соединений. А после ревизии WebSocket стало понятно, что отдельного внимания требует транспортный уровень и работа с origin-политиками. В какой-то момент стало ясно: это уже не набор отдельных багов.

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

Поэтому вместо хаотичных исправлений появился технический roadmap. Не список новых функций. А список инженерных задач, которые постепенно устраняют накопленные архитектурные компромиссы.

Что именно предстоит исправить

Visibility-aware WebSocket Lifecycle

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

Задача — сделать жизненный цикл WebSocket зависимым от состояния вкладки и активности пользователя.

Auth Hardening

Текущая система авторизации уже работает, но содержит упрощения, допустимые для ранних версий продукта. Предстоит перевести генерацию кодов на криптографически стойкие источники случайности, сделать атомарный claim запросов входа и усилить защиту сценариев восстановления доступа.
(Часть задач из первоначального roadmap уже была закрыта во время подготовки статьи. В частности, релиз 0.9.49 принёс AuthService Foundation и DB-based Rate Limiter. Поэтому следующий этап посвящён уже не построению базовых механизмов, а дальнейшему усилению безопасности существующей системы.)

Media Safety

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

Теперь пришло время заниматься безопасностью загрузок:

  • ограничением размеров файлов;

  • проверкой MIME-типов;

  • защитой от SVG-атак;

  • контролем потребления памяти при обработке изображений.

Message History & Realtime Stability

История сообщений и realtime-события сегодня работают, но по мере роста объёма данных начинают проявляться эффекты гонок, устаревших ответов и пограничных состояний.

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

Transport Security

На текущем этапе соединения уже работают через HTTPS и WSS. Следующий шаг — ужесточение транспортной безопасности:

  • проверка origin;

  • отказ от передачи чувствительных данных через query-параметры;

  • CSP и дополнительные защитные заголовки;

  • дальнейшая изоляция внутренних сервисов.

Security UX

Самая недооценённая часть безопасности — прозрачность для пользователя.

Появятся:

  • события безопасности;

  • дополнительные инструменты контроля доступа к аккаунту.

Пользователь должен понимать, что происходит с его учётной записью, а не просто доверять системе на слово.

Проблема №1. Историю чужого диалога нельзя защищать фронтендом

Одна из первых вещей, которую мы пересмотрели — доступ к истории сообщений.

История загружалась по conversation_id.

Условно:

GET /history?conversation_id=...

При этом сама архитектура предполагала, что фронтенд покажет пользователю только те conversation_id, которые ему принадлежат. Это удобное предположение.

И очень опасное.Если безопасность держится на том, что клиент «не знает идентификатор», значит безопасности фактически нет.

Решение

В релизе 0.9.48 появился ConversationAccessService. Теперь любой запрос к данным диалога проходит через одну точку проверки:

CanAccessConversation(userID, conversationID)

Если пользователь не является участником диалога или группы — получает:

403 Forbidden

Причём эта проверка используется не только для истории сообщений.

Через неё теперь проходят:

  • история сообщений;

  • закрепления;

  • скрытие диалогов;

  • mute-состояния;

  • часть realtime-операций.

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

Проблема №2. Email не должен утекать через публичные API

До аудита использовалась одна модель пользователя практически для всего. Это было удобно, но привело к интересному эффекту.

Публичные методы вроде:

/users
/users/search
/users/by-alias

могли вернуть больше информации, чем реально требовалось клиенту, но гораздо информативнее и полезнее человеку, который решил изучить API продукта чуток внимательнее и глубже. В том числе как оказалось в выдачу попадал и чувствительный email.

Решение

Разделить публичные и приватные данные по разным моделям. Модель была разделена на:

PublicUserDTO
PrivateUserDTO

Теперь публичный API возвращает только действительно публичную информацию:

{
  "id": "...",
  "username": "...",
  "alias": "..."
}

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

Проблема №3. Rate Limiter, который сначала не работал

Следующим шагом стал релиз 0.9.49.

Лимиты на вход, лимиты там и сям. Кто их любит? Из пользователей никто. А вот их отсутствие очень любят ломатели софта, так как это дает им неограниченные возможности для создания неограниченного количества запросов там, где надо бы их ограничивать. Отсюда появился DB-based Rate Limiter. И тут произошло самое интересное. Мы были уверены, что всё работает. Пока не начали тестировать.

Что пошло не так

В качестве идентификатора клиента использовался адрес подключения, который идет на бек с фронта. То есть фактически не реальный адрес пользователя, а наш собственный nginx. В логах это выглядело примерно так:

127.0.0.1:53120
127.0.0.1:53121
127.0.0.1:53122

Проблема оказалась в том, что порт менялся на каждом запросе. Для лимитера это были разные пользователи. Количество попыток никогда не достигало лимита. То есть защита существовала только на бумаге.

Решение

Пришлось разобрать следующее. Нынешний Reverse proxy был настроен, но не до конца. Как итог перелопатили:

  • работу nginx;

  • X-Real-IP;

  • X-Forwarded-For;

  • алгоритмы fixed window.

В результате мы переписали лимитер, и теперь ограничения работают для:

  • входа через устройство;

  • восстановления доступа;

  • поиска пользователей.

Во время тестирования запросов подряд, выходящих за пределы установленного лимита, теперь корректно получаем честный стоп:

429 Too Many Requests

Именно тогда стало понятно, что защита действительно работает и этот гештальт можно закрыть.

Проблема №4. Логи тоже являются поверхностью атаки

Ещё один интересный момент обнаружился во время ревизии. В логах присутствовали данные, которые не должны были туда попадать:

  • токены - я даже ужаснулся, что допустил такое;

  • части WebSocket payload;

  • некоторые служебные данные.

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

Чем больше пользователей и инфраструктуры появляется, тем опаснее становятся собственные логи, ведь есть любители заглянуть на вкладки в консоль и сеть :)

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

Что изменилось в подходе

Самое интересное произошло не в коде. Изменился подход к разработке. Раньше цикл выглядел так:

идея → фича → релиз

Теперь он выглядит иначе:

идея → безопасность → архитектура → фича → релиз

Это медленнее.

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

Цена ранних решений

Самое интересное, что ни одна из найденных проблем не была результатом плохого решения. Почти все они были результатом правильных решений, принятых слишком рано или слишком поздно. Когда в системе 2 пользователя, централизованный сервис доступа кажется избыточным. Когда в системе нет восстановления доступа, кажется, что отдельный AuthService не нужен. Когда проектом пользуешься только ты сам и несколько твоих знакомых, друзей, то отсутствие Rate Limiter не кажется проблемой.

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

Что дальше

После аудита появилась отдельная дорожная карта технических релизов.

Следующие этапы:

  • Visibility-aware WebSocket Lifecycle;

  • Media Safety;

  • Message History Stability;

  • Transport Security;

  • Security UX.

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

Итог

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

Поэтому если у вас есть собственный проект — покажите его людям. Особенно тем, кто способен найти в нём проблемы. Они могут оказаться лучшими помощниками в развитии продукта.

Спасибо всем, кто тестирует Pulse, пишет замечания и не боится критиковать.

И отдельное спасибо domix32. Некоторые архитектурные решения в проекте появились именно благодаря вашему комментарию.

Спасибо, что дочитали до этого места. Я открыт для любых замечаний, комментариев и пожеланий. А всем остальным желаю удачи, простых решений и легкого кодинга в своих проектах. Буду рад почитать ваши решения!