Compare commits
28 Commits
dc9d7abe14
...
8aa9d94acf
Author | SHA1 | Date | |
---|---|---|---|
8aa9d94acf
|
|||
217e1fc86b
|
|||
605fd88b6e
|
|||
7a252c6bfa
|
|||
89e06769f1
|
|||
9d4d326d6a
|
|||
298e2238a8
|
|||
d28c245a1a
|
|||
2b4abf6338
|
|||
34c2d57e5b
|
|||
287fb02f8c
|
|||
d028ac0176
|
|||
ddab52b1a5
|
|||
ada82f07b6
|
|||
cad5b4a6f8
|
|||
6a7f91ef1d
|
|||
6db2b4cc1f
|
|||
4c5097701f
|
|||
886acead63
|
|||
35c0fe4723
|
|||
a38c3c6fae
|
|||
57da808a6a
|
|||
558ed729dc
|
|||
14c7ffb8d4
|
|||
a16619b78e
|
|||
90997e4eb5
|
|||
c60932088d
|
|||
258781307d
|
11
MD_JAIL.php
11
MD_JAIL.php
@ -5,7 +5,7 @@
|
|||||||
declare(strict_types = 1);
|
declare(strict_types = 1);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A class that, once initialized, forces the programmer to make security instructions implicit.
|
* A class that, once initialized, forces the programmer to make security instructions explicit.
|
||||||
* If an object of the class has been created, not specifying security instructions
|
* If an object of the class has been created, not specifying security instructions
|
||||||
* leads to an error.
|
* leads to an error.
|
||||||
* A restriction on basic file operations is not practical in an md context because of
|
* A restriction on basic file operations is not practical in an md context because of
|
||||||
@ -178,7 +178,7 @@ final class MD_JAIL {
|
|||||||
*
|
*
|
||||||
* @return void
|
* @return void
|
||||||
*/
|
*/
|
||||||
private function _apply_basedir_restriction():void {
|
private function _apply_basedir_restriction():void {
|
||||||
|
|
||||||
if (empty($this->_open_basedir)) {
|
if (empty($this->_open_basedir)) {
|
||||||
throw new MDJailSecurityOptionNotSetException("It has not been specified, which memory limit the script should hold. Set MD_JAIL->open_basedir = string.");
|
throw new MDJailSecurityOptionNotSetException("It has not been specified, which memory limit the script should hold. Set MD_JAIL->open_basedir = string.");
|
||||||
@ -187,7 +187,7 @@ final class MD_JAIL {
|
|||||||
throw new Exception('Failed to set open_basedir restrictions');
|
throw new Exception('Failed to set open_basedir restrictions');
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Enforces security options previously set.
|
* Enforces security options previously set.
|
||||||
@ -208,6 +208,7 @@ final class MD_JAIL {
|
|||||||
|
|
||||||
$this->_status = self::STATUS_SPECIFIED;
|
$this->_status = self::STATUS_SPECIFIED;
|
||||||
$this->__destruct();
|
$this->__destruct();
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
$this->_apply_memory_limit();
|
$this->_apply_memory_limit();
|
||||||
@ -233,6 +234,9 @@ final class MD_JAIL {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Destructor. Throws an exception if the settings have not been set.
|
||||||
|
*/
|
||||||
public function __destruct() {
|
public function __destruct() {
|
||||||
|
|
||||||
if ($this->_status !== self::STATUS_SPECIFIED) {
|
if ($this->_status !== self::STATUS_SPECIFIED) {
|
||||||
@ -255,6 +259,5 @@ final class MD_JAIL {
|
|||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
238
MD_STD.php
238
MD_STD.php
@ -44,11 +44,56 @@ final class MD_STD {
|
|||||||
public static function realpath(string $path):string {
|
public static function realpath(string $path):string {
|
||||||
|
|
||||||
$output = \realpath($path);
|
$output = \realpath($path);
|
||||||
if (!\is_string($output)) throw new MDFileDoesNotExist("The file {$path} does not exist or is not readable.");
|
if (!\is_string($output)) {
|
||||||
|
throw new MDFileDoesNotExist("The file {$path} does not exist or is not readable.");
|
||||||
|
}
|
||||||
return $output;
|
return $output;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Wrapper around mkdir, that throws an exception, if the folder cannot be
|
||||||
|
* created.
|
||||||
|
*
|
||||||
|
* @see https://www.php.net/manual/en/function.mkdir.php
|
||||||
|
*
|
||||||
|
* @param string $pathname The directory path.
|
||||||
|
* @param integer $mode Permissions.
|
||||||
|
* @param boolean $recursive Allows the creation of nested directories
|
||||||
|
* specified in the pathname.
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public static function mkdir(string $pathname, int $mode = 0775, bool $recursive = false):void {
|
||||||
|
|
||||||
|
// One reason, to throw an error, is that the folder already exists.
|
||||||
|
if (\is_dir($pathname)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (\mkdir($pathname, $mode, $recursive) === false) {
|
||||||
|
throw new Exception("Failed to create directory: $pathname");
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Wrapper around unlink, that throws an exception if the file failed to be
|
||||||
|
* removed.
|
||||||
|
*
|
||||||
|
* @see https://www.php.net/manual/en/function.unlink.php
|
||||||
|
*
|
||||||
|
* @param string $filename File path.
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public static function unlink(string $filename):void {
|
||||||
|
|
||||||
|
if (\unlink($filename) === false) {
|
||||||
|
throw new Exception("Failed to delete: $filename");
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets contents of a folder.
|
* Gets contents of a folder.
|
||||||
*
|
*
|
||||||
@ -81,7 +126,9 @@ final class MD_STD {
|
|||||||
public static function ob_get_clean():string {
|
public static function ob_get_clean():string {
|
||||||
|
|
||||||
$output = \ob_get_clean();
|
$output = \ob_get_clean();
|
||||||
if ($output === false) throw new MDOutputBufferNotStarted("Output buffer was not started");
|
if ($output === false) {
|
||||||
|
throw new MDOutputBufferNotStarted("Output buffer was not started");
|
||||||
|
}
|
||||||
return $output;
|
return $output;
|
||||||
|
|
||||||
}
|
}
|
||||||
@ -96,8 +143,12 @@ final class MD_STD {
|
|||||||
*/
|
*/
|
||||||
public static function startsWith(string $haystack, string $needle):bool {
|
public static function startsWith(string $haystack, string $needle):bool {
|
||||||
|
|
||||||
if (substr($haystack, 0, \strlen($needle)) == $needle) return true;
|
if (substr($haystack, 0, \strlen($needle)) === $needle) {
|
||||||
else return false;
|
return true;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -114,7 +165,9 @@ final class MD_STD {
|
|||||||
$output = false;
|
$output = false;
|
||||||
foreach ($needles as $needle) {
|
foreach ($needles as $needle) {
|
||||||
$output = self::startsWith($haystack, $needle);
|
$output = self::startsWith($haystack, $needle);
|
||||||
if ($output == true) return $output;
|
if ($output == true) {
|
||||||
|
return $output;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return $output;
|
return $output;
|
||||||
|
|
||||||
@ -154,7 +207,9 @@ final class MD_STD {
|
|||||||
public static function json_encode(array $value, int $options = 0, int $depth = 512):string {
|
public static function json_encode(array $value, int $options = 0, int $depth = 512):string {
|
||||||
|
|
||||||
$output = \json_encode($value, $options, $depth);
|
$output = \json_encode($value, $options, $depth);
|
||||||
if ($output === false) throw new Exception("JSON output could not be generated");
|
if ($output === false) {
|
||||||
|
throw new Exception("JSON output could not be generated");
|
||||||
|
}
|
||||||
return $output;
|
return $output;
|
||||||
|
|
||||||
}
|
}
|
||||||
@ -169,11 +224,45 @@ final class MD_STD {
|
|||||||
public static function strtotime(string $datetime):int {
|
public static function strtotime(string $datetime):int {
|
||||||
|
|
||||||
$output = \strtotime($datetime);
|
$output = \strtotime($datetime);
|
||||||
if ($output === false) throw new MDInvalidInputDate("Invalid input date {$datetime}.");
|
if ($output === false) {
|
||||||
|
throw new MDInvalidInputDate("Invalid input date {$datetime}.");
|
||||||
|
}
|
||||||
return $output;
|
return $output;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initializes a curl request with the given presets.
|
||||||
|
*
|
||||||
|
* @param string $url URL to query.
|
||||||
|
* @param integer $timeout Timeout in milliseconds.
|
||||||
|
*
|
||||||
|
* @return resource
|
||||||
|
*/
|
||||||
|
public static function curl_init(string $url, int $timeout) {
|
||||||
|
|
||||||
|
$curl = \curl_init();
|
||||||
|
|
||||||
|
\curl_setopt($curl, CURLOPT_URL, $url);
|
||||||
|
\curl_setopt($curl, CURLOPT_HEADER, false);
|
||||||
|
\curl_setopt($curl, CURLOPT_CONNECTTIMEOUT_MS, $timeout); //timeout in seconds
|
||||||
|
\curl_setopt($curl, CURLOPT_TIMEOUT_MS, $timeout); //timeout in seconds
|
||||||
|
\curl_setopt($curl, CURLOPT_FOLLOWLOCATION, true);
|
||||||
|
// \curl_setopt($curl, CURLOPT_COOKIESESSION, true);
|
||||||
|
\curl_setopt($curl, CURLOPT_AUTOREFERER, true);
|
||||||
|
\curl_setopt($curl, CURLOPT_USERAGENT, 'Mozilla/5.0 (Windows NT 6.1; WOW64; rv:10.0.2) Gecko/20100101 Firefox/10.0.2');
|
||||||
|
|
||||||
|
/*
|
||||||
|
if (!file_exists(__DIR__ . '/../../curled.txt')) {
|
||||||
|
touch (__DIR__ . '/../../curled.txt');
|
||||||
|
}
|
||||||
|
file_put_contents(__DIR__ . '/../../curled.txt', $url . PHP_EOL, FILE_APPEND);
|
||||||
|
*/
|
||||||
|
|
||||||
|
return $curl;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Wrapper for curling contents from the web.
|
* Wrapper for curling contents from the web.
|
||||||
*
|
*
|
||||||
@ -184,28 +273,67 @@ final class MD_STD {
|
|||||||
*/
|
*/
|
||||||
public static function runCurl(string $url, int $timeout = 1200):string {
|
public static function runCurl(string $url, int $timeout = 1200):string {
|
||||||
|
|
||||||
$curl = \curl_init();
|
$curl = self::curl_init($url, $timeout);
|
||||||
|
|
||||||
\curl_setopt($curl, CURLOPT_URL, $url);
|
|
||||||
\curl_setopt($curl, CURLOPT_HEADER, false);
|
|
||||||
\curl_setopt($curl, CURLOPT_CONNECTTIMEOUT_MS, $timeout); //timeout in seconds
|
|
||||||
\curl_setopt($curl, CURLOPT_TIMEOUT_MS, $timeout); //timeout in seconds
|
|
||||||
\curl_setopt($curl, CURLOPT_RETURNTRANSFER, true);
|
\curl_setopt($curl, CURLOPT_RETURNTRANSFER, true);
|
||||||
\curl_setopt($curl, CURLOPT_FOLLOWLOCATION, true);
|
|
||||||
// \curl_setopt($curl, CURLOPT_COOKIESESSION, true);
|
|
||||||
\curl_setopt($curl, CURLOPT_AUTOREFERER, true);
|
|
||||||
\curl_setopt($curl, CURLOPT_USERAGENT, 'Mozilla/5.0 (Windows NT 6.1; WOW64; rv:10.0.2) Gecko/20100101 Firefox/10.0.2');
|
|
||||||
$result = \curl_exec($curl);
|
$result = \curl_exec($curl);
|
||||||
|
|
||||||
// if ($err = curl_errno($curl)) echo $err;
|
// if ($err = curl_errno($curl)) echo $err;
|
||||||
// if ($errmsg = curl_error($curl)) echo $errmsg;
|
// if ($errmsg = curl_error($curl)) echo $errmsg;
|
||||||
|
|
||||||
\curl_close($curl);
|
\curl_close($curl);
|
||||||
if (\is_bool($result)) return "";
|
if (\is_bool($result)) {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
return $result;
|
return $result;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Wrapper for curling multiple pages from the web at ones and returning their contents.
|
||||||
|
* Adapted from hushuilong's comment at https://www.php.net/manual/de/function.curl-multi-init.php#105252.
|
||||||
|
*
|
||||||
|
* @param array<string> $urls URL to query.
|
||||||
|
* @param integer $timeout Timeout in milliseconds.
|
||||||
|
*
|
||||||
|
* @return array<string>
|
||||||
|
*/
|
||||||
|
public static function runCurlMulti(array $urls, int $timeout = 1200):array {
|
||||||
|
|
||||||
|
if (!($mh = curl_multi_init())) {
|
||||||
|
throw new exception("Failed to set up multi handle");
|
||||||
|
}
|
||||||
|
|
||||||
|
$curl_array = [];
|
||||||
|
foreach($urls as $i => $url) {
|
||||||
|
|
||||||
|
$curl_array[$i] = self::curl_init($url, $timeout);
|
||||||
|
|
||||||
|
curl_setopt($curl_array[$i], CURLOPT_RETURNTRANSFER, true);
|
||||||
|
curl_multi_add_handle($mh, $curl_array[$i]);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
$running = null;
|
||||||
|
do {
|
||||||
|
usleep(10000);
|
||||||
|
curl_multi_exec($mh, $running);
|
||||||
|
} while($running > 0);
|
||||||
|
|
||||||
|
$res = [];
|
||||||
|
foreach($urls as $i => $url) {
|
||||||
|
$res[$i] = curl_multi_getcontent($curl_array[$i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach($urls as $i => $url){
|
||||||
|
curl_multi_remove_handle($mh, $curl_array[$i]);
|
||||||
|
}
|
||||||
|
curl_multi_close($mh);
|
||||||
|
|
||||||
|
return $res;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Function lang_getfrombrowser gets the browser language based on HTTP headers.
|
* Function lang_getfrombrowser gets the browser language based on HTTP headers.
|
||||||
*
|
*
|
||||||
@ -221,7 +349,9 @@ final class MD_STD {
|
|||||||
|
|
||||||
// $_SERVER['HTTP_ACCEPT_LANGUAGE'] verwenden, wenn keine Sprachvariable mitgegeben wurde
|
// $_SERVER['HTTP_ACCEPT_LANGUAGE'] verwenden, wenn keine Sprachvariable mitgegeben wurde
|
||||||
if ($lang_variable === "") {
|
if ($lang_variable === "") {
|
||||||
if (isset($_SERVER['HTTP_ACCEPT_LANGUAGE'])) $lang_variable = $_SERVER['HTTP_ACCEPT_LANGUAGE'];
|
if (isset($_SERVER['HTTP_ACCEPT_LANGUAGE'])) {
|
||||||
|
$lang_variable = $_SERVER['HTTP_ACCEPT_LANGUAGE'];
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// wurde irgendwelche Information mitgeschickt?
|
// wurde irgendwelche Information mitgeschickt?
|
||||||
@ -232,7 +362,9 @@ final class MD_STD {
|
|||||||
|
|
||||||
// Den Header auftrennen
|
// Den Header auftrennen
|
||||||
$accepted_languages = preg_split('/,\s*/', $lang_variable);
|
$accepted_languages = preg_split('/,\s*/', $lang_variable);
|
||||||
if (!is_array($accepted_languages)) return $default_language;
|
if (!is_array($accepted_languages)) {
|
||||||
|
return $default_language;
|
||||||
|
}
|
||||||
|
|
||||||
// Die Standardwerte einstellen
|
// Die Standardwerte einstellen
|
||||||
$current_lang = $default_language;
|
$current_lang = $default_language;
|
||||||
@ -259,7 +391,8 @@ final class MD_STD {
|
|||||||
if (isset($matches[2])) {
|
if (isset($matches[2])) {
|
||||||
// die Qualität benutzen
|
// die Qualität benutzen
|
||||||
$lang_quality = (float)$matches[2];
|
$lang_quality = (float)$matches[2];
|
||||||
} else {
|
}
|
||||||
|
else {
|
||||||
// Kompabilitätsmodus: Qualität 1 annehmen
|
// Kompabilitätsmodus: Qualität 1 annehmen
|
||||||
$lang_quality = 1.0;
|
$lang_quality = 1.0;
|
||||||
}
|
}
|
||||||
@ -340,7 +473,9 @@ final class MD_STD {
|
|||||||
public static function openssl_random_pseudo_bytes(int $length):string {
|
public static function openssl_random_pseudo_bytes(int $length):string {
|
||||||
|
|
||||||
$output = \openssl_random_pseudo_bytes($length);
|
$output = \openssl_random_pseudo_bytes($length);
|
||||||
if ($output === false) throw new Exception("Failed generating random pseudo bytes using openssl_random_pseudo_bytes");
|
if ($output === false) {
|
||||||
|
throw new Exception("Failed generating random pseudo bytes using openssl_random_pseudo_bytes");
|
||||||
|
}
|
||||||
return $output;
|
return $output;
|
||||||
|
|
||||||
}
|
}
|
||||||
@ -356,7 +491,9 @@ final class MD_STD {
|
|||||||
|
|
||||||
$input = \explode(PHP_EOL, $input);
|
$input = \explode(PHP_EOL, $input);
|
||||||
$output = "";
|
$output = "";
|
||||||
foreach ($input as $line) $output .= \trim($line) . PHP_EOL;
|
foreach ($input as $line) {
|
||||||
|
$output .= \trim($line) . PHP_EOL;
|
||||||
|
}
|
||||||
return $output;
|
return $output;
|
||||||
|
|
||||||
}
|
}
|
||||||
@ -412,7 +549,7 @@ final class MD_STD {
|
|||||||
*
|
*
|
||||||
* @return void
|
* @return void
|
||||||
*/
|
*/
|
||||||
public static function ensure_file(string $filepath, array $accepted_mimetype = []) {
|
public static function ensure_file(string $filepath, array $accepted_mimetype = []):void {
|
||||||
|
|
||||||
if (!\file_exists($filepath)) {
|
if (!\file_exists($filepath)) {
|
||||||
throw new MDFileDoesNotExist("File " . basename($filepath) . " does not exist");
|
throw new MDFileDoesNotExist("File " . basename($filepath) . " does not exist");
|
||||||
@ -430,4 +567,57 @@ final class MD_STD {
|
|||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Wrapper around exec, to be used with editing functions.
|
||||||
|
* Pipes STDERR to STDOUT and throws an Exception on any error.
|
||||||
|
*
|
||||||
|
* @param string $cmds Commands to run.
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public static function exec_edit(string $cmds):void {
|
||||||
|
|
||||||
|
$error = \shell_exec($cmds . ' 2>&1 1>/dev/null');
|
||||||
|
if (!empty($error)) {
|
||||||
|
throw new \Exception('Shell error: ' . $error . PHP_EOL . PHP_EOL . 'Command was: ' . $cmds);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Wrapper around levenshtein(), that only compares the first 250
|
||||||
|
* characters (levenshtein can't handle more than 255).
|
||||||
|
*
|
||||||
|
* @param string $str1 First string.
|
||||||
|
* @param string $str2 Second string.
|
||||||
|
*
|
||||||
|
* @return integer
|
||||||
|
*/
|
||||||
|
public static function levenshtein(string $str1, string $str2):int {
|
||||||
|
|
||||||
|
if (\strlen($str1) > 250) {
|
||||||
|
$str1 = \substr($str1, 0, 250);
|
||||||
|
}
|
||||||
|
if (\strlen($str2) > 250) {
|
||||||
|
$str2 = \substr($str2, 0, 250);
|
||||||
|
}
|
||||||
|
|
||||||
|
return \levenshtein($str1, $str2);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Converts a string to color codes.
|
||||||
|
*
|
||||||
|
* @param string $str Input string.
|
||||||
|
*
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public static function string_to_color_code(string $str):string {
|
||||||
|
|
||||||
|
$code = substr(dechex(crc32($str)), 0, 6);
|
||||||
|
return $code;
|
||||||
|
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
88
MD_STD_CACHE.php
Normal file
88
MD_STD_CACHE.php
Normal file
@ -0,0 +1,88 @@
|
|||||||
|
<?PHP
|
||||||
|
/**
|
||||||
|
* Provides static functions for simple caching.
|
||||||
|
*
|
||||||
|
* @author Joshua Ramon Enslin <joshua@museum-digital.de>
|
||||||
|
*/
|
||||||
|
declare(strict_types = 1);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Provides caching functions.
|
||||||
|
*/
|
||||||
|
final class MD_STD_CACHE {
|
||||||
|
|
||||||
|
/** @var string */
|
||||||
|
public static string $redis_host = '127.0.0.1';
|
||||||
|
/** @var integer */
|
||||||
|
public static int $redis_port = 6379;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Shutdown function for caching contents of output buffer.
|
||||||
|
*
|
||||||
|
* @param string $redisKey Key to cache by in redis.
|
||||||
|
* @param integer $expiry Expiration time in seconds.
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public static function shutdown_cache_through_redis(string $redisKey, int $expiry = 3600):void {
|
||||||
|
|
||||||
|
$outputT = trim(MD_STD::minimizeHTMLString(MD_STD::ob_get_clean()));
|
||||||
|
echo $outputT;
|
||||||
|
|
||||||
|
$redis = new Redis();
|
||||||
|
$redis->connect(self::$redis_host, self::$redis_port, 1, null, 0, 0, ['auth' => [MD_CONF::$redis_pw]]);
|
||||||
|
$redis->set($redisKey, $outputT);
|
||||||
|
$redis->expire($redisKey, $expiry);
|
||||||
|
$redis->close();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Caches and serves a page through redis. Should be called at the start
|
||||||
|
* of the script generating a page.
|
||||||
|
*
|
||||||
|
* @param string $redisKey Key to cache by in redis.
|
||||||
|
* @param integer $expiry Expiration time in seconds.
|
||||||
|
*
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public static function serve_page_through_redis_cache(string $redisKey, int $expiry = 3600):string {
|
||||||
|
|
||||||
|
if (PHP_SAPI === 'cli') {
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
|
||||||
|
$redis = new Redis();
|
||||||
|
$redis->connect(self::$redis_host, self::$redis_port, 1, null, 0, 0, ['auth' => [MD_CONF::$redis_pw]]);
|
||||||
|
if ($redis->ping() !== false) {
|
||||||
|
|
||||||
|
ob_start();
|
||||||
|
|
||||||
|
if (($redisResult = $redis->get($redisKey))) {
|
||||||
|
|
||||||
|
if (strlen($redisResult) > 3) {
|
||||||
|
$redis->close();
|
||||||
|
return $redisResult;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
register_shutdown_function(function(string $redisKey, int $expiry = 3600) :void {
|
||||||
|
self::shutdown_cache_through_redis($redisKey, $expiry);
|
||||||
|
}, $redisKey);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
|
||||||
|
register_shutdown_function(function(string $redisKey, int $expiry = 3600) :void {
|
||||||
|
self::shutdown_cache_through_redis($redisKey, $expiry);
|
||||||
|
}, $redisKey);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
$redis->close();
|
||||||
|
|
||||||
|
return '';
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
@ -9,7 +9,6 @@ declare(strict_types = 1);
|
|||||||
* functions.
|
* functions.
|
||||||
*/
|
*/
|
||||||
final class MD_STD_IN {
|
final class MD_STD_IN {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Validates and sanitizes input integers to be in line with MySQL
|
* Validates and sanitizes input integers to be in line with MySQL
|
||||||
* autoincrement IDs.
|
* autoincrement IDs.
|
||||||
@ -45,7 +44,9 @@ final class MD_STD_IN {
|
|||||||
*/
|
*/
|
||||||
public static function sanitize_id_or_zero($input):int {
|
public static function sanitize_id_or_zero($input):int {
|
||||||
|
|
||||||
if ($input === "") return 0;
|
if ($input === "") {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
$input = \filter_var($input, \FILTER_VALIDATE_INT, [
|
$input = \filter_var($input, \FILTER_VALIDATE_INT, [
|
||||||
'options' => [
|
'options' => [
|
||||||
@ -77,7 +78,9 @@ final class MD_STD_IN {
|
|||||||
FILTER_SANITIZE_STRING,
|
FILTER_SANITIZE_STRING,
|
||||||
FILTER_FLAG_NO_ENCODE_QUOTES);
|
FILTER_FLAG_NO_ENCODE_QUOTES);
|
||||||
|
|
||||||
if ($output === false) return "";
|
if ($output === false) {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
while (strpos($output, " ") !== false) {
|
while (strpos($output, " ") !== false) {
|
||||||
$output = str_replace(" ", " ", $output);
|
$output = str_replace(" ", " ", $output);
|
||||||
}
|
}
|
||||||
@ -86,6 +89,29 @@ final class MD_STD_IN {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* String sanitization for 3 o4 6 characters RGB color codes (sans the leading #).
|
||||||
|
*
|
||||||
|
* @param mixed $input Input string.
|
||||||
|
*
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public static function sanitize_rgb_color($input):string {
|
||||||
|
|
||||||
|
$output = \filter_var($input,
|
||||||
|
FILTER_SANITIZE_STRING,
|
||||||
|
FILTER_FLAG_NO_ENCODE_QUOTES);
|
||||||
|
|
||||||
|
if ($output === false
|
||||||
|
|| ((preg_match('/^[a-zA-Z0-9]{3}$/', $output)) === false && (preg_match('/^[a-zA-Z0-9]{6}$/', $output)) === false)
|
||||||
|
) {
|
||||||
|
throw new MDInvalidColorCode("Invalid color code provided: " . $output);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $output;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Retrieves HTTP input texts from GET or POST variables, whatever is provided.
|
* Retrieves HTTP input texts from GET or POST variables, whatever is provided.
|
||||||
* If neither is given, returns a provided default.
|
* If neither is given, returns a provided default.
|
||||||
@ -104,10 +130,12 @@ final class MD_STD_IN {
|
|||||||
else if (isset($_POST[$var_name])) {
|
else if (isset($_POST[$var_name])) {
|
||||||
$output = self::sanitize_text($_POST[$var_name]);
|
$output = self::sanitize_text($_POST[$var_name]);
|
||||||
}
|
}
|
||||||
else $output = self::sanitize_text($default);
|
else {
|
||||||
|
$output = self::sanitize_text($default);
|
||||||
|
}
|
||||||
|
|
||||||
if (!empty($allowed) and !\in_array($output, $allowed, true)) {
|
if (!empty($allowed) and !\in_array($output, $allowed, true)) {
|
||||||
Throw new MDpageParameterNotFromListException("Parameter `{$var_name}` must be any of the allowed values: '" . implode('\', \'', $allowed) . "'");
|
throw new MDpageParameterNotFromListException("Parameter `{$var_name}` must be any of the allowed values: '" . implode('\', \'', $allowed) . "'");
|
||||||
}
|
}
|
||||||
|
|
||||||
return $output;
|
return $output;
|
||||||
@ -129,10 +157,12 @@ final class MD_STD_IN {
|
|||||||
if (isset($_POST[$var_name])) {
|
if (isset($_POST[$var_name])) {
|
||||||
$output = self::sanitize_text($_POST[$var_name]);
|
$output = self::sanitize_text($_POST[$var_name]);
|
||||||
}
|
}
|
||||||
else $output = self::sanitize_text($default);
|
else {
|
||||||
|
$output = self::sanitize_text($default);
|
||||||
|
}
|
||||||
|
|
||||||
if (!empty($allowed) and !\in_array($output, $allowed, true)) {
|
if (!empty($allowed) and !\in_array($output, $allowed, true)) {
|
||||||
Throw new MDpageParameterNotFromListException("Parameter `{$var_name}` must be any of the allowed values: '" . implode('\', \'', $allowed) . "'");
|
throw new MDpageParameterNotFromListException("Parameter `{$var_name}` must be any of the allowed values: '" . implode('\', \'', $allowed) . "'");
|
||||||
}
|
}
|
||||||
|
|
||||||
return $output;
|
return $output;
|
||||||
@ -148,7 +178,9 @@ final class MD_STD_IN {
|
|||||||
*/
|
*/
|
||||||
public static function sanitize_url($input):string {
|
public static function sanitize_url($input):string {
|
||||||
|
|
||||||
if ($input === "") return "";
|
if ($input === "") {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
$output = \filter_var($input, FILTER_SANITIZE_URL);
|
$output = \filter_var($input, FILTER_SANITIZE_URL);
|
||||||
if (($output = \filter_var($output, FILTER_VALIDATE_URL)) === false) {
|
if (($output = \filter_var($output, FILTER_VALIDATE_URL)) === false) {
|
||||||
@ -168,7 +200,9 @@ final class MD_STD_IN {
|
|||||||
*/
|
*/
|
||||||
public static function sanitize_email($input):string {
|
public static function sanitize_email($input):string {
|
||||||
|
|
||||||
if ($input === "") return "";
|
if ($input === "") {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
$output = \filter_var($input, FILTER_SANITIZE_EMAIL);
|
$output = \filter_var($input, FILTER_SANITIZE_EMAIL);
|
||||||
if (($output = \filter_var($output, FILTER_VALIDATE_EMAIL)) === false) {
|
if (($output = \filter_var($output, FILTER_VALIDATE_EMAIL)) === false) {
|
||||||
@ -205,7 +239,9 @@ final class MD_STD_IN {
|
|||||||
*/
|
*/
|
||||||
public static function validate_isbn(string $input):string {
|
public static function validate_isbn(string $input):string {
|
||||||
|
|
||||||
if ($input === "") return "";
|
if ($input === "") {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
// Remove hyphens
|
// Remove hyphens
|
||||||
$input = trim(strtr($input, ["-" => "", "–" => ""]));
|
$input = trim(strtr($input, ["-" => "", "–" => ""]));
|
||||||
@ -231,5 +267,4 @@ final class MD_STD_IN {
|
|||||||
throw new MDgenericInvalidInputsException("ISBNs must be either 10 or 13 characters long.");
|
throw new MDgenericInvalidInputsException("ISBNs must be either 10 or 13 characters long.");
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -38,7 +38,8 @@ final class MD_STD_SEC {
|
|||||||
) {
|
) {
|
||||||
$validity = true;
|
$validity = true;
|
||||||
}
|
}
|
||||||
$_SESSION['csrf-token'] = null; unset($_SESSION['csrf-token']);
|
$_SESSION['csrf-token'] = null;
|
||||||
|
unset($_SESSION['csrf-token']);
|
||||||
|
|
||||||
return $validity;
|
return $validity;
|
||||||
|
|
||||||
@ -46,8 +47,8 @@ final class MD_STD_SEC {
|
|||||||
|
|
||||||
const BRUTE_FORCE_DELAY_DEFAULT = 2000; // 2000 microseconds = 2 milliseconds
|
const BRUTE_FORCE_DELAY_DEFAULT = 2000; // 2000 microseconds = 2 milliseconds
|
||||||
const BRUTE_FORCE_DELAY_MULTIPLIER_COMMON = 1.08;
|
const BRUTE_FORCE_DELAY_MULTIPLIER_COMMON = 1.08;
|
||||||
const BRUTE_FORCE_DELAY_MULTIPLIER_PER_USER = 3;
|
const BRUTE_FORCE_DELAY_MULTIPLIER_PER_USER = 1.8;
|
||||||
const BRUTE_FORCE_DELAY_MULTIPLIER_PER_IP = 8;
|
const BRUTE_FORCE_DELAY_MULTIPLIER_PER_IP = 4;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Prevent brute force attacks by delaying the login .
|
* Prevent brute force attacks by delaying the login .
|
||||||
@ -67,7 +68,9 @@ final class MD_STD_SEC {
|
|||||||
$logfile_common = \sys_get_temp_dir() . "/logins_{$tool_name}.json";
|
$logfile_common = \sys_get_temp_dir() . "/logins_{$tool_name}.json";
|
||||||
|
|
||||||
// Ensure the log files exist
|
// Ensure the log files exist
|
||||||
if (!\file_exists($logfile_common)) \file_put_contents($logfile_common, "[]");
|
if (!\file_exists($logfile_common)) {
|
||||||
|
\file_put_contents($logfile_common, "[]");
|
||||||
|
}
|
||||||
|
|
||||||
// Hash entered username and IP to prevent malicious strings from
|
// Hash entered username and IP to prevent malicious strings from
|
||||||
// entering the system.
|
// entering the system.
|
||||||
@ -102,7 +105,7 @@ final class MD_STD_SEC {
|
|||||||
// Translate counters into delay multipliers
|
// Translate counters into delay multipliers
|
||||||
$delay_multiplier_common = $loginLog['common']['count'];
|
$delay_multiplier_common = $loginLog['common']['count'];
|
||||||
$delay_multiplier_per_user = $loginLog['usr'][$hash_user]['count'];
|
$delay_multiplier_per_user = $loginLog['usr'][$hash_user]['count'];
|
||||||
$delay_multiplier_per_ip = $loginLog['usr'][$hash_ip]['count'];
|
$delay_multiplier_per_ip = $loginLog['ip'][$hash_ip]['count'];
|
||||||
|
|
||||||
// Calculate delay
|
// Calculate delay
|
||||||
$delay_micoseconds = \intval(self::BRUTE_FORCE_DELAY_DEFAULT *
|
$delay_micoseconds = \intval(self::BRUTE_FORCE_DELAY_DEFAULT *
|
||||||
@ -122,4 +125,24 @@ final class MD_STD_SEC {
|
|||||||
return true;
|
return true;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Send CSP headers.
|
||||||
|
*
|
||||||
|
* @param array{default-src: string, connect-src: string, script-src: string, img-src: string, media-src: string, style-src: string, frame-src: string, object-src: string, base-uri: string, form-action: string, frame-ancestors?: string} $directives Directives to send. Font source is always set to 'self', and hence excluded.
|
||||||
|
* @param string $frame_ancestors Frame ancestors directive. Default is to not set it.
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public static function sendContentSecurityPolicy(array $directives, string $frame_ancestors = ""):void {
|
||||||
|
|
||||||
|
$policy = 'Content-Security-Policy: default-src ' . $directives['default-src'] . '; connect-src ' . $directives['connect-src'] . '; script-src ' . $directives['script-src'] . '; img-src ' . $directives['img-src'] . '; media-src ' . $directives['media-src'] . '; style-src ' . $directives['style-src'] . '; font-src \'self\'; frame-src ' . $directives['frame-src'] . '; object-src ' . $directives['object-src'] . '; base-uri ' . $directives['base-uri'] . '; form-action ' . $directives['form-action'] . '; manifest-src \'self\';';
|
||||||
|
|
||||||
|
if (!empty($frame_ancestors)) {
|
||||||
|
$policy .= ' frame-ancestors ' . $frame_ancestors . ';';
|
||||||
|
}
|
||||||
|
|
||||||
|
header($policy);
|
||||||
|
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -5,7 +5,6 @@ declare(strict_types = 1);
|
|||||||
* Exception thrown by MDJail if a required security option has not been set.
|
* Exception thrown by MDJail if a required security option has not been set.
|
||||||
*/
|
*/
|
||||||
final class MDJailSecurityOptionNotSetException extends Exception {
|
final class MDJailSecurityOptionNotSetException extends Exception {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Error message.
|
* Error message.
|
||||||
*
|
*
|
||||||
@ -17,5 +16,4 @@ final class MDJailSecurityOptionNotSetException extends Exception {
|
|||||||
return $errorMsg;
|
return $errorMsg;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user