# Credential Obfuscation for Nextcloud Tools ## Problem When credentials are embedded in Go binaries via `ldflags`, they appear as plain text and can be easily extracted using the `strings` command: ```bash $ strings ~/bin/nextcloud-client https://teamworkapps.com wltbagent@shortcutsolutions.net 1b8a28ca2fc26820fee3f9a8524c351b ``` This is a security risk for distributed binaries. ## Solution We've implemented a **XOR cipher with random key** approach: 1. **At Build Time**: Credentials are XOR-encrypted with a randomly generated 256-bit key 2. **Encoded**: The encrypted data is Base64-encoded for safe Go embedding 3. **At Runtime**: The binary de-obfuscates credentials in memory only 4. **Never on Disk**: De-obfuscated credentials never touch the disk ## How It Works ### Build Process The `build-obfuscated.sh` script: 1. **Generates random key**: 256-bit (64 hex characters) via `openssl rand -hex 32` 2. **XOR encrypts credentials**: Each credential XOR'd with the key 3. **Base64 encodes**: Makes it safe for Go string embedding 4. **Embeds via ldflags**: Encrypted credentials + key embedded in binary ```bash # Generate random key OBFUSCATION_KEY=$(openssl rand -hex 32) # XOR encrypt with key obfuscated=$(xor_encrypt "$CREDENTIAL" "$OBFUSCATION_KEY") # Base64 encode for embedding obfuscated_b64=$(echo -n "$obfuscated" | base64 -w0) # Build with obfuscated credentials go build -ldflags="-X main.ObfuscatedServer=$obfuscated_b64 \ -X main.ObfuscatedUser=$obfuscated_b64 \ -X main.ObfuscatedPassword=$obfuscated_b64 \ -X main.ObfuscationKey=$OBFUSCATION_KEY" ... ``` ### Runtime De-obfuscation The `obfuscation.go` package provides de-obfuscation: ```go // DeobfuscateString reverses XOR obfuscation func DeobfuscateString(obfuscatedBase64, key string) (string, error) { // Decode base64 obfuscated, _ := base64.StdEncoding.DecodeString(obfuscatedBase64) // XOR de-obfuscate result := make([]byte, len(obfuscated)) keyBytes := []byte(key) for i := 0; i < len(obfuscated); i++ { result[i] = obfuscated[i] ^ keyBytes[i%len(keyBytes)] } return string(result), nil } ``` At runtime, tools call: ```go server, user, password, err := GetDeobfuscatedCredentials() if err != nil { // Handle error } // Now use server, user, password (these are the actual values) ``` ## Security Levels ### Current Implementation (Medium Security) **Strengths:** - ✅ Prevents casual extraction with `strings` - ✅ Credentials appear as Base64 gibberish in binary - ✅ Key changes on every build (unique per binary) - ✅ De-obfuscation happens only in memory **Weaknesses:** - ⚠️ Key is embedded in binary - ⚠️ De-obfuscation code is public - ⚠️ Determined attacker with source could reverse it **Result:** Good for preventing casual access, but not secure against determined reverse engineering. ## Usage ### Build with Obfuscation ```bash cd projects/nextcloud-integration # Use obfuscated build script ./build-obfuscated.sh https://teamworkapps.com ``` The script will: 1. Generate a unique 256-bit obfuscation key 2. Encrypt all credentials with XOR cipher 3. Base64 encode encrypted data 4. Build all tools with embedded encrypted credentials + key 5. Display security information ### Verify Obfuscation ```bash # Check that strings doesn't show plain credentials strings ~/bin/nextcloud-client | grep -i "teamworkapps" # Should NOT find plain server URL # Instead, you'll see base64 gibberish ``` ### Run Tools Tools work exactly the same - obfuscation is transparent: ```bash ~/bin/nextcloud-client --op list --path "/" ~/bin/nextcloud-mail --op list-folders ``` ## Security Comparison | Method | Security | Complexity | Distribution | |---------|----------|------------|--------------| | Plain ldflags (current) | Low | Simple | ❌ Not recommended | | XOR Obfuscation (this) | Medium | Medium | ✅ Better for sharing | | Environment Variables | High | Low | ✅ Best for production | | HSM/Key Management | Very High | High | ✅ Enterprise grade | | Network Auth | Very High | High | ✅ No credentials in binary | ## Better Security Options (For Production) ### Option 1: Environment Variables (Recommended) Remove credentials entirely from binary, require environment variables: ```go // Read from environment at runtime server := os.Getenv("NEXTCLOUD_SERVER") user := os.Getenv("NEXTCLOUD_USER") password := os.Getenv("NEXTCLOUD_PASSWORD") if server == "" || user == "" || password == "" { log.Fatal("NEXTCLOUD_SERVER, NEXTCLOUD_USER, NEXTCLOUD_PASSWORD required") } ``` **Pros:** No credentials in binary at all **Cons:** Requires runtime configuration ### Option 2: Interactive First Run Prompt for credentials on first run, then cache encrypted: ```go if !credentialsFile.Exists() { username := prompt("Nextcloud username: ") password := prompt("Nextcloud password: ") // Encrypt and store locally encrypted := encrypt(username, password, derivedKey) credentialsFile.Write(encrypted) } ``` **Pros:** Never in binary, user controls storage **Cons:** One-time setup required ### Option 3: Network Authentication Credentials stored on a secure server, binary authenticates to fetch them: ```go // Authenticate to credential server token := authenticateToServer(derivedMachineID) credentials := fetchCredentials(token) ``` **Pros:** Most secure, can revoke access **Cons:** Requires infrastructure, network dependency ### Option 4: Hardware Security Module (HSM) Credentials stored in HSM, binary requests operations: ```go // Never see actual credentials result := hsm.Sign(data) ``` **Pros:** Enterprise-grade security **Cons:** Expensive hardware, complex setup ## Implementation Status ### Completed - ✅ `build-obfuscated.sh` - XOR encryption with random key - ✅ `obfuscation/obfuscation.go` - Runtime de-obfuscation package - ✅ Base64 encoding for safe embedding - ✅ Unique key per build ### Pending Integration To enable obfuscation in tools: 1. Import obfuscation package 2. Replace ldflags with obfuscated versions 3. Call `GetDeobfuscatedCredentials()` at runtime 4. Remove plain Build* variables Example migration: ```go // Old way (plain credentials) config.URL = BuildServerURL config.User = BuildUsername // New way (obfuscated) server, user, password, _ := GetDeobfuscatedCredentials() config.URL = server config.User = user config.Token = password ``` ## Recommendation **For Personal Use:** - Current XOR obfuscation is sufficient - Prevents casual extraction - Easy to use, no setup **For Distribution/Production:** - Use environment variables (most secure, simplest) - Or implement network authentication for enterprise - Never distribute binaries with embedded credentials **For Open Source Projects:** - Document credential requirements - Provide setup guide - Never commit credentials - Use .env.example files --- *Credential Obfuscation: 2026-02-20*