Безопасность
Pug Injection Protection
Проблема (исправлена): Pug-код рендерится на сервере через Node.js. Вредоносный Pug мог выполнить произвольный код:
div(data-calc=process.cwd()) test
- let x = require('child_process').execSync('ls -la').toString()
Решение: В email-gen-api/server.js добавлена функция validatePugSafety(), которая проверяет Pug перед рендером. Запрещённые паттерны:
| Паттерн | Что блокирует |
|---|---|
require( |
Подключение модулей |
process |
Доступ к process |
child_process |
Запуск процессов |
exec, execSync, spawn |
Выполнение команд |
eval(, Function( |
Динамическое выполнение |
global |
Глобальный объект |
__dirname, __filename |
Пути файловой системы |
fs. |
Файловые операции |
Buffer |
Работа с буферами |
При обнаружении возвращается 400 Bad Request с описанием найденного паттерна.
Дополнительная защита: email-gen-api работает в Docker-контейнере без привилегий и без доступа к хостовой файловой системе (кроме email-gen volume).
Конкурентность рендера
Проблема (исправлена): renderWithNode() писал результат в общий файл public/index.html. При одновременных запросах один пользователь мог получить HTML другого.
Решение: Каждый запрос генерирует уникальное имя файла через crypto.randomBytes(16):
render_a1b2c3d4e5f6.html
Файл создаётся, читается, удаляется — атомарно для каждого запроса.
Авторизация
- Хэширование: scrypt (salt 16 bytes + hash 64 bytes)
- Сессии: Cookie
z51_token, HttpOnly, SameSite=Lax, TTL 7 дней - Очистка: Просроченные сессии удаляются каждые 30 минут
- Middleware: Все
/api/endpoints (кроме/api/auth/*) требуют авторизацию - Admin:
/api/admin/*доступен только пользователям сrole: admin - Проекты:
userCanAccessProject()проверяетuser.projects(массив или["*"])
Аудит-логи
Формат: JSONL файлы data/_system/logs/YYYY-MM.jsonl, одна строка = одно действие:
{"ts":1712345678000,"userId":"abc123","login":"admin","action":"letter","project":"Реаспект","details":{},"ip":"172.17.0.1"}
Что логируется:
- Все успешные мутации (POST/PUT/DELETE) — автоматически через обёртку
send() - Логин (успешный и неудачный) — явный вызов
- Логаут — явный вызов
Фильтрация: Admin UI (Настройки → Логи) с фильтрами по пользователю, проекту, действию, пагинация.
Ротация: Файлы старше 6 месяцев удаляются при старте сервера.
Сетевая безопасность
- Docker порты проброшены только на
127.0.0.1(не наружу) - Nginx — единственная точка входа (80/443)
- HTTP → HTTPS редирект
- TLS 1.2+, strong ciphers
client_max_body_size 30m(для GIF-загрузок)
Ограничения
- Сессии хранятся в памяти Node.js — теряются при перезапуске контейнера (пользователи перелогинятся)
MAX_BODY_SIZE = 30 MB— достаточно для GIF, но ограничивает загрузку очень больших файлов