Вступление
Эта статья расскажет, какими общими способами можно защитить свое приложение от взлома или получения персональной информации третьими лицами посредством исполнения нескольких простых шагов, но стоит помнить, что нет вещей, которые невозможно взломать, все зависит лишь от потраченного времени и наличия мотивации у взломщика, но т.к. у нас стоит цель обезопасить наше приложение, то давайте усложним им жизнь как можно сильнее.
Содержание
Часть 1: Обновление до последних версий
Обновление до последней версии фреймворка, языка и используемых библиотек может включать в себя увеличение производительности, количества функционала и, конечно же, повышение безопасности, не будем пренебрегать этим советом.
Чтобы посмотреть текущие версии фреймворка и языка, нужно ввести следующую команду в консоль:
flutter doctor -v
Команда для обновления Flutter и Dart SDK до последних стабильных версий:
flutter upgrade
Для обновления библиотек и зависимостей вашего проекта воспользуйтесь пакетным менеджером или иным инструментом.
Часть 2: Обфускация
Обфускация кода — это процесс изменения двоичного кода приложения, чтобы его было труднее понять людям. Обфускация меняет имена функций и классов в вашем скомпилированном коде во что-то бессмысленное для человека, но не для машины, зато людям, пытающимся реконструировать ваш код, будет гораздо труднее. Язык Dart имеет механизмы обфускации и поддерживает их для следующих платформ:
Android / iOS / macOS - поддерживается.
Linux / Windows - не поддерживается.
Web - не поддерживается, но код автоматически минимизируется.
Обфускация кода производится только для релизных версий!
Чтобы включить обфускацию, нужно при создании apk указать следующие параметры:
--obfuscate --split-debug-info=//
Для примера:
flutter build apk --obfuscate --split-debug-info=build/app/outputs/symbols
--obfuscate
— отвечает за включение обфускации в вашем приложении.
--split-debug-info=//
— это карта символов отладки, когда ваше приложение дает сбой, то консоль разработчика Google Play регистрирует этот сбой, чтобы была возможность проверить и отладить, но поскольку у конечного пользователя версия с включенной обфускацией, то отправляемый отчет будет написан с бессмысленными именами и не будет возможности его отладить. Для этого как раз и нужна карта символов отладки, которая будет использоваться внутри Play Console для преобразования символов отчета о сбое в удобочитаемые имена для появления возможности отладки приложения.
Наличие символов отладки внутри приложения никак не влияет и не помогает взломать ваше приложение людям извне!
Более того, наличие этого параметра может даже снизить финальный размер apk вплоть до 0.5мб.
Минимизация (минификация) — это процесс удаления всех ненужных символов из исходного кода интерпретируемых языков программирования или языков разметки без изменения его функциональности. Эти ненужные символы обычно включают пробелы, символы новой строки, комментарии и иногда разделители блоков, которые используются для повышения удобочитаемости кода, но не требуются для его выполнения. Минимизация уменьшает размер исходного кода, делая его передачу по сети более эффективной.
Пример для JavaScript:
// Этот комментарий будет убран после минимизации
var array = [];
for (var i = 0; i < 20; i++) {
array[i] = i;
}
Тоже самое, но после минимизации:
for(var a=[i=0];i<20;a[i]=i++);
Часть 3: Экран авторизации
Использовать механизм регистрации-логина просто необходимо для повышения безопасности вашего приложения от нежеланного входа. В зависимости от типа вашего приложения (оффлайн или онлайн), будут различаться используемые способы для реализации авторизации.
Если у вас оффлайн приложение:
-
passcode_screen — пакет для Android и iOS, меню регистрации-логина.
-
Написать свой механизм оффлайновой авторизации.
Если у вас онлайн приложение:
-
google_sign_in — пакет для Android и iOS, вход посредством Google.
-
Используйте Firebase.
-
Используйте свой сервер.
Можно использовать не только пароль, но и биометрическую авторизацию, к примеру, по отпечатку пальца, лицу и т.д., с этим может помочь следующий плагин:
local_auth — для Android и iOS, поддерживает аутентификацию по отпечатку пальца, touch ID, face ID, пин-коду...
Часть 4: Хэширование
Мы уже поговорили про экран авторизации, и т.к. при любом условии в нем используется какой-либо "пароль", то нам нужно его как-то защищать. Для этих целей я рекомендую ознакомиться с процессом хэширования.
Хэширование — процесс преобразования любого объема информации в уникальный набор символов, который присущ только этому массиву входящей информации. Этот набор символов и будет называться хэшем.
Пример хэширования слова "Brain" с помощью алгоритма SHA-1:
97fb724268c2de1e6432d3816239463a6aaf8450
Основная суть этого процесса заключается в том, что в базе данных мы будем хранить не сам пароль, а его хэш, и т.к. хэш нельзя конвертировать обратно, то и пароль взломщик узнать не сможет, получается что мы будем отправлять не пароль, а его хэш, и сравнивать хэш введенного пароля со значением хэша в базе данных.
Хэширование можно использовать не только для пароля, но и для любой информации, которую вам не потребуется расшифровывать обратно, а просто сравнивать с некоторым значением для подтверждения.
Чтобы воспользоваться хэшированием, вы можете использовать следующий пакет:
crypto — поддерживаемые алгоритмы: SHA-1, SHA-224, SHA-256, SHA-384, SHA-512, SHA-512/224, SHA-512/256, MD5, HMAC (HMAC-MD5, HMAC-SHA1, HMAC-SHA256).
Пример функции хэширования строки алгоритмом SHA-512 (язык Dart):
static String hashSHA512(String text) {
return sha512.convert(utf8.encode(text)).toString();
}
Часть 5: Базы данных
Перед разработкой любого приложения нужно решить, каким образом будут хранится данные, используя Flutter у нас есть достаточно обширный выбор в этом вопросе, если мы говорим про хранение данных онлайн, то это облако или удаленные сервера, а если про оффлайн, то это локальная база данных. В любом из случаев лучше, несомненно, выбирать базу данных со встроенной защитой.
Локальные (оффлайн) базы данных:
-
Shared Preferences — обычное хранилище для настроек приложения у платформы Android, также плагин включает в себя стандартное хранилище у iOS. Слабые механизмы защиты данных. Подходит для хранения пар ключ-значения небольших объемов данных.
-
Flutter Secure Storage — улучшенная версия Shared Preferences, имеет шифрование, но оно по умолчанию выключено. Также подходит для хранения пар ключ-значения небольших объемов данных.
-
Hive — NoSQL база данных, имеющая встроенный механизм шифрования AES-256.
-
Isar — NoSQL база данных, улучшенная версия Hive, добавлены новые возможности и увеличена производительность, использование встроенного механизма шифрования AES-256 опционально.
-
ObjectBox — NoSQL база данных, основными положительными сторонами этой базы данных является скорость и возможность синхронизации данных между устройствами и серверами, не имеет шифрования.
-
SQFlite — SQL база данных, аналог SQLite для Flutter, не имеет шифрования.
Таким образом, мы можем взять базу данных со встроенной защитой и использовать её, или же взять без встроенной защиты и самостоятельно шифровать отправляемые и расшифровывать получаемые данные. Также хотел бы обратить внимание на плагин Flutter Secure Storage, он идеально подойдет для дополнительной защиты ключей, паролей и т.д., которые мы храним локально на устройстве.
Часть 6: Шифрование
Шифрование — это процесс кодирования информации с целью предотвращения несанкционированного доступа. В случае кражи или утечки зашифрованные данные будут недоступны для прочтения без соответствующего ключа.
Для реализации шифрования во Flutter предлагаю использовать следующий пакет:
encrypt — поддерживает алгоритмы Salsa20, RSA, AES (режимы CBC, CFB-64, CTR, ECB, OFB64, OFB-64/GCTR, SIC).
Пример использования плагина (язык Dart):
import "package:encrypt/encrypt.dart";
import "package:encrypt/encrypt.dart" as encrypt;
import "package:flutter_secure_storage/flutter_secure_storage.dart";
class AES {
static const storage = FlutterSecureStorage(
aOptions: AndroidOptions(encryptedSharedPreferences: true));
static late final key;
static late final iv;
static final enc =
encrypt.Encrypter(encrypt.AES(Key.fromBase64(key), mode: AESMode.cbc));
static void initAES() async {
storage.write(key: "password", value: null);
storage.write(key: "key", value: encrypt.Key.fromSecureRandom(32).base64);
storage.write(key: "iv", value: encrypt.IV.fromSecureRandom(16).base64);
key = await storage.read(key: "key");
iv = await storage.read(key: "iv");
}
static void readAES() async {
key = await storage.read(key: "key");
iv = await storage.read(key: "iv");
}
static encryptAES(text) {
return enc.encrypt(text, iv: IV.fromBase64(iv)).base64;
}
static decryptAES(text) {
return enc.decrypt(Encrypted.fromBase64(text), iv: IV.fromBase64(iv));
}
}
В примере выше используется алгоритм AES CBC, а также дополнительная база данных Flutter Secure Storage со своим собственным включенным шифрованием для хранения ключей от основного механизма шифрования. Приписки "base64" и "fromBase64" используются для работы со строками.
Часть 7: Рут-устройства
Root или суперпользователь — это специальная учётная запись в системе, обладатель которой имеет возможность выполнить абсолютно любые операции в системе.
Плюсы:
-
Получив доступ ко всем файлам системы, вы можете производить любые манипуляции связанные с вашим устройством, вплоть до удаления системных файлов и неудаляемых без прав суперпользователя программ, таких как встроенные Сервисы и Службы Google.
-
Можно настраивать гаджет как угодно пользователю: увеличивать громкость динамика, проводить системную настройку камеры, редактировать чувствительность микрофона, редактировать файловую систему, сменить системный шрифт, анимации...
-
Возможность тонкой настройки и разгона/замедления работы процессора.
-
Кардинальная очистка встроенной памяти (скрытый кэш) с помощью специальных утилит.
-
Использование всех функций программ, требующих root-права для полноценной работы.
-
Полная блокировка рекламы.
-
Все возможности для взлома приложений.
-
Возможность повысить уровень безопасности приложений путём тонкого контроля доступа к различным компонентам системы — аккаунтам, файлам, календарям, телефону, смс...
Минусы:
-
Сложности в процессе получения root-прав, однако это в то же время и плюс, так как неопытный пользователь тем самым не сможет легко сломать устройство, что убережёт его от необходимости перепрошивки.
-
Шанс снижения безопасности устройства или поломки его операционной системы.
-
Любые программы, в том числе вирусы, могут получить доступ к root правам и причинить вред устройству.
-
Невозможность устанавливать OTA-обновления системы (обновление по воздуху).
-
Root-права действительны только до следующей перепрошивки или сброса (есть также Temporary Root, действующий до первой перезагрузки).
-
Пользователь лишится технической поддержки и гарантии от производителя устройства.
Т.к. у нас стоит цель — защитить наше приложение, то мы должны отказаться от поддержки рут-устройств. Для этого нам нужно знать, есть ли у устройства пользователя рут-права, в этом нам помогут следующие плагины:
root (только Android).
root_check (только Android).
Если у пользователя все-таки есть расширенные права, то теперь мы можем накладывать на него любые ограничения в нашем приложении или же попросту сразу завершать работу приложения:
SystemNavigator.pop()
— только для Android.
exit(0)
— для Android и iOS (iOS может начать подозревать ваше приложение, использование нежелательно).
У iOS нет проверенных инструментов для намеренного выхода из приложения, но можно использовать следующий плагин:
minimize_app — для Android и iOS, позволит закидывать рабочее приложение в фоновый режим (минимизировать его).
Заключение
В данной статье были приведены наиболее общие советы по тому, как можно обезопасить любое приложение, написанное с помощью Flutter. Подведем итог:
Часть 1: Обновление до последних версий — общее обновление защиты.
Часть 2: Обфускация — против реверс-инжиниринга.
Часть 3: Экран авторизации — от несанкционированного входа другим человеком.
Часть 4: Хэширование — от перехвата и подбора пароля.
Часть 5: Базы данных — для защиты данных.
Часть 6: Шифрование — от нежеланного чтения персональных данных.
Часть 7: Рут-устройства — от входа в приложение на root-устройствах.
Если у вас есть ещё идеи, то пишите их в комментариях и я обновлю статью.
Автор: Алексей