How safe is your tax rebate e-invoice app? Learn how to protect yourself.

How safe is your tax rebate e-invoice app? Learn how to protect yourself.

Published on Author char49labsLeave a comment

More and more apps are available in Google Play Store allowing to manage your invoices.
Some apps are focused on small businesses in order to make quotes or invoices for
clients, but other apps are also targeting individuals. For instance in Portugal, the
government is encouraging people to ask for invoices when buying products. These invoices
can be then deduced in your tax form.

At Char49, we checked two of the most popular Android apps targeting the portuguese
market. Combined them they have between 100.000 to 500.000 downloads according to
Google Play Store.

These 2 apps benefit from the fact that the government does not provide an app for their
own service.

Char49 performed an assessment on these 2 apps in order to check how the personal
information (fiscal number, password, invoices, etc.) are handled. We focused in 3 main
1. Data Storage
2. Network Communication
3. Debug/Log Messages


Data Storage


Storing sensitive information in smartphones is a hard task. Usually, it is not recommended
to store credentials on the device. However, both apps give the possibility to store
passwords locally.

Let’s start on the app where the credentials are just stored in plain text in the database. As
shown, in the following code, the fiscal number (NIF) and the password are simply inserted
in the SQLite database:

public static void insert(User user) {
SQLiteDatabase db =
ContentValues values = new ContentValues();
values.put("name", user.getName());
values.put("email", user.getEmail());
values.put(UserEntry.COLUMN_NIF, user.getTaxId());
values.put(UserEntry.COLUMN_PICTURE, user.getPicPath());
values.put(UserEntry.COLUMN_ADDRESS, user.getAddress());
values.put(UserEntry.COLUMN_COUNTRY, user.getCountry());
values.put(UserEntry.COLUMN_UPDATED, user.getUpdated());
values.put(UserEntry.COLUMN_PASSWORD, user.getPassword());
values.put(UserEntry.COLUMN_TUBPROCESS, user.getTubProcess());
long id = db.insert("user", null, values);
if (id > 0) {

Regarding the second app, it appears that the developers tried to obfuscate the credentials.
Indeed, at first, we found the following functions:

private static String getPassword (Context context) {
rot13Decode (context.getSharedPreferences("any_prefnameefactura",
0).getString("taxadeiva", null));
private static String getNif (Context context) {
rot13Decode (context.getSharedPreferences("any_prefnameefactura",
0).getString("taxadeiva2", null));

At some point, the fiscal number (NIF) and the password were stored using the Shared
Preferences API. In order to not store the credentials in plaintext, the developers used an old
and well-know cipher called “ROT13”, which is a simple letter substitution cipher that
replaces a letter with the 13th letter after it in the alphabet, i.e a A becomes a N.

However, we found that this code is not anymore used in the current version. The
developers decided to move on and to store the credentials in an SQLite database. But this
time, the password is encrypted using AES, symetric encryption mechanism.

In the following capture, you can see the following elements:
contanif: Fiscal number in clear text,
contapass: Password encrypted (note: the encrypted password is encoded in
base64 in order to use printable characters),
contanome: First and last name of the individual.

As we can expect, the key to encrypt and decrypt the password are hardcoded in the code
as shown below (keys have been redacted):

public Boolean DecrypterSetup(Context context) throws Exception {
byte[] initializationVector = new byte[16];
this.secretKySpec = new
SecretKeySpec(SecretKeyFactory.getInstance("PBKDF2WithHmacSHA1"). generateSe
cret(new PBEKeySpec("yXXXXXXX".toCharArray(), "42XXXXXX".getBytes(), 1024,
256)).getEncoded(), "AES");
this.cipher = Cipher.getInstance("AES/CBC/PKCS7Padding");
SharedPreferences settingsSharedPref =
context.getSharedPreferences("any_prefnameefactura", 0);
String strIV = settingsSharedPref.getString("IVect", "");
if (strIV.length() == 0) {
new Random().nextBytes(initializationVector);
Editor editSharedPref = settingsSharedPref.edit();
Base64.encodeToString(initializationVector, 0));
if (!editSharedPref.commit()) {
return Boolean.valueOf(false);
initializationVector = Base64.decode(strIV, 0);
this.ivParmSpec = new IvParameterSpec(initializationVector);
return Boolean.valueOf(true);

In the previous extract, you can also identify that the initialization vector is stored in a Shared Preferences file.



Regarding how invoices are stored, both apps store them in a SQLite database without any
protection. One of the apps is storing very detailed information for each invoice as shown

As you can see, you can retrieve the following elements:
● Merchant fiscal number (nifemitente),
● Merchant name (nomeemitente),
● Invoice unique identifier (numerodocumento),
● Customer fiscal number (nifadquirente),

Comparing to the other app tested, the same kind of information is also stored but with less


Network Communication

In a standard mobile application assessment, we look for unencrypted communications to
external server. However, in our case, both apps have to communicate with the following
portuguese websites:

These websites are only available using TLS communication (HTTPS). So, we focused our
analysis on insecure configuration or implementation.

One of the apps, they added an extra security layer with the implementation of a technique
called “Certificate Pinning”. In order to ensure that the app is connecting to the legitimate servers, the app keeps a fingerprint from the TLS certificate provided by the websites. The fingerprint of the previous websites is defined inside the application on the following class:

public final class xxKeyManager extends CertPinManager {
protected Map<String, String> getPins() {
Map<String, String> map = new HashMap();
return map;

And then, we found that this class is used to authenticate the user or retrieve data from the
government websites as shown below:

TrustManager[] tm = new TrustManager[]{new
SSLContext sc = SSLContext.getInstance("TLS");
sc.init(null, tm, null);
SSLSocketFactory ssFactory = new
String csrf = getCSRF(ssFactory);
if (csrf == null || isCancelled()) {
return null;
Map<String, String> result = authenticate(user,
csrf, ssFactory);

Regarding the other app, there is no Certificate Pinning implemented but the TLS certificate
is correctly checked. However, during our investigation, we found that the app is connecting
to an external server using HTTP protocol:

The performed requests are limited but seems to be used in order to obtain details about a
merchant. The app sends a list of merchant fiscal numbers and the server replies with some
information such as merchant name, merchant address, merchant category, etc.

Here is a request performed by the app:

An attacker by sniffing the network is able to identify which merchants used to be frequented by the user.


Debug/Log Messages

A most common mistake performed by developers is to let debug messages inside
production code. Sometimes these debug messages can leak sensitive information.

In the case of one of the apps, we found that all the requests performed against the
government websites are logged. In addition, the full content of the request is displayed,
which allows to retrieve the credentials of the user when the authentication request is
performed, as shown below:



Keeping sensitive information securely in Android devices is a hard task. In addition,
developers need to keep in mind which risk scenarios could be used against their app (theft,
rooted device, usb debugging enabled, etc.).

Regarding the data manipulated by the app, some choices should be made.

Concerning passwords, we strongly recommend to NOT store them in the device. If the
device is stolen, an attacker could retrieve it and use it against the service. In a worst
scenario, if the user shares this password to other services, the impact could be important. If it is really needed to store passwords, the Android Keystore should be used, but it is only
available in recent Android versions.

Concerning personal information, we recommend to store them in an encrypted SQLite
database. The password should be derived from a PIN or a password asked to the user.

Network communication should be always enforced encryption (TLS) to ensure
confidentiality and integrity. Developers should consider the network as hostile. Implementing Certificate Pinning is a very good security level. However, the most important
is to ensure that the TLS certificate from the server is correctly checked and only strong
ciphers are allowed. In many assessments, we found developers disabling TLS certificate
checking during the development phase and then forgetting to re-enable it in production.

Debug and log messages should be disabled in production environment in order to avoid
leaking sensitive information.

Finally, during the assessment of these apps we didn’t find malicious behavior, for instance
the app trying to steal credentials or personal information. The main mistakes found here are
mainly due to a lack of security and privacy awareness.

Char49 recommend end users to be careful when installing those kind of apps and mainly
what kind of data you are allowing the app to access. As security practices, we
recommend to:
● Check the permissions asked by the app (the less, the better) and accept only the ones that are truly necessary,
● Not use the “Remember password” in the app,
● Avoid using apps manipulating sensitive information in public Wifi or not trusted
● Use a strong password to lock your smartphone (at least 6 characters),
● Not let your phone unattended.

Leave a Reply

Your email address will not be published. Required fields are marked *