1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136
<?php
/**
* TfishSecurityUtility class file.
*
* @copyright Simon Wilkinson 2013-2017 (https://tuskfish.biz)
* @license https://www.gnu.org/licenses/old-licenses/gpl-2.0.en.html GNU General Public License (GPL) V2
* @author Simon Wilkinson <[email protected]>
* @version Release: 1.0
* @since 1.0
* @package security
*/
// Enable strict type declaration.
declare(strict_types=1);
if (!defined("TFISH_ROOT_PATH")) die("TFISH_ERROR_ROOT_PATH_NOT_DEFINED");
/**
* Security utilities class.
*
* Provides methods to conduct basic security operations such as generating salts and hashing
* passwords etc.
*
* @copyright Simon Wilkinson 2013-2017 (https://tuskfish.biz)
* @license https://www.gnu.org/licenses/old-licenses/gpl-2.0.en.html GNU General Public License (GPL) V2
* @author Simon Wilkinson <[email protected]>
* @version Release: 1.0
* @since 1.0
* @package security
*/
class TfishSecurityUtility
{
/**
* Evaluates the strength of a password to resist brute force cracking.
*
* Issues warnings if deficiencies are found. Requires a minimum length of 15 characters.
* Due to revision of advice on best practices most requirements have been relaxed, as user
* behaviour tends to be counter-productive. Basically, it's up to you, the admin, to choose
* a sane password.
*
* @param string $password Input password.
* @return array Array of evaluation warnings as strings.
*/
public static function checkPasswordStrength(string $password)
{
$evaluation = array('strong' => true);
// Length must be > 15 characters to prevent brute force search of the keyspace.
if (mb_strlen($password, 'UTF-8') < 15) {
$evaluation['strong'] = false;
$evaluation[] = TFISH_PASSWORD_MINIMUM_LENGTH_WEAKNESS;
}
/**
// Must contain at least one upper case letter.
if (!preg_match('/[A-Z]/', $password)) {
$evaluation['strong'] = false;
$evaluation[] = TFISH_PASSWORD_UPPER_CASE_WEAKNESS;
}
// Must contain at least one lower case letter.
if (!preg_match('/[a-z]/', $password)) {
$evaluation['strong'] = false;
$evaluation[] = TFISH_PASSWORD_LOWER_CASE_WEAKNESS;
}
// Must contain at least one number.
if (!preg_match('/[0-9]/', $password)) {
$evaluation['strong'] = false;
$evaluation[] = TFISH_PASSWORD_NUMBERIC_WEAKNESS;
}
// Must contain at least one symbol.
if (!preg_match('/[^a-zA-Z0-9]/', $password)) {
$evaluation['strong'] = false;
$evaluation[] = TFISH_PASSWORD_SYMBOLIC_WEAKNESS;
}
*/
return $evaluation;
}
/**
* Generate a psuedo-random salt of arbitrary length.
*
* This is used to salt user passwords, to make them more difficult to brute force crack.
*
* @param int $length Length of required salt.
* @return string $salt
*/
public static function generateSalt(int $length = 64)
{
$salt = base64_encode(random_bytes($length));
return $salt;
}
/**
* Recursively hashes a salted password to harden it against dictionary attacks.
*
* Recursively hashing a password a large number of times directly increases the amount of
* effort that must be spent to brute force or even dictionary attack a hash, because each
* attempt will consume $iterations more cycles.
*
* @param string $password Input password.
* @param int $iterations Number of iterations to run, you want this to be a large number
* (100,000 or more).
* @param string $site_salt The Tuskfish site salt, found in the configuration file.
* @param string $user_salt The user-specific salt for this user, found in the user database
* table.
* @return string Password hash.
*/
public static function recursivelyHashPassword(string $password, int $iterations,
string $site_salt, string $user_salt = '')
{
$iterations = (int) $iterations;
// Force a minimum number of iterations (1).
$iterations = $iterations > 0 ? $iterations : 1;
$password = $site_salt . $password;
if ($user_salt) {
$password .= $user_salt;
}
for ($i = 0; $i < $iterations; $i++) {
$password = hash('sha256', $password);
}
return $password;
}
}