In this article, I will show you how to encrypt your sensitive data in your flutter application, and most importantly how to check it on both iOS and Android platforms.

I took Flutter technology as example, but Android and iOS native developers may be interested too, as we will dissect applications!

Some times ago, I maintained a set of iOS applications that uses sensitive data, composed by a set of databases and text files. Clients choose to secure their data through a gateway (secured server), but demonstration data where embedded into the application. I am wondering, if I had to make it more secure, what can be done.

The plan
  • First I will create a little application creating a database containing “sensitive” data
  • Then we will understand how can we retrieve this database, and understand limits of this solution
  • We will encrypt our sensitive database using sqflite_sqlcipher package
  • Finally we will be able to compare security enforcement, and I will show you limits of that solution
It’s important to maintain a set of good tools for your development, maintainance, et cetera…

I commonly use DB Browser for sqlite (https://sqlitebrowser.org/) for reading, writing and encrypting database. This is a simple and powerful tool available for Windows and MAC OS.

Creating an example (Flutter Application)

The first step is to create an application that will use sensitive data stored in a database, as experienced in one of my previous job. To do that, I will use the sqflite package.

sqflite | Flutter Package

You will understand in a couple of minute why

I divided the code into three parts, that you will be able to explore through the github link at the bottom of the article:

  • CreditCard class, defining my sensitive data
  • DBService class, responsible of handling database connection and requests (get all credit cards, insert a credit card)
  • main.dart file, describing a page displaying all credit cards stored in the database, and allowing user to add a “random” credit card. To have a reasonable widget size, I developed the Cardwidget, a simple display of a credit card

Added to that, I added into my Flutter application two files: encrypted_test.db, an encrypted database using a desktop version of sql cipher, and db.properties, a file containing database password.

I open encrypted database using a password written directly in the code, I will show you why later 😉

When it comes to load a database coming from assets, you have to make a local editable copy of it first.

After debugging the application, we can test security lacks!

My brain right now!

Testing how secure it is

For convenience purpose, I will do it on iOS application. Later, I will do the same onto an Android apk.

Retrieving sensitive data from installed iOS application

As you may know, you can retrieve data from installed iOS application easily, because you can access to your application directories using Finder (equivalent of Windows File Explorer).

The only thing I need is the database path; you can easily do it by printing the database path (during the execution).

By default, local databases are stored in application’s Documents directory

Using DB Browser for sqlite tool (https://sqlitebrowser.org/), you can see on the background of the picture that I’m able to read all the content, without any password prompt. Don’t be confused: even if database name is “encrypted.db”, its not encrypted yet.

Retrieving sensitive data from the iOS Application file

Another way to do this inspection is to open the application package content.

First build your application (debug will be enough here) and go to the build folder of your Flutter project. Then find the iOS application built.

Flutter application is named Runner (currently imposed by Flutter framework)

Then open the package content.

Finally find your data, that may be stored in Frameworks/App.framework/flutter_assets/ folder.

At this stage, encrypted_test.db is not encrypted, so you will be able to read content.

Conclusion: if you want to secure sensitive databases, your embedded database shall be encrypted, as well as your editable (read/write) copy used during the execution of your application.

Encrypt your database using SQL Cipher technology

On the Flutter pub package website, I found a package very interesting: sqflite_sqlcipher. I used that technology for my website, so I’m a little familiar with it. But the main interest is that it’s build on top of the sqflite Flutter package!

sqflite_sqlcipher | Flutter Package

Hold on, it will go fast

So first replace sqflite dependency by sqflite_sqlcipher one.

#  sqflite: ^1.3.2 1
sqflite_sqlcipher: ^1.1.4

Then in the DBService class, I replaced the way to open a database, adding password input parameter.

Future _initDatabase() async {
if(_dbConf.isAsset) {
await _copyAssetToLocal();
}
print("Database path: ${await getDatabasesPath()}");
_database = await openDatabase(
_dbConf.name,
password: _dbConf.password,
onCreate: DBService._onCreate,
version: 1,
);
return _database;
}
That’s all.

Let’s see how secured it goes

In this situation, I’m going to show you how to retrieve data from Android side, on a signed apk file. I want to know is signing an application with a keystore add extra security, especially to the text assets db.properties I have. As a reminder, this file is here to demonstrate that this is not secured (and we will see why).

There is two ways to retrieve the apk file: directly from the output of flutter build command, or from an installed apk (on your device).

Before investigating apk, I will show you both ways :D.

Retrieve installed APK (the hard way)

Even if this is my own application, I feel like an hacker…

Before starting, check that tools using adb are terminated (check that you closed all applications using adb such as intelliJ, Android Studio, Flutter debug plugin,…).

First, connect your device to your computer, and check that your android device is recognized, using adb devices command in a Terminal (cmd console on Windows computers).

I suggest you to have only one device found. Else each command will need you to specify the device.

Then, you will have to find the package name of your application. You can find it in the build.gradle file, located in ./android/app/ folder. In my case, this is ‘com.example.encrypted_db’.

Locate your apk path using the command adb shell pm path [your_package_name].

Then retrieve the apk using adb pull [device_path] [computer_path] command.

Retrieve compiled apk (the easy way)

You can retrieve the compiled apk too, that may be easier. At this stage, I consider that you have created your keystore, modified required files and compiled the apk using the command flutter build apk.

And… that’s it. The command show you where the apk file is!

Ok now let’s go into serious business

Retrieve embedded file from an apk

Serious things start here. First, an apk is nonetheless an archive file. So here is the trick: change its extension to “.zip”, then decompress it. It may only work in a Terminal/cmd console.

Next, go to the unzipped folder. You will be able to find the database in the assets/flutter_assets/ folder.

And (not) magically, when we try to open it using DB Browser for sqlite, a popup is asking you to write password.

That the level of security we wanted. But the password is plain, either in db.properties file and in the source code. We can easily find db.properties as plain text file inside the apk, so that’s definitively the worst it.

Finding password in the compiled code is a little bit harder.

To do that, we will have to use specific tools, that will dump data from compiled objects. As you may know, in release apk your code will be compiled a-head of time (AOT), in the form of “.so” files.

On my side, I used the arm linux toolchains for MAC OS (https://github.com/thinkski/osx-arm-linux-toolchains). You may have a windows equivalent, but I haven’t tested on that platform.

Then, we will dump objects from the main compiled file: libapp.so, located inside the unzipped apk in the lib/arm64-v8a folder.

Finally dump object using command aarch64-unknown-linux-gnu-objdump -s [compiled_file] (or equivalent on windows and linux).

You shall to store output into a file to process data later 😉

I wrote ouput into reverse.txt file, let’s see what’s in there. By searching plain password text, we can see that we are able to retrieve database name and password, if we know what we are searching!

In red, plain password. In green, database name.

Conclusion: we saw that a database properties file is not a good idea, because it will be embedded “as-is”. Having password directly in the code is a little bit better, but there is still a solution to retrieve password.

Also, I’m not an expert in security, so maybe there is other more efficient way to do this.

Conclusion

When thinking about security, we have to understand that there is no 100% safe solution. In any case, someone can retrieve content from an encrypted database.

As developers, our goal is to make them difficult to retrieve data. That’s the purpose of the encrypted database.

The message you send to other engineers (or hackers)

To go furthermore, we can imagine that database password may be retrieved from a secured server. In this scenario, we could imagine that user may authenticate first to the server (even an anonymous authentication), then retrieve password through https secured protocol, and finally open the encrypted database with it.

As always, we will be able to get the full example used in that article here:

GONZALEZD/flutter_demos

Follow Flutter Community on Twitter: https://www.twitter.com/FlutterComm


Flutter Secure your sensitive data was originally published in Flutter Community on Medium, where people are continuing the conversation by highlighting and responding to this story.