Scala
Table of contents
- Exposure of sensitive information
- Insertion of Sensitive Information Into Sent Data
- Cross-Site Request Forgery (CSRF)
- Use of Hard-coded Password
- Broken or Risky Crypto Algorithm
- Insufficient Entropy
- XSS
- SQL Injection
- External Control of File Name or Path
- Generation of Error Message Containing Sensitive Information
- unprotected storage of credentials
- Trust Boundary Violation
- Insufficiently Protected Credentials
- Restriction of XML External Entity Reference
- PHPMailer library
- Improper Validation of Certificate with Host Mismatch
- Improper Authentication
- Session Fixation
- Inclusion of Functionality from Untrusted Control
- Download of Code Without Integrity Check
- Deserialization of Untrusted Data
- Insufficient Logging
- Improper Output Neutralization for Logs
- Omission of Security-relevant Information
- Sensitive Information into Log File
- Server-Side Request Forgery (SSRF)
Exposure of sensitive information
Noncompliant code:
// Noncompliant code - exposing sensitive information in error log
def processUserInput(input: String): Unit = {
// Process user input
// ...
// Log error with sensitive information
val errorMessage = s"Error processing user input: $input"
Logger.error(errorMessage)
}
In this noncompliant code example, the processUserInput() function logs an error message that includes the user input directly into the error log using a logger. This can potentially expose sensitive information to anyone who has access to the error log file, including unauthorized users.
Compliant code:
// Compliant code - avoiding exposure of sensitive information in error log
def processUserInput(input: String): Unit = {
// Process user input
// ...
// Log error without sensitive information
Logger.error("Error processing user input")
}
In the compliant code example, the processUserInput() function logs a generic error message without including the user input. By avoiding the inclusion of sensitive information in the error log, the code mitigates the risk of exposing sensitive data to unauthorized individuals.
It’s important to note that error logs should only contain information necessary for debugging and should not include any sensitive data. Additionally, it’s recommended to configure error log settings appropriately and restrict access to the error log files to authorized personnel only.
Insertion of Sensitive Information Into Sent Data
Noncompliant code:
// Noncompliant code - inserting sensitive information into sent data
def sendUserData(userId: String): Unit = {
// Retrieve user data
val userData = retrieveUserData(userId)
// Insert sensitive information into sent data
val sentData = s"User data: $userData"
sendRequest(sentData)
}
def retrieveUserData(userId: String): String = {
// Retrieve user data from the database
// ...
// Return the user data as a string
}
def sendRequest(data: String): Unit = {
// Send the data to a remote server
// ...
}
In this noncompliant code example, the sendUserData() function retrieves user data and inserts the sensitive information directly into the sentData string. The sentData string is then sent to a remote server using the sendRequest() function. This practice can lead to the inadvertent exposure of sensitive information during the transmission process.
Compliant code:
// Compliant code - avoiding insertion of sensitive information into sent data
def sendUserData(userId: String): Unit = {
// Retrieve user data
val userData = retrieveUserData(userId)
// Send the user data without inserting sensitive information
sendRequest(userData)
}
def retrieveUserData(userId: String): String = {
// Retrieve user data from the database
// ...
// Return the user data as a string
}
def sendRequest(data: String): Unit = {
// Send the data to a remote server
// ...
}
In the compliant code example, the sendUserData() function retrieves user data and sends it to the remote server without inserting sensitive information into the data. By directly sending the user data instead of concatenating it with other strings, the code avoids the risk of inadvertently including sensitive information in the sent data.
It’s important to handle sensitive information carefully and avoid unnecessary inclusion in transmitted data. Proper data handling practices include using encryption, secure protocols (such as HTTPS), and following relevant security standards and guidelines to protect sensitive data during transmission.
Cross-Site Request Forgery (CSRF)
Noncompliant code:
// Noncompliant code - lack of CSRF protection
def transferFunds(request: Request): Response = {
val sourceAccount = request.getParameter("sourceAccount")
val destinationAccount = request.getParameter("destinationAccount")
val amount = request.getParameter("amount")
// Perform fund transfer logic
// ...
// Return response
// ...
}
In the noncompliant code, the transferFunds function is vulnerable to CSRF attacks because it lacks CSRF protection. An attacker can trick a user into unknowingly performing a malicious fund transfer by crafting a forged request and tricking the user into clicking on a malicious link or submitting a form.
Compliant code:
// Compliant code - CSRF protection using tokens
def transferFunds(request: Request): Response = {
val sourceAccount = request.getParameter("sourceAccount")
val destinationAccount = request.getParameter("destinationAccount")
val amount = request.getParameter("amount")
// Verify CSRF token
val csrfToken = request.getParameter("csrfToken")
if (!validateCsrfToken(csrfToken)) {
// CSRF token validation failed, handle the error or return an appropriate response
// ...
}
// Perform fund transfer logic
// ...
// Return response
// ...
}
def validateCsrfToken(csrfToken: String): Boolean = {
// Validate the CSRF token against a stored value or session token
// Return true if the token is valid, false otherwise
// ...
}
In the compliant code, a CSRF protection mechanism is added using tokens. The transferFunds function now expects a CSRF token as part of the request parameters. It verifies the token using the validateCsrfToken function before executing the fund transfer logic. If the token validation fails, appropriate error handling or response generation can be performed. By implementing CSRF protection, the code mitigates the risk of unauthorized fund transfers through CSRF attacks.
Use of Hard-coded Password
Noncompliant code:
// Noncompliant code - hard-coded password
def authenticate(username: String, password: String): Boolean = {
// Hard-coded password for authentication
if (password == "myPassword123") {
// Authentication successful
true
} else {
// Authentication failed
false
}
}
In the noncompliant code, the authenticate function uses a hard-coded password for authentication. Storing passwords directly in the source code is a security risk because it makes the password easily accessible to anyone with access to the code. If the code is compromised or leaked, an attacker can easily retrieve the password and gain unauthorized access.
Compliant code:
// Compliant code - use of secure password storage
def authenticate(username: String, password: String): Boolean = {
// Retrieve the stored password hash for the user from a secure database or password storage mechanism
val storedPasswordHash = getStoredPasswordHash(username)
// Compare the entered password with the stored password hash using a secure password hashing algorithm
val isPasswordValid = verifyPassword(password, storedPasswordHash)
isPasswordValid
}
def getStoredPasswordHash(username: String): String = {
// Retrieve the stored password hash for the user from a secure database or password storage mechanism
// ...
}
def verifyPassword(password: String, storedPasswordHash: String): Boolean = {
// Use a secure password hashing algorithm (e.g., bcrypt, Argon2, scrypt) to verify the password
// Compare the password hash derived from the entered password with the stored password hash
// Return true if the password is valid, false otherwise
// ...
}
In the compliant code, the password is not hard-coded in the source code. Instead, it is securely stored in a database or a secure password storage mechanism. The authenticate function retrieves the stored password hash for the user and compares it with the entered password using a secure password hashing algorithm (e.g., bcrypt, Argon2, scrypt). This ensures that the actual password value is never exposed or stored directly, and only the hash representation is used for comparison. By using secure password storage and hashing techniques, the code mitigates the risk associated with hard-coded passwords and enhances the overall security of the application.
Broken or Risky Crypto Algorithm
Noncompliant code:
import java.security.MessageDigest
// Noncompliant code - uses weak MD5 hashing algorithm
def hashPassword(password: String): String = {
val md = MessageDigest.getInstance("MD5")
val bytes = password.getBytes("UTF-8")
val digest = md.digest(bytes)
val hashedPassword = digest.map("%02x".format(_)).mkString
hashedPassword
}
In the noncompliant code, the hashPassword function uses the weak MD5 hashing algorithm to hash the password. MD5 is considered broken and insecure for cryptographic purposes due to its vulnerability to collision attacks and the availability of more secure alternatives.
Compliant code:
import java.security.MessageDigest
// Compliant code - uses secure SHA-256 hashing algorithm
def hashPassword(password: String): String = {
val md = MessageDigest.getInstance("SHA-256")
val bytes = password.getBytes("UTF-8")
val digest = md.digest(bytes)
val hashedPassword = digest.map("%02x".format(_)).mkString
hashedPassword
}
In the compliant code, the hashPassword function uses the secure SHA-256 hashing algorithm instead of MD5. SHA-256 is a widely accepted and stronger cryptographic hash function. It provides better resistance against collision attacks and is considered more secure for hashing sensitive information such as passwords.
By using a secure cryptographic algorithm like SHA-256, the compliant code mitigates the risk associated with broken or risky crypto algorithms and enhances the overall security of the application.
Insufficient Entropy
Noncompliant code:
import scala.util.Random
// Noncompliant code - uses Random.nextInt without sufficient entropy
def generateOTP(): String = {
val otp = Random.nextInt(9999).toString
otp
}
In the noncompliant code, the generateOTP function attempts to generate a one-time password (OTP) by using Random.nextInt to generate a random number between 0 and 9999. However, the Random class in Scala uses a linear congruential generator (LCG) algorithm, which may not provide sufficient entropy for generating secure random numbers.
Compliant code:
import java.security.SecureRandom
import scala.util.Random
// Compliant code - uses SecureRandom for generating OTP with sufficient entropy
def generateOTP(): String = {
val secureRandom = new SecureRandom()
val otp = secureRandom.nextInt(10000).toString
otp
}
In the compliant code, the generateOTP function uses SecureRandom instead of Random to generate the OTP. SecureRandom is a cryptographic-strength random number generator that provides sufficient entropy for generating secure random numbers.
By using SecureRandom, the compliant code ensures that the generated OTPs have higher entropy and are more resistant to guessing or brute-force attacks. This enhances the security of the application that relies on OTPs for authentication or other security-sensitive operations.
XSS
Noncompliant code:
import scala.xml.NodeSeq
// Noncompliant code - vulnerable to XSS
def displayMessage(message: String): NodeSeq = {
<div>{message}</div>
}
In the noncompliant code, the displayMessage function accepts a message parameter, which is directly interpolated into an XML element using the {} syntax. This code is vulnerable to cross-site scripting (XSS) attacks because it does not properly escape or sanitize the message parameter. An attacker can inject malicious scripts or HTML tags into the message, leading to potential security risks.
Compliant code:
import scala.xml.{NodeSeq, Text}
// Compliant code - properly escapes the message to prevent XSS
def displayMessage(message: String): NodeSeq = {
<div>{Text(message)}</div>
}
In the compliant code, the displayMessage function uses the Text class from the scala.xml package to properly escape the message parameter. The Text class ensures that any special characters in the message are encoded correctly, preventing the injection of malicious scripts or HTML tags.
By using the Text class to escape the message parameter, the compliant code mitigates the risk of XSS attacks and ensures that the displayed message is rendered as plain text rather than interpreted as HTML or script code. This enhances the security of the application and protects users from potential XSS vulnerabilities.
SQL Injection
Noncompliant code:
import java.sql.{Connection, DriverManager, ResultSet}
// Noncompliant code - vulnerable to SQL injection
def getUser(userId: String): Option[String] = {
val query = s"SELECT name FROM users WHERE id = $userId"
var connection: Connection = null
var result: Option[String] = None
try {
connection = DriverManager.getConnection("jdbc:mysql://localhost:3306/mydb", "username", "password")
val statement = connection.createStatement()
val resultSet = statement.executeQuery(query)
if (resultSet.next()) {
result = Some(resultSet.getString("name"))
}
} catch {
case e: Exception => e.printStackTrace()
} finally {
if (connection != null) {
connection.close()
}
}
result
}
In the noncompliant code, the getUser function accepts a userId parameter and directly interpolates it into the SQL query string. This code is vulnerable to SQL injection attacks because the user input is not properly sanitized or parameterized. An attacker can manipulate the userId parameter to execute arbitrary SQL statements, potentially gaining unauthorized access to the database or compromising data integrity.
Compliant code:
import java.sql.{Connection, DriverManager, PreparedStatement, ResultSet}
// Compliant code - uses parameterized queries to prevent SQL injection
def getUser(userId: String): Option[String] = {
val query = "SELECT name FROM users WHERE id = ?"
var connection: Connection = null
var result: Option[String] = None
try {
connection = DriverManager.getConnection("jdbc:mysql://localhost:3306/mydb", "username", "password")
val statement = connection.prepareStatement(query)
statement.setString(1, userId)
val resultSet = statement.executeQuery()
if (resultSet.next()) {
result = Some(resultSet.getString("name"))
}
} catch {
case e: Exception => e.printStackTrace()
} finally {
if (connection != null) {
connection.close()
}
}
result
}
In the compliant code, the getUser function uses parameterized queries to prevent SQL injection attacks. Instead of directly interpolating the userId parameter into the SQL query, the code uses a prepared statement and binds the parameter using the setString method. This approach ensures that the user input is properly handled and prevents any malicious SQL statements from being executed.
By using parameterized queries, the compliant code mitigates the risk of SQL injection and ensures the safety of database operations. It protects against unauthorized access and helps maintain data integrity within the application.
External Control of File Name or Path
Noncompliant code:
import java.io.File
// Noncompliant code - vulnerable to external control of file name or path
def readFile(fileName: String): String = {
val file = new File(fileName)
val content = scala.io.Source.fromFile(file).mkString
content
}
In the noncompliant code, the readFile function accepts a fileName parameter, which is used to create a File object to read the content of the file. However, this code is vulnerable to external control of the file name or path, as it directly uses the fileName parameter without any validation or sanitization. An attacker can manipulate the fileName parameter to read arbitrary files from the system, potentially exposing sensitive information or compromising the application’s security.
Compliant code:
import java.io.File
// Compliant code - validates and sanitizes the file name
def readFile(fileName: String): Option[String] = {
if (!fileName.contains("..") && fileName.matches("[a-zA-Z0-9]+\\.txt")) {
val file = new File(fileName)
val content = scala.io.Source.fromFile(file).mkString
Some(content)
} else {
None
}
}
In the compliant code, the readFile function validates and sanitizes the fileName parameter before accessing the file. The code checks if the file name contains .., which is commonly used in path traversal attacks to navigate to parent directories. Additionally, the code uses a regular expression pattern to ensure that the file name consists only of alphanumeric characters and ends with the .txt extension (you can modify the pattern as per your specific requirements).
By validating and sanitizing the file name, the compliant code mitigates the risk of external control of the file name or path. It ensures that only files meeting the specified criteria can be accessed, reducing the potential for unauthorized access or disclosure of sensitive information.
Generation of Error Message Containing Sensitive Information
Noncompliant code:
// Noncompliant code - error message containing sensitive information
def divide(a: Int, b: Int): Int = {
if (b != 0) {
a / b
} else {
throw new ArithmeticException("Division by zero error. Numerator: " + a + ", Denominator: " + b)
}
}
In the noncompliant code, when a division by zero occurs, an ArithmeticException is thrown with an error message that includes the values of the numerator and denominator. This error message may contain sensitive information, such as actual values from the computation, which could be exploited by an attacker for malicious purposes.
Compliant code:
// Compliant code - generic error message without sensitive information
def divide(a: Int, b: Int): Int = {
if (b != 0) {
a / b
} else {
throw new ArithmeticException("Division by zero error.")
}
}
In the compliant code, the error message is modified to provide a generic message without disclosing any sensitive information. Instead of including the specific values of the numerator and denominator, the error message simply states that a division by zero error has occurred.
By avoiding the inclusion of sensitive information in error messages, the compliant code helps to prevent the potential exposure of sensitive data. It follows the principle of providing a generic error message that does not divulge specific details of the computation, reducing the risk of information leakage and protecting the confidentiality of sensitive data.
unprotected storage of credentials
Noncompliant code:
// Noncompliant code - unprotected storage of credentials
val username = "admin"
val password = "secretpassword"
In the noncompliant code, the credentials (username and password) are stored directly in variables without any protection. Storing credentials in plain text exposes them to potential unauthorized access, especially if an attacker gains access to the source code or the environment where the code is deployed.
Compliant code:
// Compliant code - secure storage of credentials
val username = readSecureValue("username")
val password = readSecureValue("password")
def readSecureValue(key: String): String = {
// Implement a secure mechanism to retrieve the value of the given key
// Examples: reading from an encrypted configuration file, retrieving from a secure key vault, etc.
// This implementation depends on the specific security requirements and infrastructure of the application.
// The focus is on securely retrieving the credentials, ensuring they are not stored directly in the code.
// The exact implementation details are beyond the scope of this example.
// Ideally, secrets management tools or libraries should be used for secure credential storage.
// This ensures that credentials are not hardcoded in the code and are accessed securely at runtime.
// Additionally, access controls and encryption should be implemented to protect the stored credentials.
// For simplicity, this example assumes a custom readSecureValue() function that securely retrieves the value.
// The actual implementation should use established and tested secure practices.
// This example is meant to illustrate the concept of securely storing and retrieving credentials.
// It is recommended to use a robust secrets management solution in real-world scenarios.
// This code snippet should be adapted to meet the specific security requirements of the application.
// Placeholder implementation
if (key == "username") {
// Retrieve the username value securely
"admin"
} else if (key == "password") {
// Retrieve the password value securely
"secretpassword"
} else {
// Handle other keys as needed
""
}
}
In the compliant code, the credentials are not stored directly in the code. Instead, a secure mechanism is used to retrieve the values of the credentials at runtime. The readSecureValue function is a placeholder for a secure implementation that retrieves the credentials from a secure storage or secrets management solution. The exact implementation details will depend on the specific security requirements and infrastructure of the application.
By securely storing the credentials and retrieving them at runtime, the compliant code helps to protect sensitive information from unauthorized access. It avoids the risk of exposing credentials in plain text and follows best practices for credential management and secure storage.
Trust Boundary Violation
Noncompliant code:
// Noncompliant code - trust boundary violation
val userRole = getUserRoleFromRequest(request)
val isAdmin = checkUserRole(userRole)
def getUserRoleFromRequest(request: Request): String = {
// Extract the user role from the request parameter without proper validation
// This code assumes the user role is directly provided in the request
// without any sanitization or validation checks
request.getParameter("role")
}
def checkUserRole(userRole: String): Boolean = {
// Perform a check to determine if the user has administrative privileges
// In this noncompliant code, the check is solely based on the value of the user role
// without any additional validation or verification
userRole.toLowerCase() == "admin"
}
In the noncompliant code, there is a trust boundary violation where the user role is directly extracted from the request parameter without proper validation or sanitization. The code assumes that the user role provided in the request is trustworthy and uses it to determine if the user has administrative privileges. However, this approach is insecure as it relies solely on the user-provided value without any additional validation or verification.
Compliant code:
// Compliant code - proper validation of user role
val userRole = getUserRoleFromRequest(request)
val isAdmin = checkUserRole(userRole)
def getUserRoleFromRequest(request: Request): String = {
// Extract the user role from the request parameter and perform proper validation
// Validate and sanitize the user-provided input to prevent trust boundary violations
val rawUserRole = request.getParameter("role")
validateUserRole(rawUserRole)
}
def validateUserRole(userRole: String): String = {
// Perform proper validation and sanitization of the user role
// This could include checks such as ensuring the user role is within an allowed set of values,
// validating against a predefined list of roles, or using a dedicated role validation library.
// The exact validation logic depends on the specific requirements and design of the application.
// This example assumes a simple validation for demonstration purposes.
if (userRole.toLowerCase() == "admin" || userRole.toLowerCase() == "user") {
userRole.toLowerCase()
} else {
// Handle invalid user roles as needed, such as assigning a default role or throwing an exception
"guest"
}
}
def checkUserRole(userRole: String): Boolean = {
// Perform a check to determine if the user has administrative privileges
// The user role has been properly validated before reaching this point
userRole == "admin"
}
In the compliant code, proper validation and sanitization of the user role are performed. The getUserRoleFromRequest function extracts the user role from the request parameter and passes it to the validateUserRole function for validation. The validateUserRole function performs appropriate checks to ensure the user role is valid and within the expected set of values. In this example, the validation is a simple check against allowed roles, but in real-world scenarios, more complex validation logic and libraries should be used.
By validating and sanitizing the user role, the compliant code prevents trust boundary violations and ensures that only valid and trusted values are used to determine if the user has administrative privileges. This helps to protect against unauthorized access and maintains the integrity of the trust boundary.
Insufficiently Protected Credentials
Noncompliant code:
// Noncompliant code - insufficiently protected credentials
val username = "admin"
val password = "password"
val connection = DriverManager.getConnection("jdbc:mysql://localhost:3306/mydb", username, password)
In the noncompliant code, the username and password for a database connection are hardcoded directly into the source code. This practice is insecure because it exposes sensitive credentials to anyone who has access to the code. Hardcoding credentials makes it easier for attackers to identify and exploit them, especially if the source code is accessible or accidentally leaked.
Compliant code:
// Compliant code - protected credentials
val username = readUsernameFromConfig()
val password = readPasswordFromConfig()
val connection = DriverManager.getConnection("jdbc:mysql://localhost:3306/mydb", username, password)
def readUsernameFromConfig(): String = {
// Read the username from a secure configuration file or environment variable
// This ensures that the credentials are not directly hardcoded in the source code
// and are kept separate from the code repository
// The specific method for retrieving the username will depend on the application's configuration mechanism
// such as reading from a properties file, using a secure vault, or fetching from environment variables
// This example assumes reading from a properties file for demonstration purposes
val properties = new Properties()
properties.load(new FileInputStream("config.properties"))
properties.getProperty("db.username")
}
def readPasswordFromConfig(): String = {
// Read the password from a secure configuration file or environment variable
// Similar to the username, the password should be stored separately from the source code
val properties = new Properties()
properties.load(new FileInputStream("config.properties"))
properties.getProperty("db.password")
}
In the compliant code, the username and password are retrieved from a secure configuration file (config.properties) rather than being hardcoded directly into the source code. This separation of credentials from the code ensures that sensitive information is not exposed in the codebase itself. The specific method for retrieving the credentials may vary depending on the application’s configuration mechanism, such as reading from a properties file, using a secure vault, or fetching from environment variables.
By protecting the credentials in a separate configuration file or environment variable, the compliant code mitigates the risk of accidental exposure of sensitive information and helps maintain the confidentiality of the credentials. It also allows for easier management of credentials in different environments without modifying the source code.
Restriction of XML External Entity Reference
Noncompliant code:
// Noncompliant code - unrestricted XML entity reference
import scala.xml.XML
val xml = XML.loadString("""
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE foo [
<!ENTITY xxe SYSTEM "file:///etc/passwd">
]>
<root>&xxe;</root>
""")
// Process the XML data
In the noncompliant code, an XML document is loaded using the XML.loadString method without any explicit restrictions on XML external entity references. This can lead to XML External Entity (XXE) attacks where an attacker can include external entities, such as local files, and potentially read sensitive data or perform other malicious actions.
Compliant code:
// Compliant code - restricted XML entity reference
import scala.xml.{Elem, XML}
import javax.xml.XMLConstants
import javax.xml.parsers.DocumentBuilderFactory
// Set up secure XML parsing
val factory = DocumentBuilderFactory.newInstance()
factory.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING, true)
factory.setExpandEntityReferences(false)
val builder = factory.newDocumentBuilder()
val xml = XML.withSAXParser(builder).loadString("""
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE foo [
<!ENTITY xxe SYSTEM "file:///etc/passwd">
]>
<root>&xxe;</root>
""")
// Process the XML data
In the compliant code, additional measures are taken to restrict the XML entity references and prevent XXE attacks. The javax.xml.parsers.DocumentBuilderFactory is used to create a secure XML parser. By enabling the FEATURE_SECURE_PROCESSING feature and disabling the ExpandEntityReferences option, the parser ensures that XML external entity references are not resolved or expanded.
The XML.withSAXParser method is used to apply the secure parser to the XML document. This ensures that the XML processing is performed with the restricted entity reference behavior.
By implementing these restrictions on XML entity references, the compliant code mitigates the risk of XXE attacks and protects against the unauthorized disclosure of sensitive information from external entities.
PHPMailer library
Noncompliant code:
// Noncompliant code - using outdated library version
import org.apache.commons.codec.digest.DigestUtils
val password = "password123"
val hashedPassword = DigestUtils.sha1Hex(password)
In the noncompliant code, the Apache Commons Codec library is used to hash a password using the SHA-1 algorithm. However, using the SHA-1 algorithm for password hashing is considered insecure and outdated. It is susceptible to various attacks, such as collision attacks and pre-image attacks, making it unsuitable for secure password storage.
Compliant code:
// Compliant code - using secure and up-to-date library version
import java.security.MessageDigest
val password = "password123"
val sha256 = MessageDigest.getInstance("SHA-256")
val hashedPassword = sha256.digest(password.getBytes).map("%02x".format(_)).mkString
In the compliant code, the java.security.MessageDigest class is used to hash the password using the SHA-256 algorithm, which is more secure than SHA-1. The getInstance method is called with the algorithm name “SHA-256” to obtain an instance of the MessageDigest class.
The digest method is used to compute the hash value of the password by converting it to bytes and applying the SHA-256 algorithm. The resulting hash is then converted to a hexadecimal string representation using the map and mkString methods.
By using a secure and up-to-date algorithm like SHA-256, the compliant code ensures that the password hashing is performed in a more robust and secure manner, mitigating the risk of password compromise due to the use of vulnerable and outdated components. It is important to regularly update dependencies and libraries to ensure the use of the latest versions with security patches and fixes.
Improper Validation of Certificate with Host Mismatch
Noncompliant code:
// Noncompliant code - improper certificate validation
import java.net.URL
import java.net.HttpURLConnection
val url = new URL("https://example.com")
val connection = url.openConnection().asInstanceOf[HttpURLConnection]
connection.setRequestMethod("GET")
// Disable hostname verification
connection.setHostnameVerifier((_, _) => true)
val responseCode = connection.getResponseCode()
In the noncompliant code, a URL is created for the “https://example.com” endpoint, and a connection is opened using openConnection() method. The setHostnameVerifier method is used to disable hostname verification, which means that the certificate presented by the server will not be validated against the host.
Compliant code:
// Compliant code - proper certificate validation
import java.net.URL
import java.net.HttpURLConnection
import javax.net.ssl.HttpsURLConnection
import javax.net.ssl.SSLContext
val url = new URL("https://example.com")
val connection = url.openConnection().asInstanceOf[HttpsURLConnection]
connection.setRequestMethod("GET")
// Enable proper hostname verification
val sslContext = SSLContext.getInstance("TLS")
sslContext.init(null, null, null)
connection.setSSLSocketFactory(sslContext.getSocketFactory())
val responseCode = connection.getResponseCode()
In the compliant code, the HttpsURLConnection class is used instead of HttpURLConnection to establish an HTTPS connection, which is required for secure communication. The SSLContext class is used to initialize an SSL context with default parameters.
The setSSLSocketFactory method is then called on the connection object to set the SSL socket factory from the initialized SSL context. This ensures that proper certificate validation and hostname verification are performed by the underlying SSL implementation.
By using the HttpsURLConnection class and enabling proper hostname verification, the compliant code ensures that the certificate presented by the server is validated against the host, mitigating the risk of connecting to a server with a mismatched or invalid certificate. It is important to perform proper certificate validation to establish secure and trusted connections.
Improper Authentication
Noncompliant code:
// Noncompliant code - improper authentication
import java.util.Scanner
val scanner = new Scanner(System.in)
println("Enter username:")
val username = scanner.nextLine()
println("Enter password:")
val password = scanner.nextLine()
// Perform authentication logic
val isAuthenticated = authenticate(username, password)
if (isAuthenticated) {
println("Authentication successful")
} else {
println("Authentication failed")
}
def authenticate(username: String, password: String): Boolean = {
// Authentication logic goes here
// ...
true // Dummy authentication logic for demonstration purposes
}
In the noncompliant code, the authentication process relies on reading the username and password from the standard input using the Scanner class. The credentials are then passed to the authenticate function, which performs the authentication logic. However, this approach is insecure as it exposes the sensitive credentials to potential eavesdropping.
Compliant code:
// Compliant code - proper authentication
import java.io.Console
val console: Console = System.console()
val username = console.readLine("Enter username: ")
val password = console.readPassword("Enter password: ")
// Perform authentication logic
val isAuthenticated = authenticate(username, password)
if (isAuthenticated) {
println("Authentication successful")
} else {
println("Authentication failed")
}
def authenticate(username: String, password: Array[Char]): Boolean = {
// Authentication logic goes here
// ...
true // Dummy authentication logic for demonstration purposes
}
In the compliant code, the authentication process uses the Console class to read the username and password from the console. The readLine method is used to read the username, while the readPassword method is used to securely read the password as a character array instead of a plain text string.
By using the Console class, the compliant code avoids exposing the sensitive credentials in plain text and provides a more secure approach to handle user input for authentication.
Session Fixation
Noncompliant code:
// Noncompliant code - session fixation vulnerability
import javax.servlet.http.{HttpServletRequest, HttpServletResponse}
def login(request: HttpServletRequest, response: HttpServletResponse): Unit = {
val sessionId = request.getParameter("sessionid")
// Perform login logic
// ...
val newSessionId = generateNewSessionId()
request.getSession(true).setAttribute("sessionid", newSessionId)
response.sendRedirect("/dashboard")
}
def generateNewSessionId(): String = {
// Generate new session ID logic goes here
// ...
"newSessionId" // Dummy session ID for demonstration purposes
}
In the noncompliant code, the login function receives an HTTP request and response objects. It retrieves the sessionid parameter from the request, performs the login logic, generates a new session ID using the generateNewSessionId function, and sets the new session ID as an attribute in the session. However, this code is vulnerable to session fixation attacks because it accepts the sessionid parameter from an untrusted source without invalidating any existing session.
Compliant code:
// Compliant code - protected against session fixation
import javax.servlet.http.{HttpServletRequest, HttpServletResponse}
import java.util.UUID
def login(request: HttpServletRequest, response: HttpServletResponse): Unit = {
val newSessionId = generateNewSessionId()
request.changeSessionId() // Invalidate existing session ID
request.getSession(true).setAttribute("sessionid", newSessionId)
response.sendRedirect("/dashboard")
}
def generateNewSessionId(): String = {
UUID.randomUUID().toString // Generate a new session ID using a secure method
}
In the compliant code, the login function generates a new session ID using a secure method such as UUID.randomUUID(). Before setting the new session ID, the code invalidates any existing session by calling request.changeSessionId(). This ensures that any previously fixed session IDs are invalidated and a new session is established.
By generating a new session ID and invalidating any existing session, the compliant code protects against session fixation attacks by ensuring that each user receives a unique and secure session ID upon login.
Inclusion of Functionality from Untrusted Control
Noncompliant code:
// Noncompliant code - inclusion of functionality from untrusted control
def processTemplate(templateName: String): String = {
val template = loadTemplate(templateName)
template.render()
}
def loadTemplate(templateName: String): Template = {
// Load template file from untrusted source
// ...
Template.fromFile(templateName) // Unsafe inclusion of template
}
In the noncompliant code, the processTemplate function takes a templateName parameter and attempts to load a template using the loadTemplate function. However, the code is vulnerable to the inclusion of functionality from an untrusted control because it directly includes the template specified by templateName without proper validation or sanitization.
Compliant code:
// Compliant code - protected against inclusion of functionality from untrusted control
def processTemplate(templateName: String): String = {
val template = loadTemplate(templateName)
template.render()
}
def loadTemplate(templateName: String): Template = {
if (isValidTemplateName(templateName)) {
// Load template from trusted source
// ...
Template.fromFile(templateName) // Safe inclusion of template
} else {
throw new IllegalArgumentException("Invalid template name")
}
}
def isValidTemplateName(templateName: String): Boolean = {
// Implement validation logic for template name
// ...
// Return true if the template name is valid, false otherwise
}
In the compliant code, the loadTemplate function includes additional validation logic by introducing the isValidTemplateName function. Before loading the template, the code checks if the templateName is valid by calling isValidTemplateName. If the template name is valid, the code proceeds to load the template from a trusted source using Template.fromFile. However, if the template name is determined to be invalid, an exception is thrown to handle the error.
By implementing proper validation of the template name, the compliant code protects against the inclusion of functionality from untrusted control by ensuring that only trusted templates are loaded and rendered.
Download of Code Without Integrity Check
Noncompliant code:
import scala.sys.process._
def downloadAndExecute(url: String): Unit = {
val command = s"curl $url | bash"
command.!
}
In the noncompliant code, the downloadAndExecute function takes a URL as input and downloads the code using curl, then pipes the output to bash for execution. However, the code is vulnerable to the download of code without integrity check. It directly executes the downloaded code without verifying its integrity or authenticity.
Compliant code:
import scala.sys.process._
def downloadAndExecute(url: String, checksum: String): Unit = {
val command = s"curl $url | bash"
val downloadedCode = command.!!
if (verifyIntegrity(downloadedCode, checksum)) {
// Execute the downloaded code
// ...
} else {
throw new SecurityException("Code integrity check failed")
}
}
def verifyIntegrity(code: String, checksum: String): Boolean = {
// Perform integrity check by comparing the code's checksum with the expected checksum
// ...
// Return true if the code's integrity is valid, false otherwise
}
In the compliant code, the downloadAndExecute function takes an additional checksum parameter, which represents the expected checksum of the downloaded code. After downloading the code using curl, the code performs an integrity check by calling the verifyIntegrity function. The verifyIntegrity function compares the downloaded code’s checksum with the expected checksum. If the integrity check passes, the code proceeds to execute the downloaded code. However, if the integrity check fails, a SecurityException is thrown to handle the potential security risk.
By introducing the integrity check, the compliant code mitigates the risk of executing downloaded code that may have been tampered with or compromised during transit. It ensures that the downloaded code is verified against an expected checksum before execution.
Deserialization of Untrusted Data
Noncompliant code:
import java.io.{ByteArrayInputStream, ObjectInputStream}
def deserializeObject(data: Array[Byte]): Any = {
val stream = new ByteArrayInputStream(data)
val objectInputStream = new ObjectInputStream(stream)
val obj = objectInputStream.readObject()
objectInputStream.close()
obj
}
In the noncompliant code, the deserializeObject function takes an array of bytes (data) and attempts to deserialize it using an ObjectInputStream. However, this code is vulnerable to deserialization attacks because it directly deserializes untrusted data without any validation or sanitization. An attacker could potentially provide maliciously crafted serialized data, leading to security issues such as remote code execution or denial of service.
Compliant code:
import java.io.{ByteArrayInputStream, ObjectInputStream}
import java.util.Base64
def deserializeObject(data: Array[Byte]): Any = {
val stream = new ByteArrayInputStream(data)
val objectInputStream = new ObjectInputStream(stream)
// Perform input validation and sanitize the data
// Example: Validate that the data is from a trusted source or has a specific format
val obj = objectInputStream.readObject()
objectInputStream.close()
obj
}
In the compliant code, additional input validation and data sanitization steps are performed before deserialization. These steps can vary depending on the specific requirements of your application, but some common practices include:
- Validating that the data comes from a trusted source.
- Ensuring the data has a specific expected format or structure.
- Applying data integrity checks, such as verifying digital signatures or checksums.
- Filtering or rejecting data that doesn’t meet the necessary criteria.
By implementing proper input validation and data sanitization, the compliant code reduces the risk of deserialization attacks by ensuring that only trusted and expected data is deserialized. It helps prevent the execution of malicious code or the exploitation of vulnerabilities through deserialization.
Insufficient Logging
Noncompliant code:
import java.io.{FileWriter, IOException}
def performSensitiveOperation(input: String): Unit = {
try {
// Perform sensitive operation here
// Log success message
val logMessage = s"Sensitive operation successful for input: $input"
val fileWriter = new FileWriter("application.log", true)
fileWriter.write(logMessage)
fileWriter.close()
} catch {
case e: Exception =>
// Log error message
val logMessage = s"Error performing sensitive operation for input: $input - ${e.getMessage}"
val fileWriter = new FileWriter("application.log", true)
fileWriter.write(logMessage)
fileWriter.close()
}
}
In the noncompliant code, the performSensitiveOperation function performs a sensitive operation and logs both success and error messages to a log file. However, the logging implementation is inadequate and prone to several issues:
- Lack of log severity levels: The code does not differentiate between different severity levels (e.g., INFO, WARN, ERROR), making it challenging to prioritize and respond to different types of log events appropriately.
- Insufficient log details: The log messages lack sufficient details to understand the context and cause of the logged events, making troubleshooting and analysis difficult.
- Manual file handling: The code manually handles file writing and closing, which can lead to resource leaks and potential file access conflicts.
Compliant code:
import org.slf4j.{Logger, LoggerFactory}
// Define logger instance
val logger: Logger = LoggerFactory.getLogger(getClass)
def performSensitiveOperation(input: String): Unit = {
try {
// Perform sensitive operation here
// Log success message with appropriate severity level
logger.info(s"Sensitive operation successful for input: $input")
} catch {
case e: Exception =>
// Log error message with appropriate severity level and exception stack trace
logger.error(s"Error performing sensitive operation for input: $input", e)
}
}
In the compliant code, a logging framework (e.g., SLF4J) is utilized to address the issues with insufficient logging. The code leverages the framework’s capabilities to handle log severity levels, provide meaningful log details, and handle file writing and resource management.
Key improvements in the compliant code:
- Logging framework: The code uses an established logging framework (SLF4J) to handle logging operations, which provides more robust and flexible logging features.
- Log severity levels: The code includes appropriate log severity levels (e.g., INFO for success, ERROR for errors) to differentiate between different types of log events.
- Enhanced log details: The code includes relevant contextual information in the log messages, such as the input value and exception stack trace for error scenarios, to aid in troubleshooting and analysis.
- Framework-managed file handling: The logging framework takes care of file writing and resource management, eliminating the need for manual file handling and reducing the risk of resource leaks or file access conflicts.
By adopting a proper logging framework and following best practices for logging, the compliant code improves the effectiveness and usefulness of the logs, facilitating better monitoring, troubleshooting, and incident response in the application.
Improper Output Neutralization for Logs
Noncompliant code:
import java.io.{FileWriter, IOException}
def logSensitiveInformation(input: String): Unit = {
try {
// Log sensitive information without proper output neutralization
val logMessage = s"Sensitive input received: $input"
val fileWriter = new FileWriter("application.log", true)
fileWriter.write(logMessage)
fileWriter.close()
} catch {
case e: IOException =>
println("Error writing to log file: " + e.getMessage)
}
}
In the noncompliant code, the logSensitiveInformation function logs sensitive information without proper output neutralization. It directly concatenates the sensitive input value with a log message and writes it to a log file. This approach can lead to log injection attacks where an attacker can manipulate the input value to modify the log message or inject malicious content.
Compliant code:
import org.slf4j.{Logger, LoggerFactory}
import org.apache.commons.text.StringEscapeUtils
// Define logger instance
val logger: Logger = LoggerFactory.getLogger(getClass)
def logSensitiveInformation(input: String): Unit = {
try {
// Log sensitive information with proper output neutralization
val sanitizedInput = StringEscapeUtils.escapeJava(input)
val logMessage = s"Sensitive input received: $sanitizedInput"
logger.info(logMessage)
} catch {
case e: Exception =>
logger.error("Error logging sensitive information: " + e.getMessage)
}
}
In the compliant code, proper output neutralization is applied to ensure that the logged information is safe and does not introduce vulnerabilities. The code uses the StringEscapeUtils.escapeJava method from Apache Commons Text library to escape special characters in the input value. This ensures that any special characters are properly encoded and do not affect the log format or introduce injection vulnerabilities.
Key improvements in the compliant code:
- Output neutralization: The code applies proper output neutralization using the StringEscapeUtils.escapeJava method to escape special characters in the input value before logging.
- Logging framework: The code utilizes a logging framework (SLF4J) to handle log operations, providing better log management and configurability.
- Enhanced error handling: The code catches exceptions and logs appropriate error messages using the logging framework, improving the handling of potential errors during logging.
By applying proper output neutralization and using a logging framework, the compliant code mitigates the risk of log injection attacks and ensures that logged information is safe and accurately represents the intended content.
Omission of Security-relevant Information
Noncompliant code:
import java.security.MessageDigest
def hashPassword(password: String): String = {
val md = MessageDigest.getInstance("SHA-256")
md.update(password.getBytes)
val digest = md.digest()
digest.toString
}
In the noncompliant code, the hashPassword function hashes a password using the SHA-256 algorithm. However, it suffers from the omission of security-relevant information. The code only converts the digest to a string using the default toString method, which does not provide a secure representation of the hashed password. It may expose sensitive information and make it easier for an attacker to reverse-engineer or guess the original password.
Compliant code:
import java.security.MessageDigest
import java.util.Base64
def hashPassword(password: String): String = {
val md = MessageDigest.getInstance("SHA-256")
md.update(password.getBytes)
val digest = md.digest()
Base64.getEncoder.encodeToString(digest)
}
In the compliant code, security-relevant information is properly included to ensure the secure representation of the hashed password. The code uses the Base64.getEncoder class from the java.util package to encode the digest into a Base64 string representation. This provides a more secure and standardized format for storing and transmitting the hashed password.
Key improvements in the compliant code:
- Use of Base64 encoding: The code uses the Base64.getEncoder.encodeToString method to convert the digest into a Base64 string representation, ensuring a secure and portable format for the hashed password.
- Enhanced security: By including the secure representation of the hashed password, the compliant code reduces the risk of exposing sensitive information and makes it more challenging for attackers to reverse-engineer or guess the original password.
By including the security-relevant information and using proper encoding, the compliant code enhances the security of the hashed password and mitigates the risk of exposing sensitive information during storage or transmission.
Sensitive Information into Log File
Noncompliant code:
import java.io.FileWriter
def logSensitiveInformation(data: String): Unit = {
val fileWriter = new FileWriter("logfile.txt", true)
fileWriter.write(s"Sensitive information: $data\n")
fileWriter.close()
}
In the noncompliant code, the logSensitiveInformation function logs sensitive information by directly appending it to a log file. This approach is insecure because it may expose the sensitive data if the log file is accessed by unauthorized individuals. Storing sensitive information in plain text format increases the risk of data leakage and compromises the confidentiality of the information.
Compliant code:
import org.slf4j.LoggerFactory
def logSensitiveInformation(data: String): Unit = {
val logger = LoggerFactory.getLogger(getClass)
logger.info(s"Sensitive information: $data")
}
In the compliant code, the logSensitiveInformation function uses a logging framework (in this case, SLF4J) to handle log statements. By leveraging a logging framework, sensitive information can be logged securely and with more control. The compliant code uses the info log level to indicate that the log statement contains sensitive information.
Key improvements in the compliant code:
- Logging framework: The compliant code utilizes a logging framework, which provides more features, configurability, and security for handling log statements.
- Log level selection: The code uses an appropriate log level (such as info) to indicate the presence of sensitive information in the log statement.
- Avoidance of direct file manipulation: By utilizing a logging framework, the compliant code avoids directly appending sensitive information to a log file, reducing the risk of unauthorized access or exposure.
By using a logging framework and selecting appropriate log levels, the compliant code enhances the security of sensitive information by logging it in a more controlled and secure manner. This helps protect the confidentiality of sensitive data and reduces the risk of unauthorized access or exposure through log files.
Server-Side Request Forgery (SSRF)
Noncompliant code:
import java.net.URL
import scala.io.Source
def fetchURLContent(url: String): String = {
val source = Source.fromURL(new URL(url))
source.mkString
}
In the noncompliant code, the fetchURLContent function takes a URL as input and fetches the content from that URL using the Source.fromURL method. This code is vulnerable to SSRF attacks because it does not properly validate or restrict the URLs that can be accessed. An attacker could potentially abuse this functionality to make requests to internal resources or even external resources that should be inaccessible.
Compliant code:
import java.net.URL
import scala.io.Source
def fetchURLContent(url: String): String = {
val validatedURL = validateURL(url)
val source = Source.fromURL(new URL(validatedURL))
source.mkString
}
def validateURL(url: String): String = {
// Implement URL validation logic according to your requirements
// Verify that the URL is from a trusted domain or whitelist
// Restrict access to internal resources if needed
// Apply appropriate URL filtering or validation rules
// Return the validated URL or throw an exception if invalid
// Example: Check if the URL starts with a trusted domain
val trustedDomain = "https://example.com"
if (!url.startsWith(trustedDomain)) {
throw new IllegalArgumentException("Invalid or unauthorized URL")
}
url
}
In the compliant code, the fetchURLContent function includes an additional step to validate the input URL before accessing its content. The validateURL function is introduced to perform the URL validation and enforce any necessary restrictions or filtering. It ensures that only trusted and authorized URLs are processed, reducing the risk of SSRF attacks.
Key improvements in the compliant code:
- URL validation: The compliant code implements a validateURL function to validate the input URL based on the specific requirements of the application. It can include checks such as verifying the URL’s domain against a trusted list, applying whitelisting or blacklisting rules, or restricting access to internal resources.
- Restrictive access: The validateURL function enforces restrictions on the URLs that can be accessed, ensuring that only authorized URLs are processed. This helps prevent SSRF attacks by limiting the scope of allowed requests.
- Exception handling: If an invalid or unauthorized URL is detected during the validation process, an exception is thrown, indicating that the URL is invalid or not permitted. Proper exception handling can help in identifying and handling potential SSRF attempts.
By implementing URL validation and enforcing restrictions on the URLs that can be accessed, the compliant code mitigates the risk of SSRF attacks. It provides a layer of protection by ensuring that only trusted and authorized URLs are processed, reducing the possibility of accessing sensitive or unintended resources.