Lack of data validation - Token - Scala

Lack of data validation - Token - Scala

Need

Implementation of proper data validation for JWT access tokens

Context

  • Usage of Scala 2.13 for developing scalable and high-performance applications
  • Usage of jjwt library for JSON Web Token (JWT) generation and verification
  • Usage of Play Framework for building web applications in Scala

Description

Non compliant code

        import io.jsonwebtoken.Jwts
import play.api.mvc._

def validateToken(request: Request[AnyContent]): Boolean = {
  val token = request.headers.get("Authorization").getOrElse("")
  if (token.nonEmpty) {
    val parsedToken = Jwts.parser().parseClaimsJws(token)
    true
  } else {
    false
  }
}
        
        

The above Scala code represents a method in a backend application using Play framework that validates a JWT token. This method is vulnerable because it does not check if the JWT token's signature is valid.

The validateToken method retrieves the JWT token from the "Authorization" header of the incoming request. If a token is present, it uses the Jwts.parser().parseClaimsJws(token) function from the io.jsonwebtoken library to parse the token. However, this function does not validate the token's signature. Therefore, even if the token is tampered with or its signature is removed, the function will still parse it successfully and the method will return true.

This means that an attacker can modify the token or remove its signature, and their requests will still be accepted by the server. This is a serious security vulnerability as it can lead to unauthorized access to protected resources.

Steps

  • Implement token signature validation
  • Use a secure secret key for token signing
  • Verify the token's signature before accepting it
  • Handle invalid or tampered tokens appropriately

Compliant code

        import io.jsonwebtoken.{Claims, Jws, Jwts, SignatureException}
import play.api.mvc._

def validateToken(request: Request[AnyContent], secretKey: String): Boolean = {
  val token = request.headers.get("Authorization").getOrElse("")
  if (token.nonEmpty) {
    try {
      val parsedToken: Jws[Claims] = Jwts.parser().setSigningKey(secretKey.getBytes).parseClaimsJws(token)
      true
    } catch {
      case e: SignatureException =>
        println("Invalid token signature")
        false
    }
  } else {
    false
  }
}
        
        

The updated code now includes a secret key for token signing and verification. The setSigningKey method is used to set the secret key for the parser. This key is used to verify the signature of the incoming token.

The parseClaimsJws method is used to parse the token. If the token has been tampered with or the signature does not match, a SignatureException is thrown. This exception is caught and handled by logging an error message and returning false, indicating that the token is invalid.

This way, only tokens with a valid signature that matches the secret key are accepted. Any tampered or invalid tokens are rejected, fixing the vulnerability.

References