Мысли программиста / TeaCoder: post #832 — TG.ME

Всем привет! 👋

Недавно я решил провести аудит защищенности и анализ архитектуры в известной open-source платформе Dokploy. Это классный self-hosted инструмент для деплоя, и с недавнего времени в нем есть платные Enterprise-фичи. Доступ к ним закрывается проверкой лицензионного ключа.

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

⚡️ В чем фундаментальный провал архитектуры

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

Логика валидации в Dokploy завязана на пару уязвимых мест:

- Бэкенд на Node.js отправляет POST-запрос на licenses-api.dokploy.com (эндпоинты /licenses/activate и /licenses/validate).

- Сервер ждет в ответ самый обычный JSON вида {"valid": true}.

- Локальное состояние лицензии дополнительно дублируется флагами в PostgreSQL в таблице user (колонки enableEnterpriseFeatures и isValidEnterpriseLicense).

☠️ Как сработала концепция обхода (Network Spoofing + MitM)

Вся защита рухнула за счет комбинации подмены сетевого трафика и ослабления TLS-стека:

- Bypass TLS Validation: В Swarm-сервис Dokploy прокидывается переменная окружения NODE_TLS_REJECT_UNAUTHORIZED=0. Это заставляет Node.js проглатывать ошибки вадидации кастомного SSL-сертификата.

- Mock Infrastructure: В той же Docker-сети поднимается контейнер Nginx с SSL-сертификатом, сгенерированным через OpenSSL под домен licenses-api.dokploy.com. Nginx принимает HTTPS-трафик на 443 порту и перенаправляет его на Python-контроллер, который на любые POST-запросы возвращает HTTP status 200 и payload {"valid": true}.

- DNS Poisoning внутри контейнера: В файле /etc/hosts внутри целевого контейнера Dokploy домен licenses-api.dokploy.com перенаправляется на локальный IP-адрес нашего Nginx-прокси внутри Docker-сети.

Как только бэкенд совершает очередной запрос к "своему" API, он получает фейковый ответ, принимает его за чистую монету и разблокирует платные фичи.

💡 Как строить такую защиту правильно?

Для коммерческого Self-Hosted софта простой fetch() и незащищенный JSON не работают.

Ответ сервера лицензий должен быть токеном (например, JWT), подписанным приватным ключом компании. Бэкенд валидирует этот токен зашитым публичным ключом. Без приватного ключа сгенерировать подпись на фейковом сервере просто не получится.

Вдобавок внедряйте Certificate Pinning. Если приложение ходит во внешний API, валидируйте отпечаток SSL-сертификата напрямую, а не полагайтесь на системный TLS-стек, который легко глушится переменными окружения.

Разбор проведен исключительно в образовательных целях.

Всем удачного кодинга и грамотного проектирования! 🚀
❤29🔥14😱8👍2
July 8, 2026 2.1K 21 16