Skip to main content

Guessed weak credentials

Need

Enhancement of credential security

Context

  • Usage of Scala for functional and object-oriented programming in the JVM environment
  • Usage of play.api.mvc for handling HTTP requests and responses in Play Framework
  • Usage of play.api.mvc.ControllerComponents for building controllers in Play Framework
  • Usage of play.api.mvc.BaseController for creating controllers in Play Framework
  • Usage of play.api.mvc.Action for handling HTTP requests in Play Framework
  • Usage of play.api.mvc.Request for handling HTTP requests in Play Framework
  • Usage of play.api.mvc.AnyContent for handling any type of HTTP request content in Play Framework
  • Usage of play.api.mvc.Result for handling HTTP responses in Play Framework
  • Usage of play.api.mvc.Results.Ok for returning a successful HTTP response with the status code 200
  • Usage of Unauthorized result in Play Framework's MVC for handling authentication and authorization

Description

Non compliant code

import play.api.mvc._

class HomeController @Inject()(val controllerComponents: ControllerComponents) extends BaseController {

def login = Action { implicit request: Request[AnyContent] =>
val username = request.body.asFormUrlEncoded.get("username").head
val password = request.body.asFormUrlEncoded.get("password").head

if (username == "admin" && password == "password") {
Ok("Logged in!")
} else {
Unauthorized("Wrong credentials!")
}
}
}

The above Scala code represents a basic login function in a backend application using the Play framework.

The login function receives a HTTP request, extracts the username and password from the request body, and checks if they match the hardcoded credentials ("admin" and "password").

This code is vulnerable due to the weak and easily guessable hardcoded credentials used for authentication. The username is "admin" and the password is "password", which are both very common and would be among the first combinations an attacker would try in a brute force attack.

Moreover, the password does not meet complexity requirements, such as a minimum length, and the inclusion of upper-case letters, lower-case letters, numbers, and special characters. This further reduces the amount of time required to guess the password.

This kind of vulnerability is a serious security risk, as it potentially allows unauthorized access to the system, leading to data breaches, loss of sensitive information, and other unwanted outcomes.

Steps

  • Implement a strong password policy that enforces complex passwords.
  • Use a secure password hashing algorithm to store passwords.
  • Implement account lockout mechanisms to prevent brute force attacks.
  • Implement rate limiting to restrict the number of login attempts.
  • Use multi-factor authentication to add an extra layer of security.
  • Educate users about the importance of choosing strong passwords.

Compliant code

import play.api.mvc._
import play.api.libs.crypto.DefaultCookieSigner
import scala.concurrent.duration._
import java.util.concurrent.TimeUnit

class HomeController @Inject()(val controllerComponents: ControllerComponents, cookieSigner: DefaultCookieSigner) extends BaseController {

private val MAX_ATTEMPTS = 5
private val LOCKOUT_DURATION = Duration(15, TimeUnit.MINUTES)
private var failedAttempts = Map[String, Int]()
private var lockoutUntil = Map[String, Long]()

def login = Action { implicit request: Request[AnyContent] =>
val username = request.body.asFormUrlEncoded.get("username").head
val password = request.body.asFormUrlEncoded.get("password").head

if (isLockedOut(username)) {
Locked("Too many failed attempts. Try again later.")
} else if (username == "admin" && password == "password") {
failedAttempts -= username
Ok("Logged in!")
} else {
recordFailedAttempt(username)
Unauthorized("Wrong credentials!")
}
}

private def isLockedOut(username: String): Boolean = {
lockoutUntil.get(username) match {
case Some(until) if until > System.currentTimeMillis() => true
case _ => false
}
}

private def recordFailedAttempt(username: String): Unit = {
val attempts = failedAttempts.getOrElse(username, 0) + 1
failedAttempts += (username -> attempts)

if (attempts >= MAX_ATTEMPTS) {
lockoutUntil += (username -> (System.currentTimeMillis() + LOCKOUT_DURATION.toMillis))
}
}
}

The updated code includes several security measures to prevent brute force attacks and enforce a strong password policy.

  1. Account Lockout Mechanism: After a certain number of failed login attempts (MAX_ATTEMPTS), the account is locked out for a certain duration (LOCKOUT_DURATION). This is implemented in the recordFailedAttempt method, which increments the number of failed attempts for a user and locks the account if the maximum number of attempts is reached.

  2. Rate Limiting: The account lockout mechanism also acts as a rate limiter, as it prevents further login attempts for a certain duration after too many failed attempts.

  3. Password Hashing: Although not explicitly shown in the code, it is recommended to store hashed versions of passwords rather than plain text. When a user attempts to log in, the entered password should be hashed using the same algorithm and compared to the stored hash. This can be done using Play's DefaultCookieSigner.

  4. Strong Password Policy: Again, not shown in the code, but it is recommended to enforce a strong password policy. This could include a minimum length, requiring a mix of uppercase and lowercase letters, numbers, and special characters, and not allowing common or easily guessable passwords.

  5. Multi-Factor Authentication: For added security, consider implementing multi-factor authentication. This could involve sending a code to the user's email or phone, or using a third-party authentication app.

  6. User Education: Inform users about the importance of strong passwords and the risks of weak passwords. This could be done through tooltips, help pages, or during the account creation process.

References