Skip to main content

Securing Flutter Repositories

 Many of us have Flutter projects hosted in public repositories, for example, when we maintain a library or package published in the Pub dependency manager, or also because we work on an Open Source project we may need (from the very beginning) to avoid having sensitive data hosted in the repository. I am referring to keys, tokens, and any other sensitive data encoded somewhere in the project uploaded to the repository. 

Dash the Flutter mascot and Ocotocat the GitHub mascot in space with padlocks for security in Flutter projects.

It may seem that this does not affect private repositories, that the project partners are trustworthy people who will not leak company information, but poor repository management can allow access to any member of the company, and any member of the company receiving a successful hack can cause a catastrophe. This is what happened at a company I was working for some time ago. It received a cyber attack where the attacker allegedly had at least access to some of the company's repositories. This made me think that securing private repositories may give the impression at first glance that it is less necessary than securing public repositories. But as you just read, this happens more often than you might think.

Far from telling stories, let's start securing our repository.


Golden rule

No sensitive data has to be uploaded. This means that we cannot upload sensitive project data to the repository either as a file or in hardcoded form. It is a widespread practice to have api keys as constants in some file or class of the project. Even users and passwords! 


How can we avoid this?

The easiest way is to work with .env files. These files contain credentials in key-value format for services used by the program they’re building. They’re meant to be stored locally and not be uploaded to code repositories online for everyone to read. Each developer in a team typically carries one or more .env files.

To do so, we will use the flutter_dotenv dependency by following the steps below:

1. Create the .env file in the root of the project.

Android Studio with a file called .env where the confidential data of the project will be collected.

2. Before modifying the .env file, let's add it to the .gitignore file so that it cannot be uploaded by accident. This is a snapshot of my .gitignore file:

.gitignore file excluding the newly created .env file.

Important note here. If we use any Google service (Firebase, etc...) we have to add them to the .gitignore file (from 48 to 50 line). An example of this would be the following:

.gitignore file excluding more sensitive files from the project.

As you can see, they were added them into .gitignore file using a regex. The point is there is sensitive information inside:

Contents of the file google-services.json

3. Now we need to add the new .env file as an asset within the pubspec.yaml 

Assets of the pubspec.yaml file where you can see the .env file added.

4. Finally, in the main.dart file we must load the .env file so that we can access our key/value dictionary (.env file), inside our code.

Documentation and sample Dart code for loading the .env file at application start-up.

In my case, I created a custom function invoked inside the main function that prints by console if the loading was not successful.

Documentation and sample Dart code to handle errors when loading the .env file at application start-up.

And now what?

Well, it will depend a bit on your needs. If you are using Firebase, you will need to take an extra step. FlutterFire CLI, the Firebase tool generates a firebase_options.dart file that you need to protect. We have two options, both with their pros and cons.

Option 1: Add this auto-generated file to the .gitignore. As you can imagine this has a big disadvantage, that the importing will fail when someone new downloads the project.

Option 2: Modify the autogenerated file to load this information from the .env file, because although it will be deleted when it is autogenerated again, there is no reason to autogenerate it continuously and in practice it will not be done very often. 

We will use option 2. Besides preferring this option, we can see how to add data to the .env file. 

First, we will modify the firebase_options.dart file. For each platform we have a method with the sensitive data that we have to modify using our .env file:

Code in Dart after modification of the firebase_options.dart file so that the data is loaded by the .env file instead.

Within the file we will use key-value pairs separated by line breaks between them.

Example key dictionary example value contained in the .env file as an example.

Conclusions

As you can see, and for obvious reasons, it is much better to have our project and repository secured. We recently discussed how to set up CI in a flutter project. 

Securing and accessing to sensitive data within the CI system has not been addressed so as not to make this guide too long. I will soon create a specific post for this point.

See you in the next post, greetings!










Comments

© 2020 Mobile Dev Hub