Skip to main content

Technical information leak - Stacktrace

Need

Prevention of technical information leakage from system error traces

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 global execution context for managing concurrent operations in Scala
  • Usage of scala.concurrent.Future for asynchronous programming in Scala

Description

Non compliant code

import play.api.mvc._
import scala.concurrent.ExecutionContext.Implicits.global
import scala.concurrent.Future

class HomeController @Inject()(cc: ControllerComponents) extends AbstractController(cc) {

def index() = Action.async { implicit request: Request[AnyContent] =>
Future {
try {
// Some risky operation that might throw an exception
throw new Exception("An error occurred.")
} catch {
case e: Exception => InternalServerError(e.getStackTrace.mkString("\\n"))
}
}
}
}

This code represents a simple Play Framework controller in a Scala backend application. It has a single action index() which is asynchronously executed. Inside this action, a risky operation is performed that might throw an exception.

In the event of an exception, the catch block captures it and the stack trace of the exception is directly returned as a response with a status of InternalServerError(500).

The vulnerability here is the improper handling of exceptions which leads to the disclosure of technical information. When the exception is caught, the stack trace (which is a technical detail of the system's internal workings) is directly sent back to the user. This stack trace can provide an attacker with valuable information about the system, such as the structure of the code, the libraries being used, and potential points for exploiting further vulnerabilities. This is a classic example of a Technical Information Leak via Stacktrace vulnerability.

Steps

  • Avoid exposing stack traces in error responses.
  • Instead of returning the stack trace directly, handle the exception and provide a generic error message.
  • Log the stack trace for debugging purposes, but do not include it in the response to the user.
  • Consider using a centralized error handling mechanism to handle exceptions consistently across the application.
  • Implement proper error handling and error response formats to provide meaningful information to the user without exposing sensitive technical details.

Compliant code

import play.api.mvc._
import play.api.Logger
import scala.concurrent.ExecutionContext.Implicits.global
import scala.concurrent.Future

class HomeController @Inject()(cc: ControllerComponents) extends AbstractController(cc) {

private val logger = Logger(this.getClass)

def index() = Action.async { implicit request: Request[AnyContent] =>
Future {
try {
// Some risky operation that might throw an exception
throw new Exception("An error occurred.")
} catch {
case e: Exception =>
logger.error("An error occurred: ", e)
InternalServerError("An error occurred. Please try again later.")
}
}
}
}

The original code was vulnerable because it exposed the stack trace of an exception directly in the HTTP response. This could potentially leak sensitive technical information to an attacker.

The fixed code avoids this vulnerability by logging the exception and returning a generic error message to the user. The stack trace is logged using Play's built-in Logger class, which allows developers to view the stack trace for debugging purposes without exposing it to the user.

The try-catch block is used to handle any exceptions that might be thrown by the risky operation. If an exception is thrown, it is caught and logged, and a generic error message is returned in the HTTP response.

This approach provides meaningful information to the user (i.e., that an error occurred) without exposing sensitive technical details. It also ensures that exceptions are handled consistently across the application, as all exceptions are caught and handled in the same way.

In addition, by using a centralized logging mechanism, it is easier to manage and monitor errors across the entire application. This can help with identifying and fixing issues more quickly, as well as improving the overall reliability and robustness of the application.

References