Akka-HTTP: basicAuthentication directive promotes insecure passwords #18858 (#20820)

the Credentials Object used in the authenticateBasic Directive can be verified against passwords that are stored with a hashing function. By providing the same hashing function as an argument the clear-text password in the HttpCredentials will be hashed the same way prior to comparing against the stored secret.
This commit is contained in:
Tobias Pfeifer 2016-06-25 11:24:54 +02:00 committed by Konrad Malawski
parent 6777e7f7d9
commit 3723959b69

View file

@ -268,24 +268,35 @@ sealed trait Credentials
object Credentials { object Credentials {
case object Missing extends Credentials case object Missing extends Credentials
abstract case class Provided(identifier: String) extends Credentials { abstract case class Provided(identifier: String) extends Credentials {
/**
* First applies the passed in `hasher` function to the received secret part of the Credentials
* and then safely compares the passed in `secret` with the hashed received secret.
* This method can be used if the secret is not stored in plain text.
* Use of this method instead of manual String equality testing is recommended in order to guard against timing attacks.
*
* See also [[EnhancedString#secure_==]], for more information.
*/
def verify(secret: String, hasher: String String): Boolean
/** /**
* Safely compares the passed in `secret` with the received secret part of the Credentials. * Safely compares the passed in `secret` with the received secret part of the Credentials.
* Use of this method instead of manual String equality testing is recommended in order to guard against timing attacks. * Use of this method instead of manual String equality testing is recommended in order to guard against timing attacks.
* *
* See also [[EnhancedString#secure_==]], for more information. * See also [[EnhancedString#secure_==]], for more information.
*/ */
def verify(secret: String): Boolean def verify(secret: String): Boolean = verify(secret, x => x)
} }
def apply(cred: Option[HttpCredentials]): Credentials = { def apply(cred: Option[HttpCredentials]): Credentials = {
cred match { cred match {
case Some(BasicHttpCredentials(username, receivedSecret)) case Some(BasicHttpCredentials(username, receivedSecret))
new Credentials.Provided(username) { new Credentials.Provided(username) {
def verify(secret: String): Boolean = secret secure_== receivedSecret def verify(secret: String, hasher: String String): Boolean = secret secure_== hasher(receivedSecret)
} }
case Some(OAuth2BearerToken(token)) case Some(OAuth2BearerToken(token))
new Credentials.Provided(token) { new Credentials.Provided(token) {
def verify(secret: String): Boolean = secret secure_== token def verify(secret: String, hasher: String String): Boolean = secret secure_== hasher(token)
} }
case Some(GenericHttpCredentials(scheme, token, params)) case Some(GenericHttpCredentials(scheme, token, params))
throw new UnsupportedOperationException("cannot verify generic HTTP credentials") throw new UnsupportedOperationException("cannot verify generic HTTP credentials")