Skip to main content

SEC00-G: Do Not Hardcode Secrets


Introduction

Privileged passwords and other secrets are essential for authentication in app-to-app (A2A) and application-to-database (A2D) communications and access.

Applications often deploy with hardcoded default credentials, making them easy targets for hackers using scanning tools and simple guessing or dictionary-style attacks. Development operation (DevOps) tools frequently have secrets hardcoded in scripts or files, compromising the security of the entire automation process. Anyone accessing the class files can decompile them and uncover sensitive information.

Secrets can include:

  • User or auto-generated passwords: These are passwords created either by users themselves or automatically generated by systems for securing accounts and resources.
  • API and other application keys or credentials (including those within containers): These are keys or credentials used to authenticate and authorize access to APIs and applications, which might be stored within container environments.
  • SSH keys: These are cryptographic keys used to securely access servers and other remote systems over the Secure Shell (SSH) protocol.
  • Database and other system-to-system passwords: These are passwords used for authentication between databases and other interconnected systems to ensure secure communication and data exchange.
  • Private certificates for secure communication (TLS, SSL, etc.): These are digital certificates used to establish secure communication channels over protocols like Transport Layer Security (TLS) and Secure Sockets Layer (SSL).
  • Private encryption keys for systems like PGP: These are cryptographic keys used for encrypting and decrypting data in systems like Pretty Good Privacy (PGP) to ensure data confidentiality.
  • RSA and other one-time password devices: These are devices or systems that generate one-time passwords based on RSA algorithms or similar technologies for enhanced security during authentication processes.

Additionally, if hard-coded secrets are used, a new patch will need to be deployed every time a password is leaked, creating significant security and maintenance issues. To mitigate this risk, avoid hardcoding secrets and use secure methods for managing and accessing sensitive information.

Non-Compliant Code Example (Hard-Coded Database Password)

The username and password fields in the SQL connection request are hardcoded in this non-compliant code example:

// Non-compliant Code Example

public final function getConnection() : Connection {
return DriverManager.getConnection(
"jdbc:mysql://localhost/dbName", "username", "password"
)
}

Note that the one- and two-argument java.sql.DriverManager.getConnection methods can be misused.

Compliant Solution (Config File)

Rather than hardcoding at the point of use, extract secrets into a separate configuration file and load them at runtime. This treats the credential file as sensitive (including adding to .gitignore to avoid accidentally checking it into the code repository).

This compliant solution reads credentials from a configuration file located in a secure directory:

// Compliant Solution

public final function getConnection() : Connection {
var username : String // read at runtime from a secure config file
var password : String // read at runtime from a secure config file
return DriverManager.getConnection(
"jdbc:mysql://localhost/dbName", username, password
)
}

Secrets should be stored in character arrays rather than strings because the Java Virtual Machine may retain strings long after they are no longer needed. However, the above example uses strings because the DriverManager.getConnection method requires them.

This method might have the downside of storing the credentials in plain text on your system. In addition, it is difficult to share credentials with teammates safely. One way to share the sensitive configuration file is to encrypt it.

Compliant Solution (Environment Variables)

To prevent accidentally publishing your application secrets to public repositories, set them as environment variables in the operating system instead of including them in your code. Then, you can import these environment variables at runtime and use them as needed.

Environment variables are not specific to applications but to the operating system running your application. The method of setting environment variables differs from operating system to operating system.

You can retrieve these values at runtime, for example, using the Java System.getenv method:

// Compliant Solution

public final function getConnectionCS() : Connection {
var username : String = System.getenv("USERNAME")
var password : String = System.getenv("PASSWORD")
return DriverManager.getConnection("jdbc:mysql://localhost/dbName",
username, password)
}

This is acceptable, provided the program fails in this method and that no state data is changed before the exception is thrown. It is certainly appropriate for private and package-private functions.

Compliant Solution (System Properties)

Gosu supports system properties that can be set using a command line with -D when an application is launched or loaded at runtime with the System.getProperty method. Properties can be stored in a text file and loaded at runtime:

// Compliant Solution

public final function getConnectionProperties() : Connection {
var propFile = new FileInputStream("secrets.txt")
var p = new Properties(System.getProperties())
p.load(propFile)
var username : String = System.getProperty("mysql.user.name")
var password : String = System.getProperty("mysql.user.password")
return DriverManager.getConnection("jdbc:mysql://localhost/dbName",
username, password)
}

The properties file only needs to be set up once, so it is unlikely that this operation would be performed in this function. Instead, it is common for this operation to be performed in a singleton class.

Compliant Solution (AWS Secrets Manager)

Secrets Manager enables you to replace hardcoded credentials in your code, including passwords, with an API call to Secrets Manager to retrieve the secret programmatically. This helps ensure the secret cannot be compromised by someone examining your code, as the secret no longer exists in the code. Also, you can configure Secrets Manager to rotate the secret according to a specified schedule automatically. This enables you to replace long-term secrets with short-term ones, significantly reducing the risk of compromise.

The following diagram illustrates the most basic scenario. It shows how database credentials can be stored in Secrets Manager and used in an application to access the database.

AWS Secrets Manager

In the above diagram, the database administrator creates a set of credentials on the Personnel database for use by an application called MyCustomApp and configures those credentials with the permissions required for the application to access the Personnel database.

  • The database administrator stores the credentials as a secret named MyCustomAppCreds in Secrets Manager. Then, Secrets Manager encrypts and stores the credentials within the secret as the protected secret text.
  • When MyCustomApp accesses the database, the application queries Secrets Manager for the secret named MyCustomAppCreds.
  • Secrets Manager retrieves the secret, decrypts the protected secret text, and returns the secret to the client app over a secured (HTTPS with TLS) channel.
  • The client application parses the credentials, connection string, and any other required information from the response and uses it to access the database server.

This approach can be further improved by using dynamic, ephemeral credentials like those provided by HashiCorp’s Vault. Vault can auto-generate credentials for each use case and only allow them to be active for a short time, minimizing the consequences of a leak. However, adopting this approach requires that you trust Vault (or an equivalent tool) because you will need to grant it root privileges to manage your resources. Generated credentials should be stored in a secrets manager like the AWS secrets manager.

Risk Assessment

Hardcoding sensitive information exposes that information to attackers. Therefore, the severity of this rule can vary depending on the kind of information disclosed. Frequently, the information disclosed is password or key information, which can lead to remote exploitation. Consequently, a high severity rating is given but may be adjusted downwards according to the nature of the sensitive data.

RuleSeverityLikelihoodRemediation CostPriorityLevel
SEC00-GHighLikelyMediumL12L1

Additional Resources