Skip to content
Merged
59 changes: 58 additions & 1 deletion inc/spbc-firewall.php
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
<?php

use CleantalkSP\Variables\Get;
use CleantalkSP\Variables\Post;
use CleantalkSP\Variables\Request;
use CleantalkSP\Variables\Server;
use CleantalkSP\SpbctWP\DB;
use CleantalkSP\SpbctWP\Firewall;
Expand All @@ -8,6 +11,7 @@
use CleantalkSP\SpbctWP\Firewall\TC;
use CleantalkSP\SpbctWP\Firewall\WAF;
use CleantalkSP\SpbctWP\Firewall\WafBlocker;
use CleantalkSP\SpbctWP\Firewall\UploadChecker;
use CleantalkSP\SpbctWP\Helpers\IP;
use CleantalkSP\SpbctWP\Variables\Cookie;
use CleantalkSP\SpbctWP\RenameLoginPage;
Expand Down Expand Up @@ -152,7 +156,60 @@ function spbc_upload_checker__check()
{
global $spbc;
if ( $spbc->settings['upload_checker__file_check'] && !empty($_FILES) ) {
$upload_checker = new Firewall\UploadChecker(array(
/** @var WP_Error|null $run_checker_error */
$run_checker_error = null;
if (is_user_logged_in()) {
if (is_admin()) {
$action = Post::getString('action') ?: Get::getString('action') ?: '';
if ($action === 'upload-plugin' || $action === 'upload-theme') {
if ($action === 'upload-plugin') {
if (!wp_verify_nonce(Request::getString('_wpnonce') ?: '', 'plugin-upload')) {
// Install plugins interface - exit if nonce is wrong
$run_checker_error = new WP_Error(403, __('You do not have sufficient permissions to upload files.', 'security-malware-firewall'));
}
if (!current_user_can('install_plugins')) {
// Install plugins interface - exit if no permission to do that
$run_checker_error = new WP_Error(403, __('You do not have sufficient permissions to upload files.', 'security-malware-firewall'));
}
}
if ($action === 'upload-theme') {
if (!wp_verify_nonce(Request::getString('_wpnonce') ?: '', 'theme-upload')) {
// Install themes interface - exit if nonce is wrong
$run_checker_error = new WP_Error(403, __('You do not have sufficient permissions to upload files.', 'security-malware-firewall'));
}
if (!current_user_can('install_themes')) {
// Install themes interface - exit if no permission to do that
$run_checker_error = new WP_Error(403, __('You do not have sufficient permissions to upload files.', 'security-malware-firewall'));
}
}
if (!current_user_can('install_plugins')) {
// Install plugins/themes interface - exit if no permission to do that
$run_checker_error = new WP_Error(403, __('You do not have sufficient permissions to upload files.', 'security-malware-firewall'));
}
Comment thread
Glomberg marked this conversation as resolved.
} elseif (!current_user_can('upload_files') || !wp_verify_nonce(Request::getString('_wpnonce') ?: '', 'media-form')) {
// Media interface - exit if no permission to uploading
$run_checker_error = new WP_Error(403, __('You do not have sufficient permissions to upload files.', 'security-malware-firewall'));
}
} elseif (!current_user_can('upload_files')) {
// Not admin area - exit if no permission to uploading
$run_checker_error = new WP_Error(403, __('You do not have sufficient permissions to upload files.', 'security-malware-firewall'));
}
}

// RateLimit for all uploads, but permission check only for logged-in users
if ( ! $run_checker_error && UploadChecker::hasRateOverlimit() ) {
$run_checker_error = new WP_Error(429, __('You have exceeded the upload limit. Please try again later.', 'security-malware-firewall'));
}
Comment thread
Glomberg marked this conversation as resolved.
Comment thread
Glomberg marked this conversation as resolved.

if ( $run_checker_error ) {
wp_die(
$run_checker_error->get_error_message(),
__('Upload Checker Exceeded', 'security-malware-firewall'),
array('response' => $run_checker_error->get_error_code())
);
}

$upload_checker = new UploadChecker(array(
'upload_checker__do_check_wordpress_modules' => $spbc->settings['upload_checker__do_check_wordpress_modules'],
'api_key' => $spbc->api_key,
));
Expand Down
24 changes: 22 additions & 2 deletions lib/CleantalkSP/SpbctWP/Firewall/UploadChecker.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,16 +2,25 @@

namespace CleantalkSP\SpbctWP\Firewall;

use CleantalkSP\Common\RateLimit\RateLimiterConfig;
use CleantalkSP\Security\Firewall\Result;
use CleantalkSP\SpbctWP\Helpers\Data;
use CleantalkSP\SpbctWP\Scanner\FileInfoExtended;
use CleantalkSP\SpbctWP\SpbcRateLimit\SpbcRateLimiter;
use CleantalkSP\Variables\Post;
use CleantalkSP\Variables\Server;
use FilesystemIterator;

class UploadChecker extends FirewallModule
{
const BINARY_CHECK_THRESHOLD = 0.3; // Threshold for binary file detection, ratio of non-printable characters to total length

const RATE_LIMITER_LIMIT = 5;

const RATE_LIMITER_TIMEFRAME = 600;

const RATE_LIMITER_ACTION = 'spbct_upload_checker_limits';

public $module_name = 'UploadChecker';

/**
Expand Down Expand Up @@ -165,9 +174,9 @@ private function runCheckForFilesGlobalVariable($global_files_variable)
Server::get('QUERY_STRING', null, 'url') !== 'action=upload-theme' &&
in_array(Data::getMIMEType($file_path), $this->waf_file_mime_check_zip)
) {
$file_check_result = self::checkUploadedArchive($file_path);
$file_check_result = $this->checkUploadedArchive($file_path);
} else {
$file_check_result = self::checkFileContent($file_path);
$file_check_result = $this->checkFileContent($file_path);
}

// if we have a result, return it immediately
Expand Down Expand Up @@ -336,4 +345,15 @@ private function isBinaryFile($file_path)

return $ratio > self::BINARY_CHECK_THRESHOLD;
}

public static function hasRateOverlimit()
{
$rate_limit_config = new RateLimiterConfig(self::RATE_LIMITER_ACTION, self::RATE_LIMITER_LIMIT, self::RATE_LIMITER_TIMEFRAME);
$rate_limiter = new SpbcRateLimiter($rate_limit_config);
$check_passed = $rate_limiter->checkPassed();
if ($rate_limiter->process_ok) {
return !$check_passed;
}
return false; // No limit exceeded by default
}
}
Loading