Перейти к содержанию

Безопасность

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

Безопасность

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

Хранение конфиденциальной информации

Никогда не храните конфиденциальные ключи API в коде приложения. Все, что включено в ваш код, может быть доступно открытым текстом любому, кто проверит пакет приложения. Такие инструменты, как react-native-dotenv и react-native-config, отлично подходят для добавления переменных, специфичных для окружения, например, конечных точек API, но их не следует путать с переменными окружения на стороне сервера, которые часто могут содержать секреты и ключи API.

Если для доступа к какому-либо ресурсу из вашего приложения необходимо иметь API-ключ или секрет, наиболее безопасным способом будет создание уровня оркестровки между вашим приложением и ресурсом. Это может быть бессерверная функция (например, с помощью AWS Lambda или Google Cloud Functions), которая может перенаправить запрос с требуемым API-ключом или секретом. Секреты в коде на стороне сервера не могут быть доступны потребителям API так же, как и секреты в коде вашего приложения.

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

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

Асинхронное хранение

Async Storage — это поддерживаемый сообществом модуль для React Native, который обеспечивает асинхронное, незашифрованное хранилище ключевых значений. Async Storage не разделяется между приложениями: каждое приложение имеет свою собственную среду песочницы и не имеет доступа к данным других приложений.

Использовать асинхронное хранение, когда... Не использовать асинхронное хранение для...
Хранение нечувствительных данных во время работы приложения Хранение токенов
Хранение состояния Redux Секреты
хранения состояния GraphQL
Хранение глобальных переменных для всего приложения

Заметки разработчика

Async Storage — это React Native эквивалент локального хранилища из интернета

Безопасное хранение

React Native не поставляется в комплекте с каким-либо способом хранения конфиденциальных данных. Однако существуют готовые решения для платформ Android и iOS.

iOS — Услуги связки ключей

Keychain Services позволяет безопасно хранить небольшие фрагменты конфиденциальной информации для пользователя. Это идеальное место для хранения сертификатов, токенов, паролей и любой другой конфиденциальной информации, которой не место в Async Storage.

Android — Безопасные общие предпочтения

Shared Preferences — это эквивалент Android для постоянного хранилища данных типа "ключ-значение". Данные в Shared Preferences по умолчанию не шифруются, но Encrypted Shared Preferences оборачивает класс Shared Preferences для Android и автоматически шифрует ключи и значения.

Android — хранилище ключей

Система Android Keystore позволяет хранить криптографические ключи в контейнере, чтобы затруднить их извлечение из устройства.

Чтобы использовать сервисы iOS Keychain или Android Secure Shared Preferences, вы можете либо написать мост самостоятельно, либо использовать библиотеку, которая обернет их для вас и предоставит унифицированный API на свой страх и риск. Некоторые библиотеки, которые стоит рассмотреть:

Обратите внимание на непреднамеренное хранение или раскрытие конфиденциальной информации. Это может произойти случайно, например, сохранение конфиденциальных данных формы в состоянии redux и сохранение всего дерева состояний в Async Storage. Или отправка пользовательских токенов и личной информации в службу мониторинга приложений, такую как Sentry или Crashlytics.

Аутентификация и глубокое связывание

Аутентификация и глубокое связывание

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

Например, если вы создаете приложение для электронной коммерции, вы можете использовать app://products/1 для глубокой ссылки на ваше приложение и открытия страницы подробностей о продукте с идентификатором 1. Вы можете думать об этом как об URL в Интернете, но с одним важным отличием:

Глубокие ссылки небезопасны, и вы никогда не должны отправлять по ним конфиденциальную информацию.

Причина, по которой глубокие ссылки не являются безопасными, заключается в отсутствии централизованного метода регистрации схем URL. Как разработчик приложений, вы можете использовать практически любую схему URL по своему выбору, настроив ее в Xcode для iOS или добавив намерение на Android.

Ничто не мешает вредоносному приложению перехватить вашу глубокую ссылку, зарегистрировавшись в той же схеме, а затем получить доступ к данным, которые содержит ваша ссылка. Отправка чего-то вроде app://products/1 не опасна, но отправка токенов вызывает опасения в плане безопасности.

Когда операционная система предлагает на выбор два или более приложений для открытия ссылки, Android покажет пользователю Disambiguation dialog и попросит его выбрать, какое приложение использовать для открытия ссылки. Однако в iOS операционная система сделает выбор за вас, так что пользователь останется в неведении. Apple предприняла шаги для решения этой проблемы в более поздних версиях iOS (iOS 11), где был введен принцип "первый пришел — первый обслужил", хотя эта уязвимость все еще может быть использована различными способами, о которых вы можете прочитать подробнее здесь. Использование универсальных ссылок позволит безопасно связывать контент внутри вашего приложения в iOS.

OAuth2 и перенаправления

Протокол аутентификации OAuth2 невероятно популярен в настоящее время и считается самым полным и безопасным протоколом. Протокол OpenID Connect также основан на нем. В OAuth2 пользователю предлагается пройти аутентификацию через третью сторону. После успешного завершения, эта третья сторона перенаправляет обратно запрашивающему приложению проверочный код, который можно обменять на JWT — JSON Web Token. JWT — это открытый стандарт для безопасной передачи информации между сторонами в Интернете.

В Интернете этот шаг перенаправления безопасен, поскольку URL-адреса в Интернете гарантированно уникальны. Для приложений это не так, поскольку, как уже упоминалось ранее, не существует централизованного метода регистрации схем URL! Чтобы решить эту проблему безопасности, необходимо добавить дополнительную проверку в виде PKCE.

PKCE, произносится как "Pixy", означает Proof of Key Code Exchange и является расширением спецификации OAuth 2. Оно включает в себя добавление дополнительного уровня безопасности, который проверяет, что запросы на аутентификацию и обмен токенами исходят от одного и того же клиента. PKCE использует алгоритм криптографического хэширования SHA 256. SHA 256 создает уникальную "подпись" для текста или файла любого размера:

  • Всегда одинаковая длина независимо от входного файла
  • Гарантированно всегда дает один и тот же результат для одного и того же входного файла
  • Односторонний (то есть, вы не можете перепрограммировать его, чтобы узнать исходный вход)

Теперь у вас есть два значения:

  • код_верификатора — большая случайная строка, сгенерированная клиентом
  • код_задачи — SHA 256 кодового_верификатора

Во время первоначального запроса /authorize клиент также отправляет код_задачи для кода_верификатора, который он хранит в памяти. После того, как запрос авторизации был выполнен правильно, клиент также отправляет код_верификатора, который был использован для генерации кода_задачи. IDP вычисляет код_задачи, проверяет, совпадает ли он с тем, что был задан в самом первом запросе /authorize, и отправляет маркер доступа, только если значения совпадают.

Это гарантирует, что только приложение, которое запустило начальный поток авторизации, сможет успешно обменять проверочный код на JWT. Таким образом, даже если вредоносное приложение получит доступ к проверочному коду, сам по себе он будет бесполезен. Чтобы увидеть это в действии, посмотрите этот пример.

Библиотека для нативного OAuth — это react-native-app-auth. React-native-app-auth — это SDK для взаимодействия с провайдерами OAuth2. Она содержит встроенные библиотеки AppAuth-iOS и AppAuth-Android и может поддерживать PKCE.

Identity Provider

React-native-app-auth может поддерживать PKCE, только если ваш Identity Provider поддерживает его.

Identity Provider - это авторизация, которую вы часто видете в приложениях и на сайтах. Например: "Войти с помощью Google", "Войти с помощью Twitter", "Войти с помощью facebook" и тд.

OAuth2 с PKCE

OAuth2 с PKCE

Сетевая безопасность

Ваши API всегда должны использовать SSL-шифрование. SSL-шифрование защищает запрашиваемые данные от чтения открытым текстом между тем, как они покидают сервер, и тем, как они достигают клиента. Вы будете знать, что конечная точка безопасна, потому что она начинается с https://, а не с http://.

SSL Pinning

Использование конечных точек https может сделать ваши данные уязвимыми для перехвата. При использовании https клиент будет доверять серверу только в том случае, если он может предоставить действительный сертификат, подписанный доверенным центром сертификации, который предварительно установлен на клиенте. Злоумышленник может воспользоваться этим, установив на устройство пользователя вредоносный сертификат корневого центра сертификации, и тогда клиент будет доверять всем сертификатам, подписанным злоумышленником. Таким образом, полагаясь только на сертификаты, вы все равно можете стать уязвимым для атаки man-in-the-middle attack.

SSL pinning — это техника, которую можно использовать на стороне клиента, чтобы избежать этой атаки. Она работает путем встраивания (или прикрепления) списка доверенных сертификатов к клиенту во время разработки, так что только запросы, подписанные одним из доверенных сертификатов, будут приняты, а любые самоподписанные сертификаты — нет.

При использовании SSL pinning следует помнить об истечении срока действия сертификата. Срок действия сертификатов истекает каждые 1-2 года, и когда он истекает, его необходимо обновить как в приложении, так и на сервере. Как только сертификат на сервере будет обновлен, все приложения со старым сертификатом, встроенным в них, перестанут работать.

Резюме

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

Комментарии