Проверка подлинности данных приложения во Flutter | Кодементор

Безопасность! одна из самых важных и меняющих правила игры тем в мире разработки программного обеспечения. В мире мобильных приложений такие приложения, как Youtube, Spotify, Netflix, Uber, WhatsApp, являются одними из самых популярных на рынке. Их используют миллионы людей. Эти приложения завоевали популярность не только потому, что у них приятный пользовательский интерфейс или отличный контент. Они популярны из-за своей надежности. Эти приложения достаточно безопасны, чтобы вызвать доверие у своих клиентов. Чтобы добиться как надежности, так и доверия, вам необходимо поработать над частью безопасности вашего программного обеспечения. Итак, сегодня мы изучим отличный прием безопасности, который не позволит хакерам взломать ваше мобильное приложение, манипулируя данными вашего приложения.

Тип атаки

Будучи мобильным разработчиком, вы, должно быть, слышали о данные приложения. Если не проблема, позвольте мне объяснить вам. Все приложения (корневые или нет) имеют каталог данных по умолчанию, который /data/data/<package_name>. По умолчанию сюда помещаются базы данных приложений, настройки и все остальные данные. Если приложение ожидает, что будут храниться огромные объемы данных, или по другим причинам хочет «улучшить внутреннюю память», на SDCard есть соответствующий каталог (Android/data/<package_name>). Теперь вы знаете, где хранятся данные, хакеры могут легко добраться до этих мест и могут манипулировать данными любого приложения, если они не защищены. Если данные приложения изменены правильным образом, хакеры могут легко получить доступ к некоторому премиум-контенту (каковы бы ни были их намерения), не тратя ни доллара. Есть открытый исходный код инструмент в форум xda-разработчиков поиграться с этим типом взлома.

Тип решения

Мне приходят в голову два типа решений, которые могут избежать такого типа взлома:

  1. Не храните ничего на мобильном телефоне пользователя 😅.
  2. Если вы планируете что-то хранить. Вы шифруете содержимое и сохраняете.

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

Проблема с шифрованием

Согласно моему опыту разработки, не все шифруется и хранится (кроме личных данных пользователя). Что делать, если вам нужно загрузить файл конфигурации (xml или json) и сохранить его при первом запуске вашего приложения (или загрузить, когда есть обновление с сервера) и использовать его для управления некоторой бизнес-логикой, например, для изменения языка приложения как в зависимости от доступности или проверки ограничения доступа, которое пользователь может иметь для содержимого вашего приложения.

Что вы будете делать, если попадете в такую ​​ситуацию? Не волнуйтесь, есть решение этой проблемы. И решение:

Аутентифицировать с помощью HMAC

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

Как работает HMAC?

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

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

В приведенном выше примере маленький закрытый ящик представляет собой HMAC. Ваш ключ — это ключ HMAC. Фотография — это данные, к которым вы применяете HMAC. Источник : StackExchange

Преимущества использования этого решения

  1. Вам не нужно шифровать файл.
  2. Вам не нужно копировать весь контент в зашифрованное хранилище.
  3. Вам не нужно писать какой-то сложный алгоритм для проверки подлинности файла.
  4. Вычислять быстрее, а размер дайджеста небольшой, т.е. 128 бит. Видеть это.

План атаки

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

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

В приведенном выше видео я сохраняю текущий уровень пользователя в файле. Когда я нажимаю на Сохранять кнопка Я создаю дайджест или хеш-код, используя алгоритм HMAC, и когда я нажимаю кнопку Проверять button Я читаю содержимое сохраненного файла и генерирую новый дайджест или хеш-код. Затем я сравниваю новый дайджест со старым дайджестом, чтобы проверить, одинаковы ли они или нет. Если оба не совпадают, то кто-то вручную изменил данные в файле и покажет пользователю сообщение о том, что вы пытаетесь обмануть.

Давайте код

Позвольте мне провести вас через создание вышеуказанного приложения.

  1. Создайте новый флаттер-проект.
  2. Удалите весь код из main.dart файл и вставьте следующий код:
import 'package:flutter/material.dart';
import 'src/app.dart';

void main() => runApp(MyApp());
  1. Создайте новый пакет под lib каталог и назовите его как src .

  2. Внутри каталога src создайте новый файл и назовите его как app.dart. Ниже приведен код файла app.dart.

import 'package:flutter/material.dart';
import 'secure.dart';

class MyApp extends StatelessWidget{
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        appBar: AppBar(
          title: Text("Secure"),
        ),
        body: SecureScreen(),
      ),
    );
  }

}
  1. Теперь нам нужно добавить несколько зависимостей в наш pubspec.yaml файл.
dependencies: flutter: sdk: flutter

  # The following adds the Cupertino Icons font to your application. # Use with the CupertinoIcons class for iOS style icons. cupertino_icons: ^0.1.2
  crypto: ^2.0.6
  path_provider: ^0.4.1
  simple_permissions: ^0.1.9

crypto библиотека является основным сердцем нашего приложения. Он содержит алгоритм HMAC. path_provider поможет использовать для записи файла во внешнее хранилище. simple_permissions поможет разобраться с разрешениями во время выполнения.

  1. В настоящее время я обрабатываю разрешения для Android. Так что для этого вам просто нужно отредактировать AndroidManifest.xml файл внутри каталога android. Добавьте ниже разрешение в AndroidManifest файл.
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
  1. Последний и последний шаг нашего приложения. Создайте новый файл в каталоге src и назовите его как secure.dart файл. Скопируйте и вставьте приведенный ниже код.
import 'dart:io';

import 'package:flutter/material.dart';
import 'package:crypto/crypto.dart';
import 'dart:convert';
import 'package:path_provider/path_provider.dart';
import 'package:simple_permissions/simple_permissions.dart';

class SecureScreen extends StatefulWidget {
  @override
  State<StatefulWidget> createState() {
    return SecureState();
  }
}

class SecureState extends State<SecureScreen> {
  String _content;
  Digest _fileDigest;

  @override
  void initState() {
    super.initState();
    checkPermission().then((bool value) {
      if (!value) {
        requestPermission();
      }
    });
  }

  @override
  Widget build(BuildContext context) {
    return Container(
      child: Column(
        children: <Widget>[inputField(), writeButton(), verifyButton()],
      ),
    );
  }

  inputField() {
    return TextField(
      decoration: InputDecoration(
          labelText: "Sensitive Data", hintText: "Sensitive Data"),
      onSubmitted: (value) {
        _content = value;
      },
      onChanged: (newValue) {
        print(newValue);
      },
    );
  }

  verifyButton() {
    return RaisedButton(
        child: Text("Verify"),
        onPressed: () {
          readCounter().then((String value) {
            Digest digest = hmac(value); //The final digest which will be used for verification if (digest.toString() == _fileDigest.toString()) {
              //Content is same snackbar('Data is correct.');
            } else {
              //Someone changed the content snackbar('You trying to cheat.');
            }
          });
        });
  }

  void snackbar(String msg) {
    final snackBar = SnackBar(
      content: Text(msg),
      action: SnackBarAction(
        label: 'Dismiss',
        onPressed: () {
          // Some code to undo the change! },
      ),
    );

    // Find the Scaffold in the Widget tree and use it to show a SnackBar! Scaffold.of(context).showSnackBar(snackBar);
  }

  writeButton() {
    return RaisedButton(
        child: Text("Save"),
        onPressed: () {
          print("Write Button $_content");
          String body = """ {"user_details":{"name":"Sagar","age": "25""email":"sagarsuri56@gmail.com","current_level": $_content}} """;
          _content = body;
          writeCounter(_content);
          Digest digest = hmac(_content); //The final digest which will be used for verification _fileDigest = digest;
        });
  }

  Digest hmac(String data) {
    var key =
        utf8.encode("p@ssw0rd"); //Secret key used for authentication var bytes = utf8.encode(data);
    var hmacSha256 = new Hmac(sha256, key);
    Digest digest = hmacSha256.convert(
        bytes); //The final digest which will be used for verification return digest;
  }
}

Future<String> get _localPath async {
  final directory = await getExternalStorageDirectory();
  print("directory ${directory.path}");
  return directory.path;
}

Future<File> get _localFile async {
  final path = await _localPath;
  return File('$path/config.txt');
}

writeCounter(String content) async {
  final file = await _localFile;
  // Write the file await file.writeAsString('$content');
}

Future<String> readCounter() async {
  try {
    final file = await _localFile;

    // Read the file String contents = await file.readAsString();

    return contents;
  } catch (e) {
    // If we encounter an error, return empty string return "";
  }
}

Future<bool> checkPermission() async {
  bool res =
      await SimplePermissions.checkPermission(Permission.WriteExternalStorage);
  return res;
}

requestPermission() async {
  await SimplePermissions.requestPermission(
      Permission.WriteExternalStorage);
}

Приведенный выше код действительно очень прост. Я создал 3 виджета в столбце. Первый виджет TextField который будет принимать данные от пользователя. В этой демонстрации я пишу JSON в файл. Вводимый пользователем текст будет значением current_level ключ JSON . Ниже TextField есть два RaisedButtons . Сохранять Кнопка запишет содержимое в файл и сгенерирует дайджест или хэш-код содержимого и сохранит его в переменной (я не реализую полный поток БД). Проверять Кнопка прочитает содержимое из файла и сгенерирует новый хеш-код или дайджест. Затем проверит, соответствует ли текущий дайджест старому дайджесту или нет. Если это не соответствует вашему приложению «взломал». После этого вы можете делать все, что хотите. Заблокируйте пользователя или загрузите новый файл с сервера. Выбор за вами 😃.

Что дальше?

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

Я хотел бы услышать ваше мнение относительно этого подхода и вашего решения для такого рода проблемы.

Вот мы и подошли к концу этой статьи. Надеюсь вам понравился материал. Оцените мою работу громкими хлопками( 50 это макс 😅). Если у вас есть какие-либо сомнения или вам нужны какие-либо разъяснения относительно разработки приложений Flutter. Свяжитесь со мной в LinkedIn.


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

Похожие записи

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *