Skip to content

Secret Management

Storing secrets like API keys or tokens directly in an app's binary can be risky, but simple obfuscation techniques can add a layer of protection against casual reverse engineering. One such method is XOR encryption, which scrambles the secret using a key, making it harder to extract from the binary through static analysis. While this approach isn’t foolproof, it provides a lightweight security measure without requiring runtime retrieval.

Use Cases

  • API Keys & Authentication Tokens: Protect sensitive credentials embedded in your app
  • Feature Flags & Unlock Codes: Secure premium feature activation without plaintext exposure
  • Configuration Values: Obscure sensitive configuration parameters

Advantages & Limitations

AdvantagesLimitations
Minimal implementation complexityNot cryptographically secure (can be reversed with effort)
Negligible performance impactXOR key must also be protected from exposure
Prevents trivial binary scanningInappropriate for high-security secrets (e.g., cryptographic keys)
Works offline without external dependenciesShould be used alongside other security measures

WARNING

Never rely solely on obfuscation for critical secrets. Use proxy keys that can be revoked and rotated by your backend. Design your security architecture assuming secrets may eventually be exposed.

Implementation

  1. Create a Swift File
    • In Xcode, go to File > New > File From Template...
    • Select Swift File, name it TokenManager.swift, and paste the following code:
swift
import Foundation

class TokenManager {

    private func xor(data: [UInt8], with key: [UInt8]) -> [UInt8] {
        return data.enumerated().map { $0.element ^ key[$0.offset % key.count] }
    }

    func generateObfuscatedToken(token: String, XORKey: [UInt8]) {
        let tokenKeyBytes = [UInt8](token.utf8)
        let obfuscatedBytes = xor(data: tokenKeyBytes, with: XORKey)
        print("Obfuscated token: \(obfuscatedBytes)")
    }

    func decodeToken(obfuscatedToken: [UInt8], XORKey: [UInt8]) -> String? {
        let decryptedBytes = xor(data: obfuscatedToken, with: XORKey)
        let data = Data(decryptedBytes)
        return String(data: data, encoding: .utf8)
    }

}
  1. Generate Obfuscated Values (Development Only)
    • In MainViewController.swift
    • Decide on a random XOR key, this is an array of [UInt8] numbers.
    • In the func viewDidLoad() add the code below and run the app.
    • TokenManager will print out the obfuscated token in the Xcode console.
swift
// XOR key used to create secrets (must be random)
let secretsKey: [UInt8] = [0x31, 0x7C, 0x9A, 0x4E, 0x0F, 0x85, 0x6D, 0x2B]

let tokenManager = TokenManager()
tokenManager.generateObfuscatedToken(token: "SECRET", XORKey: secretsKey)

WARNING

Remove this code before production deployment

  1. Expose Secrets to JavaScript
  • In MainViewController.swift store your XOR key as a class property and use it to decode obfuscated values at runtime:
swift
class ViewController: UIViewController, WKUIDelegate, WKScriptMessageHandler, WKNavigationDelegate {
    // Class properties
    private let secretsKey: [UInt8] = [0x31, 0x7C, 0x9A, 0x4E, 0x0F, 0x85, 0x6D, 0x2B]

    // ...existing code...

    func webView(_ webView: WKWebView, didFinish navigation: WKNavigation!) {
        let tokenManager = TokenManager()

        // Decode obfuscated values (replace with your actual obfuscated bytes)
        let mySecret = tokenManager.decodeToken(
            obfuscatedToken: [98, 57, 217, 28, 74, 209],
            XORKey: secretsKey
        )

        // Inject values into JavaScript context
        let javaScriptConstants = """
            const __MY_SECRET__ = "\(mySecret ?? "")";
            const __DEBUG_MODE__ = \(MainViewController.debugMode);
            const __DEVICE_NAME__ = "\(UIDevice.current.name)";
            // ...other constants...
        """
        evaluateJavascript(javaScript: javaScriptConstants)
        // ...other code...
    }
}

Now when the app runs the __MY_SECRET__ global constant will be available to the JavaScript.

Best Practices

  1. Randomize XOR Keys: Use truly random values for your XOR key, not predictable patterns
  2. Layer Security: Combine obfuscation with additional protections like code guards and integrity checks
  3. Monitor Usage: Implement server-side monitoring to detect unusual API key usage patterns
  4. Plan for Compromise: Design your backend to quickly revoke and rotate compromised secrets