You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
264 lines
8.7 KiB
264 lines
8.7 KiB
<?php
|
|
|
|
declare(strict_types=1);
|
|
|
|
/**
|
|
* @author Robert Strutts
|
|
* @copyright Copyright (c) 2022, Robert Strutts.
|
|
* @license MIT
|
|
*/
|
|
|
|
namespace CodeHydrater;
|
|
|
|
final class security {
|
|
|
|
use traits\security\csrf_token_functions;
|
|
use traits\security\session_hijacking_functions;
|
|
|
|
/**
|
|
* Get unique IDs for database
|
|
* @return int
|
|
*/
|
|
public static function get_unique_number(): int {
|
|
return abs(crc32(microtime()));
|
|
}
|
|
|
|
/**
|
|
* Get token
|
|
* @return string
|
|
*/
|
|
public static function get_unique_id(): string {
|
|
$more_entropy = true;
|
|
$prefix = ""; // Blank is a rand string
|
|
return md5(uniqid($prefix, $more_entropy));
|
|
}
|
|
|
|
public static function use_hmac(string $algo, string $pepper) {
|
|
if (!function_exists("hash_hmac_algos")) {
|
|
throw new \Exception("hash_hmac not installed!");
|
|
}
|
|
if (strpos($algo, "md") !== false) {
|
|
throw new \Exception("MD is too weak!");
|
|
}
|
|
if (strpos($algo, "sha1") !== false) {
|
|
throw new \Exception("sha1 is too weak!");
|
|
}
|
|
$allowed = hash_hmac_algos();
|
|
if (in_array(strtolower($algo), $allowed)) {
|
|
return hash_hmac($algo, $pepper);
|
|
}
|
|
throw new \Exception("hmac algo not found!");
|
|
}
|
|
|
|
/*
|
|
* Consider MD5 and SHA1 which are fast and efficient,
|
|
* making them ideal for check summing and file verification.
|
|
* However, their speed makes them unsuitable for hashing a
|
|
* user’s password. With today’s computational power of
|
|
* modern CPUs/GPUs and cloud computing, a hashed password
|
|
* can be cracked by brute force in a matter of minutes.
|
|
* It’s fairly trivial to quickly generate billions of MD5
|
|
* hashes from random words until a match is found, thereby
|
|
* revealing the original plain-text password. Instead,
|
|
* intentionally slower hashing algorithms such as bcrypt
|
|
* or Argon2 should be used.
|
|
*/
|
|
|
|
public static function find_default_hash_algo() {
|
|
if (defined("PASSWORD_ARGON2ID"))
|
|
return PASSWORD_ARGON2ID;
|
|
if (defined("PASSWORD_ARGON2"))
|
|
return PASSWORD_ARGON2;
|
|
if (defined("PASSWORD_DEFAULT"))
|
|
return PASSWORD_DEFAULT;
|
|
if (defined("PASSWORD_BCRYPT"))
|
|
return PASSWORD_BCRYPT;
|
|
return false;
|
|
}
|
|
|
|
/*
|
|
* The password_hash() function not only uses a secure
|
|
* one-way hashing algorithm, but it automatically handles
|
|
* salt and prevents time based side-channel attacks.
|
|
*/
|
|
|
|
public static function do_password_hash(string $password): bool | string {
|
|
$pwd_peppered = self::make_hash($password);
|
|
$hash_algo = \main_tts\configure::get(
|
|
"security",
|
|
"hash_algo"
|
|
);
|
|
if (! empty($hash_algo)) {
|
|
return password_hash($pwd_peppered, $hash_algo);
|
|
}
|
|
return password_hash($pwd_peppered, self::find_default_hash_algo());
|
|
}
|
|
|
|
public static function do_password_verify(
|
|
string $input_pwd, $db_password
|
|
): bool {
|
|
$pwd_peppered = self::make_hash($input_pwd);
|
|
return password_verify($pwd_peppered, $db_password);
|
|
}
|
|
|
|
/**
|
|
* Make a secure Hash
|
|
* @param string $text to encode
|
|
* @param string $level (weak, low, high, max)
|
|
* @return string new Hashed
|
|
*/
|
|
public static function make_hash(string $text): string {
|
|
$level = bootstrap\configure::get('security', 'hash_level');
|
|
if (empty($level)) {
|
|
$level = "normal";
|
|
}
|
|
$pepper = bootstrap\configure::get('security', 'pepper_pwd');
|
|
if (strlen($pepper) < 12) {
|
|
throw new \Exception("Pepper Password, too short!");
|
|
}
|
|
$salt = bootstrap\configure::get('security', 'salt_pwd');
|
|
if (strlen($salt) < 5) {
|
|
throw new \Exception("Salt Password, too short!");
|
|
}
|
|
|
|
switch (strtolower($level)) {
|
|
case 'max':
|
|
// Prefer computing using HMAC
|
|
if (function_exists("hash_hmac")) {
|
|
return hash_hmac("sha512", $text, $pepper);
|
|
}
|
|
// Sha512 hash is the next best thing
|
|
if (function_exists("hash")) {
|
|
return hash("sha512", $salt . $text . $pepper);
|
|
}
|
|
case 'normal':
|
|
// Prefer computing using HMAC
|
|
if (function_exists("hash_hmac")) {
|
|
return hash_hmac("sha256", $text, $pepper);
|
|
}
|
|
// Sha256 hash is the next best thing
|
|
if (function_exists("hash")) {
|
|
return hash("sha256", $salt . $text . $pepper);
|
|
}
|
|
case 'weak':
|
|
throw \Exception("Too weak of a Hash FN");
|
|
// return sha1($salt . $text . $pepper);
|
|
case 'low':
|
|
throw \Exception("Too weak of a Hash FN");
|
|
// return md5($salt . md5($text . $pepper));
|
|
default:
|
|
break;
|
|
}
|
|
return self::use_hmac($level, $pepper);
|
|
}
|
|
|
|
/**
|
|
* @method filter_class
|
|
* @param type $class
|
|
* Please NEVER add a period or SLASH as it will allow BAD things!
|
|
* IT should be a-zA-Z0-9_ and that's it.
|
|
* @retval string of safe class name
|
|
*/
|
|
public static function filter_class(string $class): string {
|
|
if (bootstrap\requires::is_dangerous($class)) {
|
|
throw new \Exception("Dangerious URI!");
|
|
}
|
|
return preg_replace('/[^a-zA-Z0-9_]/', '', $class);
|
|
}
|
|
|
|
/**
|
|
* Filter possible unsafe URI, prevent ../up-level hackers
|
|
* @param string $uri
|
|
* @return string Safe URI
|
|
*/
|
|
public static function filter_uri(string $uri): string {
|
|
if (bootstrap\requires::is_dangerous($uri) === true) {
|
|
throw new \Exception("Dangerious URI!");
|
|
}
|
|
return bootstrap\requires::filter_file_name($uri);
|
|
}
|
|
|
|
public static function id_hash(): string {
|
|
return crc32($_SESSION['user_id']);
|
|
}
|
|
|
|
public static function get_valid_ip(string $ip) {
|
|
return (filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_IPV4|FILTER_FLAG_IPV6));
|
|
}
|
|
public static function get_valid_public_ip(string $ip) {
|
|
return (filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_IPV4|FILTER_FLAG_IPV6|FILTER_FLAG_NO_PRIV_RANGE));
|
|
}
|
|
|
|
/**
|
|
* Is the server on the local test domain name
|
|
* @return bool SERVER Domain name is on whitelist
|
|
*/
|
|
public static function is_server_name_on_domain_list(array $whitelist): bool {
|
|
if (!isset($_SERVER['SERVER_NAME'])) {
|
|
return false;
|
|
}
|
|
return (in_array($_SERVER['SERVER_NAME'], $whitelist));
|
|
}
|
|
|
|
/**
|
|
* Check if same Domain as Server
|
|
* @return bool
|
|
*/
|
|
public static function request_is_same_domain(): bool {
|
|
if (!isset($_SERVER['HTTP_REFERER'])) {
|
|
// No referer send, so can't be same domain!
|
|
return false;
|
|
} else {
|
|
$referer_host = parse_url($_SERVER['HTTP_REFERER'] . PHP_URL_HOST);
|
|
if ($referer_host === false) {
|
|
return false; // Malformed URL
|
|
}
|
|
$refed_host = $referer_host['host'] ?? "";
|
|
|
|
$server_host = $_SERVER['HTTP_HOST'];
|
|
return ($refed_host === $server_host);
|
|
}
|
|
}
|
|
|
|
|
|
public static function get_client_ip_address() {
|
|
$ipaddress = '';
|
|
if (isset($_SERVER['HTTP_CLIENT_IP'])) {
|
|
$ipaddress = $_SERVER['HTTP_CLIENT_IP'];
|
|
} else if (isset($_SERVER['HTTP_X_FORWARDED_FOR'])) {
|
|
$ipaddress = $_SERVER['HTTP_X_FORWARDED_FOR'];
|
|
} else if (isset($_SERVER['HTTP_X_FORWARDED'])) {
|
|
$ipaddress = $_SERVER['HTTP_X_FORWARDED'];
|
|
} else if (isset($_SERVER['HTTP_FORWARDED_FOR'])) {
|
|
$ipaddress = $_SERVER['HTTP_FORWARDED_FOR'];
|
|
} else if (isset($_SERVER['HTTP_FORWARDED'])) {
|
|
$ipaddress = $_SERVER['HTTP_FORWARDED'];
|
|
} else if (isset($_SERVER['REMOTE_ADDR'])) {
|
|
$ipaddress = $_SERVER['REMOTE_ADDR'];
|
|
} else {
|
|
$ipaddress = 'UNKNOWN';
|
|
}
|
|
return $ipaddress;
|
|
}
|
|
|
|
/**
|
|
* Make sure uploads (LIKE Images, etc...) do NOT run PHP code!!
|
|
* Checks for PHP tags inside of file.
|
|
* @param string $file
|
|
* @return bool true if PHP was found
|
|
*/
|
|
public static function file_contains_php(string $file): bool {
|
|
$file_handle = fopen($file, "r");
|
|
while (!feof($file_handle)) {
|
|
$line = fgets($file_handle);
|
|
$pos = strpos($line, '<?php');
|
|
if ($pos !== false) {
|
|
fclose($file_handle);
|
|
return true;
|
|
}
|
|
}
|
|
fclose($file_handle);
|
|
return false;
|
|
}
|
|
|
|
}
|
|
|