J2EE Misconfiguration: Plaintext Password in Configuration File
Description
J2EE Misconfiguration: Plaintext Password in Configuration File is a vulnerability where a Java Enterprise Edition application stores passwords in plaintext within configuration files. These configuration files, including properties files, XML configuration, and deployment descriptors, often contain database credentials, LDAP passwords, service account credentials, and API keys stored without any encryption or protection. Anyone who gains access to the file system or configuration management system can read these credentials and use them to access protected resources.
Risk
Storing plaintext passwords in configuration files creates severe security risks. Configuration files are frequently backed up, versioned in source control, copied during deployments, and accessible to system administrators and developers. A single exposed configuration file can compromise database servers, directory services, email systems, and other critical infrastructure. Attackers who gain read access to the file system through directory traversal, backup exposure, or source code leaks immediately obtain working credentials. The risk extends beyond the immediate credentials, as password reuse means exposed credentials may work on other systems.
Solution
Never store plaintext passwords in configuration files. Use industry-standard encryption to protect sensitive credentials before storing them in configuration. Implement proper key management for encryption keys separate from the encrypted data. Use secure credential vaults like HashiCorp Vault, AWS Secrets Manager, or Azure Key Vault. Leverage container orchestration secrets management in Kubernetes or Docker Swarm. Use environment variables for sensitive data instead of configuration files. Implement proper file permissions to restrict access to configuration files. Encrypt sensitive configuration sections using J2EE server features or external tools.
Common Consequences
| Impact | Details |
|---|---|
| Access Control | Scope: Access Control Bypass Protection Mechanism - Exposed credentials allow attackers to bypass authentication and gain unauthorized access to databases, LDAP directories, and other protected resources. |
Example Code
Vulnerable Code
# Vulnerable: database.properties with plaintext credentials
# File: /WEB-INF/classes/database.properties
# Vulnerable: Database credentials in plaintext
db.driver=com.mysql.jdbc.Driver
db.url=jdbc:mysql://db.company.internal:3306/production
db.username=app_admin
db.password=Pr0duct1on_P@ssw0rd_2024!
# Vulnerable: Connection pool settings with credentials
pool.maxActive=100
pool.maxIdle=30
pool.validationQuery=SELECT 1
# Vulnerable: LDAP configuration with plaintext password
# File: /WEB-INF/classes/ldap.properties
ldap.url=ldap://ldap.company.internal:389
ldap.base=dc=company,dc=com
ldap.username=cn=admin,dc=company,dc=com
ldap.password=LDAPAdmin123!
# Vulnerable: Service account credentials
ldap.service.dn=cn=webapp,ou=services,dc=company,dc=com
ldap.service.password=S3rv1c3P@ss!
<!-- Vulnerable: persistence.xml with plaintext credentials -->
<!-- File: /META-INF/persistence.xml -->
<persistence xmlns="http://java.sun.com/xml/ns/persistence" version="2.0">
<persistence-unit name="ProductionPU">
<provider>org.hibernate.jpa.HibernatePersistenceProvider</provider>
<properties>
<property name="javax.persistence.jdbc.driver"
value="com.mysql.jdbc.Driver"/>
<property name="javax.persistence.jdbc.url"
value="jdbc:mysql://db.company.internal:3306/production"/>
<!-- Vulnerable: Plaintext credentials -->
<property name="javax.persistence.jdbc.user" value="db_admin"/>
<property name="javax.persistence.jdbc.password"
value="DatabasePassword123!"/>
<property name="hibernate.show_sql" value="false"/>
<property name="hibernate.hbm2ddl.auto" value="validate"/>
</properties>
</persistence-unit>
</persistence>
<!-- Vulnerable: context.xml with datasource credentials -->
<!-- File: /META-INF/context.xml -->
<Context>
<!-- Vulnerable: Database resource with plaintext password -->
<Resource name="jdbc/ProductionDB"
auth="Container"
type="javax.sql.DataSource"
driverClassName="com.mysql.jdbc.Driver"
url="jdbc:mysql://db.company.internal:3306/production"
username="webapp_user"
password="W3b@ppP@ss123!"
maxTotal="100"
maxIdle="30"
maxWaitMillis="10000"/>
<!-- Vulnerable: Mail session with plaintext credentials -->
<Resource name="mail/Session"
auth="Container"
type="javax.mail.Session"
mail.smtp.host="smtp.company.internal"
mail.smtp.port="587"
mail.smtp.auth="true"
mail.smtp.user="[email protected]"
password="Sm7pP@ssw0rd!"/>
</Context>
// Vulnerable: Java code loading plaintext credentials
public class VulnerableConfigLoader {
public DataSource getDataSource() throws Exception {
// Vulnerable: Loading plaintext credentials from properties
Properties props = new Properties();
props.load(getClass().getResourceAsStream("/database.properties"));
BasicDataSource ds = new BasicDataSource();
ds.setDriverClassName(props.getProperty("db.driver"));
ds.setUrl(props.getProperty("db.url"));
ds.setUsername(props.getProperty("db.username"));
ds.setPassword(props.getProperty("db.password")); // Plaintext!
return ds;
}
// Vulnerable: Hardcoded credentials as fallback
private static final String DEFAULT_PASSWORD = "DefaultP@ss123";
public Connection getConnection() throws SQLException {
String password = System.getProperty("db.password", DEFAULT_PASSWORD);
return DriverManager.getConnection(DB_URL, DB_USER, password);
}
}
Fixed Code
// Fixed: Secure credential management with encryption
import javax.crypto.Cipher;
import javax.crypto.SecretKey;
import javax.crypto.spec.SecretKeySpec;
import java.util.Base64;
public class SecureConfigLoader {
private final SecretKey encryptionKey;
public SecureConfigLoader() {
// Fixed: Load encryption key from secure source (HSM, env var, etc.)
String keyString = System.getenv("CONFIG_ENCRYPTION_KEY");
if (keyString == null) {
throw new IllegalStateException("Encryption key not configured");
}
this.encryptionKey = new SecretKeySpec(
Base64.getDecoder().decode(keyString), "AES");
}
public String decryptPassword(String encryptedPassword) throws Exception {
Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding");
cipher.init(Cipher.DECRYPT_MODE, encryptionKey);
byte[] decrypted = cipher.doFinal(
Base64.getDecoder().decode(encryptedPassword));
return new String(decrypted);
}
public DataSource getDataSource() throws Exception {
Properties props = new Properties();
props.load(getClass().getResourceAsStream("/database.properties"));
BasicDataSource ds = new BasicDataSource();
ds.setDriverClassName(props.getProperty("db.driver"));
ds.setUrl(props.getProperty("db.url"));
ds.setUsername(props.getProperty("db.username"));
// Fixed: Decrypt password before use
String encryptedPassword = props.getProperty("db.password.encrypted");
ds.setPassword(decryptPassword(encryptedPassword));
return ds;
}
}
# Fixed: database.properties with encrypted credentials
# File: /WEB-INF/classes/database.properties
db.driver=com.mysql.jdbc.Driver
db.url=jdbc:mysql://db.company.internal:3306/production
db.username=app_admin
# Fixed: Password encrypted with AES-GCM
db.password.encrypted=AQICAHi9bG...base64_encrypted_value...==
// Fixed: Using environment variables for credentials
public class EnvironmentConfigLoader {
public DataSource getDataSource() {
// Fixed: Credentials from environment variables
String dbUrl = getRequiredEnv("DB_URL");
String dbUser = getRequiredEnv("DB_USERNAME");
String dbPassword = getRequiredEnv("DB_PASSWORD");
BasicDataSource ds = new BasicDataSource();
ds.setDriverClassName("com.mysql.jdbc.Driver");
ds.setUrl(dbUrl);
ds.setUsername(dbUser);
ds.setPassword(dbPassword);
return ds;
}
private String getRequiredEnv(String name) {
String value = System.getenv(name);
if (value == null || value.isEmpty()) {
throw new IllegalStateException(
"Required environment variable not set: " + name);
}
return value;
}
}
// Fixed: Using JNDI datasource (credentials in server config)
public class JndiConfigLoader {
public DataSource getDataSource() throws NamingException {
// Fixed: Credentials managed by application server
InitialContext ctx = new InitialContext();
return (DataSource) ctx.lookup("java:comp/env/jdbc/ProductionDB");
}
}
// Fixed: Using HashiCorp Vault for secrets
import com.bettercloud.vault.Vault;
import com.bettercloud.vault.VaultConfig;
import com.bettercloud.vault.response.LogicalResponse;
public class VaultConfigLoader {
private final Vault vault;
public VaultConfigLoader() throws Exception {
// Fixed: Vault authentication via token or AppRole
VaultConfig config = new VaultConfig()
.address(System.getenv("VAULT_ADDR"))
.token(System.getenv("VAULT_TOKEN"))
.build();
this.vault = new Vault(config);
}
public DataSource getDataSource() throws Exception {
// Fixed: Retrieve credentials from Vault
LogicalResponse response = vault.logical()
.read("secret/data/database/production");
Map<String, String> data = response.getData();
BasicDataSource ds = new BasicDataSource();
ds.setDriverClassName("com.mysql.jdbc.Driver");
ds.setUrl(data.get("url"));
ds.setUsername(data.get("username"));
ds.setPassword(data.get("password"));
return ds;
}
}
<!-- Fixed: Spring configuration with encrypted properties -->
<!-- File: applicationContext.xml -->
<beans xmlns="http://www.springframework.org/schema/beans">
<!-- Fixed: Jasypt encrypted properties -->
<bean id="propertyConfigurer"
class="org.jasypt.spring31.properties.EncryptablePropertyPlaceholderConfigurer">
<constructor-arg ref="configurationEncryptor"/>
<property name="location" value="classpath:database.properties"/>
</bean>
<bean id="configurationEncryptor"
class="org.jasypt.encryption.pbe.StandardPBEStringEncryptor">
<property name="algorithm" value="PBEWithMD5AndTripleDES"/>
<!-- Key from environment variable -->
<property name="passwordEnvName" value="JASYPT_ENCRYPTOR_PASSWORD"/>
</bean>
<bean id="dataSource" class="org.apache.commons.dbcp2.BasicDataSource">
<property name="driverClassName" value="${db.driver}"/>
<property name="url" value="${db.url}"/>
<property name="username" value="${db.username}"/>
<!-- Fixed: Property value is encrypted, decrypted at runtime -->
<property name="password" value="${db.password}"/>
</bean>
</beans>
# Fixed: Jasypt encrypted database.properties
db.driver=com.mysql.jdbc.Driver
db.url=jdbc:mysql://db.company.internal:3306/production
db.username=app_admin
# Fixed: ENC() wrapper indicates encrypted value
db.password=ENC(G6N718UuyPE5bHyWKyuLQSm02auQPUtm)
# Fixed: Kubernetes secret for credentials
# File: database-secret.yaml
apiVersion: v1
kind: Secret
metadata:
name: database-credentials
type: Opaque
stringData:
# These will be base64 encoded automatically
DB_USERNAME: app_admin
DB_PASSWORD: ProductionPassword123!
---
# Fixed: Pod referencing secret
apiVersion: v1
kind: Pod
metadata:
name: webapp
spec:
containers:
- name: webapp
image: myapp:latest
envFrom:
- secretRef:
name: database-credentials
CVE Examples
- CVE-2019-3799: Spring Cloud Config Server exposed plaintext credentials in configuration files.
- CVE-2018-1000130: Jolokia exposed sensitive configuration including credentials.
- CVE-2017-7657: Eclipse Jetty leaked credentials through configuration files.
References
- MITRE Corporation. "CWE-555: J2EE Misconfiguration: Plaintext Password in Configuration File." https://cwe.mitre.org/data/definitions/555.html
- OWASP. "Secrets Management Cheat Sheet."
- OWASP. "Password Storage Cheat Sheet."