2020-11-11 17:20:56 +01:00
< ? PHP
/**
* Provides class MD_JAIL .
*/
declare ( strict_types = 1 );
/**
* A class that , once initialized , forces the programmer to make security instructions implicit .
* If an object of the class has been created , not specifying security instructions
* leads to an error .
* A restriction on basic file operations is not practical in an md context because of
* the way transations are loaded through MDTlLoader .
*/
final class MD_JAIL {
const STATUS_NONE = 0 ;
const STATUS_STARTED = 1 ;
const STATUS_SPECIFIED = 2 ; // Determines that everything is fine.
/** @var integer */
private int $_status = self :: STATUS_NONE ;
/**
* @ var integer
* Maximum execution time in seconds .
*/
public int $max_execution_time ;
/**
* @ var string []
* Specifies which paths may be used by this script .
*/
private array $_open_basedir = [];
/**
* @ var string
* Specifies the maximum RAM the script may use .
*/
public string $memory_limit ;
2020-11-12 00:12:11 +01:00
/**
* Static function providing an advisory on how to harden the php . ini or
* . user . ini .
*
2020-11-12 19:54:43 +01:00
* @ param array { shell_access_whitelist : string [], sys_function_whitelist : string [], file_function_whitelist : string [], file_uploads : bool , allow_url_fopen : bool , max_input_vars : integer , max_input_nesting_level : integer , post_max_size : string , curl : bool } $requested_resources Requested resources .
2020-11-12 00:12:11 +01:00
*
* @ return string
*/
public static function check_server_setup ( array $requested_resources ) : string {
# ini_set("open_basedir", __DIR__ . "/../../");
$disable_functions = [
" dl " , # Loads a PHP extension at runtime
" opcache_get_status " , # Gets opcache status
" phpinfo " , # For obvious reasons
" parse_ini_file " , # For obvious reasons
" show_source " , " highlight_file " , # Aliases; serve the content of a PHP file
" php_uname " , # Returns information about the operating system PHP is running on
" phpcredits " , # Credits page of PHP authors
" php_strip_whitespace " , # Return PHP source with stripped comments and whitespace
" popen " , " pclose " , # Similar to fopen, but capable of shell access
" virtual " , # Perform an Apache sub-request
];
$disable_aliases = [
" ini_set " => " ini_alter " , # Alias of ini_set(), ini_set is preferred
" disk_free_space " => " diskfreespace " , # Alias of disk_free_space()
" PHP_SAPI " => " php_sapi_name " , # Alias of constant PHP_SAPI
" stream_set_write_buffer " => " set_file_buffer " , # Alias of stream_set_write_buffer()
];
$shell_access_functions = array_diff ([ " exec " , " passthru " , " proc_close " , " proc_get_status " , " proc_nice " , " proc_open " , " proc_terminate " , " shell_exec " , " system " ], $requested_resources [ 'shell_access_whitelist' ]);
$sys_functions = array_diff ([ " get_current_user " , " getmyuid " , " getmygid " , " getmypid " , " getmyinode " , " getlastmod " , " getenv " , " putenv " ], $requested_resources [ 'sys_function_whitelist' ]);
$file_functions = array_diff ([ " chgrp " , " chgrp " , " lchgrp " , " lchown " , " link " , " linkinfo " , " symlink " ], $requested_resources [ 'file_function_whitelist' ]);
if ( $requested_resources [ 'curl' ] === false ) {
$disable_functions [] = " curl_init " ;
$disable_functions [] = " curl_exec " ;
$disable_functions [] = " curl_multi_init " ;
$disable_functions [] = " curl_multi_exec " ;
}
$disabledWOAliases = array_merge ( $disable_functions , $shell_access_functions , $sys_functions , $file_functions );
$output = '## php.ini' . PHP_EOL . PHP_EOL ;
$output .= PHP_EOL . " php_value[disable_functions] = \" " . implode ( " , " , array_merge ( $disabledWOAliases , $disable_aliases )) . " \" " ;
if ( $requested_resources [ 'file_uploads' ] === false ) {
$output .= PHP_EOL . " php_value[file_uploads] = 0 " ;
}
if ( $requested_resources [ 'allow_url_fopen' ] === false ) {
$output .= PHP_EOL . " php_value[allow_url_fopen] = 0 " ;
}
$output .= PHP_EOL . PHP_EOL . '## .user.ini' . PHP_EOL ;
if ( $requested_resources [ 'file_uploads' ] === false ) {
2020-11-12 19:54:43 +01:00
$output .= PHP_EOL . " upload_max_filesize = 1 " ;
2020-11-12 00:12:11 +01:00
}
if ( $requested_resources [ 'max_input_vars' ] != ini_get ( " max_input_vars " )) {
2020-11-12 19:54:43 +01:00
$output .= PHP_EOL . " max_input_vars = " . $requested_resources [ 'max_input_vars' ];
2020-11-12 00:12:11 +01:00
}
if ( $requested_resources [ 'max_input_nesting_level' ] != ini_get ( " max_input_nesting_level " )) {
2020-11-12 19:54:43 +01:00
$output .= PHP_EOL . " max_input_nesting_level = " . $requested_resources [ 'max_input_nesting_level' ];
}
if ( $requested_resources [ 'post_max_size' ] != ini_get ( " post_max_size " )) {
$output .= PHP_EOL . " post_max_size = " . $requested_resources [ 'post_max_size' ];
2020-11-12 00:12:11 +01:00
}
$output .= PHP_EOL . PHP_EOL . '## PHPStan Directives' . PHP_EOL . PHP_EOL . " disallowedFunctionCalls: " . PHP_EOL ;
foreach ( $disable_aliases as $to_keep => $disabled ) {
$output .= '
-
function : \ '' . $disabled . ' () \ '
message : \ 'use ' . $to_keep . ' instead\'' ;
}
foreach ( $disabledWOAliases as $disabled ) {
$output .= '
- function : \ '' . $disabled . '()\'' ;
}
return $output ;
}
2020-11-11 17:20:56 +01:00
/**
* Registers an additional accessible directory for open_basedir .
*
* @ param string $dir Directory to register .
*
* @ return void
*/
public function register_accessible_dir ( string $dir ) : void {
$this -> _open_basedir [] = $dir ;
}
/**
* Applies the memory limit setting .
*
* @ return void
*/
private function _apply_memory_limit () : void {
if ( ! isset ( $this -> memory_limit )) {
throw new MDJailSecurityOptionNotSetException ( " It has not been specified, which memory limit the script should hold. Set MD_JAIL->memory_limit = string. " );
}
if ( ini_set ( " memory_limit " , $this -> memory_limit ) === false ) {
throw new Exception ( 'Failed to change memory_limit to ' . $this -> memory_limit );
}
}
/**
* Applies the maximum execution time setting .
*
* @ return void
*/
private function _apply_time_limit () : void {
if ( ! isset ( $this -> max_execution_time )) {
throw new MDJailSecurityOptionNotSetException ( " It has not been specified, which maximum execution time the script should hold. Set MD_JAIL->max_execution_time = integer. " );
}
if ( set_time_limit ( $this -> max_execution_time ) === false ) {
throw new Exception ( 'Failed to change max_execution_time to ' . $this -> max_execution_time );
}
}
/**
* Applies basedir restrictions .
*
* @ return void
2020-11-12 00:12:11 +01:00
*/
2020-11-11 17:20:56 +01:00
private function _apply_basedir_restriction () : void {
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. " );
}
if ( ini_set ( " open_basedir " , implode ( ':' , $this -> _open_basedir )) === false ) {
throw new Exception ( 'Failed to set open_basedir restrictions' );
}
}
/**
* Enforces security options previously set .
*
* @ return void
*/
public function enforce () : void {
2020-11-11 17:25:41 +01:00
// Special instructions on CLI, so as to not disturb PHPUnit
if ( PHP_SAPI === 'cli' ) {
if ( ! isset ( $this -> memory_limit )) {
throw new MDJailSecurityOptionNotSetException ( " It has not been specified, which memory limit the script should hold. Set MD_JAIL->memory_limit = string. " );
}
if ( ! isset ( $this -> max_execution_time )) {
throw new MDJailSecurityOptionNotSetException ( " It has not been specified, which maximum execution time the script should hold. Set MD_JAIL->max_execution_time = integer. " );
}
$this -> _status = self :: STATUS_SPECIFIED ;
$this -> __destruct ();
}
2020-11-11 17:20:56 +01:00
$this -> _apply_memory_limit ();
$this -> _apply_time_limit ();
// Set accessible file paths
2020-11-12 00:12:11 +01:00
$this -> _apply_basedir_restriction ();
2020-11-11 17:20:56 +01:00
$this -> _status = self :: STATUS_SPECIFIED ;
$this -> __destruct ();
}
/**
* Setup function . Registers a shutdown function that throws an error
* if the security specifications have not been made .
*
* @ return void
*/
public function __construct () {
$this -> _status = self :: STATUS_STARTED ;
}
public function __destruct () {
if ( $this -> _status !== self :: STATUS_SPECIFIED ) {
echo " Security specifications need to be set. " ;
if ( ! isset ( $this -> memory_limit )) {
echo " Set memory limit " ;
}
if ( ! isset ( $this -> max_execution_time )) {
echo " Set max_execution_time " ;
}
if ( empty ( $this -> _open_basedir )) {
echo " Set open_basedir " ;
}
throw new MDJailSecurityOptionNotSetException ( " Security specifications need to be set. " );
}
}
}