Безпека LEX AI: GDPR-аудит, 10 виправлень і 7 рівнів захисту
5 паралельних white-hat агентів перевірили платформу на відповідність GDPR та OWASP Top 10. Знайшли 23 вразливості — від SQL-ін\
Безпека LEX AI: GDPR-аудит, 10 виправлень і 7 рівнів захисту
Юридична платформа обробляє найчутливіші дані: судові справи, контракти, персональну інформацію клієнтів. Безпека — не фіча, а фундамент. Ми провели повний security audit силами 5 паралельних AI-агентів і виправили всі критичні знахідки за одну сесію.
Ця стаття — прозорий розбір: що знайшли, що виправили, і як побудована повна архітектура захисту LEX AI.
Як проводили аудит
Замість класичного ручного пентесту ми запустили 5 спеціалізованих white-hat агентів паралельно, кожен зі своєю зоною відповідальності:
| Агент | Фокус | Файлів перевірено |
|---|---|---|
| 🔍 Data Collection | Cookie consent, трекінг, OAuth scopes | 42 |
| 💾 Data Storage | БД-схеми, retention, Redis, Qdrant, MinIO | 53 |
| 👤 User Rights | GDPR Art. 15-22 (доступ, видалення, портабельність) | 25 |
| 🛡️ OWASP Top 10 | Injection, XSS, Auth, CORS, CSRF, rate limiting | 45 |
| 🌐 Data Transfers | Third-party API, sub-processors, cross-border | 48 |
Кожен агент автономно сканував кодову базу, перевіряв відповідність стандартам і створив структурований звіт з CVSS-оцінками.
Що знайшли: 23 вразливості
Критичні (виправлені)
1. Google Ads завантажувався ДО cookie consent
index.html містив hardcoded <script> тег Google Ads, який виконувався при кожному завантаженні сторінки — до того, як React-додаток встигав показати банер cookie consent. Кожен відвідувач вже мав дані відправлені в Google, навіть якщо потім відхилив аналітику.
Виправлення: Google Ads тепер завантажується динамічно тільки після consentStore.isAllowed('analytics'). Додано Google Consent Mode v2 з denied за замовчуванням:
gtag('consent', 'default', {
analytics_storage: 'denied',
ad_storage: 'denied',
ad_user_data: 'denied',
ad_personalization: 'denied',
});
2. JWT Secret з fallback на відомий рядок
Декілька файлів містили fallback-значення для JWT-секрету. Якщо при деплої змінна оточення не встановлена — додаток тихо працював з передбачуваним секретом, що дозволяло генерувати валідні JWT для будь-якого користувача.
Виправлення: Додаток тепер крашиться при старті, якщо JWT-секрет не встановлений через змінну оточення. Fallback-значення повністю видалені.
3. SQL Injection через інтерполяцію параметрів
Кілька місць у коді використовували пряму інтерполяцію параметрів у SQL-рядки замість параметризованих запитів. У поєднанні з п.2 це створювало прямий вектор SQL Injection.
Виправлення: Всі SQL-запити переведено на параметризовані плейсхолдери ($1, $2, ...).
Високі (виправлені)
4. Конверсійний трекінг без перевірки consent — всі gtag('event', 'conversion') виклики (реєстрація, оплата, top-up) тепер перевіряють consentStore.isAllowed('analytics') перед відправкою.
5. Nginx CORS відображав будь-який Origin — SSE-ендпоінти використовували $http_origin напряму, що дозволяло будь-якому сайту робити запити з credentials. Замінено на строгий whitelist дозволених доменів.
6. XSS через dangerouslySetInnerHTML — 3 компоненти рендерили HTML з бази без санітизації. Додано DOMPurify:
dangerouslySetInnerHTML={{ __html: DOMPurify.sanitize(html) }}
7. Динамічні SQL-таблиці без whitelist — деякі функції приймали імена таблиць як параметри без валідації. Додано строгий allowlist дозволених таблиць і колонок.
8. Cleanup-функції ніколи не запускались — функції очищення застарілих даних (сесії, видалені документи, токени) існували, але не були прив'язані до cron. Додано автоматичні cron-задачі.
9. Email-адреси логувались у plaintext — 9+ місць в auth-контролерах. Додано maskEmail(): user@example.com → us***@example.com.
10. OAuth реєстрація без rate limiting — ендпоінт реєстрації OAuth-клієнтів дозволяв необмежену кількість запитів. Додано rate limiting по IP.
7 рівнів захисту LEX AI
Безпека платформи побудована за принципом defense in depth — кожен рівень компенсує можливі слабкості іншого.
Рівень 1: Cloudflare (Edge Protection)
Весь трафік проходить через Cloudflare перед тим, як досягне нашого сервера:
- DDoS Protection — автоматична фільтрація volumetric та application-layer атак
- WAF (Web Application Firewall) — захист від OWASP Top 10 на edge-рівні
- Bot Management — блокування зловмисних ботів
- Origin CA — TLS між Cloudflare і нашим origin-сервером
- Always HTTPS — примусове перенаправлення з HTTP
Рівень 2: TLS 1.3 (Transport Encryption)
- TLS 1.0/1.1 вимкнено, тільки TLS 1.2/1.3
- Тільки ECDHE-сюїти (Forward Secrecy)
- HSTS з 1-річним max-age і includeSubDomains
- SSL session cache для продуктивності без компромісів
Рівень 3: Nginx (Reverse Proxy + Security Headers)
Nginx — перший сервер, який бачить запит після Cloudflare:
| Header | Значення | Захист від |
|---|---|---|
| HSTS | max-age=31536000; includeSubDomains | Downgrade атаки |
| X-Frame-Options | SAMEORIGIN | Clickjacking |
| X-Content-Type-Options | nosniff | MIME sniffing |
| Referrer-Policy | strict-origin-when-cross-origin | Information leakage |
| CSP | Повна політика (12 директив) | XSS, injection |
Content Security Policy включає:
default-src 'self'— все заблоковано за замовчуваннямobject-src 'none'— повна блокировка плагінівbase-uri 'self'— захист від base tag injection- Whitelist тільки для: Google OAuth, Stripe, Cloudflare Insights
Рівень 4: Application Security (Express.js)
Multi-layer rate limiting — кожен тип ендпоінту (auth, chat, API, password reset) має окремі ліміти по IP або User ID. При падінні Redis rate limiting працює через in-memory fallback.
CORS — Express-рівень валідує origins незалежно від Nginx через строгий whitelist дозволених доменів.
Рівень 5: Authentication (6 методів)
LEX AI підтримує 6 методів автентифікації:
- Email + Password — bcrypt хешування, account lockout після невдалих спроб (15 хв)
- Google OAuth 2.0 — мінімальні scopes (profile + email), idToken верифікація
- WebAuthn / Passkeys — біометрична автентифікація через FIDO2, challenge TTL 5 хв
- Diia (Дія) — державна автентифікація, session TTL 10 хв
- OIDC / Authentik — SSO через Authentik
- API Keys — для MCP-клієнтів (Claude Desktop, Claude Code), database-backed з audit log
Dual Auth Middleware автоматично визначає тип токена і застосовує відповідну стратегію верифікації для кожного методу автентифікації.
Рівень 6: Database Security
- PgBouncer з SCRAM-SHA-256 автентифікацією
- Connection pooling з обмеженнями на кількість клієнтів та розмір пулу
- Statement timeout для захисту від DOS через повільні запити
- Docker bridge network ізолює БД від зовнішнього доступу
- Parameterized queries скрізь (PostgreSQL плейсхолдери)
Рівень 7: Data Protection (GDPR)
Реалізовані права:
- Art. 15 (Доступ) — повний JSON-експорт всіх даних користувача
- Art. 17 (Видалення) — каскадне видалення з усіх сховищ даних, анонімізація трекінгу
- Art. 20 (Портабельність) — машинно-зчитуваний JSON формат
Cookie Consent:
- 4 категорії: essential (завжди), functional, analytics, marketing
- Дефолт: всі non-essential вимкнені (privacy by default)
- Версіонування consent (v1.0)
E2EE для документів:
- AES-256-GCM шифрування
- X25519 ECDH key exchange (envelope encryption)
- Зашифровані документи недоступні для AI-аналізу (by design)
Автоматичне очищення — регулярне видалення застарілих сесій, soft-deleted документів та OAuth токенів за налаштованими інтервалами.
Що залишається зробити
Аудит виявив і речі, які потребують більше часу:
| Задача | Пріоритет |
|---|---|
| Зберігати consent реєстрації на сервері (зараз тільки UI) | High |
| Передавати consent через OAuth redirect flow | High |
| Реалізувати Art. 18 (обмеження обробки) | Medium |
| Реалізувати Art. 21 (право заперечити) | Medium |
| Оновити Privacy Policy щодо Google Ads | Medium |
| Додати Google Cloud Vision до DPA як sub-processor | Medium |
| Column-level encryption для PII полів | Medium |
| Nonce-based CSP замість unsafe-inline | Low |
Висновки
- AI-агенти для security audit — 5 паралельних агентів покрили більше поверхні атаки за 3 хвилини, ніж ручний review за день
- Defense in depth працює — жодна окрема вразливість не давала повний доступ до системи завдяки багаторівневій архітектурі
- GDPR — це код, не документ — права користувачів мають бути реалізовані в коді (export, delete, consent), а не тільки описані в Privacy Policy
- Прозорість будує довіру — ми публікуємо результати аудиту, бо вважаємо, що юридична платформа має бути відкритою щодо своєї безпеки
Весь код виправлень доступний у PR #1224.
Реєстрація: legal.org.ua