Вступление


При разработке мобильного приложения следует учитывать, что данные, которыми оперирует это приложение, могут представлять определенный интерес для третьих лиц. Степень ценности этих данных варьируется в широких пределах, тем не менее, даже наиболее простая приватная информация, например, пароль входа в приложение, требует проработки ее защиты. Особенно это важно в свете распространения мобильных приложений на все сферы электронных услуг, включая финансовые, банковские операции, хранение и передачу личных данных и так далее. Всем интересующимся — добро пожаловать под кат.


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


Защита мобильного приложения


Основные виды атак на мобильное приложение:


  • Декомпиляция файла приложения (.ipa-файлы для Apple iOS и .apk-файлы для Google Android) и разбор локально сохраненных данных. Защита этого, наиболее важного в настоящее время, уровня целиком лежит на плечах мобильного разработчика.
  • Перехват данных, передаваемых по сети (MITM-атаки). Большинство мобильных приложений являются клиент-серверными, следовательно, постоянно передают и принимают большие объемы информации. И хотя современная мобильная и веб-разработка активно завершают переход на HTTPS-протокол общения, тем не менее, не стоит полагаться на единственный рубеж защиты в виде защищенного канала связи.
  • Рутование устройства и атака на приложение и применяемые в нем алгоритмы через внешние отладочные инструменты.

Перечень основных уязвимостей приложений


Рассмотрим уязвимости общего характера, без привязки к конкретной платформе. Здесь и далее используется аббревиатура КВД — критически важные данные пользователей. К КВД относятся любые данные, которые не должны быть доступны третьей стороне, это касается как персональных данных пользователя (дата рождения, адрес проживания, личная переписка), так и его приватных данных (пароли, данные кредитных карт, номера банковских счетов, номера заказов и так далее).


Перечень основных уязвимостей следующий:


  • Использование незащищенных локальных хранилищ.


    • Опасность: Очень высокая.
    • Комментарий: Встречается повсеместно, выражается в хранении КВД в незащищенных или слабо защищенных локальных хранилищах, специфических для конкретной платформы. Вскрытие третьей стороной — элементарное, и, как правило, не требуется наличие специальных навыков у атакующего.
    • Защита: Хранить КВД можно только в защищенных хранилищах платформы.

  • Хранение КВД в коде.


    • Опасность: Высокая.
    • Комментарий: Уязвимость касается хранения КВД внутри кода (в статических константных строках, в ресурсах приложения и т.п.). Яркие примеры: хранение соли для пароля (password salt) в константе или макросе, которая применяется по всему коду для шифрования паролей; хранение приватного ключа для асимметричных алгоритмов; хранение паролей и логинов для серверных узлов или баз данных. Легко вскрывается третьей стороной при наличии базовых навыков декомпиляции.
    • Защита: Не хранить никакие КВД в коде или ресурсах приложения.

  • Применение алгоритмов с хранением приватного ключа.


    • Опасность: Высокая.
    • Комментарий: Уязвимость актуальна в случае, если приватная информация алгоритма (приватный ключ) вынужденно сохраняется в коде или ресурсах мобильного приложения (чаще всего так и бывает). Легко вскрывается методом декомпиляции.
    • Защита: В мобильной разработке желательно применять только современные симметричные алгоритмы с генерируемым случайным одноразовым ключом, обладающие высокой стойкостью с взлому методом грубой силы, либо выводить асимметричный приватный ключ за пределы приложения, либо персонализировать этот ключ (как пример — приватным ключом может выступать пользовательский код входа, сохраненный в зашифрованном виде в защищенном хранилище операционной системы).

  • Использование асимметричного алгоритма с приватным ключом, известным серверу.


    • Опасность: Зависит от степени защищенности сервера.
    • Комментарий: Уязвимость носит двойной характер. Хранение приватного ключа допускает возможность расшифровки пользовательских данных на стороне сервера. Во-первых, это некорректно с точки зрения безопасности (если сервер будет взломан — атакующий также получит доступ к приватным данным пользователей), а во-вторых, это нарушает приватность персональных данных. Пользователь всегда должен быть уверен, что его персональная информация не известна никому, кроме него самого (только если он явно не дал разрешение на ее публикацию). Часто приложения позиционируют себя как защищенные, но на деле таковыми не являются, так как содержат внутри себя средства для расшифровки персональной информации.
    • Защита: Без явной необходимости и явного разрешения пользователя (чаще всего через лицензионное соглашение) ни приложение, ни сервер не должны иметь никакой возможности расшифровать приватные данные пользователя. Простейший пример — пароль пользователя должен уходить на сервер уже в виде хеша, и проверяться должен хеш, а не исходный пароль (серверу абсолютно незачем знать пользовательский пароль; если же пользователь его забыл — для такой ситуации существует давно отлаженный механизм восстановления пароля, в том числе с двухфакторной авторизацией клиента для повышенной безопасности процедуры восстановления).

  • Использование самописных алгоритмов шифрования и защиты.


    • Опасность: Средняя.
    • Комментарий: Это прямое нарушение принципа Керкгоффса. Выражается в попытке разработчика изобрести "свой личный, не известный никому, а поэтому супер-защищенный алгоритм шифрования". Любое отклонение от существующих, многократно проверенных и изученных, математически доказанных алгоритмов шифрования в 99% случаев оборачивается быстрым взломом подобной "защиты". Требует наличия средне-высоких навыков у атакующего.
    • Защита: Следует подбирать подходящий алгоритм только из отлаженных и актуальных общеизвестных криптографических алгоритмов.

  • Передача КВД во внешнюю среду в открытом виде.


    • Опасность: Средняя.
    • Комментарий: Выражается в передаче КВД без применения шифрования по любому доступному каналу связи с внешней средой, будь то передача данных стороннему приложению или передача в сеть. Может быть вскрыто опосредованно путем вскрытия не приложения, а его хранилища, или целевого приложения. Взлом требователен к наличию навыков у атакующего, при условии, что хранилище является защищенным.
    • Защита: Любые КВД перед выходом за пределы приложения должны быть зашифрованы. Локальные хранилища платформы не являются областью приложения, они тоже должны получать на вход только зашифрованные данные.

  • Игнорирование факта наличия рутованных или зараженных устройств.


    • Опасность: Средняя.
    • Комментарий: Рутованные устройства — это девайсы, где выполнена модификация для получения прав суперпользователя на любые операции, изначально запрещенные производителем операционной системы. Выполняется пользователем на своем устройстве самостоятельно, и не обязательно добровольно (клиент может быть не в курсе, что устройство взломано). Установка приложения на рутованный девайс нивелирует все штатные средства защиты операционной системы.
    • Защита: Если это технически возможно для платформы — то желательно запрещать работу приложения, если удалось понять, что запуск производится на рутованном устройстве, или хотя бы предупреждать об этом пользователя (спасибо за дополнение DjPhoeniX).

  • Хранение КВД в защищенных хранилищах, но в открытом виде.


    • Опасность: Средняя.
    • Комментарий: Разработчики зачастую склонны сохранять КВД в защищенные системные хранилища без дополнительной защиты, поскольку системные механизмы хорошо сопротивляются взлому. Однако уровень их стойкости падает до минимума в случае, если устройство рутованное.
    • Защита: КВД не должны использоваться в приложении без дополнительного шифрования. Как только надобность в "открытых" КВД отпала — они немедленно должны быть либо зашифрованы, либо уничтожены.

  • Перевод части функционала во встроенные веб-движки.


    • Опасность: Средняя.
    • Комментарий: Чаще всего выглядит как передача КВД во встроенный браузер, где загружается внешняя веб-страница, выполняющая свою часть функционала. Уровень защиты в этом случае резко снижается, особенно для рутованных устройств.
    • Защита: Не использовать встроенный браузер и встроенный веб-движок в операциях с КВД. На крайний случай — шифровать КВД перед передачей.

  • Реверсивная инженерия алгоритмов, представляющих интеллектуальную ценность.


    • Опасность: Низкая, зависит от ценности алгоритма.
    • Комментарий: Если при разработке приложения внутри компании используются некие собственные алгоритмы, которые могут представлять высокую ценность для потенциальных конкурентов или взломщиков, то эти алгоритмы должны быть защищены от постороннего доступа.
    • Защита: Автоматическая или ручная обфускация кода.


Специфика разработки мобильных приложений


Есть несколько общих для всех мобильных платформ моментов, которые следует соблюдать при разработке.


Защита пользовательским кодом


  • Если приложение защищено пользовательским паролем (PIN-кодом, сканом отпечатка пальца, графическим паролем и т.д.), то при уходе приложения в фон ("сворачивании") оно должно немедленно отображать окно ввода этого защитного кода, перекрывая собой весь экран приложения. Это исключает возможность для злоумышленника получить приватную информацию в случае кражи устройства, пока приложение все еще запущено и находится в спящем режиме.
  • Любой пользовательский код должен иметь ограниченное количество попыток ввода (например, 5 раз), затем, в случае неудачи, приложение должно автоматически разлогиниваться (или и вовсе блокироваться, зависит от конкретного приложения).
  • В настоящее время при использовании цифровых кодов строго рекомендуется использовать ограничение на длину кода в минимум 6 цифр (больше можно, меньше — нельзя).

Функционирование клиент-серверного приложения


  • Для клиент-серверных приложений очень полезно применять сессионный механизм с ограниченным временем жизни сессии. Это позволит избежать "простаивания" приложения в незащищенном режиме, если пользователь просто забыл закрыть его и оставил устройство в свободном доступе. Следует учитывать, что срок действия сессии и ее идентификатор относятся к КВД, со всеми вытекающими отсюда последствиями. Одним из удачных примеров реализации подобного механизма является получение абсолютного значения времени с сервера после прохождения процедуры авторизации пользователя (дата и время должны показывать, когда именно сессия станет неактивной). Дату и время окончания действия сессии не следует генерировать на устройстве, это снижает безопасность и гибкость приложения.
  • Клиент-серверное приложение не должно осуществлять изменение КВД в локальном режиме. Любое действие, требующее изменения КВД, должно проходить синхронизацию с сервером. Исключение из этого правила составляет только пользовательский код входа, задаваемый лично пользователем и сохраненный в защищенном локальном хранилище.

Работа с датами


  • При оперировании важными для работы приложения датами, вроде времени уничтожения сессии, не следует опираться на относительное время. То есть, передаваемые с сервера данные не должны содержать дату в виде "плюс N секунд/часов/дней от текущего момента". В силу наличия потенциально высоких задержек в передаче данных по сети от мобильного приложения к серверу и обратно, подобный способ синхронизации будет обладать слишком большой погрешностью. Кроме того, атакующий (или просто недобросовестный пользователь) может попросту сменить локальный пояс на устройстве, нарушив таким образом логику работы ограничительных механизмов приложения. Всегда нужно передавать только абсолютное значение времени.
  • Абсолютные значения следует передавать с применением универсальных способов обмена подобной информацией, без привязки к часовому поясу конкретного пользовательского устройства. Чаще всего, оптимальным вариантом является поведение приложения, при котором данные отображаются пользователю в его локальном часовом поясе, но их хранение и передача осуществляется в формате, не привязанном к тайм-зоне. Подходящими форматами для дат и времени являются либо универсальный UNIX timestamp, сохраненный в переменной 64-битного целого знакового типа (UNIX timestamp — это количество секунд, прошедшее с 1 января 1970 года), либо, на крайний случай, строка в полном формате ISO-8601 с нулевой тайм-зоной. Предпочтителен именно UNIX timestamp, он позволяет избежать потенциальных ошибок и проблем с конвертацией строк в дату и обратно на разных мобильных платформах.

Дополнительные рекомендации


  • Приложение не должно отображать приватную пользовательскую информацию большими, яркими, хорошо читаемыми шрифтами, без явной на то необходимости и без отдельного запроса пользователя, чтобы исключить возможность чтения этих данных издали с экрана устройства.
  • Не стоит слепо доверять библиотекам с открытым исходным кодом, которые предлагают некую защиту приватным данным пользователей. Исключение составляют библиотеки, проверенные временем и используемые в крупных проектах корпораций (например, встроенное шифрование в открытом движке базы данных Realm). Штатных механизмов защиты операционной системы и общедоступных проверенных криптографических алгоритмов в подавляющем большинстве случаев будет более, чем достаточно.
  • Абсолютно недопустимо использовать криптографические библиотеки с закрытым исходным кодом (даже если они платные). В таких решениях вы никак не сможете проверить, насколько эффективна данная библиотека, а также насколько "честная" у нее защита (нет ли там механизма backdoor, или не отсылаются ли "защищенные" данные какой-то третьей стороне).
  • В релизных сборках приложений должно быть отключено логгирование данных в системную консоль и незащищенные файлы. Специфические логи для разработчиков могут присутствовать, но желательно в зашифрованном виде, во избежание доступа третьих лиц к закрытой служебной информации, которая может присутствовать в логах.

Специфическая информация по платформе iOS


Рассмотрим доступные для разработчика хранилища данных при разработке под iOS:


  • NSUserDefaults.


    • Штатная защищенность: Отсутствует.
    • Комментарий: Используется только для хранения безопасных для функционирования приложения данных, не связанных с приватной информацией. Чаще всего туда записывают только клиентские настройки интерфейса. Для других данных это хранилище не подходит, так как представляет собой обычный файл и вскрывается за несколько секунд.

  • Бинарные файлы (NSKeyedArchiver).


    • Штатная защищенность: Средняя, если пользоваться атрибутом NSFileProtectionComplete по ключу NSFileProtectionKey, иначе — отсутствует (спасибо за дополнение agee).
    • Комментарий: Сами по себе являются физическими файлами и могут быть элементарно доступны третьей стороне. Уровень защиты зависит исключительно от примененных к данным алгоритмов шифрования. Это не самое удобное место для хранения данных, поэтому без особой необходимости (чаще всего это возможность передавать эти данные, к примеру, по электронной почте) лучше подобный способ не применять. Если файл создан со значением атрибута NSFileProtectionComplete для ключа NSFileProtectionKey, то операционная система удерживает этот файл в зашифрованном состоянии, пока устройство заблокировано или находится в состоянии загрузки, но при несоблюдении этих условий данные будут доступны на чтение и запись, как в обычном файле, так что это лишь временная мера с узкой областью применения.

  • Базы данных.


    • Штатная защищенность: Зависит от движка и разработчика.
    • Комментарий: Современные движки БД поддерживают внутреннее шифрование данных. Разработчику понадобится найти баланс между шифрованием и производительностью, включить шифрование базы для критических данных, а также дополнительно применять шифрование для КВД перед записью в базу.

  • Связка ключей (Keychain).


    • Штатная защищенность: Максимальная (не распространяется на устройства с джейлбрейком).
    • Комментарий: Наиболее удачное место для хранения любых КВД. Однако, учитывая, что устройство может быть рутованным, все КВД должны быть зашифрованы дополнительно перед сохранением в Keychain. Кроме того, нужно с осторожностью использовать облачную синхронизацию из-за возможности автоматического сохранения Keychain в облаке. В случае, если приложение использует CloudKit, необходимо внимательно следить за данными, которые не должны синхронизироваться (если таковые имеются), и исключать их из набора копируемых данных.


Специфическая информация по платформе Android


Я слабо разбираюсь в платформе Android, поэтому нижеизложенный список — это краткое тезисное изложение базовых материалов, которые мне удалось найти по этой платформе:


  • Наиболее удачным вариантом пользовательского кода является графический код, цифровой (6 цифр или более) — как дополнительный вариант.
  • Запрос разрешений (permissions) на определенные виды активности приложения обязателен и должен выполняться явно для пользователя с разъяснением, для чего именно будет использовано разрешение. Запрашивать определенное разрешение нужно только в случае прямой необходимости его использования, также, запрос на разрешение нужно показывать именно в тот момент, когда оно понадобилось, не ранее. Кроме того, если целевая версия Android SDK равна 23 (или новее) — то следует проводить запрос разрешений только через систему разрешений совместимости (Compatibility Permissions System).
  • HTTP-клиент должен быть настроен на принудительное использование защищенного канала связи (HTTPS).
  • В релизной сборке приложения должны быть отключены все отладочные функции, например, опция debuggable, во избежание возможности подключения к программе внешним отладочным приложением.
  • На экранах приложения, где размещается приватная информация пользователя, будет не лишним принудительный запрет на программное создание скриншотов окна приложения, а также отключение показа скриншотов в диспетчере задач.
  • Любая приватная информация может дополнительно предваряться запросом личного ключа пользователя, заданного им для входа в приложение (если таковой имеется).
  • Внутри кода приложения для резолвинга путей рекомендуется пользоваться getCanonicalPath вместо getAbsolutePath.
  • Используемые в приложении открытые компоненты (например, Service или Content Provider) должны быть обязательно закрыты с помощью флага exported = false в манифесте приложения (Android Manifest). Это позволит запретить доступ к этим компонентам из другого приложения.

Кроме того, нужно с осторожностью использовать доступные хранилища информации:


  • Shared Preferences.


    • Штатная защищенность: Отсутствует.
    • Комментарий: Должно использоваться только по прямому назначению, а именно — для хранения общедоступных незащищенных пользовательских настроек. Остальные данные здесь размещаться не должны.

  • Базы данных (SQLite).


    • Штатная защищенность: Зависит от разработчика.
    • Комментарий: Представляет собой обыкновенные файлы, так что все КВД перед записью должны быть соответствующим образом зашифрованы. База допускает возможность автоматического шифрования, его включение будет хорошим усилением защиты данных. В качестве ключа шифрования наиболее целесообразно применять код защиты, задаваемый лично пользователем приложения (код, в свою очередь, должен быть зашифрован и сохранен в Account Manager, см. описание ниже).

  • Account Manager.


    • Штатная защищенность: Высокая (только до API версии 18).
    • Комментарий: Здесь следует размещать все КВД, но предварительно обязательно нужно выполнить дополнительное шифрование данных. После API 18 использование не рекомендуется.

  • KeyStore.


    • Штатная защищенность: Максимальная (не распространяется на рутованные устройства, доступна с API 18).
    • Комментарий: Аналогично Account Manager, но настоятельно рекомендуется пользоваться этим хранилищем вместо него, в случае, если KeyStore доступно разработчику (API v.18, Android 4.3 и новее).


Заключение


Также стоит упомянуть, что количество применяемых уровней защиты зависит от конкретного приложения. К примеру, если приложение вообще не является клиент-серверным, не содержит никаких КВД, а также не оперирует ценными внутренними алгоритмами, то вообще нет смысла навешивать на него какую-либо защиту. Если же приложение ориентировано, например, на выполнение банковских операций, или хранение пользовательских паролей, то степень его безопасности должна быть наивысшей. Однако перечисленные ранее общие уязвимости мобильного сектора достаточно легко могут быть исключены из приложения, чаще всего это не вносит особых дополнительных затрат, если применение требуемого уровня защиты было начато на ранних этапах разработки приложения. А вот внедрение защиты пост-фактум в уже работающее приложение вполне может быть сопряжено со значительными затратами сил и времени разработчиков. Поэтому выбор и согласование уровня защищенности, а также перечня КВД в разрабатываемом приложении, должны выполняться на самых ранних этапах проектирования.

Поделиться с друзьями
-->

Комментарии (30)


  1. GDXRepo
    02.05.2017 13:59
    -1

    К сожалению, перевод Markdown-разметки поломался при перемещении из моего редактора, пока никак не получается сделать нормальные таблицы. Пока бьюсь над этим.


    1. GDXRepo
      02.05.2017 14:12

      Так и не получилось сделать нормальную ширину у заголовков, прошерстил помощь — не нашел ничего похожего. Очень прошу модераторов помочь пофиксить ширину табличных столбцов.


    1. GDXRepo
      02.05.2017 15:41

      Для улучшения читаемости перевел все таблицы на вложенные списки.


  1. GDXRepo
    02.05.2017 15:42
    -1

    Просьба всем, кто ставит минусы в карму: если уж ставите минус, то отписывайтесь сразу, что следует поправить в статье. Это образовательный материал, и нужно сделать его полезным для будущих читателей, а не просто выставлять молчаливые оценки.


  1. DjPhoeniX
    02.05.2017 18:14

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


    1. GDXRepo
      02.05.2017 18:21

      Согласен с вами. Хотя, даже имея под рукой отладчик, если по мобильному приложению гоняются только хеши (т.е. отсутствует сама процедура расшифровки) — взломщик через отладку получит пользу только в виде изучения алгоритма, а это уже требует приличной квалификации атакующего. Запрещать работу на рутованных устройствах — хороший подход, только лично я пока не находил методов по 100%-му определению факта, что устройство взломано, по крайней мере, на iOS.


      1. DjPhoeniX
        03.05.2017 16:55
        +2

        Отсутствие «расшифрованных» данных сильного усложнения исследователям не принесёт:
        — Если данные используются только в виде хэшей, то эти хэши так или иначе используются. Разреверсить протокол обмена с сервером имея отладчик — несложно. Вешаем брейкпоинт на какие-нибудь сетевые функции (зависит от платформы) и гуляем по стек-трейсу до момента заполнения. Опять же, логирование на этом слое тоже обычно достаточно (даже если используется TLS — можно повеситься отладчиком/логгером до зашифровки).
        — Если данные шифруются системными библиотеками — это ничем не помогает, просто опять же гуляем по стек-трейсу и ищем вызовы этих самых библиотек.
        — Если данные шифруются в application-space и имена функций не определяются — всё равно можно найти в стеке необходимые функции, хоть и сложнее.

        Единственный метод, дающий хоть какую-то надежду — использование статической библиотеки шифрования без каких-либо отладочных наименований (в случае с андроидом — обфускация бинарного байт-кода). Но и это не панацея.


        1. GDXRepo
          03.05.2017 17:26

          Познавательно, спасибо! Поправил в статье этот пункт.


        1. Asen
          04.05.2017 02:03

          Обфускация, действительно, Не панацея, особенно когда обфусцированный код присутствует >90% времени перед глазами. Его даже не всегда обязательно делать более читабельным :)


          1. Asen
            04.05.2017 02:05

            А еще учитывая, что сейчас время опенсорса, когда все на виду, и нужно лишь хорошенько потеребить гитхаб


          1. DjPhoeniX
            04.05.2017 13:28

            Пореверсите на досуге что-то вроде Bitwig Studio. Не андроид, но та же java. Все классы слиты в один большой jar, и имена классов и методов заменены на случайные трёхсимвольные последовательности. Кое-что понять можно, но я полтора раза мозг вывихнул, пока понял вообще порядок инициализации.


            1. Asen
              04.05.2017 13:39

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


              1. DjPhoeniX
                04.05.2017 13:49

                В чём смысл того ковыряния — говорить не буду, но могу сказать, что такие «комья» сильно усложняют разборку приложения. Очень сильно. Я не первый раз сталкивался с реверсингом, и имею не сильно большой, но всё же опыт в java. И всё-таки не достиг цели, что о чём-то говорит. Как минимум одного такой метод победил — может, и не зря keygen-ов для этой программы всё никак не появляется для последних версий? :)


            1. port443
              04.05.2017 17:34

              Тут стоит добавить, что как минимум ProGuard умеет складывать несколько пакетов (package) в один.


      1. port443
        03.05.2017 20:38

        Просто по определению, рут/джейл можно обнаружить только тогда, когда это сделано не в целях компрометации приложения. В противном же случае это тоже самое, что мы с вами не можем гарантировать, что мы сейчас не в Матрице :)


        1. DjPhoeniX
          03.05.2017 20:57

          Тут можно войну вести бесконечно. Часть программ отказываются запускаться в VirtualBox/QEMU, например, хотя их довольно несложно заставить это сделать — но от новичков спасает. Точно так же, джейл например определяется по наличию библиотек вроде apt (обычным fopen или stat), но никто не мешает взять и подменить поведение этого метода для исследуемой приложеньки… Короче, чем глубже защищаемся, тем дольше придётся ломать, но абсолют недостижим.


          1. port443
            03.05.2017 21:26

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


  1. port443
    03.05.2017 00:06

    пароль пользователя должен уходить на сервер уже в виде хеша, и проверяться должен хеш, а не исходный пароль

    Как в таком случае должен выглядеть обмен данными, если учесть то, что на сервере хранится солёный хеш пароля?

    Сервер шлёт соль клиенту; клиент солит/хеширует пароль и отправляет обратно; сервер сравнивает?

    Или добавить случайности: сервер генерирует случайное число, и отправляет вместе с солью клиенту; клиент солит/хеширует пароль, затем солит полученным случайным числом и отправляет обратно; сервер солит этим же числом хеш, хранящийся у него, и сравнивает. Так получится обмен, который нельзя проиграть второй раз.


    1. GDXRepo
      03.05.2017 00:09

      Вариантов несколько. Первый и самый простой — соль генерируется случайно на клиенте, одноразово используется для засолки пароля при хешировании, и этот хеш идет на сервер. А серверу все равно, какая была соль, потому что ему не требуется эти данные расшифровывать, его задача — лишь сравнение, так что ему эта соль не нужна, ее клиент использует 1 раз и уничтожит. Либо второй вариант — да, применять пересылку соли и/или публичного ключа. Здесь уже нет смысла в дополнительной генерации чисел, если соль сама по себе создается серваком на основе генератора псевдослучайных чисел. Проблема тут в том, что если каким-то образом сетевой канал будет вскрыт, то и соль будет обнаружена. Однако только такой способ и получится применить, если требуется расшифровка данных на стороне сервера, тут никуда не денешься. С асимметричными там поинтереснее, перебрасываться можно хешами и, если надо, публичными ключами, но приватные ключи тогда нужно куда-то зашивать — тут уже начинаются проблемы рутованных устройств и хранения/защиты приватных ключей на устройстве/сервере.


      1. deej
        03.05.2017 21:58

        Возник вопрос по первому варианту. Что и с чем сервер будет сравнивать? Если клиент посолит пароль одноразовой солью и захеширует, как сервер поймет, что за хеш ему прислали?
        Можете объяснить подробнее или привести пример реализации?


        1. GDXRepo
          03.05.2017 22:00

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


          1. DjPhoeniX
            04.05.2017 01:35

            Окей, а если потребуется авторизоваться под тем же аккаунтом, скажем, со второго телефона?


            1. port443
              04.05.2017 01:55

              И ещё один момент: получается, что прослушав канал, можно проиграть обмен ещё раз, авторизовавшись на сервере. В этом случае посылать солёный хеш и посылать пароль в открытом виде — суть одно и тоже.


              1. GDXRepo
                04.05.2017 11:58

                По авторизации — да, если вы пройдете сквозь HTTPS, то можно. Разница лишь в том, что сам пароль вы не узнаете, даже получив возможность авторизоваться с его хешем, поэтому это не одно и то же.


            1. GDXRepo
              04.05.2017 11:57

              Как правило, приложения, требующие высокого уровня защиты, например, банковские, в принципе не должны допускать авторизацию со второго телефона. Это просто ни к чему, одному человеку вряд ли так уж понадобится именно с двух разных телефонов управлять одним и тем же аккаунтом, где у него деньги лежат. Во всяком случае везде, где я работал по банковскому направлению, второй телефон считался чем-то не самым правильным.


              1. vikarti
                04.05.2017 12:36

                Почему вдруг?
                Если там просто деньги и немного — зачем проблемы создавать?
                Тиньков и Альфа точно не мешают например. Как и Яндекс.Деньги (их приложение по сути — банковское же).
                Единственное — Тиньков любит иногда flash-sms показывать которые не перехватывается стандартными средствами и сразу на экране девайса показываются (что может быть неудобно если пользователю хочется таки редирект, на экран компьютера, есть сервисы) ну и если включена замена push и Тинькова — она включена для одного конкретного устройства (включишь на другом — выключается на прежнем). А вот допустим оплата через NFC работает именно что со всех телефонов у них.

                C рутом — Сбербанк при детекте рута не вырубается напрочь а просто оставляет возможность работать только по созданным шаблонам.
                Конечно если реально большие деньги на кону — то и подходы из https://habrahabr.ru/post/111232/ вполне могут быть (но вот уж там — явно никакие приложения на телефон ставить не будут, разве что только на просмотр).

                С рутом вообще еще та проблема есть что CyanogenMod/Lineage OS в принципе как рут детектится часто, даже когда он там отключен (в смысле приложение НЕ может получить рут, без специального разрешения пользователя). Так же и в некоторых кастомных прошивках.
                А допустим для Nexus 5 кастомная прошивка это единственный способ получить андроид с последними обновлениями в том числе и безопасности.

                Вообще — в случае с банковским приложением и накрученными защитами — мы защищаем приложение от злого юзера (который и так имеет легальный доступ) или от посторонних приложений которые влезли? Если второе — так достаточно показать один раз предупреждение чтобы юзер был в курсе.

                Вообще на Android есть такая штука как SafetyNet (описание со ссылками смотрим на https://koz.io/inside-safetynet/ ) — гугловский детектор одного из трех состояний: все полностью ок, на девайсе… странности… но вероятно угрозы приложению нет, все плохо. Используется куча динамически обновляемых детектов, в процессе на устройство грузится код от гугла.
                Обойти при наличии рута саму проверку можно… но пока публично известные публично способы просто подменяют ответ для приложения по сигнатурам.
                А вот если сделано идеологически верно, и результаты проверки отсылаются на свой сервер, который проверяет что они верны у гугла то с обходом уже все сильно сложнее.


  1. agee
    05.05.2017 17:25

    Бинарные файлы (NSKeyedArchiver).

    Штатная защищенность: Отсутствует.

    Не совсем корректно. Файл можно создать с атрибутом NSFileProtectionComplete по ключу NSFileProtectionKey.


    The file is stored in an encrypted format on disk and cannot be read from or written to while the device is locked or booting.

    Не стоит, конечно, хранить там какие-то ключи для общения с сервером. Но вполне подходит для хранения временной чувствительной информации, которую нельзя прочесть третьему лицу, если девайс заблокирован.


    1. GDXRepo
      05.05.2017 20:01

      Спасибо, дополню этот пункт.


  1. titanium007
    05.05.2017 20:02

    " асимметричных алгоритмов вроде AES" не совсем понятно что имелось ввиду...AES симметричный алгоритм


    1. GDXRepo
      05.05.2017 20:02

      Согласен, поправил, спасибо.