Using cutting edge encryption

Akka HTTP comes with support for Basic Authentication, and that in turn supports plugging in password hashing algorithms from solid third party encryption libraries, such as bcrypt. HTTP Basic Authentication is based on a very simple model, where each request is expected to contain an HTTP header specific to authentication, in order to be allowed to access a protected realm i. e. a protected set of URLs on our web site. Here’s a detailed description of how Basic Authentication works: https://developer.mozilla.org/en-US/docs/Web/HTTP/Authentication#Basic_authentication_scheme

Akka HTTP only gives us minimal framework support for authentication in that it provides the authenticateBasic directive, which extracts the credentials, the user and password, from the HTTP headers and provides a safe way to compare the incoming password to our stored password for the resource, which is usually read from a database. There’s a good description of how this works at the Akka site: https://doc.akka.io/docs/akka-http/current/routing-dsl/directives/security-directives/authenticateBasicAsync.html.

It’s important to understand that this directive performs two tasks for us:

  • It carries out the HTTP Basic Authentication conversation with the client, usually a browser: It parses out the headers and when it cannot find the credentials, it will generate the appropriate rejection, based on how basic authentication is supposed to work. Normally, this means that standard browsers display an authentication challenge dialog box, making the user enter his password. Read up on the basic auth link above to understand how this works.
  • It provides a safe compare function to validate the password: When the browser sends the password, we will have to compare it to our saved password for that user’s account, and accept or reject it based on weather or not they match. This directive can do this correctly, in cryptographic sense.

This article focuses on the latter of these two tasks. If you’re interested in the first step read my other article on how the authentication challenge can be customized: https://synkre.com/akka-http-prevent-browser-password-challenge/

So back to our main point: How do we compare the password and why is it important to use the supplied directive for comparison. The short answer is that if the code performing the password comparison is not carefully written, the time it takes to come back with a result can be used to guess the password. These are called timing attacks: https://emerose.com/timing-attacks-explained.The directive provides a verify method for password comparison, so that timing attacks cannot work.

There is one one more important step that needs to happen though: User passwords must be stored encrypted, and the incoming cleartext password first should also be encrypted, then compared with our stored password in encrypted form. Unfortunately, this is where the authenticateBasic directive cannot always help us because it wants to do the comparison itself, while most crypto libraries also want to do the comparison themselves, to protect us from timing attacks.

Why use a crypto library

In order to understand why using a carefully written crypto library is important, please read through this introduction to password hashing with bcrypt: https://synkre.com/how-secure-is-bcrypt. Bcrypt has its own password checker function Bcrypt.checkpw, as explained here http://www.mindrot.org/projects/jBCrypt/. I will show, how we can make bcrypt work with a modified version of the directive that I got from the following GitHub discussion https://github.com/akka/akka-http/issues/1796

Follow these steps to get basic authentication work with encrypted passwords using bcrypt:

  1. Add bcrypt to your project
  2. Create a modified authenticateBasic directive
  3. Create your modified authentication function
  4. Use the directive with the authentication function in a route to protect a realm

Adding bcrypt

<dependency>
    <groupId>org.mindrot.bcrypt</groupId>
    <artifactId>bcrypt</artifactId>
    <version>0.3</version>
</dependency>

Create the modified directive

def authenticateBasicAsync[T](realm: String,
                              authenticate: (String, String) => Future[Option[T]]): Directive1[T] = {
  def challenge = HttpChallenges.basic(realm)
  extractCredentials.flatMap {
    case Some(BasicHttpCredentials(username, password)) =>
      onSuccess(authenticate(username, password)).flatMap {
        case Some(client) => provide(client)
        case None => reject(AuthenticationFailedRejection(CredentialsRejected, challenge))
      }
    case _ => reject(AuthenticationFailedRejection(CredentialsMissing, challenge))
  }
}

Create your modified authentication function

def myAuthenticator(username: String, password: String): Future[Option[String]] = {
  def getPassword(userName: String): String = ???

  if (BCrypt.checkpw(password, getPassword(username))
      Future.successful(usersByName.get(username).flatMap(_.userId))
  else
      Future.successful(None)
}

A word of caution: Now that we have access to the incoming password directly, because our modified directive exposes it, it is easy to forget about the timing attack risk, and use a standard string compare function. We must make sure that the compare function used is written correctly so that its runtime cannot allow the guessing of passwords. We are using a brcypt library function here. Whatever crypto kit we are using, we must verify that we use a compare function that is timing attack safe.

The credit for the last two steps goes to the participants of the GitHub chat mentioned above, not me. https://github.com/akka/akka-http/issues/1796.

Using the directive in a route

authenticateBasicAsync(
   realm = "my secure site", myUserPassAuthenticator) { userId =>
    ...do protected stuff here..
})

Conclusion

We have just added cutting edge bcrypt password encryption to our Akka HTTP application, by slightly modifying the built in directive.