Secret Keys Security in Apps

Bo Liu
Axel Springer Tech
Published in
4 min readJan 18, 2024

Background

Perhaps since I studied Information Security in university I always pay attention to how to prevent secret leaking during development. As an App developer, I know how critical AppSec is. Many apps often face security risks including phishing, data leaks, fraud, broken access control, injection, cryptographic failures etc. Though Apple already provides many security measures, such as sandbox, forcing apps to use HTTPS, certifications and signatures, we still need to be concerned about some hidden risks.

In regular development life, some devs store secret keys in .plist file but it’s literally dangerous since the .plist file could easily be found in ipa. Then how about writing hard-coded API Keys in the codebase? To be honest, decompiling isn’t hard work for most hackers. If your Keys are stored in plain text, no sweat for getting them.

What’s the solution?

To prevent directly exposing sensitive information in the codebase and repository, encryption is always an excellent choice. Even if hackers get our strings of encoded secret keys via some vicious approaches, they are not able to straightly use them. Then, which cryptographic strategy is suitable for us? In my experience, we attempted to symmetric-key algorithm plus public-key encryption to keep our keys safe. Encoding secret content with RSA and encrypting private key with DES, which we learn from SSH. The whole secure system design was quite complex, but I think it’s worth it for a Fintech company or a business with payment. However, it looks over-design for our WELT News app currently. Therefore, we consider to select a light solution. Meanwhile, my colleague Shayan recommended a nice tool called Arkana. It is simple and easy to use, let me introduce it further.

Arkana

Arkana derives from Latin arcanum, meaning secret. From the name we can learn that this tool was born to keep information secure. It supports Android and iOS projects. The basic principle is generating Kotlin or Swift code including encoded secrets based on plaintext in a .env file.

Installation

It can be installed by gem:

gem install arkana

Usage

First of all, store the secret keys in a dotenv file. Learn more about dotenv here. For example:

MySecretAPIKey = 1234556
MyPassword = axelspringer
SeviceAPIKeyDebug = 111111
SeviceAPIKeyRelease = 222222

We can save this file in a safe place and don’t advise you to commit it.

Second, let’s create a YAML config file for Arkana. You could set up many stuffs in it, such as SPM or Cocoapods, Kotlin or Swift, environment, which secrets you want to encode etc. Arkana offers a template.yml with comments. Please configure it according to your demands. Here is an example of it:

import_name: 'ArkanaKeys'
swift_declaration_strategy: let
package_manager: spm
environments:
- Debug
- Release
global_secrets:
- MySecretAPIKey
- MyPassword
environment_secrets:
- SeviceAPIKey

Once we finish these two files, we can generate the code by arkana command:

Usage: arkana [options]
-c /path/to/your/.arkana.yml, Path to your config file. Defaults to '.arkana.yml'
--config-filepath
-e /path/to/your/.env, Path to your dotenv file. Defaults to '.env' if one exists.
--dotenv-filepath
-f, --flavor FrostedFlakes Flavors are useful, for instance, when generating secrets for white-label projects. See the README for more information
-i debug,release, Optionally pass the environments that you want Arkana to generate secrets for. Useful if you only want to build a certain environment, e.g. just Debug in local machines, while only building Staging and Release in CI. Separate the keys using a comma, without spaces. When omitted, Arkana generate secrets for all environments.
--include-environments
-l, --lang kotlin Language to produce keys for, e.g. kotlin, swift. Defaults to 'swift'. See the README for more information

Assume our dotenv file is called .env.keys and the YAML file is called .arkana.yml, we can generate the swift code in debug environment by this command:

arkana -c .arkana.yml -e .env.keys -i debug -l swift

Cool, we get a swift package called ArkanaKeys now. Then, we can integrate it into our Xcode project as a local dependency. When we need to use the keys in our codebase, let’s say:

ArkanaKeys.Debug().seviceAPIKey
ArkanaKeys.Global().myPassword

And don’t forget to import the package name in the file.

In the package sources, we can find the secrets are encrypted as hex arrays.

public extension ArkanaKeys {
struct Global: ArkanaKeysGlobalProtocol {
public init() {}

@inline(__always)
public let mySecretAPIKey: Int = {
let encoded: [UInt8] = [
0xe3, 0x58, 0x9d, 0x79, 0x81, 0x57, 0x2c
]
return ArkanaKeys.decode(encoded: encoded, cipher: ArkanaKeys.salt)
}()
@inline(__always)
public let myPassword: String = {
let encoded: [UInt8] = [
0xb8, 0x2, 0xc6, 0x2b, 0xd0, 0x9, 0x69, 0x21, 0xcb, 0x62
]
return ArkanaKeys.decode(encoded: encoded, cipher: ArkanaKeys.salt)
}()
}
}

It’s a simple symmetric-key method and calls the key salt. The salt is also a hex array with a length of 64. Every time you generate the code by command, the salt always changes, which keeps certain security.

fileprivate static let salt: [UInt8] = [
0xd2, 0x6a, 0xae, 0x4d, 0xb4, 0x62, 0x1a, 0x49, 0xad, 0xb, 0x41, 0x15, 0xf5, 0x35, 0xf9, 0x22, 0x7, 0x12, 0x8d, 0x10, 0x72, 0x24, 0xd1, 0xfa, 0x86, 0xc1, 0xd7, 0x21, 0xc3, 0xf5, 0xc, 0x20, 0x9b, 0xac, 0x7c, 0x6c, 0x6a, 0x83, 0x4, 0x27, 0x8a, 0x5b, 0xe9, 0x7f, 0x3d, 0x62, 0xec, 0x1a, 0xda, 0xf2, 0x7a, 0xb5, 0x1, 0xc0, 0xdd, 0x3c, 0x53, 0x8b, 0x6f, 0x19, 0xf6, 0xe3, 0xbd, 0xd5
]

The encryption algorithm is not too complex, enough reliable and efficient. Thus, Arkana is a good choice for us and makes our lives safer. However, we still need to note that there is no absolute security in this world. We must adopt flexible means to respond to potential risks at any time.

--

--