Weak Encoding for Password

Description

Weak Encoding for Password is a vulnerability that occurs when a product obscures passwords using trivial encoding methods such as Base64, ROT13, XOR with a fixed key, or simple character substitution, rather than employing proper cryptographic protection. Encoding transforms data into a different representation but is designed to be reversible by anyone who knows the encoding scheme - it provides no security against determined attackers. When passwords are merely encoded rather than properly hashed or encrypted, anyone who gains access to the encoded data can easily decode it to retrieve the original plaintext password. This creates a false sense of security while leaving credentials fundamentally unprotected.

Risk

Using weak encoding for passwords creates severe security risks because the protection is illusory. Base64 and similar encodings are standardized, well-documented, and instantly reversible using freely available tools. Attackers who gain access to configuration files, databases, or memory dumps containing encoded passwords can decode them within seconds. The risk is amplified when developers mistakenly believe encoding provides security and therefore implement weaker access controls on encoded credential storage. Unlike hashing, encoding preserves the original password, meaning a single breach exposes all affected credentials in their original form. This enables credential stuffing attacks if users reuse passwords across systems.

Solution

Never use encoding (Base64, hex, URL encoding, etc.) as a password protection mechanism. For password storage, use strong one-way hashing algorithms specifically designed for passwords, such as Argon2id, bcrypt, or scrypt, with appropriate cost factors. For passwords that must be recoverable (such as for connecting to external services), use strong encryption with keys at least 128 bits in length, storing the encryption key separately from the encrypted password. Implement proper key management practices including key rotation and access controls. If legacy systems use encoded passwords, plan migration to proper cryptographic protection. Use static analysis tools to detect encoding functions applied to password variables.

Common Consequences

ImpactDetails
Access ControlScope: Access Control

Attackers who gain access to encoded passwords can easily decode them to obtain plaintext credentials. This enables privilege escalation, identity assumption, and unauthorized access to protected resources.

Example Code

Vulnerable Code (Java)

The following code demonstrates vulnerable password encoding patterns:

import java.util.Base64;
import java.util.Properties;

public class VulnerablePasswordStorage {

    // Vulnerable: Base64 encoding for password "protection"
    public void storePasswordVulnerable(String username, String password) {
        // Base64 encoding provides ZERO security
        String encoded = Base64.getEncoder().encodeToString(password.getBytes());

        Properties props = new Properties();
        props.setProperty(username + ".password", encoded);
        // Store to file...

        // Anyone can decode:
        // echo "UGFzc3dvcmQxMjM=" | base64 -d
        // Result: Password123
    }

    // Vulnerable: XOR "encryption" with fixed key
    public String xorEncode(String password, String key) {
        StringBuilder result = new StringBuilder();
        for (int i = 0; i < password.length(); i++) {
            result.append((char)(password.charAt(i) ^ key.charAt(i % key.length())));
        }
        // This is trivially reversible with the same operation
        return Base64.getEncoder().encodeToString(result.toString().getBytes());
    }

    // Vulnerable: ROT13 "obfuscation"
    public String rot13(String password) {
        StringBuilder result = new StringBuilder();
        for (char c : password.toCharArray()) {
            if (c >= 'a' && c <= 'z') {
                c = (char)(((c - 'a' + 13) % 26) + 'a');
            } else if (c >= 'A' && c <= 'Z') {
                c = (char)(((c - 'A' + 13) % 26) + 'A');
            }
            result.append(c);
        }
        return result.toString();  // Applying ROT13 twice = original
    }

    // Vulnerable: Hex encoding
    public String hexEncode(String password) {
        StringBuilder hex = new StringBuilder();
        for (byte b : password.getBytes()) {
            hex.append(String.format("%02x", b));
        }
        return hex.toString();  // Trivially reversible
    }
}
// Vulnerable C# example - Base64 in registry
using Microsoft.Win32;
using System;

public class VulnerableCredentialStore
{
    // Vulnerable: Base64 encoded password in registry
    public void StoreCredentials(string username, string password)
    {
        // Base64 is NOT encryption!
        string encodedPassword = Convert.ToBase64String(
            System.Text.Encoding.UTF8.GetBytes(password)
        );

        RegistryKey key = Registry.CurrentUser.CreateSubKey(@"Software\MyApp");
        key.SetValue("Username", username);
        key.SetValue("Password", encodedPassword);  // Easily decoded
        key.Close();
    }
}

Fixed Code (Java)

import javax.crypto.SecretKeyFactory;
import javax.crypto.spec.PBEKeySpec;
import javax.crypto.Cipher;
import javax.crypto.SecretKey;
import java.security.SecureRandom;

public class SecurePasswordStorage {

    // Fixed: Proper password hashing for storage
    public String hashPassword(String password) {
        // Use bcrypt, Argon2, or PBKDF2
        int iterations = 100000;
        int keyLength = 256;
        byte[] salt = generateSalt();

        PBEKeySpec spec = new PBEKeySpec(
            password.toCharArray(),
            salt,
            iterations,
            keyLength
        );

        try {
            SecretKeyFactory factory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA256");
            byte[] hash = factory.generateSecret(spec).getEncoded();

            // Store salt + hash together
            return Base64.getEncoder().encodeToString(salt) + ":" +
                   Base64.getEncoder().encodeToString(hash);
        } finally {
            spec.clearPassword();
        }
    }

    public boolean verifyPassword(String password, String storedHash) {
        String[] parts = storedHash.split(":");
        byte[] salt = Base64.getDecoder().decode(parts[0]);
        byte[] expectedHash = Base64.getDecoder().decode(parts[1]);

        // Hash the provided password with the same salt
        byte[] actualHash = hashWithSalt(password, salt);

        // Constant-time comparison
        return MessageDigest.isEqual(expectedHash, actualHash);
    }

    // Fixed: Proper encryption for recoverable credentials
    public byte[] encryptCredential(String credential, SecretKey key) throws Exception {
        Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding");
        byte[] iv = new byte[12];
        new SecureRandom().nextBytes(iv);

        cipher.init(Cipher.ENCRYPT_MODE, key, new GCMParameterSpec(128, iv));
        byte[] encrypted = cipher.doFinal(credential.getBytes());

        // Prepend IV to encrypted data
        byte[] result = new byte[iv.length + encrypted.length];
        System.arraycopy(iv, 0, result, 0, iv.length);
        System.arraycopy(encrypted, 0, result, iv.length, encrypted.length);

        return result;
    }

    private byte[] generateSalt() {
        byte[] salt = new byte[16];
        new SecureRandom().nextBytes(salt);
        return salt;
    }
}

The fix uses proper password hashing (PBKDF2, bcrypt, or Argon2) for password storage and authenticated encryption (AES-GCM) for credentials that must be recoverable.


Exploited in the Wild

Jenkins Credential Encoding (Jenkins, Multiple CVEs)

Multiple versions of Jenkins stored credentials using Base64 encoding that was easily decoded by users with access to configuration files. While later versions implemented proper encryption, many legacy installations and backup files contained easily decoded credentials that attackers exploited during post-compromise reconnaissance.

Router Firmware Base64 Passwords (Multiple Vendors, Ongoing)

Numerous router and IoT device manufacturers have stored administrative passwords using Base64 encoding in firmware images and configuration files. Security researchers regularly extract and decode these credentials, publishing them in vulnerability disclosures and enabling attacks against deployed devices.

Source Code Repository Encoded Credentials (GitHub/GitLab, Ongoing)

Developers frequently commit Base64-encoded credentials to version control systems, believing the encoding provides protection. Automated scanning tools detect and decode these credentials, leading to widespread compromise of API keys, database passwords, and service accounts.


Tools to Test/Exploit

  • CyberChef — Web-based tool for instantly decoding Base64, hex, ROT13, and other encoding schemes.

  • Gitleaks — Detects encoded secrets in repositories and can identify common encoding patterns.

  • Base64 Command Line — Standard Unix tool for encoding/decoding Base64, demonstrating trivial reversibility.


CVE Examples

  • CVE-2019-10352 — Application stored passwords using Base64 encoding that was easily decoded.

  • CVE-2018-10936 — PostgreSQL JDBC driver transmitted Base64-encoded passwords in log files.

  • CVE-2020-5410 — Spring Cloud Config Server exposed Base64-encoded secrets in configuration.


References

  1. MITRE Corporation. "CWE-261: Weak Encoding for Password." Common Weakness Enumeration. https://cwe.mitre.org/data/definitions/261.html

  2. OWASP Foundation. "Password Storage Cheat Sheet." https://cheatsheetseries.owasp.org/cheatsheets/Password_Storage_Cheat_Sheet.html

  3. NIST. "Digital Identity Guidelines: Authentication and Lifecycle Management." SP 800-63B. https://pages.nist.gov/800-63-3/sp800-63b.html