Skip to main content

Sensitive information in source code - Credentials

Need

Protection of sensitive information in source code

Context

  • Usage of Scala for building scalable and high-performance applications
  • Usage of play.api.mvc for handling HTTP requests and responses in Play Framework
  • Usage of play.api.db for database operations in Play Framework

Description

Non compliant code

import play.api.mvc._
import play.api.db._

class Application @Inject()(db: Database) extends Controller {
def index = Action {
var connection: Connection = null
try {
connection = db.getConnection()

val statement = connection.createStatement
val resultSet = statement.executeQuery("SELECT * FROM users WHERE username='admin' AND password='password123'")
while (resultSet.next()) {
println(resultSet.getString("username"))
println(resultSet.getString("password"))
}
} finally {
connection.close()
}
}
}

The above Scala code is a Play Framework controller that connects to a database and retrieves user information. The vulnerability lies in the hard-coded database query where the username and password are directly embedded into the SQL query string.

This is a severe security risk as the credentials are exposed in the source code, making them easily accessible to anyone who has access to the code. This could lead to unauthorized access to the database and potential data breaches.

Additionally, this approach of hardcoding credentials does not allow for easy updates or changes to the credentials. If the password needs to be changed, the source code would need to be updated and redeployed, which is not a practical or secure method of managing sensitive information.

Lastly, this code is also vulnerable to SQL injection attacks. Since the SQL query is created by concatenating strings, an attacker could potentially manipulate the query to retrieve additional data or perform unauthorized actions.

To fix this vulnerability, it is recommended to use environment variables to store sensitive data such as database credentials. This way, the credentials are not exposed in the source code and can be easily updated without requiring code changes. Also, use prepared statements or parameterized queries to prevent SQL injection attacks.

Steps

  • Remove the sensitive information from the source code.
  • Store the sensitive information securely, such as in environment variables or a configuration file.
  • Use a secure method to access the sensitive information, such as a secrets manager or encryption.
  • Implement proper authentication and authorization mechanisms to protect sensitive data.
  • Regularly review and update the access controls and permissions for the sensitive information.
  • Implement secure coding practices to prevent sensitive information from being exposed in the source code.

Compliant code

import play.api.mvc._
import play.api.db._
import play.api.Configuration

class Application @Inject()(db: Database, config: Configuration) extends Controller {
def index = Action {
var connection: Connection = null
try {
connection = db.getConnection()

val username = config.get[String]("db.username")
val password = config.get[String]("db.password")

val statement = connection.prepareStatement("SELECT * FROM users WHERE username=? AND password=?")
statement.setString(1, username)
statement.setString(2, password)
val resultSet = statement.executeQuery()
while (resultSet.next()) {
println(resultSet.getString("username"))
println(resultSet.getString("password"))
}
} finally {
connection.close()
}
}
}

The updated code now securely retrieves the username and password from the application's configuration instead of having them hardcoded in the source code. This is done using Play's Configuration API, which allows us to access configuration values in a type-safe manner.

The Configuration instance is injected into the Application controller, and the username and password are retrieved with config.get[String]("db.username") and config.get[String]("db.password") respectively. These values should be stored in a configuration file that is not committed to the repository, or better yet, in environment variables.

The SQL query has also been updated to use a PreparedStatement instead of a Statement. This not only allows us to insert the username and password into the query in a safe manner (preventing SQL injection attacks), but it also automatically escapes the values, preventing potential SQL syntax issues.

Finally, the sensitive data (username and password) are no longer printed to the console. This is a good practice as console logs can be accessed by unauthorized users, potentially exposing sensitive information.

References