From 383820cd227b5d64238e5ea3e33c5790f48c0c30 Mon Sep 17 00:00:00 2001 From: ljacqu Date: Thu, 19 May 2016 21:44:24 +0200 Subject: [PATCH] #702 Implement SHA256 php registration - Refactor Bcrypt and Sha256 examples to use common abstract parent class - Implement hashing logic for Sha256 --- .../website_integration/AuthMeController.php | 126 ++++++++++++++++++ samples/website_integration/Bcrypt.php | 20 +++ samples/website_integration/Sha256.php | 48 +++++++ .../bcrypt/integration.php | 107 --------------- .../{bcrypt/form.php => index.php} | 31 +++-- samples/website_integration/sha256/form.php | 52 -------- .../sha256/integration.php | 67 ---------- 7 files changed, 213 insertions(+), 238 deletions(-) create mode 100644 samples/website_integration/AuthMeController.php create mode 100644 samples/website_integration/Bcrypt.php create mode 100644 samples/website_integration/Sha256.php delete mode 100644 samples/website_integration/bcrypt/integration.php rename samples/website_integration/{bcrypt/form.php => index.php} (66%) delete mode 100644 samples/website_integration/sha256/form.php delete mode 100644 samples/website_integration/sha256/integration.php diff --git a/samples/website_integration/AuthMeController.php b/samples/website_integration/AuthMeController.php new file mode 100644 index 000000000..d1e94ddfa --- /dev/null +++ b/samples/website_integration/AuthMeController.php @@ -0,0 +1,126 @@ +getHashFromDatabase($username); + if ($hash) { + return $this->isValidPassword($password, $hash); + } + } + return false; + } + + /** + * Returns whether the user exists in the database or not. + * + * @param string $username the username to check + * @return bool true if the user exists; false otherwise + */ + function isUserRegistered($username) { + $mysqli = $this->getAuthmeMySqli(); + if ($mysqli !== null) { + $stmt = $mysqli->prepare('SELECT 1 FROM ' . self::AUTHME_TABLE . ' WHERE username = ?'); + $stmt->bind_param('s', $username); + $stmt->execute(); + return $stmt->fetch(); + } + + // Defensive default to true; we actually don't know + return true; + } + + /** + * Registers a player with the given username. + * + * @param string $username the username to register + * @param string $password the password to associate to the user + * @return bool whether or not the registration was successful + */ + function register($username, $password) { + $mysqli = $this->getAuthmeMySqli(); + if ($mysqli !== null) { + $hash = $this->hash($password); + $stmt = $mysqli->prepare('INSERT INTO ' . self::AUTHME_TABLE . ' (username, realname, password, ip) ' + . 'VALUES (?, ?, ?, ?)'); + $username_low = strtolower($username); + $stmt->bind_param('ssss', $username, $username_low, $hash, $_SERVER['REMOTE_ADDR']); + return $stmt->execute(); + } + return false; + } + + /** + * Hashes the given password. + * + * @param $password string the clear-text password to hash + * @return string the resulting hash + */ + protected abstract function hash($password); + + /** + * Checks whether the given password matches the hash. + * + * @param $password string the clear-text password + * @param $hash string the password hash + * @return boolean true if the password matches, false otherwise + */ + protected abstract function isValidPassword($password, $hash); + + /** + * Returns a connection to the database. + * + * @return mysqli|null the mysqli object or null upon error + */ + private function getAuthmeMySqli() { + // CHANGE YOUR DATABASE DETAILS HERE BELOW: host, user, password, database name + $mysqli = new mysqli('localhost', 'root', '', 'authme'); + if (mysqli_connect_error()) { + printf('Could not connect to AuthMe database. Errno: %d, error: "%s"', + mysqli_connect_errno(), mysqli_connect_error()); + return null; + } + return $mysqli; + } + + /** + * Retrieves the hash associated with the given user from the database. + * + * @param string $username the username whose hash should be retrieved + * @return string|null the hash, or null if unavailable (e.g. username doesn't exist) + */ + private function getHashFromDatabase($username) { + // Add here your database host, username, password and database name + $mysqli = $this->getAuthmeMySqli(); + if ($mysqli !== null) { + $stmt = $mysqli->prepare('SELECT password FROM ' . self::AUTHME_TABLE . ' WHERE username = ?'); + $stmt->bind_param('s', $username); + $stmt->execute(); + $stmt->bind_result($password); + if ($stmt->fetch()) { + return $password; + } + } + return null; + } + +} \ No newline at end of file diff --git a/samples/website_integration/Bcrypt.php b/samples/website_integration/Bcrypt.php new file mode 100644 index 000000000..225baf80f --- /dev/null +++ b/samples/website_integration/Bcrypt.php @@ -0,0 +1,20 @@ +CHARS = self::initRandomChars(); + } + + protected function isValidPassword($password, $hash) { + // $SHA$salt$hash, where hash := sha256(sha256(password) . salt) + $parts = explode('$', $hash); + return count($parts) === 4 && $parts[3] === hash('sha256', hash('sha256', $password) . $parts[2]); + } + + protected function hash($password) { + $salt = $this->generateSalt(); + return '$SHA$' . $salt . '$' . hash('sha256', hash('sha256', $password) . $salt); + } + + /** + * @return string randomly generated salt + */ + private function generateSalt() { + $maxCharIndex = count($this->CHARS) - 1; + $salt = ''; + for ($i = 0; $i < self::SALT_LENGTH; ++$i) { + $salt .= $this->CHARS[mt_rand(0, $maxCharIndex)]; + } + return $salt; + } + + private static function initRandomChars() { + return array_merge(range('0', '9'), range('a', 'f')); + } + +} diff --git a/samples/website_integration/bcrypt/integration.php b/samples/website_integration/bcrypt/integration.php deleted file mode 100644 index 75911838d..000000000 --- a/samples/website_integration/bcrypt/integration.php +++ /dev/null @@ -1,107 +0,0 @@ -prepare('SELECT password FROM ' . AUTHME_TABLE . ' WHERE username = ?'); - $stmt->bind_param('s', $username); - $stmt->execute(); - $stmt->bind_result($password); - if ($stmt->fetch()) { - return $password; - } - } - return null; -} - -/** - * Returns whether the user exists in the database or not. - * - * @param string $username the username to check - * @return bool true if the user exists; false otherwise - */ -function authme_has_user($username) { - $mysqli = authme_get_mysqli(); - if ($mysqli !== null) { - $stmt = $mysqli->prepare('SELECT 1 FROM ' . AUTHME_TABLE . ' WHERE username = ?'); - $stmt->bind_param('s', $username); - $stmt->execute(); - return $stmt->fetch(); - } - - // Defensive default to true; we actually don't know - return true; -} - -/** - * Registers a player with the given username. - * - * @param string $username the username to register - * @param string $password the password to associate to the user - * @return bool whether or not the registration was successful - */ -function authme_register($username, $password) { - $mysqli = authme_get_mysqli(); - if ($mysqli !== null) { - $hash = password_hash($password, PASSWORD_BCRYPT); - $stmt = $mysqli->prepare('INSERT INTO ' . AUTHME_TABLE . ' (username, realname, password, ip) ' - . 'VALUES (?, ?, ?, ?)'); - $username_low = strtolower($username); - $stmt->bind_param('ssss', $username, $username_low, $hash, $_SERVER['REMOTE_ADDR']); - return $stmt->execute(); - } - return false; -} - diff --git a/samples/website_integration/bcrypt/form.php b/samples/website_integration/index.php similarity index 66% rename from samples/website_integration/bcrypt/form.php rename to samples/website_integration/index.php index 7801be4d8..a5509da8b 100644 --- a/samples/website_integration/bcrypt/form.php +++ b/samples/website_integration/index.php @@ -1,6 +1,6 @@ @@ -12,17 +12,24 @@ checkPassword($user, $pass)) { printf('

Hello, %s!

', htmlspecialchars($user)); echo 'Successful login. Nice to have you back!' - . '
Back to form'; + . '
Back to form'; return true; } else { echo '

Error

Invalid username or password.'; @@ -63,15 +70,15 @@ function process_login($user, $pass) { } // Register logic -function process_register($user, $pass) { - if (authme_has_user($user)) { +function process_register($user, $pass, AuthMeController $controller) { + if ($controller->isUserRegistered($user)) { echo '

Error

This user already exists.'; } else { // Note that we don't validate the password or username at all in this demo... - $register_success = authme_register($user, $pass); + $register_success = $controller->register($user, $pass); if ($register_success) { printf('

Welcome, %s!

Thanks for registering', htmlspecialchars($user)); - echo '
Back to form'; + echo '
Back to form'; return true; } else { echo '

Error

Unfortunately, there was an error during the registration.'; diff --git a/samples/website_integration/sha256/form.php b/samples/website_integration/sha256/form.php deleted file mode 100644 index 5ffecf341..000000000 --- a/samples/website_integration/sha256/form.php +++ /dev/null @@ -1,52 +0,0 @@ - - - - - AuthMe Integration Sample - - - -Hello, %s!', htmlspecialchars($user)); - echo 'Successful login. Nice to have you back!' - . '
Back to form'; - $was_successful = true; - } else { - echo '

Error

Invalid username or password.'; - } -} - -if (!$was_successful) { - echo '

Login sample

-This is a demo form for AuthMe website integration. Enter your AuthMe login details -into the following form to test it. -
- - - - -
Name
Pass
-
'; -} - -function get_from_post_or_empty($index_name) { - return trim( - filter_input(INPUT_POST, $index_name, FILTER_UNSAFE_RAW, FILTER_REQUIRE_SCALAR | FILTER_FLAG_STRIP_LOW) - ?: ''); -} -?> - - - diff --git a/samples/website_integration/sha256/integration.php b/samples/website_integration/sha256/integration.php deleted file mode 100644 index e0de0bb14..000000000 --- a/samples/website_integration/sha256/integration.php +++ /dev/null @@ -1,67 +0,0 @@ -prepare("SELECT password FROM $authme_table WHERE username = ?"); - $stmt->bind_param('s', $username); - $stmt->execute(); - $stmt->bind_result($password); - if ($stmt->fetch()) { - return $password; - } - } - return null; -} - -/** - * Checks the given clear-text password against the hash. - * - * @param string $password the clear-text password to check - * @param string $hash the hash to check the password against - * @return bool true iff the password matches the hash, false otherwise - */ -function authme_check_hash($password, $hash) { - // $SHA$salt$hash, where hash := sha256(sha256(password) . salt) - $parts = explode('$', $hash); - return count($parts) === 4 - && $parts[3] === hash('sha256', hash('sha256', $password) . $parts[2]); -}