Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

added wordfence login security plugin #455

Merged
merged 2 commits into from
Aug 1, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 16 additions & 0 deletions wp-content/plugins/wordfence-login-security/classes/.htaccess
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
<IfModule mod_rewrite.c>
RewriteEngine On
RewriteCond %{REQUEST_URI} \.php$
RewriteRule .* - [F,L,NC]
</IfModule>
<IfModule !mod_rewrite.c>
<FilesMatch "\.php$">
<IfModule mod_authz_core.c>
Require all denied
</IfModule>
<IfModule !mod_authz_core.c>
Order deny,allow
Deny from all
</IfModule>
</FilesMatch>
</IfModule>

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
@@ -0,0 +1,158 @@
<?php

namespace WordfenceLS;

class Controller_CAPTCHA {
const RESPONSE_MODE_ALLOW = 'allow';
const RESPONSE_MODE_REQUIRE_VERIFICATION = 'verify';

const RECAPTCHA_ENDPOINT = 'https://www.google.com/recaptcha/api/siteverify';

/**
* Returns the singleton Controller_CAPTCHA.
*
* @return Controller_CAPTCHA
*/
public static function shared() {
static $_shared = null;
if ($_shared === null) {
$_shared = new Controller_CAPTCHA();
}
return $_shared;
}

/**
* Returns whether or not the authentication CAPTCHA is enabled.
*
* @return bool
*/
public function enabled() {
$key = $this->site_key();
$secret = $this->_secret();
return Controller_Settings::shared()->get_bool(Controller_Settings::OPTION_ENABLE_AUTH_CAPTCHA) && !empty($key) && !empty($secret);
}

/**
* Returns the public reCAPTCHA key if set.
*
* @return string|bool
*/
public function site_key() {
return Controller_Settings::shared()->get(Controller_Settings::OPTION_RECAPTCHA_SITE_KEY);
}

/**
* Returns the private reCAPTCHA secret if set.
*
* @return string|bool
*/
protected function _secret() {
return Controller_Settings::shared()->get(Controller_Settings::OPTION_RECAPTCHA_SECRET);
}

/**
* Returns the bot/human threshold for comparing the score against, defaulting to 0.5.
*
* @return float
*/
public function threshold() {
return max(0.1, Controller_Settings::shared()->get_float(Controller_Settings::OPTION_RECAPTCHA_THRESHOLD, 0.5));
}

/**
* Determine whether or not test mode for reCAPTCHA is enabled
*
* @return bool
*/
public function test_mode() {
return Controller_Settings::shared()->get_bool(\WordfenceLS\Controller_Settings::OPTION_CAPTCHA_TEST_MODE);
}

/**
* Queries the reCAPTCHA endpoint with the given token, verifies the action matches, and returns the corresponding
* score. If validation fails, false is returned. Any other failure (e.g., mangled response or connection dropped) returns 0.0.
*
* @param string $token
* @param string $action
* @param int $timeout
* @return float|false
*/
public function score($token, $action = 'login', $timeout = 10) {
try {
$payload = array(
'secret' => $this->_secret(),
'response' => $token,
'remoteip' => Model_Request::current()->ip(),
);

$response = wp_remote_post(self::RECAPTCHA_ENDPOINT,
array(
'body' => $payload,
'headers' => array(
'Referer' => false,
),
'timeout' => $timeout,
'blocking' => true,
));

if (!is_wp_error($response)) {
$jsonResponse = wp_remote_retrieve_body($response);
$decoded = @json_decode($jsonResponse, true);
if (is_array($decoded) && isset($decoded['success'])) {
if ($decoded['success']) {
if (isset($decoded['score']) && isset($decoded['action']) && $decoded['action'] == $action) {
return (float) $decoded['score'];
}
}
return false;
}
}
}
catch (\Exception $e) {
//Fall through
}

return 0.0;
}

/**
* Returns true if the score is >= the threshold to be considered a human request.
*
* @param float $score
* @return bool
*/
public function is_human($score) {
if ($this->test_mode()) {
return true;
}

$threshold = $this->threshold();
return ($score >= $threshold || abs($score - $threshold) < 0.0001);
}

/**
* Check if the current request is an XML RPC request
* @return bool
*/
private static function is_xml_rpc() {
return defined('XMLRPC_REQUEST') && XMLRPC_REQUEST;
}

/**
* Check if captcha is required for the current request
* @return bool
*/
public function is_captcha_required() {
$required = $this->enabled() && !self::is_xml_rpc();
return apply_filters('wordfence_ls_require_captcha', $required);
}

/**
* Get the captcha token provided with the current request
* @param string $key if specified, override the default token parameter
* @return string|null the captcha token, if present, null otherwise
*/
public function get_token($key = 'wfls-captcha-token') {
return (isset($_POST[$key]) && is_string($_POST[$key]) && !empty($_POST[$key]) ? $_POST[$key] : null);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
<?php
173 changes: 173 additions & 0 deletions wp-content/plugins/wordfence-login-security/classes/controller/db.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,173 @@
<?php

namespace WordfenceLS;

use RuntimeException;

class Controller_DB {
const TABLE_2FA_SECRETS = 'wfls_2fa_secrets';
const TABLE_SETTINGS = 'wfls_settings';
const TABLE_ROLE_COUNTS = 'wfls_role_counts';
const TABLE_ROLE_COUNTS_TEMPORARY = 'wfls_role_counts_temporary';

const SCHEMA_VERSION = 2;

/**
* Returns the singleton Controller_DB.
*
* @return Controller_DB
*/
public static function shared() {
static $_shared = null;
if ($_shared === null) {
$_shared = new Controller_DB();
}
return $_shared;
}

/**
* Returns the table prefix for the main site on multisites and the site itself on single site installations.
*
* @return string
*/
public static function network_prefix() {
global $wpdb;
return $wpdb->base_prefix;
}

/**
* Returns the table with the site (single site installations) or network (multisite) prefix added.
*
* @param string $table
* @return string
*/
public static function network_table($table) {
return self::network_prefix() . $table;
}

public function __get($key) {
switch ($key) {
case 'secrets':
return self::network_table(self::TABLE_2FA_SECRETS);
case 'settings':
return self::network_table(self::TABLE_SETTINGS);
case 'role_counts':
return self::network_table(self::TABLE_ROLE_COUNTS);
case 'role_counts_temporary':
return self::network_table(self::TABLE_ROLE_COUNTS_TEMPORARY);
}

throw new \OutOfBoundsException('Unknown key: ' . $key);
}

public function install() {
$this->_create_schema();

global $wpdb;
$table = $this->secrets;
$wpdb->query($wpdb->prepare("UPDATE `{$table}` SET `vtime` = LEAST(`vtime`, %d)", Controller_Time::time()));
}

public function uninstall() {
$tables = array(self::TABLE_2FA_SECRETS, self::TABLE_SETTINGS, self::TABLE_ROLE_COUNTS);
foreach ($tables as $table) {
global $wpdb;
$wpdb->query('DROP TABLE IF EXISTS `' . self::network_table($table) . '`');
}
}

private function create_table($name, $definition, $temporary = false) {
global $wpdb;
if (is_array($definition)) {
foreach ($definition as $attempt) {
if ($this->create_table($name, $attempt, $temporary))
return true;
}
return false;
}
else {
return $wpdb->query('CREATE ' . ($temporary ? 'TEMPORARY ' : '') . 'TABLE IF NOT EXISTS `' . self::network_table($name) . '` ' . $definition);
}
}

private function create_temporary_table($name, $definition) {
if (Controller_Settings::shared()->get_bool(Controller_Settings::OPTION_DISABLE_TEMPORARY_TABLES))
return false;
if ($this->create_table($name, $definition, true))
return true;
Controller_Settings::shared()->set(Controller_Settings::OPTION_DISABLE_TEMPORARY_TABLES, true);
return false;
}

private function get_role_counts_table_definition($engine = null) {
$engineClause = $engine === null ? '' : "ENGINE={$engine}";
return <<<SQL
(
serialized_roles VARBINARY(255) NOT NULL,
two_factor_inactive TINYINT(1) NOT NULL,
user_count BIGINT(20) UNSIGNED NOT NULL DEFAULT 0,
PRIMARY KEY (serialized_roles, two_factor_inactive)
) {$engineClause};
SQL;
}

private function get_role_counts_table_definition_options() {
return array(
$this->get_role_counts_table_definition('MEMORY'),
$this->get_role_counts_table_definition('MyISAM'),
$this->get_role_counts_table_definition()
);
}

protected function _create_schema() {
$tables = array(
self::TABLE_2FA_SECRETS => '(
`id` int(11) unsigned NOT NULL AUTO_INCREMENT,
`user_id` bigint(20) unsigned NOT NULL,
`secret` tinyblob NOT NULL,
`recovery` blob NOT NULL,
`ctime` int(10) unsigned NOT NULL,
`vtime` int(10) unsigned NOT NULL,
`mode` enum(\'authenticator\') NOT NULL DEFAULT \'authenticator\',
PRIMARY KEY (`id`),
KEY `user_id` (`user_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;',
self::TABLE_SETTINGS => '(
`name` varchar(191) NOT NULL DEFAULT \'\',
`value` longblob,
`autoload` enum(\'no\',\'yes\') NOT NULL DEFAULT \'yes\',
PRIMARY KEY (`name`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;',
self::TABLE_ROLE_COUNTS => $this->get_role_counts_table_definition_options()
);

foreach ($tables as $table => $def) {
$this->create_table($table, $def);
}

Controller_Settings::shared()->set(Controller_Settings::OPTION_SCHEMA_VERSION, self::SCHEMA_VERSION);
}

public function require_schema_version($version) {
$current = Controller_Settings::shared()->get_int(Controller_Settings::OPTION_SCHEMA_VERSION);
if ($current < $version) {
$this->install();
}
}

public function query($query) {
global $wpdb;
if ($wpdb->query($query) === false)
throw new RuntimeException("Failed to execute query: {$query}");
}

public function get_wpdb() {
global $wpdb;
return $wpdb;
}

public function create_temporary_role_counts_table() {
return $this->create_temporary_table(self::TABLE_ROLE_COUNTS_TEMPORARY, $this->get_role_counts_table_definition_options());
}

}
Loading