The Hidden Cost of Source Code Obfuscation

Today I want to talk about source code obfuscation for Android applications. It is well known why it is important and how it can be done using Proguard. But what is the price of enabling obfuscation? How it affects your application? This is what I want to discuss today.

But why it is important to understand the price?

Think about the last time you went to shopping. When you try to decide if you want to purchase a specific product, one thing you need to consider is the price: Does the value this product gave me is bigger than it’s price? And the same question should be asked also for security controls. We, the security team, is responsible to help you, the developer (or the product managers), understand the value it can give you (what threats it can mitigate) and it’s price (how it can affect your software). By understanding both the value and the price, you can take the right decision — for you.

Should you buy tomatoes? Check the price to decide (source: pixabay)

Now that we understood why it the price is important, let’s go back to obfuscation. From my experience, there are three side effects:

  • Introducing new bugs
  • Production debugging
  • Debug vs Release builds

Let’s dive in into each one of those side effects, understand what it is and what we can do to handle it.

1. Introducing new bugs

The first side effect — enabling source code obfuscation can and will introduce new bugs. It happens because obfuscation changes your code - including changing classes/properties/functions names. This is good, as long as you don’t have code that uses classes/properties/functions names.

One example is serialization — because the serialization process is using (by default)the object properties names (see, for example, the Gson documentation). The Obfuscation process will change the properties names — which will change the serialized JSON accordingly. This could, for example, results in invalid JSON being sent to your server.

Your server, when receiving the obfuscated JSON (source: me.me)

Another example is code that uses reflection. Let say that your code is looking for a specific property foo on all the classes. This code will break when you enable obfuscation — because the property name has changed.

What can you do? Actually, good solutions exist, because this issue is so common. For example, you can exclude specific classes from being obfuscated (see this question on Stack Overflow). Another option is to explicitly specify how to serialize the class (this is the relevant Gson documentation). You still need to be aware to this issue and be prepared to have some extra work to handle it.

It also important to remember that this is an on-going process — each new class that is serialized, or new code that use reflection, could introduce this bug. You need to have a process to handle it (for example, code review question or static analysis) so you could easily solve them.

2. Production Debugging

When investigating a production crash, the only useful information you have is the exception that was thrown (what happened) and the call stack (where it happened). This information is useful when the call stack contains meaningful methods names, but what happens when the code is obfuscated? All the classes and methods will have obfuscated names — resulted in a meaningless call stack. It will be very hard to understand what is a.b.t.1 and an exception was thrown there.

This issue is also common and there is a simple solution. ProGuard generates a mapping file that translates between the random names of classes, methods, and properties to their original names. By using this file, you could translate the obfuscated call stack and make it meaningful again.

An alternative solution to the problem (source: Reddit)

That sounds simple, but there is some work to be done in order for this solution to work: First, You need to keep all the mapping files for all the versions that are currently in production. Those files should be kept relatively secure, because if a hacker get this file — the obfuscation is meaningless. Second, if you’re using an error reporting service (like Bugsnag or Fabric), you need to upload this file to there too. This adds some operational overhead that you should take into consideration.

3. Release vs Debug build

Those two side effects are related to the last side effect I wanted to discuss. The common practice is to enable obfuscation only on release build. Why? This is because of the difference between debug and release builds.

Debug build is what developers are using for their daily work. This build is focused on the developer experience — it needs to be fast, and easy to debug. Release build is focused on the user — they should work fast and secure. This is why obfuscation is done only in release build —an obfuscated code is a lot harder to debug (that was what the previous section was about). Also, obfuscation makes the build a bit slower.

The only problem — it creates a significant difference between the release and debug builds. Code obfuscation can and will introduce new bugs (this was the first issue I discussed). Enabling obfuscation only on release build means that those issues will be discovered really late — only when using the release build: by an end to end or acceptance tests — or, in the worst case, only on production.

The result of debug vs release issues (source: imgflip)

This is a serious side effect. What makes it even worse is the fact that there is not a lot you can do to handle this issue. One option is enabling obfuscation also for debug builds, but it introduces other issues (like I discussed above).

Wrapping Up

Source code obfuscation is an important security control. When done correctly, it provides another layer of protection (of course, it should never be the only protection). But like any other security control, it comes with a price. Now you have a better understanding of the price, and you can take the right decision for you — based on the threat model of your application. There are situations when you don’t have a choice — for example, when you are required to comply with a specific regulation. Even in those situations, understanding the price could help you estimate what the required effort.

And one last word — OWASP is here for your help. While working on enabling obfuscation on anAndroid application, I needed help. The Mobile Testing Guide and the people on the relevant Slack channel helped me a lot during the process. Don’t hesitate to ask for help when you need it.