Cracked weak credentials - Scala

Cracked weak credentials - Scala

Need

Enhancement of password security

Context

  • Usage of Scala 2.13 for developing scalable and high-performance applications
  • Usage of Play Framework for building web applications
  • Usage of play.api.mvc for handling HTTP requests and responses in Play Framework
  • Usage of play.api.data for handling form data in Play Framework
  • Usage of play.api.data.Forms for handling form data in Play Framework

Description

Non compliant code

        import play.api._
import play.api.mvc._
import play.api.data._
import play.api.data.Forms._

case class User(username: String, password: String)

val userForm = Form(
  mapping(
    "username" -> text,
    "password" -> text
  )(User.apply)(User.unapply)
)

def login = Action { implicit request =>
  val user = userForm.bindFromRequest.get
  val dbUser = UserDAO.find(user.username)

  if(dbUser.isDefined && dbUser.get.password == user.password) {
    Redirect(routes.Application.index()).withSession("username" -> user.username)
  } else {
    Unauthorized("Wrong username or password")
  }
}
        
        

In the above vulnerable code, we have a User case class with username and password fields. The form data is directly mapped to this case class without any sort of encryption or hashing. This means that the password is stored in plain text in the database, which is a security risk.

The login function retrieves the user from the database using the provided username and then directly compares the provided password with the one stored in the database. If they match, the user is logged in. If not, an Unauthorized error is returned.

The problem here is that the passwords are stored in plain text in the database. This means that if an attacker were to gain access to the database, they would be able to see all the passwords. Also, since the passwords are not hashed or encrypted, it would be easy for an attacker to crack weak passwords using a brute force attack.

The correct way to handle passwords is to never store them in plain text. Instead, passwords should be hashed using a strong cryptographic hash function and the hash should be stored in the database. When a user logs in, the provided password is hashed and the hash is compared with the one stored in the database. This way, even if an attacker gains access to the database, they would only see the hashes and not the actual passwords.

Steps

  • Use a strong hashing algorithm to store passwords in the database.
  • Implement a salted hash to further enhance password security.
  • Consider using a password hashing library or framework that provides built-in security features.
  • Enforce password complexity requirements to prevent weak passwords from being used.
  • Implement account lockout mechanisms to prevent brute-force attacks.
  • Regularly update and patch the application and its dependencies to address any security vulnerabilities.
  • Educate users about the importance of using strong and unique passwords.

Compliant code

        import play.api._
import play.api.mvc._
import play.api.data._
import play.api.data.Forms._
import org.mindrot.jbcrypt.BCrypt

case class User(username: String, password: String)

val userForm = Form(
  mapping(
    "username" -> text,
    "password" -> text
  )(User.apply)(User.unapply)
)

def login = Action { implicit request =>
  val user = userForm.bindFromRequest.get
  val dbUser = UserDAO.find(user.username)

  if(dbUser.isDefined && BCrypt.checkpw(user.password, dbUser.get.password)) {
    Redirect(routes.Application.index()).withSession("username" -> user.username)
  } else {
    Unauthorized("Wrong username or password")
  }
}

def register = Action { implicit request =>
  val user = userForm.bindFromRequest.get
  val hashedPassword = BCrypt.hashpw(user.password, BCrypt.gensalt())
  val newUser = User(user.username, hashedPassword)
  UserDAO.create(newUser)
  Ok("User created")
}
        
        

The updated code now uses the BCrypt hashing algorithm to store passwords in the database. BCrypt is a strong, slow hashing algorithm that is resistant to brute-force and rainbow table attacks.

In the register method, the user's plaintext password is hashed using BCrypt and a salt (generated by BCrypt.gensalt()) before it is stored in the database. This ensures that even if the database is compromised, the attacker will not have access to the plaintext passwords.

In the login method, the user's plaintext password is hashed and compared to the hashed password stored in the database using BCrypt.checkpw(). This method securely compares the hashes to prevent timing attacks.

This code also needs to be complemented with password complexity requirements on the client side to prevent weak passwords from being used. Additionally, account lockout mechanisms should be implemented to prevent brute-force attacks.

Regular updates and patches to the application and its dependencies are also necessary to address any security vulnerabilities. Users should also be educated about the importance of using strong and unique passwords.

References