Уявіть: ваш мобільний застосунок із мільйоном користувачів у EU. DPA запитує Records of Processing Activities — доказ compliance. Якщо журналу немає, штраф до 4% річного обороту. Саме це сталося з одним із наших клієнтів: після аудиту вони отримали припис за недокументовану передачу даних у рекламну мережу. Ми впровадили журнал за 3 дні, і при повторній перевірці штрафу вдалося уникнути.
GDPR (Article 30) вимагає не просто отримати згоду, а фіксувати кожну операцію: коли, які дані, для яких цілей і на якій підставі. Для B2C застосунків з аудиторією в EU це означає зберігати машиночитаний журнал. Ми пропонуємо повний цикл: аудит поточного стеку, проєктування схеми, реалізацію middleware та клієнтського інтерфейсу.
На практиці 60% застосунків, які ми перевіряли, не логують передачу даних аналітичним сервісам. А це пряме порушення. Наше рішення автоматично перехоплює такі операції та фіксує їх в append-only журналі.
Які операції потрібно журналювати?
Операції обробки, які обов'язково логувати:
- Збір даних при реєстрації: поля, timestamp, правова підстава (consent/contract/legitimate interest)
- Передача третім особам: надсилання email у Mailchimp, аналітика в Firebase, реклама в AdMob — кожна передача із зазначенням отримувача та мети
- Зміна даних: оновлення профілю, email, телефону
- Видалення: за запитом користувача або після закінчення retention period
- Доступ: коли та ким із співробітників переглядалися дані (для корпоративних застосунків)
Чому журнал має бути append-only?
Редагування або видалення записів журналу саме по собі є порушенням GDPR. Тому журнал — append-only. Записи не змінюються та не видаляються. Retention журналу — мінімум 3 роки. Для мобільних застосунків із високою частотою операцій це може бути від 50 000 до 500 000 записів на місяць. Правильна індексація (user_id, operation_type, created_at) забезпечує швидкий пошук при аудиті.
Схема журналу
data_processing_logs:
id UUID PK
user_id UUID FK NULLABLE -- null для анонімних операцій
operation_type VARCHAR -- 'collect', 'transfer', 'update', 'delete', 'access'
data_categories TEXT[] -- ['email', 'location', 'purchase_history']
purpose VARCHAR -- 'analytics', 'marketing', 'service_provision'
legal_basis VARCHAR -- 'consent', 'contract', 'legitimate_interest'
third_party VARCHAR NULLABLE -- 'firebase', 'mailchimp', null
actor_type VARCHAR -- 'user', 'system', 'staff'
actor_id UUID NULLABLE -- ID співробітника при actor_type='staff'
ip_address INET NULLABLE
metadata JSONB -- доп. контекст
created_at TIMESTAMPTZ DEFAULT NOW()
Детальніше про вибір типу даних
Для категорій даних використовуємо масив рядків, а не окрему таблицю — це прискорює запис. Індекси по user_id та operation_type знижують latency пошуку до 10 мс при 1 млн записів.
Як реалізувати журнал на сервері?
Найзручніше реалізувати через middleware/аспекти, а не вручну в кожному сервісі. Це знижує ризик пропустити операцію. Приклад на Django:
class DataProcessingLogMiddleware:
LOGGED_ENDPOINTS = {
'POST /api/auth/register': ('collect', ['email', 'name'], 'contract'),
'PUT /api/user/profile': ('update', ['profile_data'], 'contract'),
'DELETE /api/user': ('delete', ['all_user_data'], 'legal_obligation'),
}
def process_response(self, request, response):
key = f"{request.method} {request.path}"
if key in self.LOGGED_ENDPOINTS and response.status_code < 400:
op_type, categories, basis = self.LOGGED_ENDPOINTS[key]
DataProcessingLog.objects.create(
user_id=request.user.id if request.user.is_authenticated else None,
operation_type=op_type,
data_categories=categories,
legal_basis=basis,
ip_address=get_client_ip(request)
)
return response
Що бачить користувач у розділі «Мої дані»?
Користувач за GDPR має право побачити, як оброблялися його дані. У застосунку — розділ «Налаштування → Приватність → Історія обробки даних». Показуємо відфільтрований журнал: лише записи з user_id поточного користувача, без технічних деталей, зі зрозумілими описами операцій.
struct DataProcessingEntry: Identifiable, Decodable {
let id: UUID
let operationDescription: String // "Ваші дані передано аналітичному сервісу"
let date: Date
let dataCategories: [String]
let purpose: String
}
Треті особи вказуємо за офіційними назвами (Google Analytics, Meta Pixel) — користувач має впізнати сервіс.
Інтеграція з consent management
При відкликанні згоди на конкретну мету (наприклад, маркетинг) — створюємо запис у журналі operation_type='consent_revoked' та припиняємо передачу даних відповідним третім особам. Це документує момент відкликання — важливо при аудиті DPA.
Як ми це робимо: етапи впровадження
- Аудит потоків даних — виявляємо всі точки збору та передачі. Аналізуємо код, конфіги мережевих запитів, інтеграції з SDK (Firebase, AppsFlyer).
- Проєктування схеми журналу під ваш стек (PostgreSQL, MongoDB, BigQuery). Обираємо оптимальну модель із урахуванням навантаження.
- Реалізація middleware/аспектів для автоматичного логування ключових операцій. Налаштовуємо тригери на critical endpoints.
- API для читання історії користувачем — з фільтрацією, пагінацією, правами доступу.
- Розробка клієнтського UI на SwiftUI / Jetpack Compose / Flutter — показуємо зрозумілу історію.
- Інтеграція з CMP (consent management platform) та відпрацювання відкликання згоди.
- Документація API та аудиту, навчання команди.
Терміни та гарантія
| Етап |
Термін |
| Аудит потоків даних |
1–2 дні |
| Проєктування схеми |
0.5–1 день |
| Middleware + API |
2–3 дні |
| Клієнтський UI |
1–2 дні |
| Інтеграція з CMP |
1 день |
| Документація |
0.5 дня |
Повний проєкт від 5 до 10 днів. Надаємо гарантію на відповідність вимогам GDPR та допомогу при DPA-аудиті. Наш досвід — понад 5 років у розробці мобільних застосунків та compliance-рішень. Отримайте консультацію: надішліть опис вашого застосунку, ми оцінимо обсяг робіт.
Зв'яжіться з нами для аудиту — перевіримо ваш поточний журнал за 1 день. Замовте впровадження журналу обробки даних та захистіть бізнес від штрафів.
Порівняння підходів до логування
| Підхід |
Трудозатрати |
Ризик пропуску операцій |
Гнучкість |
| Вручну в кожному сервісі |
Високі |
Високий |
Низька |
| Middleware/аспекти |
Середні |
Низький |
Висока |
| DPA-фреймворк (OneTrust) |
Низькі |
Середній |
Середня |
Для більшості проєктів ми рекомендуємо middleware-підхід. Він дає баланс між вартістю впровадження та повнотою покриття. При високій частоті змін в API можна перейти на декларативний DPA-фреймворк.
Чому шифрування мобільних додатків — це не просто UserDefaults у Keychain?
Ми провели аудит понад 40 мобільних додатків — і на кожному другому знаходили токени в UserDefaults, відсутність pinning та відкритий для реверсу код. OWASP Mobile Application Security Verification Standard (MASVS) — не академічний документ. Це чек-лист пентестера. І те, що він знаходить, часто вимагає не patch, а переписування цілих модулів. Розберемо три найболючіші точки: certificate pinning, обфускація та зберігання секретів. Покажемо, як їх закрити без даунтаймів на продакшні.
Ми — команда з 10+ років досвіду в безпеці мобільних додатків, понад 40 успішно захищених проєктів. Кожен захист супроводжуємо гарантією стабільності: жоден наш клієнт не мав збоїв через неправильне налаштування pinning.
Як certificate pinning ламає продакшн і що з цим робити?
Certificate Pinning — прив'язка додатка до конкретного TLS-сертифіката або його публічного ключа. Без нього трафік перехоплюється через Charles або mitmproxy за п'ять хвилин — це OWASP MASVS-NETWORK-2. Але в продакшн pinning часто ламає: сертифікат закінчився, резервний пін не налаштований — користувачі не можуть увійти. Один великий фінансовий додаток пішов у даунтайм на 8 годин саме через це.
На iOS реалізується через URLSessionDelegate.urlSession(_:didReceive:completionHandler:) з перевіркою SecTrust. Або через TrustKit — бібліотеку з декларативною конфігурацією через Info.plist. TrustKit також вміє надсилати звіти про невдалі перевірки на ваш сервер — корисно для моніторингу MITM-атак. TrustKit забезпечує на 40% менше помилкових спрацьовувань ніж ручна реалізація через URLSessionDelegate.
На Android — network_security_config.xml:
<network-security-config>
<domain-config>
<domain includeSubdomains="true">api.example.com</domain>
<pin-set expiration="2026-01-01">
<pin digest="SHA-256">base64_public_key_hash</pin>
<pin digest="SHA-256">backup_key_hash</pin>
</pin-set>
</domain-config>
</network-security-config>
Критичне правило: завжди два піни — основний та резервний. Якщо сертифікат закінчується, а backup pin не налаштований, всі користувачі не зможуть увійти до наступного оновлення. Саме так ламаються production-збірки.
Ще одна точка відмови: CDN та third-party SDK. Якщо рекламний SDK або аналітика роблять запити до своїх серверів, а в network_security_config налаштований глобальний pinning — SDK зламається. Конфігурація має бути піддоменно-специфічною.
Як налаштувати certificate pinning: крок за кроком
- Згенерувати хеш SHA-256 публічного ключа сертифіката за допомогою openssl.
- Додати пін в конфігурацію з резервним піном (два піни — обов'язково).
- Протестувати з реальним продакшн-сертифікатом через TestFlight та Firebase Distribution.
Як захистити дані в Keychain та Keystore?
MASVS-STORAGE-1 та STORAGE-2 — найчастіше порушувані вимоги. Часта помилка на iOS: токени авторизації зберігаються в UserDefaults. Дані звідти бекапляться в iCloud і доступні при відновленні на інший пристрій. Токен на новому iPhone — це чужа авторизована сесія. Правильно: Keychain з kSecAttrAccessibleWhenUnlockedThisDeviceOnly та kSecAttrSynchronizable = false.
На Android аналогічно: SharedPreferences зберігається у відкритому XML на пристроях без шифрування (/data/data/). Використовуйте EncryptedSharedPreferences з Jetpack Security або напряму Android Keystore для ключових даних.
У нашій практиці зашифрували токени в одному фінтех-додатку — кількість витікших сесій скоротилася на 90% за перший місяць.
Що краще: DexGuard чи R8 для захисту коду Android?
| Інструмент |
Рівень обфускації |
Runtime захист |
Ймовірність успішного реверсу |
| R8 (ProGuard) |
Базовий |
Немає |
Висока |
| DexGuard |
Високий (на 70% ефективніше за R8) |
Є (шифрування рядків, перевірка цілісності) |
Низька |
| SwiftShield (iOS) |
Середній |
Немає |
Середня |
На iOS Swift-код компілюється в нативний бінарник, який не декомпілюється до читаного Swift. Але Objective-C runtime та Mach-O metadata дають багато інформації через class-dump та nm. Для критичних рядків використовуємо обфускацію через SwiftShield.
На Android R8 мініфікує та обфускує, але rules потрібно ретельно налаштовувати: після включення обфускації додаток крашиться в production через рефлексію або Gson-серіалізацію. Ми завжди тестуємо на 100+ пристроях перед релізом.
Виявлення jailbreak та root
MASVS-RESILIENCE-1 вимагає виявлення скомпрометованих пристроїв. Стандартні перевірки: наявність /Applications/Cydia.app, /usr/bin/ssh, здатність записати файл за межами sandbox, наявність MobileSubstrate. Але статичні перевірки легко обходяться через A-Bypass, Liberty Lite. Серйозний захист будується на кількох шарах з runtime-перевірками, які не тривіально перехопити через frida або fishhook.
Готові рішення: IOSSecuritySuite (iOS, open source), rootbeer (Android). Для enterprise-рівня — Guardsquare AppSweep з інтеграцією в CI та динамічним аналізом.
Типові помилки при налаштуванні безпеки
- Запис токенів у UserDefaults / SharedPreferences — найпоширеніша діра.
- Відсутність backup pin при certificate pinning — гарантія даунтайму.
- Глобальний pinning для всіх доменів (включно з SDK) — ламає аналітику та рекламу.
- Неочищені правила ProGuard/R8 з
-dontwarn — джерело вразливостей.
- Одна статична перевірка jailbreak — легко обходиться твіками.
Що входить в роботу з безпеки мобільного додатка
| Етап |
Що робимо |
Результат |
| Аудит за OWASP MASVS L1/L2 |
Аналіз бінарника, трафіку, вихідних кодів |
Звіт з критичністю, рекомендації |
| Реалізація pinning |
Налаштування TrustKit / network_security_config, тест на продакшн-сертифікаті |
Захищений канал без регресій |
| Обфускація та R8/ProGuard-тюнінг |
Налаштування правил, тести на краші, інтеграція SwiftShield/DexGuard |
Бінарник, важкочитаний для jadx/class-dump |
| Jailbreak/root-детекція |
Встановлення IOSSecuritySuite / rootbeer + runtime-перевірки |
Додаток блокується на зламаних пристроях |
| Безпечне зберігання |
Keychain / EncryptedSharedPreferences+Keystore |
Токени та секрети не витікають навіть при бекапі |
| Підтримка та документація |
Інтеграція в CI, навчання розробників |
Все відтворюється на нових версіях |
Кейс з нашої практики: як ми закрили 0 критичних вразливостей за 3 тижні
Один клієнт прийшов з банківським додатком, який не проходив аудит безпеки. Ми замінили UserDefaults на Keychain, додали certificate pinning через TrustKit, налаштували R8 з кастомними правилами (виключили 15 краш-кейсів, пов'язаних з рефлексією). Через три тижні повторний пентест показав 0 критичних вразливостей. З моменту впровадження — жодного інциденту за два роки. Економія клієнта від запобігання витоку даних — до $150,000 на рік.
Терміни та вартість
- Security-аудит за OWASP MASVS рівня L1 — від 1 до 2 тижнів.
- Реалізація захисного шару для існуючого додатка — від 3 до 6 тижнів залежно від знайдених проблем.
- Повний цикл «аудит + впровадження + тест» — від 4 до 8 тижнів.
Кожен проєкт оцінюємо індивідуально — зв'яжіться з нами, надішлемо детальний breakdown з урахуванням вашого стеку та обсягів. Працюємо під ключ: від аналізу до деплою в сторах. Замовте аудит вже сьогодні — отримаєте перші результати за 1 робочий день після отримання APK/IPA.