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.
361 lines
11 KiB
361 lines
11 KiB
<?php
|
|
|
|
/**
|
|
* Security class
|
|
*
|
|
* Standard: PSR-2
|
|
*
|
|
* @link http://www.php-fig.org/psr/psr-2 Full Documentation
|
|
*
|
|
* @package SC\DUPX\Constants
|
|
*/
|
|
|
|
defined('ABSPATH') || defined('DUPXABSPATH') || exit;
|
|
|
|
use Duplicator\Installer\Utils\Log\Log;
|
|
use Duplicator\Installer\Core\Bootstrap;
|
|
use Duplicator\Installer\Core\Params\PrmMng;
|
|
use Duplicator\Installer\Core\Params\Items\ParamItem;
|
|
use Duplicator\Libs\Snap\SnapIO;
|
|
use Duplicator\Libs\Snap\SnapUtil;
|
|
|
|
/**
|
|
* singleton class
|
|
*
|
|
* In this class all installer security checks are performed. If the security checks are not passed, an exception is thrown and the installer is stopped.
|
|
* This happens before anything else so the class must work without the initialization of all global duplicator variables.
|
|
*/
|
|
class DUPX_Security
|
|
{
|
|
const CTRL_TOKEN = 'ctrl_csrf_token';
|
|
const ROUTER_TOKEN = 'router_csrf_token';
|
|
|
|
const SECURITY_NONE = 'none';
|
|
const SECURITY_PASSWORD = 'pwd';
|
|
const SECURITY_ARCHIVE = 'archive';
|
|
|
|
/**
|
|
*
|
|
* @var self
|
|
*/
|
|
private static $instance = null;
|
|
|
|
/**
|
|
* archive path read from csrf file
|
|
*
|
|
* @var string
|
|
*/
|
|
private $archivePath = null;
|
|
|
|
/**
|
|
* installer name read from csrf file
|
|
*
|
|
* @var string
|
|
*/
|
|
private $bootloader = null;
|
|
|
|
/**
|
|
* installer url path read from csrf file
|
|
*
|
|
* @var string
|
|
*/
|
|
private $bootUrl = null;
|
|
|
|
/**
|
|
* boot log file full path read from csrf file
|
|
*
|
|
* @var string
|
|
*/
|
|
private $bootFilePath = null;
|
|
|
|
/**
|
|
* boot log file full path read from csrf file
|
|
*
|
|
* @var string
|
|
*/
|
|
private $bootLogFile = null;
|
|
|
|
/**
|
|
* package hash read from csrf file
|
|
*
|
|
* @var string
|
|
*/
|
|
private $packageHash = null;
|
|
|
|
/**
|
|
* public package hash read from csrf file
|
|
*
|
|
* @var string
|
|
*/
|
|
private $secondaryPackageHash = null;
|
|
|
|
/**
|
|
*
|
|
* @return self
|
|
*/
|
|
public static function getInstance()
|
|
{
|
|
if (is_null(self::$instance)) {
|
|
self::$instance = new self();
|
|
}
|
|
|
|
return self::$instance;
|
|
}
|
|
|
|
private function __construct()
|
|
{
|
|
DUPX_CSRF::init(DUPX_INIT, Bootstrap::getPackageHash());
|
|
|
|
if (!file_exists(DUPX_CSRF::getFilePath())) {
|
|
throw new Exception("CSRF FILE NOT FOUND\n"
|
|
. "Please, check webroot file permsission and dup-installer folder permission");
|
|
}
|
|
|
|
$this->bootloader = DUPX_CSRF::getVal('bootloader');
|
|
$this->bootUrl = DUPX_CSRF::getVal('booturl');
|
|
$this->bootLogFile = SnapIO::safePath(DUPX_CSRF::getVal('bootLogFile'));
|
|
$this->bootFilePath = SnapIO::safePath(DUPX_CSRF::getVal('installerOrigPath'));
|
|
$this->archivePath = SnapIO::safePath(DUPX_CSRF::getVal('archive'));
|
|
$this->packageHash = DUPX_CSRF::getVal('package_hash');
|
|
$this->secondaryPackageHash = DUPX_CSRF::getVal('secondaryHash');
|
|
}
|
|
|
|
/**
|
|
* archive path read from intaller.php passed by DUPX_CSFR
|
|
*
|
|
* @return string
|
|
*/
|
|
public function getArchivePath()
|
|
{
|
|
return $this->archivePath;
|
|
}
|
|
|
|
/**
|
|
* installer full path read from intaller.php passed by DUPX_CSFR
|
|
*
|
|
* @return string
|
|
*/
|
|
public function getBootFilePath()
|
|
{
|
|
return $this->bootFilePath;
|
|
}
|
|
|
|
/**
|
|
* boot log file full path read from intaller.php passed by DUPX_CSFR
|
|
*
|
|
* @return string
|
|
*/
|
|
public function getBootLogFile()
|
|
{
|
|
return $this->bootLogFile;
|
|
}
|
|
|
|
/**
|
|
* bootloader path read from intaller.php passed by DUPX_CSFR
|
|
*
|
|
* @return string
|
|
*/
|
|
public function getBootloader()
|
|
{
|
|
return $this->bootloader;
|
|
}
|
|
|
|
/**
|
|
* bootloader path read from intaller.php passed by DUPX_CSFR
|
|
*
|
|
* @return string
|
|
*/
|
|
public function getBootUrl()
|
|
{
|
|
return $this->bootUrl;
|
|
}
|
|
|
|
/**
|
|
* package hash read from intaller.php passed by DUPX_CSFR
|
|
*
|
|
* @return string
|
|
*/
|
|
public function getPackageHash()
|
|
{
|
|
return $this->packageHash;
|
|
}
|
|
|
|
/**
|
|
* package public hash read from intaller.php passed by DUPX_CSFR
|
|
*
|
|
* @return string
|
|
*/
|
|
public function getSecondaryPackageHash()
|
|
{
|
|
return $this->secondaryPackageHash;
|
|
}
|
|
|
|
/**
|
|
*
|
|
* @return boolean
|
|
* @throws Exception // if fail throw exception of return true
|
|
*/
|
|
public function check()
|
|
{
|
|
try {
|
|
// check if current package hash is equal at bootloader package hash
|
|
if ($this->packageHash !== Bootstrap::getPackageHash()) {
|
|
throw new Exception('Incorrect hash package');
|
|
}
|
|
|
|
// checks if the version of the package descriptor is consistent with the version of the files.
|
|
if (DUPX_ArchiveConfig::getInstance()->version_dup !== DUPX_VERSION) {
|
|
throw new Exception('The version of the archive is different from the version of the PHP scripts');
|
|
}
|
|
|
|
$token_tested = false;
|
|
// @todo connect with global debug
|
|
$debug = false;
|
|
|
|
$action = null;
|
|
if (DUPX_Ctrl_ajax::isAjax($action) == true) {
|
|
if (($token = self::getTokenFromInput(DUPX_Ctrl_ajax::TOKEN_NAME)) === false) {
|
|
$msg = 'Security issue' . ($debug ? ' LINE: ' . __LINE__ . ' TOKEN: ' . $token . ' KEY NAME: ' . DUPX_Ctrl_ajax::TOKEN_NAME : '');
|
|
throw new Exception($msg);
|
|
}
|
|
if (!DUPX_CSRF::check(self::getTokenFromInput(DUPX_Ctrl_ajax::TOKEN_NAME), DUPX_Ctrl_ajax::getTokenKeyByAction($action))) {
|
|
$msg = 'Security issue' . ($debug ? ' LINE: ' . __LINE__ . ' TOKEN: ' . $token . ' KEY NAME: ' . DUPX_Ctrl_ajax::getTokenKeyByAction($action) . ' KEY VALUE ' . DUPX_Ctrl_ajax::getTokenKeyByAction($action) : '');
|
|
throw new Exception($msg);
|
|
}
|
|
$token_tested = true;
|
|
} elseif (($token = self::getTokenFromInput(self::CTRL_TOKEN)) !== false) {
|
|
if (!isset($_REQUEST[PrmMng::PARAM_CTRL_ACTION])) {
|
|
$msg = 'Security issue' . ($debug ? ' LINE: ' . __LINE__ . ' TOKEN: ' . $token . ' KEY NAME: ' . PrmMng::PARAM_CTRL_ACTION : '');
|
|
throw new Exception($msg);
|
|
}
|
|
if (!DUPX_CSRF::check($token, $_REQUEST[PrmMng::PARAM_CTRL_ACTION])) {
|
|
$msg = 'Security issue' . ($debug ? ' LINE: ' . __LINE__ . ' TOKEN: ' . $token . ' KEY NAME: ' . PrmMng::PARAM_CTRL_ACTION . ' KEY VALUE ' . $_REQUEST[PrmMng::PARAM_CTRL_ACTION] : '');
|
|
throw new Exception($msg);
|
|
}
|
|
$token_tested = true;
|
|
}
|
|
|
|
if (($token = self::getTokenFromInput(self::ROUTER_TOKEN)) !== false) {
|
|
if (!isset($_REQUEST[PrmMng::PARAM_ROUTER_ACTION])) {
|
|
$msg = 'Security issue' . ($debug ? ' LINE: ' . __LINE__ . ' TOKEN: ' . $token . ' KEY NAME: ' . PrmMng::PARAM_ROUTER_ACTION : '');
|
|
throw new Exception($msg);
|
|
}
|
|
if (!DUPX_CSRF::check($token, $_REQUEST[PrmMng::PARAM_ROUTER_ACTION])) {
|
|
$msg = 'Security issue' . ($debug ? ' LINE: ' . __LINE__ . ' TOKEN: ' . $token . ' KEY NAME: ' . PrmMng::PARAM_ROUTER_ACTION . ' KEY VALUE ' . $_REQUEST[PrmMng::PARAM_ROUTER_ACTION] : '');
|
|
throw new Exception($msg);
|
|
}
|
|
$token_tested = true;
|
|
}
|
|
|
|
// At least one token must always and in any case be tested
|
|
if (!$token_tested) {
|
|
throw new Exception('Security Check Validation - No Token Found');
|
|
}
|
|
} catch (Exception $e) {
|
|
if (function_exists('error_clear_last')) {
|
|
/**
|
|
* comment error_clear_last if you want see te exception html on shutdown
|
|
*/
|
|
error_clear_last(); // phpcs:ignore PHPCompatibility.FunctionUse.NewFunctions.error_clear_lastFound
|
|
}
|
|
|
|
Log::logException($e, Log::LV_DEFAULT, 'SECURITY CHECK: ');
|
|
dupxTplRender('page-security-error', array(
|
|
'message' => $e->getMessage()
|
|
));
|
|
die();
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* get sanitized token frominput
|
|
*
|
|
* @param string $tokenName
|
|
*
|
|
* @return string
|
|
*/
|
|
protected static function getTokenFromInput($tokenName)
|
|
{
|
|
return SnapUtil::filterInputDefaultSanitizeString(SnapUtil::INPUT_REQUEST, $tokenName, false);
|
|
}
|
|
|
|
/**
|
|
* Get security tipe (NONE, PASSWORD, ARCHIVE)
|
|
*
|
|
* @return string enum type
|
|
*/
|
|
public function getSecurityType()
|
|
{
|
|
if (PrmMng::getInstance()->getValue(PrmMng::PARAM_SECURE_OK) == true) {
|
|
return self::SECURITY_NONE;
|
|
}
|
|
|
|
$archiveConfig = DUPX_ArchiveConfig::getInstance();
|
|
|
|
if ($archiveConfig->secure_on) {
|
|
return self::SECURITY_PASSWORD;
|
|
}
|
|
|
|
if (
|
|
DUPX_InstallerState::isOverwrite() &&
|
|
basename($this->bootFilePath) == 'installer.php' &&
|
|
!in_array($_SERVER['REMOTE_ADDR'], self::getSecurityAddrWhitelist())
|
|
) {
|
|
return self::SECURITY_ARCHIVE;
|
|
}
|
|
|
|
return self::SECURITY_NONE;
|
|
}
|
|
|
|
/**
|
|
* Get IPs white list for remote requests
|
|
*
|
|
* @return string[]
|
|
*/
|
|
private static function getSecurityAddrWhitelist()
|
|
{
|
|
// uncomment this to test security archive on localhost
|
|
// return array();
|
|
// -------
|
|
return array(
|
|
'127.0.0.1',
|
|
'::1'
|
|
);
|
|
}
|
|
|
|
/**
|
|
* return true if security check is passed
|
|
*
|
|
* @return bool
|
|
*/
|
|
public function securityCheck()
|
|
{
|
|
$paramsManager = PrmMng::getInstance();
|
|
$archiveConfig = DUPX_ArchiveConfig::getInstance();
|
|
$result = false;
|
|
switch ($this->getSecurityType()) {
|
|
case self::SECURITY_NONE:
|
|
$result = true;
|
|
break;
|
|
case self::SECURITY_PASSWORD:
|
|
$paramsManager->setValueFromInput(PrmMng::PARAM_SECURE_PASS);
|
|
$pass_hasher = new DUPX_PasswordHash(8, false);
|
|
$base64Pass = base64_encode($paramsManager->getValue(PrmMng::PARAM_SECURE_PASS));
|
|
$result = $pass_hasher->CheckPassword($base64Pass, $archiveConfig->secure_pass);
|
|
break;
|
|
case self::SECURITY_ARCHIVE:
|
|
$paramsManager->setValueFromInput(PrmMng::PARAM_SECURE_ARCHIVE_HASH);
|
|
$result = (strcmp(basename($this->archivePath), $paramsManager->getValue(PrmMng::PARAM_SECURE_ARCHIVE_HASH)) == 0);
|
|
break;
|
|
default:
|
|
throw new Exception('Security type not valid ' . $this->getSecurityType());
|
|
break;
|
|
}
|
|
|
|
$paramsManager->setValue(PrmMng::PARAM_SECURE_OK, $result);
|
|
$paramsManager->save();
|
|
return $result;
|
|
}
|
|
}
|
|
|