-
Notifications
You must be signed in to change notification settings - Fork 4
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #455 from CityOfNewYork/add_wordfence_login_securi…
…ty_plugin added wordfence login security plugin
- Loading branch information
Showing
198 changed files
with
14,605 additions
and
47,549 deletions.
There are no files selected for viewing
16 changes: 16 additions & 0 deletions
16
wp-content/plugins/wordfence-login-security/classes/.htaccess
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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> |
558 changes: 558 additions & 0 deletions
558
wp-content/plugins/wordfence-login-security/classes/controller/ajax.php
Large diffs are not rendered by default.
Oops, something went wrong.
158 changes: 158 additions & 0 deletions
158
wp-content/plugins/wordfence-login-security/classes/controller/captcha.php
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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); | ||
} | ||
} |
1 change: 1 addition & 0 deletions
1
wp-content/plugins/wordfence-login-security/classes/controller/cron.php
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
<?php |
173 changes: 173 additions & 0 deletions
173
wp-content/plugins/wordfence-login-security/classes/controller/db.php
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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()); | ||
} | ||
|
||
} |
Oops, something went wrong.