Основы мобильной безопасности

На телефоне «клиент» полностью в руках пользователя (и атакующего): любой секрет, зашитый в приложение, считается раскрытым.

Модель угроз мобильного приложения строится на допущении, что устройство может быть скомпрометировано (root/jailbreak), а сам APK/IPA — извлечён и разобран. Поэтому в приложении нельзя прятать секреты и нельзя доверять только клиентским проверкам. Эталон рисков — OWASP Mobile Top 10.

Мобильное приложение — это не «защищённая коробка». Файл приложения легко достать с устройства, распаковать и изучить; трафик можно перехватить; локальное хранилище на разлоченном устройстве читается напрямую. Защитнику (и разработчику) важно понять три класса проблем из OWASP Mobile Top 10 — небезопасное хранение данных, слабую защиту трафика и лёгкость реверс-инжиниринга — концептуально, без рецептов взлома чужих приложений, и закрыть их правильной архитектурой.

Зачем это знать защитнику

Разработчики часто переносят серверные привычки на клиент: «положу токен в локальное хранилище», «проверю подписку в приложении», «зашью API-ключ в код». На мобильном это не работает: всё перечисленное достаётся из приложения. Понимая это, вы проектируете так, чтобы компрометация устройства не открывала чужие данные и серверные секреты. Анализировать можно только свои приложения и легальные тренировочные APK (специальные «vulnerable apps» для обучения). Разбирать и модифицировать чужое приложение — нарушение закона (ст. 272/273 УК РФ).

Небезопасное хранение данных

Самая частая ошибка — хранить чувствительное (токены, пароли, персональные данные) в открытом виде: в SharedPreferences/UserDefaults, в обычных файлах, в логах или в локальной БД без шифрования. На разлоченном устройстве или из бэкапа это читается напрямую.

Как защититься

  • Хранить минимум. Лучший секрет на устройстве — тот, которого там нет. Не кэшируйте лишнее.
  • Использовать защищённое хранилище ОС. Android Keystore и iOS Keychain хранят ключи в аппаратно изолированной области; токены — в EncryptedSharedPreferences / Keychain, а не в обычных файлах.
  • Не писать секреты в логи. Токены и пароли не должны попадать в журналы — их видно через системные инструменты.
# Концепция: чувствительные данные — только через защищённое хранилище ОС
Android  -> Keystore + EncryptedSharedPreferences
iOS      -> Keychain (kSecAttrAccessibleWhenUnlocked)
# Что НЕ хранить в открытом виде: токены, пароли, ключи, PII

Слабая защита трафика

Если приложение ходит по HTTP или принимает любой TLS-сертификат, трафик читается и подменяется в недоверенной сети. Базовая защита — HTTPS (TLS) везде, без исключений «для отладки», доезжающих до релиза.

Certificate pinning

Pinning (закрепление сертификата) — приложение доверяет не любому валидному сертификату, а только заранее заданному (вашему серверу). Это усложняет перехват трафика через подставной сертификат.

Пиннинг — это про устойчивость к перехвату, но он не панацея: на разлоченном устройстве его можно обойти инструментами анализа. Поэтому пиннинг сочетают с принципом «сервер не доверяет клиенту»: критичные проверки и авторизация — всё равно на бэкенде.

СлабоПравильно
HTTP или «доверять всем сертификатам»HTTPS/TLS обязательно, валидация цепочки
секреты и логика «только в приложении»проверки на сервере, токены — короткоживущие
нет защиты от перехватаcertificate pinning к своему серверу

Реверс-инжиниринг приложения

Файл приложения можно распаковать и изучить: посмотреть строки, ресурсы, структуру кода. Цель атакующего — найти зашитые ключи, понять логику клиентских проверок (например, «премиум включён») и обойти их. Важно понять концептуально: код, доехавший до устройства, не является секретом.

Как защититься

  • Никаких серверных секретов в приложении. API-ключи сторонних сервисов, которые должны быть тайной, держат на своём бэкенде, а приложение ходит к нему.
  • Важные проверки — на сервере. Статус подписки, права, бизнес-правила проверяет бэкенд, а не клиент: клиентскую проверку обойдут.
  • Обфускация (R8/ProGuard и аналоги) переименовывает классы/методы и затрудняет чтение кода. Это повышает порог, но не делает код секретным — рассматривайте её как дополнительный слой, а не замену серверной защите.
  • Анти-tamper и проверка root/jailbreak могут усложнить запуск в скомпрометированной среде, но также обходятся; полагаться только на них нельзя.

Как это работает под капотом

Любое мобильное приложение — это клиент на устройстве, которое вы не контролируете. Хранилище приложения — это файлы в песочнице, доступные на разлоченном устройстве; трафик — это сетевые пакеты, видимые на пути; код — это упакованные классы и ресурсы, которые распаковываются. Поэтому «секрет в приложении» — это секрет, отданный пользователю. Вся оборона строится на двух идеях: чувствительное либо не хранят на устройстве, либо кладут в аппаратно-защищённое хранилище ОС; а доверенные решения (авторизация, секреты, бизнес-логика) принимает сервер, не клиент.

Как защититься

  • Минимизируйте данные на устройстве; чувствительное — только через Keystore/Keychain и зашифрованное хранилище, не в логах.
  • Весь трафик по HTTPS/TLS с валидацией; добавляйте certificate pinning к своему серверу.
  • Не зашивайте серверные секреты и API-ключи в приложение — храните их на бэкенде.
  • Критичные проверки (подписка, права, бизнес-правила) выполняйте на сервере; клиент к ним только обращается.
  • Обфускация и анти-tamper — полезные дополнительные слои, но не замена серверной защите; тестируйте только свои приложения.

Итоги

  • Модель угроз: устройство может быть скомпрометировано, а файл приложения — разобран; секрет в приложении считается раскрытым.
  • Три класса рисков (OWASP Mobile Top 10): небезопасное хранение, слабый трафик, лёгкий реверс.
  • Хранение — через Keystore/Keychain и шифрование; трафик — TLS + pinning; секреты и проверки — на сервере.
  • Обфускация повышает порог, но не делает клиентский код секретным.
  • Реверсить и менять можно только свои приложения и учебные APK; чужое — это ст. 272/273 УК РФ.
Проверьте себя
1. Почему API-ключ к стороннему сервису нельзя 'надёжно спрятать' внутри мобильного приложения?
AПотому что мобильные ОС запрещают хранить строки в коде
BФайл приложения можно извлечь с устройства и разобрать, поэтому зашитый секрет считается раскрытым
CПотому что ключ автоматически попадает в публичный магазин приложений
DПотому что обфускация удаляет все строковые константы при сборке
2. Где должна выполняться проверка статуса платной подписки в мобильном приложении?
AТолько в коде приложения — так быстрее и не нужен сервер
BНа сервере (бэкенде), а клиент лишь обращается за результатом
CВ обфусцированном классе — обфускация делает проверку недоступной
DВ SharedPreferences/UserDefaults на устройстве