Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Added support for salting #1040

Open
wants to merge 4 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,13 @@ class DatabasePDOLoginModule extends UsernamePasswordLoginModule
*/
protected $principalsQuery;

/**
* The database query used to load the password salt for the user.
*
* @var \AppserverIo\Lang\String
*/
protected $saltQuery;

/**
* Initialize the login module. This stores the subject, callbackHandler and sharedState and options
* for the login session. Subclasses should override if they need to process their own options. A call
Expand All @@ -75,6 +82,7 @@ class DatabasePDOLoginModule extends UsernamePasswordLoginModule
* lookupName: The datasource name used to lookup in the naming directory
* rolesQuery: The database query used to load the user's roles
* principalsQuery: The database query used to load the user
* saltQuery: The database query used to load the password salt for the user
*
* @param \AppserverIo\Psr\Security\Auth\Subject $subject The Subject to update after a successful login
* @param \AppserverIo\Psr\Security\Auth\Callback\CallbackHandlerInterface $callbackHandler The callback handler that will be used to obtain the user identity and credentials
Expand All @@ -93,6 +101,7 @@ public function initialize(Subject $subject, CallbackHandlerInterface $callbackH
$this->lookupName = new String($params->get(ParamKeys::LOOKUP_NAME));
$this->rolesQuery = new String($params->get(ParamKeys::ROLES_QUERY));
$this->principalsQuery = new String($params->get(ParamKeys::PRINCIPALS_QUERY));
$this->saltQuery = new String($params->get(ParamKeys::SALT_QUERY));
}

/**
Expand Down Expand Up @@ -148,4 +157,50 @@ protected function getRoleSets()
{
return Util::getRoleSets($this->getUsername(), new String($this->lookupName), new String($this->rolesQuery), $this);
}

/**
* Returns the salt for the user from the sharedMap data.
*
* @return \AppserverIo\Lang\String The user's salt
*/
protected function getUsersSalt()
{

if ($this->saltQuery->stringValue() == 0) {
return null;
}

// load the application context
$application = RequestHandler::getApplicationContext();

/** @var \AppserverIo\Appserver\Core\Api\Node\DatabaseNode $databaseNode */
$databaseNode = $application->getNamingDirectory()->search($this->lookupName)->getDatabase();

// prepare the connection parameters and create the DBAL connection
$connection = DriverManager::getConnection(ConnectionUtil::get($application)->fromDatabaseNode($databaseNode));

// try to load the principal's credential from the database
$statement = $connection->prepare($this->saltQuery);
$statement->bindParam(1, $this->getUsername());
$statement->execute();

// close the PDO connection
if ($connection != null) {
try {
$connection->close();
} catch (\Exception $e) {
$application
->getNamingDirectory()
->search(NamingDirectoryKeys::SYSTEM_LOGGER)
->error($e->__toString());
}
}

// query whether or not we've found a salt or not
if ($row = $statement->fetch(\PDO::FETCH_NUM)) {
return new String($row[0]);
} else {
throw new LoginException('No matching salt found in principals');
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -145,6 +145,14 @@ public function initialize(Subject $subject, CallbackHandlerInterface $callbackH
*/
abstract protected function getUsersPassword();

/**
* Returns the salt for the user from the sharedMap data.
*
* @return \AppserverIo\Lang\String The user's salt
* @throws \AppserverIo\Psr\Security\Auth\Login\LoginException Is thrown if salt can't be loaded
*/
abstract protected function getUsersSalt();

/**
* Perform the authentication of username and password.
*
Expand Down Expand Up @@ -258,6 +266,9 @@ protected function createPasswordHash(String $name, String $password)
// initialize the callback
$callback = null;

//get users salt
$hashSalt = $this->getUsersSalt();

// query whether or not we've a callback configured
if ($this->params->exists(ParamKeys::DIGEST_CALLBACK)) {
try {
Expand All @@ -270,14 +281,13 @@ protected function createPasswordHash(String $name, String $password)
$tmp->add(SharedStateKeys::LOGIN_NAME, $name);
$tmp->add(SharedStateKeys::LOGIN_PASSWORD, $password);
$callback->init($tmp);

} catch (\Exception $e) {
throw new SecurityException("Failed to load DigestCallback");
}
}

// hash and return the password
return Util::createPasswordHash($this->hashAlgorithm, $this->hashEncoding, $this->hashCharset, $name, $password, $callback);
return Util::createPasswordHash($this->hashAlgorithm, $this->hashEncoding, $this->hashCharset, $name, $password, $callback, $hashSalt);
}

/**
Expand Down Expand Up @@ -309,6 +319,10 @@ protected function validatePassword(String $inputPassword, String $expectedPassw
$valid = $inputPassword->equals($expectedPassword);
}

if ($this->hashAlgorithm == "PASSWORD_BCRYPT" || $this->hashAlgorithm == "PASSWORD_DEFAULT") {
$valid = password_verify($inputPassword, $expectedPassword);
}

// return the flag
return $valid;
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
<?php

namespace AppserverIo\Appserver\ServletEngine\Security\Utils;

/**
* @author Alexandros Weigl <[email protected]>
* @copyright 2016 TechDivision GmbH <[email protected]>
* @license http://opensource.org/licenses/osl-3.0.php Open Software License (OSL 3.0)
* @link https://github.com/appserver-io/appserver
*/
class HashKeys
{
/**
* They key for the "md5" hash algorithm
*
* @var string
*/
const MD5 = 'md5';

/**
* They key for the "sha1" hash algorithm
*
* @var string
*/
const SHA1 = 'sha1';

/**
* They key for the "sha256" hash algorithm
*
* @var string
*/
const SHA256 = 'sha256';

/**
* They key for the "sha512" hash algorithm
*
* @var string
*/
const SHA512 = 'sha512';
}
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,13 @@ class ParamKeys
*/
const ROLES_QUERY = 'rolesQuery';

/**
* The key for the "saltQuery" parameter.
*
* @var string
*/
const SALT_QUERY = 'saltQuery';

/**
* The key for the "passwordStacking" parameter.
*
Expand Down Expand Up @@ -81,6 +88,13 @@ class ParamKeys
*/
const HASH_CHARSET = 'hashCharset';

/**
* the key for the "salt" parameter.
*
* @var string
*/
const HASH_SALT = 'hashSalt';

/**
* The key for the "ignorePasswordCase" parameter.
*
Expand Down
24 changes: 20 additions & 4 deletions src/AppserverIo/Appserver/ServletEngine/Security/Utils/Util.php
Original file line number Diff line number Diff line change
Expand Up @@ -60,23 +60,40 @@ class Util
/**
* Creates and returns a hashed version of the passed password.
*
*
* @param string $hashAlgorithm The hash algorithm to use
* @param string $hashEncoding The hash encoding to use
* @param string $hashCharset The hash charset to use
* @param \AppserverIo\Lang\String $name The login name
* @param \AppserverIo\Lang\String $password The password credential
* @param mixed $callback The callback providing some additional hashing functionality
* @param string $hashSalt The hash salt to use
*
* @return \AppserverIo\Lang\String The hashed password
*/
public static function createPasswordHash($hashAlgorithm, $hashEncoding, $hashCharset, String $name, String $password, $callback)
public static function createPasswordHash($hashAlgorithm, $hashEncoding, $hashCharset, String $name, String $password, $callback, $hashSalt = null)
{
$newPassword = clone $password;
return $newPassword->md5();
switch ($hashAlgorithm) {
case HashKeys::MD5:
return $newPassword->md5($hashSalt);
case HashKeys::SHA1:
return $newPassword->sha1($hashSalt);
case HashKeys::SHA256:
return $newPassword->sha256($hashSalt);
case HashKeys::SHA512:
return $newPassword->sha512($hashSalt);
case PASSWORD_BCRYPT:
return $newPassword;
case PASSWORD_DEFAULT:
return $newPassword;
case 'default':
return $newPassword;
}
}

/**
* Execute the rolesQuery against the dsJndiName to obtain the roles for the authenticated user.
* Execute the rolesQuery against the UserName to obtain the roles for the authenticated user.
*
* @param \AppserverIo\Lang\String $username The username to load the roles for
* @param \AppserverIo\Lang\String $lookupName The lookup name for the datasource
Expand Down Expand Up @@ -156,7 +173,6 @@ public static function getRoleSets(String $username, String $lookupName, String

// load one group after another
} while ($row = $statement->fetch(\PDO::FETCH_OBJ));

} catch (NamingException $ne) {
throw new LoginException($ne->__toString());
} catch (\PDOException $pdoe) {
Expand Down
Loading