Version Description
- Breaking Change: iThemes Security requires PHP 5.4 or later.
- Enhancement: New Lockout Template screen.
- Enhancement: Add confirmation button to Login Interstitial Async Actions when on a different device.
- Enhancement: Add filter to "Lookup IP" link.
- Developer Note: There were significant changes to the internals of the iThemes Security Lockout API in this release. If you are using the ITSEC_Lockout class directly, all the API functions will continue to work, but will emit deprecation notices when legacy behavior is being used. Please update any integrations.
- Bug Fix: Brute Force module reporting invalid logins using an email address incorrectly.
- Bug Fix: Improve lockout compatibility with caching plugins.
- Bug Fix: Fix admin notice not being dismissed due to a REST API route that was more narrowly defined than necessary.
- Bug Fix: Admin Notices list did not refresh after dismissing a notice.
- Bug Fix: Strong Passwords zxcvbn Library was not evaluating penalty strings correctly.
- Bug Fix: Fix PHP warning if there are multiple detected proxy headers.
Download this release
Release Info
Developer | TimothyBlynJacobs |
Plugin | iThemes Security (formerly Better WP Security) |
Version | 7.5.0 |
Comparing to | |
See all releases |
Code changes from version 7.4.1 to 7.5.0
- better-wp-security.php +12 -1
- core/core.php +2 -1
- core/history.txt +22 -0
- core/lib.php +139 -13
- core/lib/class-itsec-lib-email-confirmation.php +31 -0
- core/lib/class-itsec-lib-login-interstitial.php +115 -29
- core/lib/class-itsec-lib-login.php +58 -0
- core/lib/class-itsec-lib-opaque-tokens.php +116 -0
- core/lib/class-itsec-mail.php +15 -6
- core/lib/class-itsec-scheduler-cron.php +3 -1
- core/lib/class-itsec-scheduler-page-load.php +1 -0
- core/lib/class-itsec-scheduler.php +10 -1
- core/lib/fingerprinting/class-itsec-fingerprint.php +71 -2
- core/lib/itsec-zxcvbn-php/matchers/dictionary.php +3 -3
- core/lib/lockout/abstract-context.php +47 -0
- core/lib/lockout/class-host-context.php +120 -0
- core/lib/lockout/class-lockout.php +169 -0
- core/lib/lockout/class-user-context.php +33 -0
- core/lib/lockout/class-username-context.php +33 -0
- core/lib/lockout/execute-lock/abstract-context.php +83 -0
- core/lib/lockout/execute-lock/class-host-context.php +115 -0
- core/lib/lockout/execute-lock/class-user-context.php +40 -0
- core/lib/lockout/execute-lock/class-username-context.php +40 -0
- core/lib/lockout/execute-lock/index.php +1 -0
- core/lib/lockout/execute-lock/source/class-configurable.php +20 -0
- core/lib/lockout/execute-lock/source/class-lockout-module.php +20 -0
- core/lib/lockout/execute-lock/source/index.php +1 -0
- core/lib/lockout/execute-lock/source/interface-source.php +16 -0
- core/lib/lockout/index.php +1 -0
- core/lib/login-interstitial/abstract-itsec-login-interstitial.php +8 -2
- core/lib/login-interstitial/class-itsec-login-interstitial-session.php +61 -2
- core/lib/mail-templates/magic-link.html +991 -0
- core/lib/schema.php +11 -0
- core/lib/validator.php +5 -1
- core/lockout.php +565 -465
- core/modules/404-detection/class-itsec-four-oh-four.php +5 -3
- core/modules/brute-force/class-itsec-brute-force.php +23 -17
- core/modules/core/class-rest-core-admin-notices-controller.php +2 -2
- core/modules/core/img/security-ebook.png +0 -0
- core/modules/core/img/sync-logo.png +0 -0
- core/modules/core/sidebar-widget-mail-list-signup.php +1 -1
- core/modules/core/sidebar-widget-sync-cross-promo.php +4 -5
- core/modules/email-confirmation/active.php +4 -0
- core/modules/email-confirmation/class-itsec-email-confirmation.php +51 -0
- core/modules/email-confirmation/index.php +1 -0
- core/modules/global/settings-page.php +1 -1
- core/modules/global/settings.php +6 -0
- core/modules/ipcheck/class-itsec-ipcheck.php +14 -2
- core/modules/pro/settings-page.php +15 -0
- core/modules/security-check/scanner.php +2 -0
- core/modules/system-tweaks/class-itsec-system-tweaks.php +4 -0
- core/package.json +3 -1
- core/templates/index.php +1 -0
- core/templates/lockout/icon.svg +1 -0
- core/templates/lockout/index.php +1 -0
- core/templates/lockout/lamp-light.svg +1 -0
- core/templates/lockout/lockout.css +163 -0
- core/templates/lockout/lockout.php +36 -0
- history.txt +12 -0
- package.json +3 -1
- readme.txt +18 -5
better-wp-security.php
CHANGED
@@ -6,12 +6,23 @@
|
|
6 |
* Description: Take the guesswork out of WordPress security. iThemes Security offers 30+ ways to lock down WordPress in an easy-to-use WordPress security plugin.
|
7 |
* Author: iThemes
|
8 |
* Author URI: https://ithemes.com
|
9 |
-
* Version: 7.
|
10 |
* Text Domain: better-wp-security
|
11 |
* Network: True
|
12 |
* License: GPLv2
|
|
|
13 |
*/
|
14 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
15 |
function itsec_load_textdomain() {
|
16 |
|
17 |
if ( function_exists( 'determine_locale' ) ) {
|
6 |
* Description: Take the guesswork out of WordPress security. iThemes Security offers 30+ ways to lock down WordPress in an easy-to-use WordPress security plugin.
|
7 |
* Author: iThemes
|
8 |
* Author URI: https://ithemes.com
|
9 |
+
* Version: 7.5.0
|
10 |
* Text Domain: better-wp-security
|
11 |
* Network: True
|
12 |
* License: GPLv2
|
13 |
+
* Requires PHP: 5.4
|
14 |
*/
|
15 |
|
16 |
+
if ( version_compare( phpversion(), '5.4.0', '<' ) ) {
|
17 |
+
function itsec_free_minimum_php_version_notice() {
|
18 |
+
echo '<div class="notice notice-error"><p>' . esc_html__( 'iThemes Security requires PHP 5.4 or higher.', 'better-wp-security' ) . '</p></div>';
|
19 |
+
}
|
20 |
+
|
21 |
+
add_action( 'admin_notices', 'itsec_free_minimum_php_version_notice' );
|
22 |
+
|
23 |
+
return;
|
24 |
+
}
|
25 |
+
|
26 |
function itsec_load_textdomain() {
|
27 |
|
28 |
if ( function_exists( 'determine_locale' ) ) {
|
core/core.php
CHANGED
@@ -24,7 +24,7 @@ if ( ! class_exists( 'ITSEC_Core' ) ) {
|
|
24 |
*
|
25 |
* @access private
|
26 |
*/
|
27 |
-
private $plugin_build =
|
28 |
|
29 |
/**
|
30 |
* Used to distinguish between a user modifying settings and the API modifying settings (such as from Sync
|
@@ -318,6 +318,7 @@ if ( ! class_exists( 'ITSEC_Core' ) ) {
|
|
318 |
ITSEC_Modules::register_module( 'database-prefix', "$path/modules/database-prefix", 'always-active' );
|
319 |
ITSEC_Modules::register_module( 'backup', "$path/modules/backup", 'default-active' );
|
320 |
ITSEC_Modules::register_module( 'core', "$path/modules/core", 'always-active' );
|
|
|
321 |
ITSEC_Modules::register_module( 'file-change', "$path/modules/file-change" );
|
322 |
ITSEC_Modules::register_module( 'file-permissions', "$path/modules/file-permissions", 'always-active' );
|
323 |
ITSEC_Modules::register_module( 'hide-backend', "$path/modules/hide-backend", 'always-active' );
|
24 |
*
|
25 |
* @access private
|
26 |
*/
|
27 |
+
private $plugin_build = 4115;
|
28 |
|
29 |
/**
|
30 |
* Used to distinguish between a user modifying settings and the API modifying settings (such as from Sync
|
318 |
ITSEC_Modules::register_module( 'database-prefix', "$path/modules/database-prefix", 'always-active' );
|
319 |
ITSEC_Modules::register_module( 'backup', "$path/modules/backup", 'default-active' );
|
320 |
ITSEC_Modules::register_module( 'core', "$path/modules/core", 'always-active' );
|
321 |
+
ITSEC_Modules::register_module( 'email-confirmation', "$path/modules/email-confirmation", 'always-active' );
|
322 |
ITSEC_Modules::register_module( 'file-change', "$path/modules/file-change" );
|
323 |
ITSEC_Modules::register_module( 'file-permissions', "$path/modules/file-permissions", 'always-active' );
|
324 |
ITSEC_Modules::register_module( 'hide-backend', "$path/modules/hide-backend", 'always-active' );
|
core/history.txt
CHANGED
@@ -811,3 +811,25 @@
|
|
811 |
Bug Fix: Hide Backend Bypass.
|
812 |
Bug Fix: Strict Standards error during Sync request.
|
813 |
Bug Fix: wp_die() if a login interstitial session fails to be created instead of throwing a fatal error.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
811 |
Bug Fix: Hide Backend Bypass.
|
812 |
Bug Fix: Strict Standards error during Sync request.
|
813 |
Bug Fix: wp_die() if a login interstitial session fails to be created instead of throwing a fatal error.
|
814 |
+
5.3.0 - 2019-09-04 - Timothy Jacobs
|
815 |
+
Breaking Change: iThemes Security requires PHP 5.4 or later.
|
816 |
+
Enhancement: New Lockout Template screen.
|
817 |
+
Bug Fix: Brute Force module reporting invalid logins using an email address incorrectly.
|
818 |
+
Developer Note: There were significant changes to the internals of the iThemes Security Lockout API in this release. If you are using the ITSEC_Lockout class directly, all the API functions will continue to work, but will emit deprecation notices.
|
819 |
+
5.3.1 - 2019-09-05 - Timothy Jacobs
|
820 |
+
Bug Fix: PHP Warning while logging interstitial updates.
|
821 |
+
5.3.2 - 2019-09-09 - Timothy Jacobs
|
822 |
+
Enhancement: Add confirmation button to Login Interstitial Async Actions when on a different device.
|
823 |
+
Bug Fix: Fix admin notice not being dismissed due to a REST API route that was more narrowly defined than necessary.
|
824 |
+
5.3.3 - 2019-10-01 - Timothy Jacobs
|
825 |
+
Bug Fix: Strong Passwords zxcvbn Library was not evaluating penalty strings correctly.
|
826 |
+
5.4.0 - 2019-10-29 - Timothy Jacobs
|
827 |
+
Enhancement: Add filter to "Lookup IP" link.
|
828 |
+
Bug Fix: PHP warning when inserting lockouts.
|
829 |
+
5.4.1 - 2019-11-12 - Timothy Jacobs
|
830 |
+
Bug Fix: Improve lockout compatibility with caching plugins.
|
831 |
+
Bug Fix: Admin Notices list did not refresh after dismissing a notice.
|
832 |
+
Bug Fix: Fix PHP warning if there are multiple detected proxy headers.
|
833 |
+
5.4.2 - 2019-11-14 - Timothy Jacobs
|
834 |
+
Tweak: Add stub Passwordless Login settings page.
|
835 |
+
Bug Fix: PHP warning if lockout_active field is missing.
|
core/lib.php
CHANGED
@@ -5,9 +5,9 @@
|
|
5 |
*
|
6 |
* Various static functions to provide information to modules and other areas throughout the plugin.
|
7 |
*
|
|
|
8 |
* @package iThemes_Security
|
9 |
*
|
10 |
-
* @since 4.0.0
|
11 |
*/
|
12 |
final class ITSEC_Lib {
|
13 |
/**
|
@@ -274,9 +274,9 @@ final class ITSEC_Lib {
|
|
274 |
/**
|
275 |
* Determines whether a given IP address is whiteliste
|
276 |
*
|
277 |
-
* @param
|
278 |
-
* @param
|
279 |
-
* @param
|
280 |
*
|
281 |
* @return boolean true if whitelisted or false
|
282 |
*/
|
@@ -521,6 +521,56 @@ final class ITSEC_Lib {
|
|
521 |
echo "<div class=\"error inline\"><p><strong>$message</strong></p></div>\n";
|
522 |
}
|
523 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
524 |
/**
|
525 |
* Get a WordPress user object.
|
526 |
*
|
@@ -549,9 +599,9 @@ final class ITSEC_Lib {
|
|
549 |
$type = gettype( $user );
|
550 |
}
|
551 |
|
552 |
-
|
553 |
|
554 |
-
|
555 |
}
|
556 |
|
557 |
if ( $user instanceof WP_User ) {
|
@@ -587,10 +637,12 @@ final class ITSEC_Lib {
|
|
587 |
*/
|
588 |
public static function get_trace_ip_link( $ip = false ) {
|
589 |
if ( empty( $ip ) ) {
|
590 |
-
|
591 |
} else {
|
592 |
-
|
593 |
}
|
|
|
|
|
594 |
}
|
595 |
|
596 |
/**
|
@@ -1189,7 +1241,7 @@ final class ITSEC_Lib {
|
|
1189 |
*
|
1190 |
* @return array
|
1191 |
*/
|
1192 |
-
public static function array_insert_before( $key, $array, $new_key, $new_value) {
|
1193 |
if ( array_key_exists( $key, $array ) ) {
|
1194 |
$new = array();
|
1195 |
foreach ( $array as $k => $value ) {
|
@@ -1272,6 +1324,9 @@ final class ITSEC_Lib {
|
|
1272 |
/**
|
1273 |
* Parse a complex header that has attributes like quality values.
|
1274 |
*
|
|
|
|
|
|
|
1275 |
* @example Parsing the Accept-Language header.
|
1276 |
*
|
1277 |
* "en-US,en;q=0.9,de;q=0.8" transforms to:
|
@@ -1282,9 +1337,6 @@ final class ITSEC_Lib {
|
|
1282 |
* 'de' => [ 'q' => 0.8' ],
|
1283 |
* ]
|
1284 |
*
|
1285 |
-
* @param string $header
|
1286 |
-
*
|
1287 |
-
* @return string[]
|
1288 |
*/
|
1289 |
public static function parse_header_with_attributes( $header ) {
|
1290 |
|
@@ -1444,7 +1496,7 @@ final class ITSEC_Lib {
|
|
1444 |
return false;
|
1445 |
}
|
1446 |
|
1447 |
-
return hash_equals( self::hash_token( $provided_token )
|
1448 |
}
|
1449 |
|
1450 |
/**
|
@@ -1756,4 +1808,78 @@ final class ITSEC_Lib {
|
|
1756 |
public static function load( $name ) {
|
1757 |
require_once( dirname( __FILE__ ) . "/lib/class-itsec-lib-{$name}.php" );
|
1758 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1759 |
}
|
5 |
*
|
6 |
* Various static functions to provide information to modules and other areas throughout the plugin.
|
7 |
*
|
8 |
+
* @since 4.0.0
|
9 |
* @package iThemes_Security
|
10 |
*
|
|
|
11 |
*/
|
12 |
final class ITSEC_Lib {
|
13 |
/**
|
274 |
/**
|
275 |
* Determines whether a given IP address is whiteliste
|
276 |
*
|
277 |
+
* @param string $ip ip to check (can be in CIDR notation)
|
278 |
+
* @param array $whitelisted_ips ip list to compare to if not yet saved to options
|
279 |
+
* @param boolean $current whether to whitelist the current ip or not (due to saving, etc)
|
280 |
*
|
281 |
* @return boolean true if whitelisted or false
|
282 |
*/
|
521 |
echo "<div class=\"error inline\"><p><strong>$message</strong></p></div>\n";
|
522 |
}
|
523 |
|
524 |
+
/**
|
525 |
+
* Print a WP core notice styled inline.
|
526 |
+
*
|
527 |
+
* @param string|WP_Error $message
|
528 |
+
* @param string $type
|
529 |
+
*
|
530 |
+
* @return string
|
531 |
+
*/
|
532 |
+
public static function inline_styled_notice( $message, $type = 'error' ) {
|
533 |
+
switch ( $type ) {
|
534 |
+
case 'error':
|
535 |
+
$bkg = '#dc3232';
|
536 |
+
$bdr = '#fbeaea';
|
537 |
+
break;
|
538 |
+
case 'warning':
|
539 |
+
$bkg = '#fff8e5';
|
540 |
+
$bdr = '#ffb900';
|
541 |
+
break;
|
542 |
+
case 'info':
|
543 |
+
$bkg = '#e5f5fa';
|
544 |
+
$bdr = '#00a0d2';
|
545 |
+
break;
|
546 |
+
case 'success':
|
547 |
+
default:
|
548 |
+
$bkg = '#ecf7ed';
|
549 |
+
$bdr = '#46b450';
|
550 |
+
break;
|
551 |
+
}
|
552 |
+
|
553 |
+
if ( is_wp_error( $message ) ) {
|
554 |
+
$messages = array();
|
555 |
+
|
556 |
+
foreach ( $message->get_error_codes() as $code ) {
|
557 |
+
foreach ( $message->get_error_messages( $code ) as $str ) {
|
558 |
+
$messages[] = $str;
|
559 |
+
}
|
560 |
+
}
|
561 |
+
|
562 |
+
$message = wp_sprintf( '%l', $messages );
|
563 |
+
}
|
564 |
+
|
565 |
+
$html = "<div style=\"background: {$bkg};border-left: 4px solid {$bdr};padding: 1px 12px; margin: 5px 0 15px;\">";
|
566 |
+
$html .= '<p style="margin: 0.5em 6px 0.5em 0;padding: 2px;vertical-align: bottom;">';
|
567 |
+
$html .= is_wp_error( $message ) ? $message->get_error_message() : $message;
|
568 |
+
$html .= '</p>';
|
569 |
+
$html .= '</div>';
|
570 |
+
|
571 |
+
return $html;
|
572 |
+
}
|
573 |
+
|
574 |
/**
|
575 |
* Get a WordPress user object.
|
576 |
*
|
599 |
$type = gettype( $user );
|
600 |
}
|
601 |
|
602 |
+
error_log( 'ITSEC_Lib::get_user() called with an invalid $user argument. Received $user variable of type: ' . $type );
|
603 |
|
604 |
+
wp_die( 'Internal Server Error' );
|
605 |
}
|
606 |
|
607 |
if ( $user instanceof WP_User ) {
|
637 |
*/
|
638 |
public static function get_trace_ip_link( $ip = false ) {
|
639 |
if ( empty( $ip ) ) {
|
640 |
+
$link = 'https://www.iptrackeronline.com/ithemes.php';
|
641 |
} else {
|
642 |
+
$link = 'http://www.iptrackeronline.com/ithemes.php?ip_address=' . urlencode( $ip );
|
643 |
}
|
644 |
+
|
645 |
+
return apply_filters( 'itsec_ip_details_link', $link, $ip );
|
646 |
}
|
647 |
|
648 |
/**
|
1241 |
*
|
1242 |
* @return array
|
1243 |
*/
|
1244 |
+
public static function array_insert_before( $key, $array, $new_key, $new_value ) {
|
1245 |
if ( array_key_exists( $key, $array ) ) {
|
1246 |
$new = array();
|
1247 |
foreach ( $array as $k => $value ) {
|
1324 |
/**
|
1325 |
* Parse a complex header that has attributes like quality values.
|
1326 |
*
|
1327 |
+
* @param string $header
|
1328 |
+
*
|
1329 |
+
* @return string[]
|
1330 |
* @example Parsing the Accept-Language header.
|
1331 |
*
|
1332 |
* "en-US,en;q=0.9,de;q=0.8" transforms to:
|
1337 |
* 'de' => [ 'q' => 0.8' ],
|
1338 |
* ]
|
1339 |
*
|
|
|
|
|
|
|
1340 |
*/
|
1341 |
public static function parse_header_with_attributes( $header ) {
|
1342 |
|
1496 |
return false;
|
1497 |
}
|
1498 |
|
1499 |
+
return hash_equals( $hashed_token, self::hash_token( $provided_token ) );
|
1500 |
}
|
1501 |
|
1502 |
/**
|
1808 |
public static function load( $name ) {
|
1809 |
require_once( dirname( __FILE__ ) . "/lib/class-itsec-lib-{$name}.php" );
|
1810 |
}
|
1811 |
+
|
1812 |
+
/**
|
1813 |
+
* Combine multiple WP_Error instances.
|
1814 |
+
*
|
1815 |
+
* @param WP_Error|null ...$errors
|
1816 |
+
*
|
1817 |
+
* @return WP_Error
|
1818 |
+
*/
|
1819 |
+
public static function combine_wp_error( $errors ) {
|
1820 |
+
$combined = new WP_Error();
|
1821 |
+
|
1822 |
+
self::add_to_wp_error( $combined, $errors );
|
1823 |
+
|
1824 |
+
return $combined;
|
1825 |
+
}
|
1826 |
+
|
1827 |
+
/**
|
1828 |
+
* Add the subsequent WP Error data to the first WP Error instance.
|
1829 |
+
*
|
1830 |
+
* @param WP_Error $add_to
|
1831 |
+
* @param WP_Error[]|null[] ...$errors
|
1832 |
+
*/
|
1833 |
+
public static function add_to_wp_error( WP_Error $add_to, $errors ) {
|
1834 |
+
if ( ! is_array( $errors ) ) {
|
1835 |
+
$errors = func_get_args();
|
1836 |
+
array_shift( $errors );
|
1837 |
+
}
|
1838 |
+
|
1839 |
+
foreach ( $errors as $error ) {
|
1840 |
+
if ( $error ) {
|
1841 |
+
foreach ( $error->get_error_codes() as $code ) {
|
1842 |
+
$add_to->add( $code, $error->get_error_message( $code ) );
|
1843 |
+
}
|
1844 |
+
}
|
1845 |
+
}
|
1846 |
+
}
|
1847 |
+
|
1848 |
+
/**
|
1849 |
+
* Render a file with only the given vars in context.
|
1850 |
+
*
|
1851 |
+
* @param string $file
|
1852 |
+
* @param array $context
|
1853 |
+
* @param bool $echo
|
1854 |
+
*
|
1855 |
+
* @return string|void
|
1856 |
+
*/
|
1857 |
+
public static function render( $file, $context = array(), $echo = true ) {
|
1858 |
+
$__echo = $echo;
|
1859 |
+
$__file = $file;
|
1860 |
+
|
1861 |
+
extract( $context, EXTR_OVERWRITE );
|
1862 |
+
unset( $file, $context, $echo );
|
1863 |
+
|
1864 |
+
if ( ! $__echo ) {
|
1865 |
+
ob_start();
|
1866 |
+
}
|
1867 |
+
|
1868 |
+
require( $__file );
|
1869 |
+
|
1870 |
+
if ( ! $__echo ) {
|
1871 |
+
return ob_get_clean() ?: '';
|
1872 |
+
}
|
1873 |
+
}
|
1874 |
+
|
1875 |
+
/**
|
1876 |
+
* Utility to mark this page as not cacheable.
|
1877 |
+
*/
|
1878 |
+
public static function no_cache() {
|
1879 |
+
nocache_headers();
|
1880 |
+
|
1881 |
+
if ( ! defined( 'DONOTCACHEPAGE' ) ) {
|
1882 |
+
define( 'DONOTCACHEPAGE', true );
|
1883 |
+
}
|
1884 |
+
}
|
1885 |
}
|
core/lib/class-itsec-lib-email-confirmation.php
ADDED
@@ -0,0 +1,31 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
class ITSEC_Lib_Email_Confirmation {
|
4 |
+
|
5 |
+
const META_KEY = '_itsec_email_confirmed';
|
6 |
+
|
7 |
+
/**
|
8 |
+
* Is the email address confirmed for the given user.
|
9 |
+
*
|
10 |
+
* @param WP_User|int $user
|
11 |
+
*
|
12 |
+
* @return bool
|
13 |
+
*/
|
14 |
+
public static function is_email_confirmed( $user ) {
|
15 |
+
$user = ITSEC_Lib::get_user( $user );
|
16 |
+
|
17 |
+
return (bool) get_user_meta( $user->ID, self::META_KEY, true );
|
18 |
+
}
|
19 |
+
|
20 |
+
/**
|
21 |
+
* Mark the email address for the given user as confirmed.
|
22 |
+
*
|
23 |
+
* @param WP_User|int $user
|
24 |
+
* @param bool $confirmed
|
25 |
+
*/
|
26 |
+
public static function mark_email_as_confirmed( $user, $confirmed = true ) {
|
27 |
+
$user = ITSEC_Lib::get_user( $user );
|
28 |
+
|
29 |
+
update_user_meta( $user->ID, self::META_KEY, $confirmed );
|
30 |
+
}
|
31 |
+
}
|
core/lib/class-itsec-lib-login-interstitial.php
CHANGED
@@ -19,6 +19,7 @@ class ITSEC_Lib_Login_Interstitial {
|
|
19 |
const R_INTERSTITIAL = 'itsec_interstitial';
|
20 |
const R_ASYNC_ACTION = 'itsec_interstitial_async_action';
|
21 |
const R_GET_STATE = 'itsec_interstitial_get_state';
|
|
|
22 |
|
23 |
const C_SAME_BROWSER = 'itsec_interstitial_browser';
|
24 |
const SAME_BROWSER_PAYLOAD = 'same-browser';
|
@@ -153,6 +154,8 @@ class ITSEC_Lib_Login_Interstitial {
|
|
153 |
* @api
|
154 |
*
|
155 |
* @param ITSEC_Login_Interstitial_Session $session
|
|
|
|
|
156 |
*/
|
157 |
public function proceed_to_next( ITSEC_Login_Interstitial_Session $session ) {
|
158 |
|
@@ -160,6 +163,7 @@ class ITSEC_Lib_Login_Interstitial {
|
|
160 |
$session->add_completed_interstitial( $current );
|
161 |
|
162 |
$session->set_current_interstitial( $this->get_next_interstitial( $session ) );
|
|
|
163 |
return $session->save();
|
164 |
}
|
165 |
|
@@ -209,6 +213,13 @@ class ITSEC_Lib_Login_Interstitial {
|
|
209 |
*/
|
210 |
public function initialize_same_browser( ITSEC_Login_Interstitial_Session $session ) {
|
211 |
ITSEC_Lib::set_cookie( self::C_SAME_BROWSER, $session->get_signature_for_payload( self::SAME_BROWSER_PAYLOAD ) );
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
212 |
}
|
213 |
|
214 |
/**
|
@@ -243,7 +254,7 @@ class ITSEC_Lib_Login_Interstitial {
|
|
243 |
wp_die( $session );
|
244 |
}
|
245 |
|
246 |
-
$
|
247 |
|
248 |
if ( isset( $_REQUEST[ self::SHOW_AFTER_LOGIN ], $this->registered[ $_REQUEST[ self::SHOW_AFTER_LOGIN ] ] ) ) {
|
249 |
$session->add_show_after( $_REQUEST[ self::SHOW_AFTER_LOGIN ] );
|
@@ -261,37 +272,12 @@ class ITSEC_Lib_Login_Interstitial {
|
|
261 |
wp_die( $session );
|
262 |
}
|
263 |
|
264 |
-
$
|
265 |
$session->save();
|
266 |
$this->show_interstitial( $session );
|
267 |
}
|
268 |
}
|
269 |
|
270 |
-
/**
|
271 |
-
* Build up the interstitial session configuration from the global state.
|
272 |
-
*
|
273 |
-
* This does not set the show afters because this is also used by the show after code.
|
274 |
-
*
|
275 |
-
* @param ITSEC_Login_Interstitial_Session $session
|
276 |
-
*/
|
277 |
-
private function build_session_from_global_state( ITSEC_Login_Interstitial_Session $session ) {
|
278 |
-
if ( isset( $_REQUEST['interim-login'] ) ) {
|
279 |
-
$session->set_interim_login();
|
280 |
-
}
|
281 |
-
|
282 |
-
if ( ! empty( $_REQUEST['redirect_to'] ) ) {
|
283 |
-
$session->set_redirect_to( $_REQUEST['redirect_to'] );
|
284 |
-
} elseif ( ! did_action( 'login_init' ) && ( $ref = wp_get_referer() ) ) {
|
285 |
-
$session->set_redirect_to( $ref );
|
286 |
-
} elseif ( ! did_action( 'login_init' ) ) {
|
287 |
-
$session->set_redirect_to( $_SERVER['REQUEST_URI'] );
|
288 |
-
}
|
289 |
-
|
290 |
-
if ( ! empty( $_REQUEST['rememberme'] ) ) {
|
291 |
-
$session->set_remember_me();
|
292 |
-
}
|
293 |
-
}
|
294 |
-
|
295 |
/**
|
296 |
* Add a message that the interstitial expired.
|
297 |
*
|
@@ -468,6 +454,12 @@ class ITSEC_Lib_Login_Interstitial {
|
|
468 |
$this->display_wp_login_message( new WP_Error( 'unsupported', esc_html__( 'Unsupported Interstitial. Please login again.', 'better-wp-security' ) ) );
|
469 |
}
|
470 |
|
|
|
|
|
|
|
|
|
|
|
|
|
471 |
$args = array(
|
472 |
'same_browser' => false,
|
473 |
);
|
@@ -480,7 +472,12 @@ class ITSEC_Lib_Login_Interstitial {
|
|
480 |
}
|
481 |
|
482 |
$this->current_session = $session;
|
483 |
-
|
|
|
|
|
|
|
|
|
|
|
484 |
|
485 |
if ( null === $result ) {
|
486 |
$this->display_wp_login_message( new WP_Error( 'unsupported', esc_html__( 'Unsupported Interstitial. Please login again.', 'better-wp-security' ) ) );
|
@@ -494,7 +491,7 @@ class ITSEC_Lib_Login_Interstitial {
|
|
494 |
$result = array();
|
495 |
}
|
496 |
|
497 |
-
if ( $args['same_browser'] && empty( $
|
498 |
$this->do_next_step( $session, array(
|
499 |
'delete' => false,
|
500 |
'allow_interim' => false,
|
@@ -719,6 +716,95 @@ class ITSEC_Lib_Login_Interstitial {
|
|
719 |
die;
|
720 |
}
|
721 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
722 |
/**
|
723 |
* Do the next step for a session.
|
724 |
*
|
19 |
const R_INTERSTITIAL = 'itsec_interstitial';
|
20 |
const R_ASYNC_ACTION = 'itsec_interstitial_async_action';
|
21 |
const R_GET_STATE = 'itsec_interstitial_get_state';
|
22 |
+
const R_SAME_BROWSER_DENY = 'itsec_interstitial_browser_deny';
|
23 |
|
24 |
const C_SAME_BROWSER = 'itsec_interstitial_browser';
|
25 |
const SAME_BROWSER_PAYLOAD = 'same-browser';
|
154 |
* @api
|
155 |
*
|
156 |
* @param ITSEC_Login_Interstitial_Session $session
|
157 |
+
*
|
158 |
+
* @return bool
|
159 |
*/
|
160 |
public function proceed_to_next( ITSEC_Login_Interstitial_Session $session ) {
|
161 |
|
163 |
$session->add_completed_interstitial( $current );
|
164 |
|
165 |
$session->set_current_interstitial( $this->get_next_interstitial( $session ) );
|
166 |
+
|
167 |
return $session->save();
|
168 |
}
|
169 |
|
213 |
*/
|
214 |
public function initialize_same_browser( ITSEC_Login_Interstitial_Session $session ) {
|
215 |
ITSEC_Lib::set_cookie( self::C_SAME_BROWSER, $session->get_signature_for_payload( self::SAME_BROWSER_PAYLOAD ) );
|
216 |
+
|
217 |
+
/**
|
218 |
+
* Fires when the login interstitial initializes the Same Browser API for async actions.
|
219 |
+
*
|
220 |
+
* @param ITSEC_Login_Interstitial_Session $session
|
221 |
+
*/
|
222 |
+
do_action( 'itsec_login_interstitial_initialize_same_browser', $session );
|
223 |
}
|
224 |
|
225 |
/**
|
254 |
wp_die( $session );
|
255 |
}
|
256 |
|
257 |
+
$session->initialize_from_global_state();
|
258 |
|
259 |
if ( isset( $_REQUEST[ self::SHOW_AFTER_LOGIN ], $this->registered[ $_REQUEST[ self::SHOW_AFTER_LOGIN ] ] ) ) {
|
260 |
$session->add_show_after( $_REQUEST[ self::SHOW_AFTER_LOGIN ] );
|
272 |
wp_die( $session );
|
273 |
}
|
274 |
|
275 |
+
$session->initialize_from_global_state();
|
276 |
$session->save();
|
277 |
$this->show_interstitial( $session );
|
278 |
}
|
279 |
}
|
280 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
281 |
/**
|
282 |
* Add a message that the interstitial expired.
|
283 |
*
|
454 |
$this->display_wp_login_message( new WP_Error( 'unsupported', esc_html__( 'Unsupported Interstitial. Please login again.', 'better-wp-security' ) ) );
|
455 |
}
|
456 |
|
457 |
+
if ( isset( $_REQUEST[ self::R_SAME_BROWSER_DENY ] ) ) {
|
458 |
+
$session->delete();
|
459 |
+
wp_redirect( wp_login_url() );
|
460 |
+
die;
|
461 |
+
}
|
462 |
+
|
463 |
$args = array(
|
464 |
'same_browser' => false,
|
465 |
);
|
472 |
}
|
473 |
|
474 |
$this->current_session = $session;
|
475 |
+
|
476 |
+
if ( ! $args['same_browser'] && 'GET' === $_SERVER['REQUEST_METHOD'] ) {
|
477 |
+
$this->display_async_action_confirmation( $session, $action );
|
478 |
+
}
|
479 |
+
|
480 |
+
$result = $interstitial->handle_async_action( $session, $action, $args );
|
481 |
|
482 |
if ( null === $result ) {
|
483 |
$this->display_wp_login_message( new WP_Error( 'unsupported', esc_html__( 'Unsupported Interstitial. Please login again.', 'better-wp-security' ) ) );
|
491 |
$result = array();
|
492 |
}
|
493 |
|
494 |
+
if ( $args['same_browser'] && empty( $result['allow_same_browser'] ) ) {
|
495 |
$this->do_next_step( $session, array(
|
496 |
'delete' => false,
|
497 |
'allow_interim' => false,
|
716 |
die;
|
717 |
}
|
718 |
|
719 |
+
/**
|
720 |
+
* Display a confirmation button for an async action.
|
721 |
+
*
|
722 |
+
* @param ITSEC_Login_Interstitial_Session $session
|
723 |
+
* @param string $action
|
724 |
+
*/
|
725 |
+
private function display_async_action_confirmation( ITSEC_Login_Interstitial_Session $session, $action ) {
|
726 |
+
if ( ! function_exists( 'login_header' ) ) {
|
727 |
+
require_once( dirname( __FILE__ ) . '/includes/function.login-header.php' );
|
728 |
+
}
|
729 |
+
|
730 |
+
$form_action = $this->get_async_action_url( $session, $action );
|
731 |
+
|
732 |
+
login_header();
|
733 |
+
?>
|
734 |
+
<style type="text/css">
|
735 |
+
.login h2 {
|
736 |
+
margin-bottom: 10px;
|
737 |
+
font-size: 14px;
|
738 |
+
}
|
739 |
+
|
740 |
+
.itsec-login-interstitial-confirm-async-action {
|
741 |
+
vertical-align: top;
|
742 |
+
display: block;
|
743 |
+
text-decoration: none;
|
744 |
+
height: 28px;
|
745 |
+
margin: 0 0 15px 0;
|
746 |
+
cursor: pointer;
|
747 |
+
-webkit-appearance: none;
|
748 |
+
border-radius: 3px;
|
749 |
+
white-space: nowrap;
|
750 |
+
box-sizing: border-box;
|
751 |
+
background: #0083E3;
|
752 |
+
color: #fff;
|
753 |
+
text-shadow: none;
|
754 |
+
padding: 20px 30px;
|
755 |
+
line-height: 0;
|
756 |
+
box-shadow: none;
|
757 |
+
font-weight: 300;
|
758 |
+
font-size: 1.2em;
|
759 |
+
border: none;
|
760 |
+
width: 100%;
|
761 |
+
text-align: center;
|
762 |
+
}
|
763 |
+
.itsec-login-interstitial-confirm-async-action:last-child {
|
764 |
+
margin-bottom: 0;
|
765 |
+
}
|
766 |
+
.itsec-login-interstitial-confirm-async-action:hover,
|
767 |
+
.itsec-login-interstitial-confirm-async-action:focus {
|
768 |
+
background: #006799;
|
769 |
+
color: #fff;
|
770 |
+
}
|
771 |
+
.itsec-login-interstitial-confirm-async-action.itsec-login-interstitial-confirm-async-action--deny {
|
772 |
+
background: #d54e21;
|
773 |
+
}
|
774 |
+
.itsec-login-interstitial-confirm-async-action.itsec-login-interstitial-confirm-async-action--deny:hover,
|
775 |
+
.itsec-login-interstitial-confirm-async-action.itsec-login-interstitial-confirm-async-action--deny:focus {
|
776 |
+
background: #983818;
|
777 |
+
}
|
778 |
+
</style>
|
779 |
+
<form action="<?php echo esc_url( $form_action ); ?>" method="post" autocomplete="off">
|
780 |
+
|
781 |
+
<h2><?php esc_html_e( 'Please Verify the Login Request', 'better-wp-security' ); ?></h2>
|
782 |
+
|
783 |
+
<?php do_action( 'itsec_login_interstitial_async_action_confirmation_before_confirm', $session, $action ); ?>
|
784 |
+
|
785 |
+
<button class="itsec-login-interstitial-confirm-async-action">
|
786 |
+
<?php esc_html_e( 'Confirm Login', 'better-wp-security' ); ?>
|
787 |
+
</button>
|
788 |
+
<button name="<?php echo esc_attr( self::R_SAME_BROWSER_DENY ); ?>" class="itsec-login-interstitial-confirm-async-action itsec-login-interstitial-confirm-async-action--deny">
|
789 |
+
<?php esc_html_e( 'Deny Login', 'better-wp-security' ); ?>
|
790 |
+
</button>
|
791 |
+
</form>
|
792 |
+
|
793 |
+
<p id="backtoblog">
|
794 |
+
<a href="<?php echo esc_url( home_url( '/' ) ); ?>" title="<?php esc_attr_e( 'Are you lost?', 'better-wp-security' ); ?>">
|
795 |
+
<?php echo esc_html( sprintf( __( '← Back to %s', 'better-wp-security' ), get_bloginfo( 'title', 'display' ) ) ); ?>
|
796 |
+
</a>
|
797 |
+
</p>
|
798 |
+
|
799 |
+
</div>
|
800 |
+
<?php do_action( 'login_footer' ); ?>
|
801 |
+
<div class="clear"></div>
|
802 |
+
</body>
|
803 |
+
</html>
|
804 |
+
<?php
|
805 |
+
die;
|
806 |
+
}
|
807 |
+
|
808 |
/**
|
809 |
* Do the next step for a session.
|
810 |
*
|
core/lib/class-itsec-lib-login.php
ADDED
@@ -0,0 +1,58 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
class ITSEC_Lib_Login {
|
4 |
+
/**
|
5 |
+
* Get a user account by the user's provided identifier.
|
6 |
+
*
|
7 |
+
* @param string $identifier
|
8 |
+
*
|
9 |
+
* @return WP_User|null
|
10 |
+
*/
|
11 |
+
public static function get_user( $identifier ) {
|
12 |
+
foreach ( self::get_user_lookup_fields() as $field ) {
|
13 |
+
if ( $user = get_user_by( $field, $identifier ) ) {
|
14 |
+
return $user;
|
15 |
+
}
|
16 |
+
}
|
17 |
+
|
18 |
+
return null;
|
19 |
+
}
|
20 |
+
|
21 |
+
/**
|
22 |
+
* Get the fields a user can provide to identify their user account.
|
23 |
+
*
|
24 |
+
* @return array
|
25 |
+
*/
|
26 |
+
public static function get_user_lookup_fields() {
|
27 |
+
$fields = array( 'login', 'email' );
|
28 |
+
|
29 |
+
if ( ITSEC_Modules::is_active( 'wordpress-tweaks' ) ) {
|
30 |
+
if ( 'email' === ITSEC_Modules::get_setting( 'wordpress-tweaks', 'valid_user_login_type' ) ) {
|
31 |
+
$fields = array( 'email' );
|
32 |
+
} elseif ( 'username' === ITSEC_Modules::get_setting( 'wordpress-tweaks', 'valid_user_login_type' ) ) {
|
33 |
+
$fields = array( 'login' );
|
34 |
+
}
|
35 |
+
}
|
36 |
+
|
37 |
+
return $fields;
|
38 |
+
}
|
39 |
+
|
40 |
+
/**
|
41 |
+
* Get the input label for the lookup field.
|
42 |
+
*
|
43 |
+
* @return string
|
44 |
+
*/
|
45 |
+
public static function get_user_lookup_fields_label() {
|
46 |
+
$fields = self::get_user_lookup_fields();
|
47 |
+
|
48 |
+
if ( count( $fields ) === 2 ) {
|
49 |
+
return esc_html__( 'Username or Email Address', 'better-wp-security' );
|
50 |
+
}
|
51 |
+
|
52 |
+
if ( 'email' === $fields[0] ) {
|
53 |
+
return esc_html__( 'Email Address', 'better-wp-security' );
|
54 |
+
}
|
55 |
+
|
56 |
+
return esc_html__( 'Username', 'better-wp-security' );
|
57 |
+
}
|
58 |
+
}
|
core/lib/class-itsec-lib-opaque-tokens.php
ADDED
@@ -0,0 +1,116 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
class ITSEC_Lib_Opaque_Tokens {
|
4 |
+
const TTL = 1800; // 30 minutes
|
5 |
+
const MAX_TTL = 604800; // Maximum TTL for tokens.
|
6 |
+
|
7 |
+
const E_INVALID = 'itsec-opaque-token-invalid';
|
8 |
+
const E_MISSING = 'itsec-opaque-token-not-found';
|
9 |
+
const E_EXPIRED = 'itsec-opaque-token-expired';
|
10 |
+
const E_HASH_FAILED = 'itsec-opaque-token-hash-failed';
|
11 |
+
|
12 |
+
/**
|
13 |
+
* Verify that a token is valid and get it's data.
|
14 |
+
*
|
15 |
+
* @param string $type
|
16 |
+
* @param string $token
|
17 |
+
* @param int $ttl
|
18 |
+
*
|
19 |
+
* @return array|WP_Error
|
20 |
+
*/
|
21 |
+
public static function verify_and_get_token_data( $type, $token, $ttl = self::TTL ) {
|
22 |
+
global $wpdb;
|
23 |
+
|
24 |
+
if ( $ttl > self::MAX_TTL ) {
|
25 |
+
_doing_it_wrong( __METHOD__, sprintf( 'Token ttl must not be greater than %d seconds.', self::MAX_TTL ), '5.3.0' );
|
26 |
+
|
27 |
+
return new WP_Error( self::E_INVALID, __( 'Invalid token.', 'better-wp-security' ) );
|
28 |
+
}
|
29 |
+
|
30 |
+
$token = base64_decode( $token );
|
31 |
+
|
32 |
+
if ( strpos( $token, '|' ) === false ) {
|
33 |
+
return new WP_Error( self::E_INVALID, __( 'Invalid token.', 'better-wp-security' ) );
|
34 |
+
}
|
35 |
+
|
36 |
+
list( $id, $unhashed ) = explode( '|', $token );
|
37 |
+
|
38 |
+
$data = $wpdb->get_row( $wpdb->prepare(
|
39 |
+
"SELECT * FROM {$wpdb->base_prefix}itsec_opaque_tokens WHERE token_id = %s AND token_type = %s LIMIT 1",
|
40 |
+
$id,
|
41 |
+
$type
|
42 |
+
), ARRAY_A );
|
43 |
+
|
44 |
+
if ( ! $data ) {
|
45 |
+
return new WP_Error( self::E_MISSING, __( 'Token not found.', 'better-wp-security' ) );
|
46 |
+
}
|
47 |
+
|
48 |
+
if ( $data['token_created_at'] + $ttl > ITSEC_Core::get_current_time_gmt() ) {
|
49 |
+
self::delete_token( $token );
|
50 |
+
|
51 |
+
return new WP_Error( self::E_EXPIRED, __( 'Token expired.', 'better-wp-security' ) );
|
52 |
+
}
|
53 |
+
|
54 |
+
if ( ! ITSEC_Lib::verify_token( $unhashed, $data['token_hashed'] ) ) {
|
55 |
+
return new WP_Error( self::E_INVALID, __( 'Invalid token.', 'better-wp-security' ) );
|
56 |
+
}
|
57 |
+
|
58 |
+
return json_decode( $data['token_data'], true );
|
59 |
+
}
|
60 |
+
|
61 |
+
/**
|
62 |
+
* Create a new token.
|
63 |
+
*
|
64 |
+
* @param string $type
|
65 |
+
* @param array $data
|
66 |
+
*
|
67 |
+
* @return string|WP_Error
|
68 |
+
*/
|
69 |
+
public static function create_token( $type, array $data ) {
|
70 |
+
global $wpdb;
|
71 |
+
|
72 |
+
$id = ITSEC_Lib::generate_token();
|
73 |
+
$token = ITSEC_Lib::generate_token();
|
74 |
+
$hashed = ITSEC_Lib::hash_token( $token );
|
75 |
+
|
76 |
+
if ( ! $id || ! $token || ! $hashed ) {
|
77 |
+
return new WP_Error( self::E_HASH_FAILED, __( 'Failed to generate token and hash.', 'better-wp-security' ) );
|
78 |
+
}
|
79 |
+
|
80 |
+
$wpdb->insert( $wpdb->base_prefix . 'itsec_opaque_tokens', array(
|
81 |
+
'token_id' => $id,
|
82 |
+
'token_hashed' => $hashed,
|
83 |
+
'token_type' => $type,
|
84 |
+
'token_data' => wp_json_encode( $data ),
|
85 |
+
'token_created_at' => gmdate( 'Y-m-d H:i:s', ITSEC_Core::get_current_time_gmt() ),
|
86 |
+
) );
|
87 |
+
|
88 |
+
return base64_encode( "{$id}|{$token}" );
|
89 |
+
}
|
90 |
+
|
91 |
+
/**
|
92 |
+
* Delete a token.
|
93 |
+
*
|
94 |
+
* @param string $token
|
95 |
+
*/
|
96 |
+
public static function delete_token( $token ) {
|
97 |
+
global $wpdb;
|
98 |
+
|
99 |
+
$token = base64_decode( $token );
|
100 |
+
list( $id ) = explode( '|', $token );
|
101 |
+
|
102 |
+
$wpdb->delete( $wpdb->base_prefix . 'itsec_opaque_tokens', array( 'token_id' => $id ) );
|
103 |
+
}
|
104 |
+
|
105 |
+
/**
|
106 |
+
* Delete tokens that are more than 7 days old.
|
107 |
+
*/
|
108 |
+
public static function delete_expired_tokens() {
|
109 |
+
global $wpdb;
|
110 |
+
|
111 |
+
$wpdb->query( $wpdb->prepare(
|
112 |
+
"DELETE FROM {$wpdb->base_prefix}itsec_opaque_tokens WHERE token_created_at < %s",
|
113 |
+
gmdate( 'Y-m-d H:i:s', ITSEC_Core::get_current_time_gmt() - self::MAX_TTL )
|
114 |
+
) );
|
115 |
+
}
|
116 |
+
}
|
core/lib/class-itsec-mail.php
CHANGED
@@ -272,6 +272,9 @@ final class ITSEC_Mail {
|
|
272 |
if ( 'user' === $lockout['type'] ) {
|
273 |
/* translators: 1: Username */
|
274 |
$lockout['description'] = sprintf( wp_kses( __( '<b>User:</b> %1$s', 'better-wp-security' ), array( 'b' => array() ) ), $lockout['id'] );
|
|
|
|
|
|
|
275 |
} else {
|
276 |
/* translators: 1: Hostname */
|
277 |
$lockout['description'] = sprintf( wp_kses( __( '<b>Host:</b> %1$s', 'better-wp-security' ), array( 'b' => array() ) ), $lockout['id'] );
|
@@ -338,7 +341,7 @@ final class ITSEC_Mail {
|
|
338 |
$style .= 'padding:5px 10px;';
|
339 |
}
|
340 |
|
341 |
-
$html .= '<th style="' . $style .'">';
|
342 |
$html .= $header;
|
343 |
$html .= '</th>';
|
344 |
}
|
@@ -421,14 +424,20 @@ final class ITSEC_Mail {
|
|
421 |
/**
|
422 |
* Add an image to the email.
|
423 |
*
|
424 |
-
* @param string $
|
425 |
-
* @param int $width
|
426 |
*/
|
427 |
-
public function add_image( $
|
428 |
-
$this->add_html( $this->get_image( $
|
429 |
}
|
430 |
|
431 |
-
public function get_image( $
|
|
|
|
|
|
|
|
|
|
|
|
|
432 |
$module = $this->get_template( 'image.html' );
|
433 |
$module = $this->replace_all( $module, array(
|
434 |
'src' => $src,
|
272 |
if ( 'user' === $lockout['type'] ) {
|
273 |
/* translators: 1: Username */
|
274 |
$lockout['description'] = sprintf( wp_kses( __( '<b>User:</b> %1$s', 'better-wp-security' ), array( 'b' => array() ) ), $lockout['id'] );
|
275 |
+
} elseif ( 'username' === $lockout['type'] ) {
|
276 |
+
/* translators: 1: Username */
|
277 |
+
$lockout['description'] = sprintf( wp_kses( __( '<b>Username:</b> %1$s', 'better-wp-security' ), array( 'b' => array() ) ), $lockout['id'] );
|
278 |
} else {
|
279 |
/* translators: 1: Hostname */
|
280 |
$lockout['description'] = sprintf( wp_kses( __( '<b>Host:</b> %1$s', 'better-wp-security' ), array( 'b' => array() ) ), $lockout['id'] );
|
341 |
$style .= 'padding:5px 10px;';
|
342 |
}
|
343 |
|
344 |
+
$html .= '<th style="' . $style . '">';
|
345 |
$html .= $header;
|
346 |
$html .= '</th>';
|
347 |
}
|
424 |
/**
|
425 |
* Add an image to the email.
|
426 |
*
|
427 |
+
* @param string $src_or_name URL of the image or the name of the mail image.
|
428 |
+
* @param int $width Max width of the image in pixels.
|
429 |
*/
|
430 |
+
public function add_image( $src_or_name, $width ) {
|
431 |
+
$this->add_html( $this->get_image( $src_or_name, $width ) );
|
432 |
}
|
433 |
|
434 |
+
public function get_image( $src_or_name, $width ) {
|
435 |
+
if ( false === strpos( $src_or_name, '.' ) ) {
|
436 |
+
$src = $this->get_image_url( $src_or_name );
|
437 |
+
} else {
|
438 |
+
$src = $src_or_name;
|
439 |
+
}
|
440 |
+
|
441 |
$module = $this->get_template( 'image.html' );
|
442 |
$module = $this->replace_all( $module, array(
|
443 |
'src' => $src,
|
core/lib/class-itsec-scheduler-cron.php
CHANGED
@@ -457,6 +457,8 @@ class ITSEC_Scheduler_Cron extends ITSEC_Scheduler {
|
|
457 |
}
|
458 |
|
459 |
public function uninstall() {
|
|
|
|
|
460 |
|
461 |
$crons = _get_cron_array();
|
462 |
|
@@ -472,4 +474,4 @@ class ITSEC_Scheduler_Cron extends ITSEC_Scheduler {
|
|
472 |
|
473 |
delete_site_option( self::OPTION );
|
474 |
}
|
475 |
-
}
|
457 |
}
|
458 |
|
459 |
public function uninstall() {
|
460 |
+
remove_action( self::HOOK, array( $this, 'process' ) );
|
461 |
+
remove_filter( 'cron_schedules', array( $this, 'register_cron_schedules' ) );
|
462 |
|
463 |
$crons = _get_cron_array();
|
464 |
|
474 |
|
475 |
delete_site_option( self::OPTION );
|
476 |
}
|
477 |
+
}
|
core/lib/class-itsec-scheduler-page-load.php
CHANGED
@@ -381,6 +381,7 @@ class ITSEC_Scheduler_Page_Load extends ITSEC_Scheduler {
|
|
381 |
}
|
382 |
|
383 |
public function uninstall() {
|
|
|
384 |
delete_site_option( self::OPTION );
|
385 |
}
|
386 |
}
|
381 |
}
|
382 |
|
383 |
public function uninstall() {
|
384 |
+
remove_action( 'init', array( $this, 'init' ) );
|
385 |
delete_site_option( self::OPTION );
|
386 |
}
|
387 |
}
|
core/lib/class-itsec-scheduler.php
CHANGED
@@ -232,6 +232,15 @@ abstract class ITSEC_Scheduler {
|
|
232 |
$this->custom_schedules[ $slug ] = $interval;
|
233 |
}
|
234 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
235 |
/**
|
236 |
* Register an event loop.
|
237 |
*
|
@@ -363,4 +372,4 @@ abstract class ITSEC_Scheduler {
|
|
363 |
public function uninstall() {
|
364 |
|
365 |
}
|
366 |
-
}
|
232 |
$this->custom_schedules[ $slug ] = $interval;
|
233 |
}
|
234 |
|
235 |
+
/**
|
236 |
+
* Get a registered custom schedules.
|
237 |
+
*
|
238 |
+
* @return array
|
239 |
+
*/
|
240 |
+
public function get_custom_schedules() {
|
241 |
+
return $this->custom_schedules;
|
242 |
+
}
|
243 |
+
|
244 |
/**
|
245 |
* Register an event loop.
|
246 |
*
|
372 |
public function uninstall() {
|
373 |
|
374 |
}
|
375 |
+
}
|
core/lib/fingerprinting/class-itsec-fingerprint.php
CHANGED
@@ -3,7 +3,7 @@
|
|
3 |
/**
|
4 |
* Class ITSEC_Fingerprint
|
5 |
*/
|
6 |
-
class ITSEC_Fingerprint {
|
7 |
|
8 |
const S_APPROVED = 'approved';
|
9 |
const S_AUTO_APPROVED = 'auto-approved';
|
@@ -696,4 +696,73 @@ class ITSEC_Fingerprint {
|
|
696 |
|
697 |
return trim( $str );
|
698 |
}
|
699 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
3 |
/**
|
4 |
* Class ITSEC_Fingerprint
|
5 |
*/
|
6 |
+
class ITSEC_Fingerprint implements JsonSerializable {
|
7 |
|
8 |
const S_APPROVED = 'approved';
|
9 |
const S_AUTO_APPROVED = 'auto-approved';
|
696 |
|
697 |
return trim( $str );
|
698 |
}
|
699 |
+
|
700 |
+
/**
|
701 |
+
* Serialize a fingerprint to JSON.
|
702 |
+
*
|
703 |
+
* For uses when you need to persist a fingerprint that hasn't been stored yet
|
704 |
+
* to short term storage.
|
705 |
+
*
|
706 |
+
* @return array
|
707 |
+
*/
|
708 |
+
public function jsonSerialize() {
|
709 |
+
if ( $this->_id ) {
|
710 |
+
return null;
|
711 |
+
}
|
712 |
+
|
713 |
+
$approved_at = $this->get_approved_at();
|
714 |
+
|
715 |
+
$values = array();
|
716 |
+
|
717 |
+
foreach ( $this->get_values() as $value ) {
|
718 |
+
$values[ $value->get_source()->get_slug() ] = $value->get_value();
|
719 |
+
}
|
720 |
+
|
721 |
+
return [
|
722 |
+
'user' => $this->get_user()->ID,
|
723 |
+
'created_at' => $this->get_created_at()->format( 'Y-m-d H:i:s' ),
|
724 |
+
'values' => $values,
|
725 |
+
'status' => $this->get_status(),
|
726 |
+
'uses' => $this->get_uses(),
|
727 |
+
'approved_at' => $approved_at ? $approved_at->format( 'Y-m-d H:i:s' ) : null,
|
728 |
+
];
|
729 |
+
}
|
730 |
+
|
731 |
+
/**
|
732 |
+
* Recreate a fingerprint from JSON.
|
733 |
+
*
|
734 |
+
* @param string $json
|
735 |
+
*
|
736 |
+
* @return ITSEC_Fingerprint|null
|
737 |
+
*/
|
738 |
+
public static function from_json( $json ) {
|
739 |
+
$decoded = json_decode( $json, true );
|
740 |
+
|
741 |
+
if ( ! $decoded ) {
|
742 |
+
return null;
|
743 |
+
}
|
744 |
+
|
745 |
+
ITSEC_Lib::load( 'fingerprinting' );
|
746 |
+
|
747 |
+
$sources = ITSEC_Lib_Fingerprinting::get_sources();
|
748 |
+
$values = array();
|
749 |
+
|
750 |
+
foreach ( $decoded['values'] as $slug => $value ) {
|
751 |
+
if ( isset( $sources[ $slug ] ) ) {
|
752 |
+
$values[] = new ITSEC_Fingerprint_Value( $sources[ $slug ], $value );
|
753 |
+
}
|
754 |
+
}
|
755 |
+
|
756 |
+
$fingerprint = new ITSEC_Fingerprint(
|
757 |
+
get_userdata( $decoded['user'] ),
|
758 |
+
new DateTime( $decoded['created_at'], new DateTimeZone( 'UTC' ) ),
|
759 |
+
$values
|
760 |
+
);
|
761 |
+
|
762 |
+
$fingerprint->_status = $decoded['status'];
|
763 |
+
$fingerprint->_uses = $decoded['uses'];
|
764 |
+
$fingerprint->_approved_at = $decoded['approved_at'] ? new DateTime( $decoded['approved_at'], new DateTimeZone( 'UTC' ) ) : null;
|
765 |
+
|
766 |
+
return $fingerprint;
|
767 |
+
}
|
768 |
+
}
|
core/lib/itsec-zxcvbn-php/matchers/dictionary.php
CHANGED
@@ -89,13 +89,13 @@ class ITSEC_Zxcvbn_Dictionary_Match extends ITSEC_Zxcvbn_Match {
|
|
89 |
for ( $j = $i; $j < $length; $j++ ) {
|
90 |
$word = substr( $pw_lower, $i, $j - $i + 1 );
|
91 |
|
92 |
-
if ( isset( $dictionary
|
93 |
$result[] = array(
|
94 |
'begin' => $i,
|
95 |
'end' => $j,
|
96 |
'token' => substr( $password, $i, $j - $i + 1 ),
|
97 |
'matched_word' => $word,
|
98 |
-
'rank' => $dictionary
|
99 |
);
|
100 |
}
|
101 |
}
|
@@ -110,7 +110,7 @@ class ITSEC_Zxcvbn_Dictionary_Match extends ITSEC_Zxcvbn_Match {
|
|
110 |
* @return object
|
111 |
*/
|
112 |
protected static function get_ranked_dictionary( $dictionary_name ) {
|
113 |
-
return json_decode( file_get_contents( dirname( __FILE__ ) . sprintf( '/ranked_frequency_list-%s.json', $dictionary_name ) ) );
|
114 |
}
|
115 |
|
116 |
public function estimate_guesses() {
|
89 |
for ( $j = $i; $j < $length; $j++ ) {
|
90 |
$word = substr( $pw_lower, $i, $j - $i + 1 );
|
91 |
|
92 |
+
if ( isset( $dictionary[ $word ] ) ) {
|
93 |
$result[] = array(
|
94 |
'begin' => $i,
|
95 |
'end' => $j,
|
96 |
'token' => substr( $password, $i, $j - $i + 1 ),
|
97 |
'matched_word' => $word,
|
98 |
+
'rank' => $dictionary[ $word ],
|
99 |
);
|
100 |
}
|
101 |
}
|
110 |
* @return object
|
111 |
*/
|
112 |
protected static function get_ranked_dictionary( $dictionary_name ) {
|
113 |
+
return json_decode( file_get_contents( dirname( __FILE__ ) . sprintf( '/ranked_frequency_list-%s.json', $dictionary_name ) ), true );
|
114 |
}
|
115 |
|
116 |
public function estimate_guesses() {
|
core/lib/lockout/abstract-context.php
ADDED
@@ -0,0 +1,47 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
namespace iThemesSecurity\Lib\Lockout;
|
4 |
+
|
5 |
+
use iThemesSecurity\Lib\Lockout\Execute_Lock\Source\Source;
|
6 |
+
|
7 |
+
abstract class Context implements Source {
|
8 |
+
|
9 |
+
/** @var string */
|
10 |
+
private $lockout_module;
|
11 |
+
|
12 |
+
/**
|
13 |
+
* ITSEC_Execute_Lockout_Context constructor.
|
14 |
+
*
|
15 |
+
* @param string $lockout_module
|
16 |
+
*/
|
17 |
+
public function __construct( $lockout_module ) {
|
18 |
+
$this->lockout_module = $lockout_module;
|
19 |
+
}
|
20 |
+
|
21 |
+
/**
|
22 |
+
* Get the registered lockout module {@see 'itsec_lockout_modules'} that
|
23 |
+
*
|
24 |
+
* @return string
|
25 |
+
*/
|
26 |
+
public function get_lockout_module() {
|
27 |
+
return $this->lockout_module;
|
28 |
+
}
|
29 |
+
|
30 |
+
/**
|
31 |
+
* @return mixed
|
32 |
+
*/
|
33 |
+
public function get_source_slug() {
|
34 |
+
return $this->get_lockout_module();
|
35 |
+
}
|
36 |
+
|
37 |
+
/**
|
38 |
+
* Make an execute lock context from the lockout context.
|
39 |
+
*
|
40 |
+
* @return Execute_Lock\Context
|
41 |
+
*/
|
42 |
+
abstract public function make_execute_lock_context();
|
43 |
+
}
|
44 |
+
|
45 |
+
require_once( __DIR__ . '/class-host-context.php' );
|
46 |
+
require_once( __DIR__ . '/class-user-context.php' );
|
47 |
+
require_once( __DIR__ . '/class-username-context.php' );
|
core/lib/lockout/class-host-context.php
ADDED
@@ -0,0 +1,120 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
namespace iThemesSecurity\Lib\Lockout;
|
4 |
+
|
5 |
+
use ITSEC_Lib;
|
6 |
+
|
7 |
+
final class Host_Context extends Context {
|
8 |
+
|
9 |
+
/** @var string */
|
10 |
+
private $host;
|
11 |
+
|
12 |
+
/** @var int */
|
13 |
+
private $login_user_id;
|
14 |
+
|
15 |
+
/** @var string */
|
16 |
+
private $login_username;
|
17 |
+
|
18 |
+
/** @var bool */
|
19 |
+
private $user_limit_triggered = false;
|
20 |
+
|
21 |
+
/**
|
22 |
+
* ITSEC_Host_Lockout_Context constructor.
|
23 |
+
*
|
24 |
+
* @param string $lockout_module
|
25 |
+
* @param string $host
|
26 |
+
*/
|
27 |
+
public function __construct( $lockout_module, $host = '' ) {
|
28 |
+
parent::__construct( $lockout_module );
|
29 |
+
$this->host = $host ?: ITSEC_Lib::get_ip();
|
30 |
+
}
|
31 |
+
|
32 |
+
/**
|
33 |
+
* Get the host being locked out.
|
34 |
+
*
|
35 |
+
* @return string
|
36 |
+
*/
|
37 |
+
public function get_host() {
|
38 |
+
return $this->host;
|
39 |
+
}
|
40 |
+
|
41 |
+
/**
|
42 |
+
* If this lockout occurred while trying to login as a user,
|
43 |
+
* this will return the user ID that was trying to be logged-in-to.
|
44 |
+
*
|
45 |
+
* @return int|null
|
46 |
+
*/
|
47 |
+
public function get_login_user_id() {
|
48 |
+
return $this->login_user_id;
|
49 |
+
}
|
50 |
+
|
51 |
+
/**
|
52 |
+
* Set who is being logged in as.
|
53 |
+
*
|
54 |
+
* @param int $user_id
|
55 |
+
*
|
56 |
+
* @return $this
|
57 |
+
*/
|
58 |
+
public function set_login_user_id( $user_id ) {
|
59 |
+
$this->login_user_id = $user_id;
|
60 |
+
|
61 |
+
return $this;
|
62 |
+
}
|
63 |
+
|
64 |
+
/**
|
65 |
+
* If this lockout occurred while trying to login to a non-existent user,
|
66 |
+
* this will return that username.
|
67 |
+
*
|
68 |
+
* @return string
|
69 |
+
*/
|
70 |
+
public function get_login_username() {
|
71 |
+
return $this->login_username;
|
72 |
+
}
|
73 |
+
|
74 |
+
/**
|
75 |
+
* Set which username is being logged in as.
|
76 |
+
*
|
77 |
+
* @param string $login_username
|
78 |
+
*
|
79 |
+
* @return Host_Context
|
80 |
+
*/
|
81 |
+
public function set_login_username( $login_username ) {
|
82 |
+
$this->login_username = $login_username;
|
83 |
+
|
84 |
+
return $this;
|
85 |
+
}
|
86 |
+
|
87 |
+
/**
|
88 |
+
* Was the user lockout limit was triggered.
|
89 |
+
*
|
90 |
+
* @return bool
|
91 |
+
*/
|
92 |
+
public function is_user_limit_triggered() {
|
93 |
+
return $this->user_limit_triggered;
|
94 |
+
}
|
95 |
+
|
96 |
+
/**
|
97 |
+
* Set that the user lockout limit was triggered.
|
98 |
+
*
|
99 |
+
* @param bool $user_limit_triggered
|
100 |
+
*
|
101 |
+
* @return Host_Context
|
102 |
+
*/
|
103 |
+
public function set_user_limit_triggered( $user_limit_triggered = true ) {
|
104 |
+
$this->user_limit_triggered = $user_limit_triggered;
|
105 |
+
|
106 |
+
return $this;
|
107 |
+
}
|
108 |
+
|
109 |
+
public function make_execute_lock_context() {
|
110 |
+
$context = new Execute_Lock\Host_Context( $this, $this->get_host() );
|
111 |
+
|
112 |
+
if ( $user_id = $this->get_login_user_id() ) {
|
113 |
+
$context->set_login_user_id( $user_id );
|
114 |
+
} elseif ( $username = $this->get_login_username() ) {
|
115 |
+
$context->set_login_username( $username );
|
116 |
+
}
|
117 |
+
|
118 |
+
return $context;
|
119 |
+
}
|
120 |
+
}
|
core/lib/lockout/class-lockout.php
ADDED
@@ -0,0 +1,169 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
namespace iThemesSecurity\Lib\Lockout;
|
4 |
+
|
5 |
+
use iThemesSecurity\Lib\Lockout\Execute_Lock\Source\Source;
|
6 |
+
|
7 |
+
final class Lockout implements Source {
|
8 |
+
|
9 |
+
/** @var int */
|
10 |
+
private $id;
|
11 |
+
|
12 |
+
/** @var string */
|
13 |
+
private $module;
|
14 |
+
|
15 |
+
/** @var \DateTime */
|
16 |
+
private $start;
|
17 |
+
|
18 |
+
/** @var \DateTime */
|
19 |
+
private $expire;
|
20 |
+
|
21 |
+
/** @var string */
|
22 |
+
private $host;
|
23 |
+
|
24 |
+
/** @var int */
|
25 |
+
private $user_id;
|
26 |
+
|
27 |
+
/** @var string */
|
28 |
+
private $username;
|
29 |
+
|
30 |
+
/** @var bool */
|
31 |
+
private $active;
|
32 |
+
|
33 |
+
/** @var Context */
|
34 |
+
private $context;
|
35 |
+
|
36 |
+
/**
|
37 |
+
* Lockout constructor.
|
38 |
+
*
|
39 |
+
* @param int $id
|
40 |
+
* @param string $module
|
41 |
+
* @param \DateTime $start
|
42 |
+
* @param \DateTime $expire
|
43 |
+
* @param string $host
|
44 |
+
* @param int $user_id
|
45 |
+
* @param string $username
|
46 |
+
* @param bool $active
|
47 |
+
* @param Context $context
|
48 |
+
*/
|
49 |
+
public function __construct( $id, $module, \DateTime $start, \DateTime $expire, $host, $user_id, $username, $active, Context $context = null ) {
|
50 |
+
$this->id = $id;
|
51 |
+
$this->module = $module;
|
52 |
+
$this->start = $start;
|
53 |
+
$this->expire = $expire;
|
54 |
+
$this->host = $host;
|
55 |
+
$this->user_id = $user_id;
|
56 |
+
$this->username = $username;
|
57 |
+
$this->active = $active;
|
58 |
+
$this->context = $context;
|
59 |
+
}
|
60 |
+
|
61 |
+
/**
|
62 |
+
* Get the unique id for this lockout.
|
63 |
+
*
|
64 |
+
* @return int
|
65 |
+
*/
|
66 |
+
public function get_id() {
|
67 |
+
return $this->id;
|
68 |
+
}
|
69 |
+
|
70 |
+
/**
|
71 |
+
* Get the lockout module responsible for this lockout.
|
72 |
+
*
|
73 |
+
* @return string
|
74 |
+
*/
|
75 |
+
public function get_module() {
|
76 |
+
return $this->module;
|
77 |
+
}
|
78 |
+
|
79 |
+
/**
|
80 |
+
* Get the date & time when the lockout started. In UTC.
|
81 |
+
*
|
82 |
+
* @return \DateTime
|
83 |
+
*/
|
84 |
+
public function get_start() {
|
85 |
+
return $this->start;
|
86 |
+
}
|
87 |
+
|
88 |
+
/**
|
89 |
+
* Get the date & time when the lockout has expired. In UTC.
|
90 |
+
*
|
91 |
+
* @return \DateTime
|
92 |
+
*/
|
93 |
+
public function get_expire() {
|
94 |
+
return $this->expire;
|
95 |
+
}
|
96 |
+
|
97 |
+
/**
|
98 |
+
* Get the host that was locked out.
|
99 |
+
*
|
100 |
+
* @return string
|
101 |
+
*/
|
102 |
+
public function get_host() {
|
103 |
+
return $this->host;
|
104 |
+
}
|
105 |
+
|
106 |
+
/**
|
107 |
+
* Get the user ID that was locked out.
|
108 |
+
*
|
109 |
+
* @return int
|
110 |
+
*/
|
111 |
+
public function get_user_id() {
|
112 |
+
return $this->user_id;
|
113 |
+
}
|
114 |
+
|
115 |
+
/**
|
116 |
+
* Get the username that was locked out.
|
117 |
+
*
|
118 |
+
* @return string
|
119 |
+
*/
|
120 |
+
public function get_username() {
|
121 |
+
return $this->username;
|
122 |
+
}
|
123 |
+
|
124 |
+
/**
|
125 |
+
* Is the lockout marked as active. This does not check that
|
126 |
+
* the lockout has not expired.
|
127 |
+
*
|
128 |
+
* @return bool
|
129 |
+
*/
|
130 |
+
public function is_active() {
|
131 |
+
return $this->active;
|
132 |
+
}
|
133 |
+
|
134 |
+
/**
|
135 |
+
* @return Context|null
|
136 |
+
*/
|
137 |
+
public function get_context() {
|
138 |
+
return $this->context;
|
139 |
+
}
|
140 |
+
|
141 |
+
/**
|
142 |
+
* Make an execute lock context for this lockout.
|
143 |
+
*
|
144 |
+
* @return Execute_Lock\Context
|
145 |
+
*/
|
146 |
+
public function make_execute_lock_context() {
|
147 |
+
if ( $context = $this->get_context() ) {
|
148 |
+
return $context->make_execute_lock_context()->with_source( $this );
|
149 |
+
}
|
150 |
+
|
151 |
+
if ( $this->get_host() ) {
|
152 |
+
return new Execute_Lock\Host_Context( $this, $this->get_host() );
|
153 |
+
}
|
154 |
+
|
155 |
+
if ( $this->get_user_id() ) {
|
156 |
+
return new Execute_Lock\User_Context( $this, $this->get_user_id() );
|
157 |
+
}
|
158 |
+
|
159 |
+
if ( $this->get_username() ) {
|
160 |
+
return new Execute_Lock\Username_Context( $this, $this->get_username() );
|
161 |
+
}
|
162 |
+
|
163 |
+
throw new \UnexpectedValueException( __( 'Unable to generate context for lockout.', 'better-wp-security' ) );
|
164 |
+
}
|
165 |
+
|
166 |
+
public function get_source_slug() {
|
167 |
+
return 'lockout-' . $this->id;
|
168 |
+
}
|
169 |
+
}
|
core/lib/lockout/class-user-context.php
ADDED
@@ -0,0 +1,33 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
namespace iThemesSecurity\Lib\Lockout;
|
4 |
+
|
5 |
+
final class User_Context extends Context {
|
6 |
+
|
7 |
+
/** @var int */
|
8 |
+
private $user_id;
|
9 |
+
|
10 |
+
/**
|
11 |
+
* ITSEC_User_Lockout_Context constructor.
|
12 |
+
*
|
13 |
+
* @param string $lockout_module
|
14 |
+
* @param int $user_id
|
15 |
+
*/
|
16 |
+
public function __construct( $lockout_module, $user_id ) {
|
17 |
+
parent::__construct( $lockout_module );
|
18 |
+
$this->user_id = $user_id;
|
19 |
+
}
|
20 |
+
|
21 |
+
/**
|
22 |
+
* Get the user ID that was locked out.
|
23 |
+
*
|
24 |
+
* @return int
|
25 |
+
*/
|
26 |
+
public function get_user_id() {
|
27 |
+
return $this->user_id;
|
28 |
+
}
|
29 |
+
|
30 |
+
public function make_execute_lock_context() {
|
31 |
+
return new Execute_Lock\User_Context( $this, $this->get_user_id() );
|
32 |
+
}
|
33 |
+
}
|
core/lib/lockout/class-username-context.php
ADDED
@@ -0,0 +1,33 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
namespace iThemesSecurity\Lib\Lockout;
|
4 |
+
|
5 |
+
final class Username_Context extends Context {
|
6 |
+
|
7 |
+
/** @var string */
|
8 |
+
private $username;
|
9 |
+
|
10 |
+
/**
|
11 |
+
* ITSEC_Username_Lockout_Context constructor.
|
12 |
+
*
|
13 |
+
* @param string $lockout_module
|
14 |
+
* @param string $username
|
15 |
+
*/
|
16 |
+
public function __construct( $lockout_module, $username ) {
|
17 |
+
parent::__construct( $lockout_module );
|
18 |
+
$this->username = $username;
|
19 |
+
}
|
20 |
+
|
21 |
+
/**
|
22 |
+
* Get the username that was locked out.
|
23 |
+
*
|
24 |
+
* @return string
|
25 |
+
*/
|
26 |
+
public function get_username() {
|
27 |
+
return $this->username;
|
28 |
+
}
|
29 |
+
|
30 |
+
public function make_execute_lock_context() {
|
31 |
+
return new Execute_Lock\Username_Context( $this, $this->get_username() );
|
32 |
+
}
|
33 |
+
}
|
core/lib/lockout/execute-lock/abstract-context.php
ADDED
@@ -0,0 +1,83 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
namespace iThemesSecurity\Lib\Lockout\Execute_Lock;
|
4 |
+
|
5 |
+
use ArrayAccess;
|
6 |
+
use ArrayIterator;
|
7 |
+
use IteratorAggregate;
|
8 |
+
use iThemesSecurity\Lib\Lockout\Execute_Lock\Source\Source;
|
9 |
+
|
10 |
+
abstract class Context implements ArrayAccess, IteratorAggregate {
|
11 |
+
|
12 |
+
/** @var Source */
|
13 |
+
private $source;
|
14 |
+
|
15 |
+
/**
|
16 |
+
* ITSEC_Execute_Lock_Context constructor.
|
17 |
+
*
|
18 |
+
* @param Source $source
|
19 |
+
*/
|
20 |
+
public function __construct( Source $source ) { $this->source = $source; }
|
21 |
+
|
22 |
+
/**
|
23 |
+
* Get the source that caused the execute lock.
|
24 |
+
*
|
25 |
+
* @return Source
|
26 |
+
*/
|
27 |
+
public function get_source() {
|
28 |
+
return $this->source;
|
29 |
+
}
|
30 |
+
|
31 |
+
/**
|
32 |
+
* Create a copy of this context with a different source.
|
33 |
+
*
|
34 |
+
* @param Source $source
|
35 |
+
*
|
36 |
+
* @return $this
|
37 |
+
*/
|
38 |
+
public function with_source( Source $source ) {
|
39 |
+
$self = clone $this;
|
40 |
+
$self->source = $source;
|
41 |
+
|
42 |
+
return $self;
|
43 |
+
}
|
44 |
+
|
45 |
+
/**
|
46 |
+
* Return the context as an array.
|
47 |
+
*
|
48 |
+
* @return array
|
49 |
+
*/
|
50 |
+
public function to_legacy() {
|
51 |
+
return array(
|
52 |
+
'type' => $this->get_source()->get_source_slug(),
|
53 |
+
);
|
54 |
+
}
|
55 |
+
|
56 |
+
public function getIterator() {
|
57 |
+
return new ArrayIterator( $this->to_legacy() );
|
58 |
+
}
|
59 |
+
|
60 |
+
public function offsetExists( $offset ) {
|
61 |
+
$legacy = $this->to_legacy();
|
62 |
+
|
63 |
+
return isset( $legacy[ $offset ] );
|
64 |
+
}
|
65 |
+
|
66 |
+
public function offsetGet( $offset ) {
|
67 |
+
$legacy = $this->to_legacy();
|
68 |
+
|
69 |
+
return $legacy[ $offset ];
|
70 |
+
}
|
71 |
+
|
72 |
+
public function offsetSet( $offset, $value ) {
|
73 |
+
// no-op
|
74 |
+
}
|
75 |
+
|
76 |
+
public function offsetUnset( $offset ) {
|
77 |
+
// no-op
|
78 |
+
}
|
79 |
+
}
|
80 |
+
|
81 |
+
require_once( __DIR__ . '/class-host-context.php' );
|
82 |
+
require_once( __DIR__ . '/class-user-context.php' );
|
83 |
+
require_once( __DIR__ . '/class-username-context.php' );
|
core/lib/lockout/execute-lock/class-host-context.php
ADDED
@@ -0,0 +1,115 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
namespace iThemesSecurity\Lib\Lockout\Execute_Lock;
|
4 |
+
|
5 |
+
use iThemesSecurity\Lib\Lockout\Execute_Lock\Source\Source;
|
6 |
+
|
7 |
+
final class Host_Context extends Context {
|
8 |
+
|
9 |
+
/** @var string */
|
10 |
+
private $host;
|
11 |
+
|
12 |
+
/** @var bool */
|
13 |
+
private $network_brute_force;
|
14 |
+
|
15 |
+
/** @var int */
|
16 |
+
private $login_user_id;
|
17 |
+
|
18 |
+
/** @var string */
|
19 |
+
private $login_username;
|
20 |
+
|
21 |
+
/**
|
22 |
+
* ITSEC_Execute_Host_Lockout_Context constructor.
|
23 |
+
*
|
24 |
+
* @param Source $source
|
25 |
+
* @param string $host
|
26 |
+
*/
|
27 |
+
public function __construct( Source $source, $host ) {
|
28 |
+
parent::__construct( $source );
|
29 |
+
$this->host = $host;
|
30 |
+
}
|
31 |
+
|
32 |
+
/**
|
33 |
+
* Get the host being locked out.
|
34 |
+
*
|
35 |
+
* @return string
|
36 |
+
*/
|
37 |
+
public function get_host() {
|
38 |
+
return $this->host;
|
39 |
+
}
|
40 |
+
|
41 |
+
/**
|
42 |
+
* Get whether this is a Network Brute Force generated lockout.
|
43 |
+
*
|
44 |
+
* @return bool
|
45 |
+
*/
|
46 |
+
public function is_network_brute_force() {
|
47 |
+
return $this->network_brute_force;
|
48 |
+
}
|
49 |
+
|
50 |
+
/**
|
51 |
+
* Set that this is a Network Brute Force generated lockout.
|
52 |
+
*
|
53 |
+
* @return $this
|
54 |
+
*/
|
55 |
+
public function set_network_brute_force() {
|
56 |
+
$this->network_brute_force = true;
|
57 |
+
|
58 |
+
return $this;
|
59 |
+
}
|
60 |
+
|
61 |
+
/**
|
62 |
+
* If this lockout occurred while trying to login as a user,
|
63 |
+
* this will return the user ID that was trying to be logged-in-to.
|
64 |
+
*
|
65 |
+
* @return int|null
|
66 |
+
*/
|
67 |
+
public function get_login_user_id() {
|
68 |
+
return $this->login_user_id;
|
69 |
+
}
|
70 |
+
|
71 |
+
/**
|
72 |
+
* Set who is being logging in as.
|
73 |
+
*
|
74 |
+
* @param int $user_id
|
75 |
+
*
|
76 |
+
* @return $this
|
77 |
+
*/
|
78 |
+
public function set_login_user_id( $user_id ) {
|
79 |
+
$this->login_user_id = $user_id;
|
80 |
+
|
81 |
+
return $this;
|
82 |
+
}
|
83 |
+
|
84 |
+
/**
|
85 |
+
* If this lockout occurred while trying to login to a non-existent user,
|
86 |
+
* this will return that username.
|
87 |
+
*
|
88 |
+
* @return string
|
89 |
+
*/
|
90 |
+
public function get_login_username() {
|
91 |
+
return $this->login_username;
|
92 |
+
}
|
93 |
+
|
94 |
+
/**
|
95 |
+
* Set which username is being logged in as.
|
96 |
+
*
|
97 |
+
* @param string $login_username
|
98 |
+
*
|
99 |
+
* @return Host_Context
|
100 |
+
*/
|
101 |
+
public function set_login_username( $login_username ) {
|
102 |
+
$this->login_username = $login_username;
|
103 |
+
|
104 |
+
return $this;
|
105 |
+
}
|
106 |
+
|
107 |
+
public function to_legacy() {
|
108 |
+
$legacy = parent::to_legacy();
|
109 |
+
|
110 |
+
$legacy['host'] = $this->get_host();
|
111 |
+
$legacy['network_lock'] = $this->is_network_brute_force();
|
112 |
+
|
113 |
+
return $legacy;
|
114 |
+
}
|
115 |
+
}
|
core/lib/lockout/execute-lock/class-user-context.php
ADDED
@@ -0,0 +1,40 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
namespace iThemesSecurity\Lib\Lockout\Execute_Lock;
|
4 |
+
|
5 |
+
use iThemesSecurity\Lib\Lockout\Execute_Lock\Source\Source;
|
6 |
+
|
7 |
+
final class User_Context extends Context {
|
8 |
+
|
9 |
+
/** @var int */
|
10 |
+
private $user_id;
|
11 |
+
|
12 |
+
/**
|
13 |
+
* ITSEC_Execute_User_Lockout_Context constructor.
|
14 |
+
*
|
15 |
+
* @param Source $source
|
16 |
+
* @param int $user_id
|
17 |
+
*/
|
18 |
+
public function __construct( Source $source, $user_id ) {
|
19 |
+
parent::__construct( $source );
|
20 |
+
$this->user_id = $user_id;
|
21 |
+
}
|
22 |
+
|
23 |
+
/**
|
24 |
+
* Get the user ID that was locked out.
|
25 |
+
*
|
26 |
+
* @return int
|
27 |
+
*/
|
28 |
+
public function get_user_id() {
|
29 |
+
return $this->user_id;
|
30 |
+
}
|
31 |
+
|
32 |
+
public function to_legacy() {
|
33 |
+
$legacy = parent::to_legacy();
|
34 |
+
|
35 |
+
$legacy['user'] = $this->get_user_id();
|
36 |
+
$legacy['user_lock'] = true;
|
37 |
+
|
38 |
+
return $legacy;
|
39 |
+
}
|
40 |
+
}
|
core/lib/lockout/execute-lock/class-username-context.php
ADDED
@@ -0,0 +1,40 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
namespace iThemesSecurity\Lib\Lockout\Execute_Lock;
|
4 |
+
|
5 |
+
use iThemesSecurity\Lib\Lockout\Execute_Lock\Source\Source;
|
6 |
+
|
7 |
+
final class Username_Context extends Context {
|
8 |
+
|
9 |
+
/** @var string */
|
10 |
+
private $username;
|
11 |
+
|
12 |
+
/**
|
13 |
+
* ITSEC_Execute_Username_Lockout_Context constructor.
|
14 |
+
*
|
15 |
+
* @param Source $source
|
16 |
+
* @param string $username
|
17 |
+
*/
|
18 |
+
public function __construct( Source $source, $username ) {
|
19 |
+
parent::__construct( $source );
|
20 |
+
$this->username = $username;
|
21 |
+
}
|
22 |
+
|
23 |
+
/**
|
24 |
+
* Get the username that was locked out.
|
25 |
+
*
|
26 |
+
* @return string
|
27 |
+
*/
|
28 |
+
public function get_username() {
|
29 |
+
return $this->username;
|
30 |
+
}
|
31 |
+
|
32 |
+
public function to_legacy() {
|
33 |
+
$legacy = parent::to_legacy();
|
34 |
+
|
35 |
+
$legacy['username'] = $this->get_username();
|
36 |
+
$legacy['user_lock'] = true;
|
37 |
+
|
38 |
+
return $legacy;
|
39 |
+
}
|
40 |
+
}
|
core/lib/lockout/execute-lock/index.php
ADDED
@@ -0,0 +1 @@
|
|
|
1 |
+
<?php // Silence is golden.
|
core/lib/lockout/execute-lock/source/class-configurable.php
ADDED
@@ -0,0 +1,20 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
namespace iThemesSecurity\Lib\Lockout\Execute_Lock\Source;
|
4 |
+
|
5 |
+
class Configurable implements Source {
|
6 |
+
|
7 |
+
/** @var string */
|
8 |
+
private $slug;
|
9 |
+
|
10 |
+
/**
|
11 |
+
* ITSEC_Execute_Lock_Source_Configurable constructor.
|
12 |
+
*
|
13 |
+
* @param string $slug
|
14 |
+
*/
|
15 |
+
public function __construct( $slug ) { $this->slug = $slug; }
|
16 |
+
|
17 |
+
public function get_source_slug() {
|
18 |
+
return $this->slug;
|
19 |
+
}
|
20 |
+
}
|
core/lib/lockout/execute-lock/source/class-lockout-module.php
ADDED
@@ -0,0 +1,20 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
namespace iThemesSecurity\Lib\Lockout\Execute_Lock\Source;
|
4 |
+
|
5 |
+
class Lockout_Module implements Source {
|
6 |
+
|
7 |
+
/** @var string */
|
8 |
+
private $module_slug;
|
9 |
+
|
10 |
+
/**
|
11 |
+
* Lockout_Module constructor.
|
12 |
+
*
|
13 |
+
* @param string $module_slug
|
14 |
+
*/
|
15 |
+
public function __construct( $module_slug ) { $this->module_slug = $module_slug; }
|
16 |
+
|
17 |
+
public function get_source_slug() {
|
18 |
+
return $this->module_slug;
|
19 |
+
}
|
20 |
+
}
|
core/lib/lockout/execute-lock/source/index.php
ADDED
@@ -0,0 +1 @@
|
|
|
1 |
+
<?php // Silence is golden.
|
core/lib/lockout/execute-lock/source/interface-source.php
ADDED
@@ -0,0 +1,16 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
namespace iThemesSecurity\Lib\Lockout\Execute_Lock\Source;
|
4 |
+
|
5 |
+
require_once( __DIR__ . '/class-lockout-module.php' );
|
6 |
+
require_once( __DIR__ . '/class-configurable.php' );
|
7 |
+
|
8 |
+
interface Source {
|
9 |
+
|
10 |
+
/**
|
11 |
+
* Get a unique slug for the lock source.
|
12 |
+
*
|
13 |
+
* @return string
|
14 |
+
*/
|
15 |
+
public function get_source_slug();
|
16 |
+
}
|
core/lib/lockout/index.php
ADDED
@@ -0,0 +1 @@
|
|
|
1 |
+
<?php // Silence is golden.
|
core/lib/login-interstitial/abstract-itsec-login-interstitial.php
CHANGED
@@ -34,7 +34,7 @@ abstract class ITSEC_Login_Interstitial {
|
|
34 |
* @param ITSEC_Login_Interstitial_Session $session
|
35 |
* @param array $args
|
36 |
*
|
37 |
-
* @return
|
38 |
*/
|
39 |
abstract public function render( ITSEC_Login_Interstitial_Session $session, array $args );
|
40 |
|
@@ -70,8 +70,14 @@ abstract class ITSEC_Login_Interstitial {
|
|
70 |
*
|
71 |
* @param ITSEC_Login_Interstitial_Session $session
|
72 |
* @param array $data
|
|
|
|
|
73 |
*/
|
74 |
-
public function submit( ITSEC_Login_Interstitial_Session $session, array $data ) {
|
|
|
|
|
|
|
|
|
75 |
|
76 |
/**
|
77 |
* Does the interstitial have async GET actions.
|
34 |
* @param ITSEC_Login_Interstitial_Session $session
|
35 |
* @param array $args
|
36 |
*
|
37 |
+
* @return void
|
38 |
*/
|
39 |
abstract public function render( ITSEC_Login_Interstitial_Session $session, array $args );
|
40 |
|
70 |
*
|
71 |
* @param ITSEC_Login_Interstitial_Session $session
|
72 |
* @param array $data
|
73 |
+
*
|
74 |
+
* @return WP_Error|null
|
75 |
*/
|
76 |
+
public function submit( ITSEC_Login_Interstitial_Session $session, array $data ) {
|
77 |
+
_doing_it_wrong( __METHOD__, 'Must override ::submit if has submit handler.', '5.3.0' );
|
78 |
+
|
79 |
+
return new WP_Error( 'internal_server_error', 'Internal Server Error' );
|
80 |
+
}
|
81 |
|
82 |
/**
|
83 |
* Does the interstitial have async GET actions.
|
core/lib/login-interstitial/class-itsec-login-interstitial-session.php
CHANGED
@@ -386,12 +386,41 @@ class ITSEC_Login_Interstitial_Session {
|
|
386 |
return $this->user;
|
387 |
}
|
388 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
389 |
/**
|
390 |
* Save the session.
|
391 |
*
|
392 |
* @return bool
|
393 |
*/
|
394 |
public function save() {
|
|
|
|
|
|
|
|
|
|
|
|
|
395 |
return update_metadata_by_mid( 'user', $this->get_id(), $this->data, self::META_KEY );
|
396 |
}
|
397 |
|
@@ -409,9 +438,28 @@ class ITSEC_Login_Interstitial_Session {
|
|
409 |
}
|
410 |
}
|
411 |
|
|
|
|
|
|
|
|
|
412 |
return $deleted;
|
413 |
}
|
414 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
415 |
/**
|
416 |
* Create a new state session.
|
417 |
*
|
@@ -421,6 +469,10 @@ class ITSEC_Login_Interstitial_Session {
|
|
421 |
* @return ITSEC_Login_Interstitial_Session|WP_Error
|
422 |
*/
|
423 |
public static function create( WP_User $user, $current = '' ) {
|
|
|
|
|
|
|
|
|
424 |
|
425 |
$data = array(
|
426 |
'uuid' => wp_generate_uuid4(),
|
@@ -432,13 +484,20 @@ class ITSEC_Login_Interstitial_Session {
|
|
432 |
'remember_me' => false,
|
433 |
'interim_login' => false,
|
434 |
'state' => array(),
|
|
|
435 |
);
|
436 |
|
437 |
if ( ! $mid = add_user_meta( $user->ID, self::META_KEY, $data ) ) {
|
438 |
-
|
|
|
|
|
|
|
439 |
}
|
440 |
|
441 |
-
|
|
|
|
|
|
|
442 |
}
|
443 |
|
444 |
/**
|
386 |
return $this->user;
|
387 |
}
|
388 |
|
389 |
+
/**
|
390 |
+
* Build up the interstitial session configuration from the global state.
|
391 |
+
*
|
392 |
+
* This does not set the show afters because this is also used by the show after code.
|
393 |
+
*/
|
394 |
+
public function initialize_from_global_state() {
|
395 |
+
if ( isset( $_REQUEST['interim-login'] ) ) {
|
396 |
+
$this->set_interim_login();
|
397 |
+
}
|
398 |
+
|
399 |
+
if ( ! empty( $_REQUEST['redirect_to'] ) ) {
|
400 |
+
$this->set_redirect_to( $_REQUEST['redirect_to'] );
|
401 |
+
} elseif ( ! did_action( 'login_init' ) && ( $ref = wp_get_referer() ) ) {
|
402 |
+
$this->set_redirect_to( $ref );
|
403 |
+
} elseif ( ! did_action( 'login_init' ) ) {
|
404 |
+
$this->set_redirect_to( $_SERVER['REQUEST_URI'] );
|
405 |
+
}
|
406 |
+
|
407 |
+
if ( ! empty( $_REQUEST['rememberme'] ) ) {
|
408 |
+
$this->set_remember_me();
|
409 |
+
}
|
410 |
+
}
|
411 |
+
|
412 |
/**
|
413 |
* Save the session.
|
414 |
*
|
415 |
* @return bool
|
416 |
*/
|
417 |
public function save() {
|
418 |
+
$this->log( 'save', [
|
419 |
+
'current' => $this->get_current_interstitial(),
|
420 |
+
'completed' => $this->get_completed_interstitials(),
|
421 |
+
'show_after' => $this->get_show_after(),
|
422 |
+
] );
|
423 |
+
|
424 |
return update_metadata_by_mid( 'user', $this->get_id(), $this->data, self::META_KEY );
|
425 |
}
|
426 |
|
438 |
}
|
439 |
}
|
440 |
|
441 |
+
if ( ! empty( $this->data['log'] ) ) {
|
442 |
+
ITSEC_Log::add_process_stop( $this->data['log'] );
|
443 |
+
}
|
444 |
+
|
445 |
return $deleted;
|
446 |
}
|
447 |
|
448 |
+
/**
|
449 |
+
* Log an update to this interstitial.
|
450 |
+
*
|
451 |
+
* @param string $code
|
452 |
+
* @param mixed|false $data
|
453 |
+
* @param array|false $overrides
|
454 |
+
*/
|
455 |
+
protected function log( $code, $data = false, $overrides = array() ) {
|
456 |
+
if ( ! empty( $this->data['log'] ) ) {
|
457 |
+
$reference = $this->data['log'];
|
458 |
+
$reference['code'] = $code;
|
459 |
+
ITSEC_Log::add_process_update( $reference, $data, $overrides );
|
460 |
+
}
|
461 |
+
}
|
462 |
+
|
463 |
/**
|
464 |
* Create a new state session.
|
465 |
*
|
469 |
* @return ITSEC_Login_Interstitial_Session|WP_Error
|
470 |
*/
|
471 |
public static function create( WP_User $user, $current = '' ) {
|
472 |
+
$log = ITSEC_Log::add_process_start( 'login-interstitial', 'create', [
|
473 |
+
'current' => $current,
|
474 |
+
'_server' => $_SERVER,
|
475 |
+
], [ 'user_id' => $user->ID ] );
|
476 |
|
477 |
$data = array(
|
478 |
'uuid' => wp_generate_uuid4(),
|
484 |
'remember_me' => false,
|
485 |
'interim_login' => false,
|
486 |
'state' => array(),
|
487 |
+
'log' => $log,
|
488 |
);
|
489 |
|
490 |
if ( ! $mid = add_user_meta( $user->ID, self::META_KEY, $data ) ) {
|
491 |
+
$error = new WP_Error( 'itsec-lib-login-interstitial-save-failed', esc_html__( 'Failed to create interstitial state.', 'better-wp-security' ) );
|
492 |
+
ITSEC_Log::add_process_stop( $log, $error );
|
493 |
+
|
494 |
+
return $error;
|
495 |
}
|
496 |
|
497 |
+
$session = new self( $user, $mid, $data );
|
498 |
+
$session->log( 'created', $mid );
|
499 |
+
|
500 |
+
return $session;
|
501 |
}
|
502 |
|
503 |
/**
|
core/lib/mail-templates/magic-link.html
ADDED
@@ -0,0 +1,991 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<style type="text/css">
|
2 |
+
p{
|
3 |
+
margin:10px 0;
|
4 |
+
padding:0;
|
5 |
+
}
|
6 |
+
table{
|
7 |
+
border-collapse:collapse;
|
8 |
+
}
|
9 |
+
h1,h2,h3,h4,h5,h6{
|
10 |
+
display:block;
|
11 |
+
margin:0;
|
12 |
+
padding:0;
|
13 |
+
}
|
14 |
+
img,a img{
|
15 |
+
border:0;
|
16 |
+
height:auto;
|
17 |
+
outline:none;
|
18 |
+
text-decoration:none;
|
19 |
+
}
|
20 |
+
body,#bodyTable,#bodyCell{
|
21 |
+
height:100%;
|
22 |
+
margin:0;
|
23 |
+
padding:0;
|
24 |
+
width:100%;
|
25 |
+
}
|
26 |
+
.mcnPreviewText{
|
27 |
+
display:none !important;
|
28 |
+
}
|
29 |
+
#outlook a{
|
30 |
+
padding:0;
|
31 |
+
}
|
32 |
+
img{
|
33 |
+
-ms-interpolation-mode:bicubic;
|
34 |
+
}
|
35 |
+
table{
|
36 |
+
mso-table-lspace:0pt;
|
37 |
+
mso-table-rspace:0pt;
|
38 |
+
}
|
39 |
+
.ReadMsgBody{
|
40 |
+
width:100%;
|
41 |
+
}
|
42 |
+
.ExternalClass{
|
43 |
+
width:100%;
|
44 |
+
}
|
45 |
+
p,a,li,td,blockquote{
|
46 |
+
mso-line-height-rule:exactly;
|
47 |
+
}
|
48 |
+
a[href^=tel],a[href^=sms]{
|
49 |
+
color:inherit;
|
50 |
+
cursor:default;
|
51 |
+
text-decoration:none;
|
52 |
+
}
|
53 |
+
p,a,li,td,body,table,blockquote{
|
54 |
+
-ms-text-size-adjust:100%;
|
55 |
+
-webkit-text-size-adjust:100%;
|
56 |
+
}
|
57 |
+
.ExternalClass,.ExternalClass p,.ExternalClass td,.ExternalClass div,.ExternalClass span,.ExternalClass font{
|
58 |
+
line-height:100%;
|
59 |
+
}
|
60 |
+
a[x-apple-data-detectors]{
|
61 |
+
color:inherit !important;
|
62 |
+
text-decoration:none !important;
|
63 |
+
font-size:inherit !important;
|
64 |
+
font-family:inherit !important;
|
65 |
+
font-weight:inherit !important;
|
66 |
+
line-height:inherit !important;
|
67 |
+
}
|
68 |
+
.templateContainer{
|
69 |
+
max-width:600px !important;
|
70 |
+
}
|
71 |
+
a.mcnButton{
|
72 |
+
display:block;
|
73 |
+
}
|
74 |
+
.mcnImage,.mcnRetinaImage{
|
75 |
+
vertical-align:bottom;
|
76 |
+
}
|
77 |
+
.mcnTextContent{
|
78 |
+
word-break:break-word;
|
79 |
+
}
|
80 |
+
.mcnTextContent img{
|
81 |
+
height:auto !important;
|
82 |
+
}
|
83 |
+
.mcnDividerBlock{
|
84 |
+
table-layout:fixed !important;
|
85 |
+
}
|
86 |
+
/*
|
87 |
+
@tab Page
|
88 |
+
@section Background Style
|
89 |
+
@tip Set the background color and top border for your email. You may want to choose colors that match your company's branding.
|
90 |
+
*/
|
91 |
+
body,#bodyTable{
|
92 |
+
/*@editable*/background-color:#FAFAFA;
|
93 |
+
}
|
94 |
+
/*
|
95 |
+
@tab Page
|
96 |
+
@section Background Style
|
97 |
+
@tip Set the background color and top border for your email. You may want to choose colors that match your company's branding.
|
98 |
+
*/
|
99 |
+
#bodyCell{
|
100 |
+
/*@editable*/border-top:0;
|
101 |
+
}
|
102 |
+
/*
|
103 |
+
@tab Page
|
104 |
+
@section Heading 1
|
105 |
+
@tip Set the styling for all first-level headings in your emails. These should be the largest of your headings.
|
106 |
+
@style heading 1
|
107 |
+
*/
|
108 |
+
h1{
|
109 |
+
/*@editable*/color:#303946;
|
110 |
+
/*@editable*/font-family:Helvetica;
|
111 |
+
/*@editable*/font-size:33px;
|
112 |
+
/*@editable*/font-style:normal;
|
113 |
+
/*@editable*/font-weight:bold;
|
114 |
+
/*@editable*/line-height:125%;
|
115 |
+
/*@editable*/letter-spacing:normal;
|
116 |
+
/*@editable*/text-align:center;
|
117 |
+
}
|
118 |
+
/*
|
119 |
+
@tab Page
|
120 |
+
@section Heading 2
|
121 |
+
@tip Set the styling for all second-level headings in your emails.
|
122 |
+
@style heading 2
|
123 |
+
*/
|
124 |
+
h2{
|
125 |
+
/*@editable*/color:#333e4c;
|
126 |
+
/*@editable*/font-family:Helvetica;
|
127 |
+
/*@editable*/font-size:24px;
|
128 |
+
/*@editable*/font-style:normal;
|
129 |
+
/*@editable*/font-weight:bold;
|
130 |
+
/*@editable*/line-height:125%;
|
131 |
+
/*@editable*/letter-spacing:normal;
|
132 |
+
/*@editable*/text-align:center;
|
133 |
+
}
|
134 |
+
/*
|
135 |
+
@tab Page
|
136 |
+
@section Heading 3
|
137 |
+
@tip Set the styling for all third-level headings in your emails.
|
138 |
+
@style heading 3
|
139 |
+
*/
|
140 |
+
h3{
|
141 |
+
/*@editable*/color:#ffffff;
|
142 |
+
/*@editable*/font-family:Helvetica;
|
143 |
+
/*@editable*/font-size:30px;
|
144 |
+
/*@editable*/font-style:normal;
|
145 |
+
/*@editable*/font-weight:bold;
|
146 |
+
/*@editable*/line-height:150%;
|
147 |
+
/*@editable*/letter-spacing:normal;
|
148 |
+
/*@editable*/text-align:center;
|
149 |
+
}
|
150 |
+
/*
|
151 |
+
@tab Page
|
152 |
+
@section Heading 4
|
153 |
+
@tip Set the styling for all fourth-level headings in your emails. These should be the smallest of your headings.
|
154 |
+
@style heading 4
|
155 |
+
*/
|
156 |
+
h4{
|
157 |
+
/*@editable*/color:#008cce;
|
158 |
+
/*@editable*/font-family:Helvetica;
|
159 |
+
/*@editable*/font-size:18px;
|
160 |
+
/*@editable*/font-style:normal;
|
161 |
+
/*@editable*/font-weight:bold;
|
162 |
+
/*@editable*/line-height:150%;
|
163 |
+
/*@editable*/letter-spacing:0px;
|
164 |
+
/*@editable*/text-align:center;
|
165 |
+
}
|
166 |
+
/*
|
167 |
+
@tab Preheader
|
168 |
+
@section Preheader Style
|
169 |
+
@tip Set the background color and borders for your email's preheader area.
|
170 |
+
*/
|
171 |
+
#templatePreheader{
|
172 |
+
/*@editable*/background-color:#f8d23a;
|
173 |
+
/*@editable*/background-image:none;
|
174 |
+
/*@editable*/background-repeat:no-repeat;
|
175 |
+
/*@editable*/background-position:center;
|
176 |
+
/*@editable*/background-size:cover;
|
177 |
+
/*@editable*/border-top:0;
|
178 |
+
/*@editable*/border-bottom:0;
|
179 |
+
/*@editable*/padding-top:3px;
|
180 |
+
/*@editable*/padding-bottom:3px;
|
181 |
+
}
|
182 |
+
/*
|
183 |
+
@tab Preheader
|
184 |
+
@section Preheader Text
|
185 |
+
@tip Set the styling for your email's preheader text. Choose a size and color that is easy to read.
|
186 |
+
*/
|
187 |
+
#templatePreheader .mcnTextContent,#templatePreheader .mcnTextContent p{
|
188 |
+
/*@editable*/color:#514d3c;
|
189 |
+
/*@editable*/font-family:Helvetica;
|
190 |
+
/*@editable*/font-size:13px;
|
191 |
+
/*@editable*/line-height:150%;
|
192 |
+
/*@editable*/text-align:left;
|
193 |
+
}
|
194 |
+
/*
|
195 |
+
@tab Preheader
|
196 |
+
@section Preheader Link
|
197 |
+
@tip Set the styling for your email's preheader links. Choose a color that helps them stand out from your text.
|
198 |
+
*/
|
199 |
+
#templatePreheader .mcnTextContent a,#templatePreheader .mcnTextContent p a{
|
200 |
+
/*@editable*/color:#514d3c;
|
201 |
+
/*@editable*/font-weight:bold;
|
202 |
+
/*@editable*/text-decoration:underline;
|
203 |
+
}
|
204 |
+
/*
|
205 |
+
@tab Header
|
206 |
+
@section Header Style
|
207 |
+
@tip Set the background color and borders for your email's header area.
|
208 |
+
*/
|
209 |
+
#templateHeader{
|
210 |
+
/*@editable*/background-color:#ffffff;
|
211 |
+
/*@editable*/background-image:none;
|
212 |
+
/*@editable*/background-repeat:no-repeat;
|
213 |
+
/*@editable*/background-position:center;
|
214 |
+
/*@editable*/background-size:cover;
|
215 |
+
/*@editable*/border-top:0;
|
216 |
+
/*@editable*/border-bottom:0;
|
217 |
+
/*@editable*/padding-top:30px;
|
218 |
+
/*@editable*/padding-bottom:50px;
|
219 |
+
}
|
220 |
+
/*
|
221 |
+
@tab Header
|
222 |
+
@section Header Text
|
223 |
+
@tip Set the styling for your email's header text. Choose a size and color that is easy to read.
|
224 |
+
*/
|
225 |
+
#templateHeader .mcnTextContent,#templateHeader .mcnTextContent p{
|
226 |
+
/*@editable*/color:#505050;
|
227 |
+
/*@editable*/font-family:Helvetica;
|
228 |
+
/*@editable*/font-size:16px;
|
229 |
+
/*@editable*/line-height:150%;
|
230 |
+
/*@editable*/text-align:center;
|
231 |
+
}
|
232 |
+
/*
|
233 |
+
@tab Header
|
234 |
+
@section Header Link
|
235 |
+
@tip Set the styling for your email's header links. Choose a color that helps them stand out from your text.
|
236 |
+
*/
|
237 |
+
#templateHeader .mcnTextContent a,#templateHeader .mcnTextContent p a{
|
238 |
+
/*@editable*/color:#008cce;
|
239 |
+
/*@editable*/font-weight:bold;
|
240 |
+
/*@editable*/text-decoration:none;
|
241 |
+
}
|
242 |
+
/*
|
243 |
+
@tab Upper Body
|
244 |
+
@section Upper Body Style
|
245 |
+
@tip Set the background color and borders for your email's upper body area.
|
246 |
+
*/
|
247 |
+
#templateUpperBody{
|
248 |
+
/*@editable*/background-color:#ffffff;
|
249 |
+
/*@editable*/background-image:none;
|
250 |
+
/*@editable*/background-repeat:no-repeat;
|
251 |
+
/*@editable*/background-position:center;
|
252 |
+
/*@editable*/background-size:cover;
|
253 |
+
/*@editable*/border-top:0;
|
254 |
+
/*@editable*/border-bottom:0;
|
255 |
+
/*@editable*/padding-top:30px;
|
256 |
+
/*@editable*/padding-bottom:10px;
|
257 |
+
}
|
258 |
+
/*
|
259 |
+
@tab Upper Body
|
260 |
+
@section Upper Body Text
|
261 |
+
@tip Set the styling for your email's upper body text. Choose a size and color that is easy to read.
|
262 |
+
*/
|
263 |
+
#templateUpperBody .mcnTextContent,#templateUpperBody .mcnTextContent p{
|
264 |
+
/*@editable*/color:#202020;
|
265 |
+
/*@editable*/font-family:Helvetica;
|
266 |
+
/*@editable*/font-size:16px;
|
267 |
+
/*@editable*/line-height:150%;
|
268 |
+
/*@editable*/text-align:center;
|
269 |
+
}
|
270 |
+
/*
|
271 |
+
@tab Upper Body
|
272 |
+
@section Upper Body Link
|
273 |
+
@tip Set the styling for your email's upper body links. Choose a color that helps them stand out from your text.
|
274 |
+
*/
|
275 |
+
#templateUpperBody .mcnTextContent a,#templateUpperBody .mcnTextContent p a{
|
276 |
+
/*@editable*/color:#008cce;
|
277 |
+
/*@editable*/font-weight:bold;
|
278 |
+
/*@editable*/text-decoration:none;
|
279 |
+
}
|
280 |
+
/*
|
281 |
+
@tab Columns
|
282 |
+
@section Column Style
|
283 |
+
@tip Set the background color and borders for your email's columns.
|
284 |
+
*/
|
285 |
+
#templateColumns{
|
286 |
+
/*@editable*/background-color:#ffffff;
|
287 |
+
/*@editable*/background-image:none;
|
288 |
+
/*@editable*/background-repeat:no-repeat;
|
289 |
+
/*@editable*/background-position:center;
|
290 |
+
/*@editable*/background-size:cover;
|
291 |
+
/*@editable*/border-top:0;
|
292 |
+
/*@editable*/border-bottom:0;
|
293 |
+
/*@editable*/padding-top:30px;
|
294 |
+
/*@editable*/padding-bottom:30px;
|
295 |
+
}
|
296 |
+
/*
|
297 |
+
@tab Columns
|
298 |
+
@section Column Text
|
299 |
+
@tip Set the styling for your email's column text. Choose a size and color that is easy to read.
|
300 |
+
*/
|
301 |
+
#templateColumns .columnContainer .mcnTextContent,#templateColumns .columnContainer .mcnTextContent p{
|
302 |
+
/*@editable*/color:#1c5057;
|
303 |
+
/*@editable*/font-family:Helvetica;
|
304 |
+
/*@editable*/font-size:16px;
|
305 |
+
/*@editable*/line-height:150%;
|
306 |
+
/*@editable*/text-align:left;
|
307 |
+
}
|
308 |
+
/*
|
309 |
+
@tab Columns
|
310 |
+
@section Column Link
|
311 |
+
@tip Set the styling for your email's column links. Choose a color that helps them stand out from your text.
|
312 |
+
*/
|
313 |
+
#templateColumns .columnContainer .mcnTextContent a,#templateColumns .columnContainer .mcnTextContent p a{
|
314 |
+
/*@editable*/color:#008cce;
|
315 |
+
/*@editable*/font-weight:bold;
|
316 |
+
/*@editable*/text-decoration:none;
|
317 |
+
}
|
318 |
+
/*
|
319 |
+
@tab Lower Body
|
320 |
+
@section Lower Body Style
|
321 |
+
@tip Set the background color and borders for your email's lower body area.
|
322 |
+
*/
|
323 |
+
#templateLowerBody{
|
324 |
+
/*@editable*/background-color:#ffffff;
|
325 |
+
/*@editable*/background-image:none;
|
326 |
+
/*@editable*/background-repeat:no-repeat;
|
327 |
+
/*@editable*/background-position:center;
|
328 |
+
/*@editable*/background-size:cover;
|
329 |
+
/*@editable*/border-top:0;
|
330 |
+
/*@editable*/border-bottom:0;
|
331 |
+
/*@editable*/padding-top:50px;
|
332 |
+
/*@editable*/padding-bottom:20px;
|
333 |
+
}
|
334 |
+
/*
|
335 |
+
@tab Lower Body
|
336 |
+
@section Lower Body Text
|
337 |
+
@tip Set the styling for your email's lower body text. Choose a size and color that is easy to read.
|
338 |
+
*/
|
339 |
+
#templateLowerBody .mcnTextContent,#templateLowerBody .mcnTextContent p{
|
340 |
+
/*@editable*/color:#505050;
|
341 |
+
/*@editable*/font-family:Helvetica;
|
342 |
+
/*@editable*/font-size:16px;
|
343 |
+
/*@editable*/line-height:150%;
|
344 |
+
/*@editable*/text-align:center;
|
345 |
+
}
|
346 |
+
/*
|
347 |
+
@tab Lower Body
|
348 |
+
@section Lower Body Link
|
349 |
+
@tip Set the styling for your email's lower body links. Choose a color that helps them stand out from your text.
|
350 |
+
*/
|
351 |
+
#templateLowerBody .mcnTextContent a,#templateLowperBody .mcnTextContent p a{
|
352 |
+
/*@editable*/color:#3fb4eb;
|
353 |
+
/*@editable*/font-weight:bold;
|
354 |
+
/*@editable*/text-decoration:none;
|
355 |
+
}
|
356 |
+
/*
|
357 |
+
@tab Footer
|
358 |
+
@section Footer Style
|
359 |
+
@tip Set the background color and borders for your email's footer area.
|
360 |
+
*/
|
361 |
+
#templateFooter{
|
362 |
+
/*@editable*/background-color:#ffffff;
|
363 |
+
/*@editable*/background-image:none;
|
364 |
+
/*@editable*/background-repeat:no-repeat;
|
365 |
+
/*@editable*/background-position:center;
|
366 |
+
/*@editable*/background-size:cover;
|
367 |
+
/*@editable*/border-top:0;
|
368 |
+
/*@editable*/border-bottom:0;
|
369 |
+
/*@editable*/padding-top:40px;
|
370 |
+
/*@editable*/padding-bottom:9px;
|
371 |
+
}
|
372 |
+
/*
|
373 |
+
@tab Footer
|
374 |
+
@section Footer Text
|
375 |
+
@tip Set the styling for your email's footer text. Choose a size and color that is easy to read.
|
376 |
+
*/
|
377 |
+
#templateFooter .mcnTextContent,#templateFooter .mcnTextContent p{
|
378 |
+
/*@editable*/color:#505050;
|
379 |
+
/*@editable*/font-family:Helvetica;
|
380 |
+
/*@editable*/font-size:16px;
|
381 |
+
/*@editable*/line-height:150%;
|
382 |
+
/*@editable*/text-align:center;
|
383 |
+
}
|
384 |
+
/*
|
385 |
+
@tab Footer
|
386 |
+
@section Footer Link
|
387 |
+
@tip Set the styling for your email's footer links. Choose a color that helps them stand out from your text.
|
388 |
+
*/
|
389 |
+
#templateFooter .mcnTextContent a,#templateFooter .mcnTextContent p a{
|
390 |
+
/*@editable*/color:#c3fdf8;
|
391 |
+
/*@editable*/font-weight:bold;
|
392 |
+
/*@editable*/text-decoration:none;
|
393 |
+
}
|
394 |
+
@media only screen and (min-width:768px){
|
395 |
+
.templateContainer{
|
396 |
+
width:600px !important;
|
397 |
+
}
|
398 |
+
|
399 |
+
} @media only screen and (max-width: 480px){
|
400 |
+
body,table,td,p,a,li,blockquote{
|
401 |
+
-webkit-text-size-adjust:none !important;
|
402 |
+
}
|
403 |
+
|
404 |
+
} @media only screen and (max-width: 480px){
|
405 |
+
body{
|
406 |
+
width:100% !important;
|
407 |
+
min-width:100% !important;
|
408 |
+
}
|
409 |
+
|
410 |
+
} @media only screen and (max-width: 480px){
|
411 |
+
#bodyCell{
|
412 |
+
padding-top:10px !important;
|
413 |
+
}
|
414 |
+
|
415 |
+
} @media only screen and (max-width: 480px){
|
416 |
+
.columnWrapper{
|
417 |
+
max-width:100% !important;
|
418 |
+
width:100% !important;
|
419 |
+
}
|
420 |
+
|
421 |
+
} @media only screen and (max-width: 480px){
|
422 |
+
.mcnRetinaImage{
|
423 |
+
max-width:100% !important;
|
424 |
+
}
|
425 |
+
|
426 |
+
} @media only screen and (max-width: 480px){
|
427 |
+
.mcnImage{
|
428 |
+
width:100% !important;
|
429 |
+
}
|
430 |
+
|
431 |
+
} @media only screen and (max-width: 480px){
|
432 |
+
.mcnCartContainer,.mcnCaptionTopContent,.mcnRecContentContainer,.mcnCaptionBottomContent,.mcnTextContentContainer,.mcnBoxedTextContentContainer,.mcnImageGroupContentContainer,.mcnCaptionLeftTextContentContainer,.mcnCaptionRightTextContentContainer,.mcnCaptionLeftImageContentContainer,.mcnCaptionRightImageContentContainer,.mcnImageCardLeftTextContentContainer,.mcnImageCardRightTextContentContainer,.mcnImageCardLeftImageContentContainer,.mcnImageCardRightImageContentContainer{
|
433 |
+
max-width:100% !important;
|
434 |
+
width:100% !important;
|
435 |
+
}
|
436 |
+
|
437 |
+
} @media only screen and (max-width: 480px){
|
438 |
+
.mcnBoxedTextContentContainer{
|
439 |
+
min-width:100% !important;
|
440 |
+
}
|
441 |
+
|
442 |
+
} @media only screen and (max-width: 480px){
|
443 |
+
.mcnImageGroupContent{
|
444 |
+
padding:9px !important;
|
445 |
+
}
|
446 |
+
|
447 |
+
} @media only screen and (max-width: 480px){
|
448 |
+
.mcnCaptionLeftContentOuter .mcnTextContent,.mcnCaptionRightContentOuter .mcnTextContent{
|
449 |
+
padding-top:9px !important;
|
450 |
+
}
|
451 |
+
|
452 |
+
} @media only screen and (max-width: 480px){
|
453 |
+
.mcnImageCardTopImageContent,.mcnCaptionBottomContent:last-child .mcnCaptionBottomImageContent,.mcnCaptionBlockInner .mcnCaptionTopContent:last-child .mcnTextContent{
|
454 |
+
padding-top:18px !important;
|
455 |
+
}
|
456 |
+
|
457 |
+
} @media only screen and (max-width: 480px){
|
458 |
+
.mcnImageCardBottomImageContent{
|
459 |
+
padding-bottom:9px !important;
|
460 |
+
}
|
461 |
+
|
462 |
+
} @media only screen and (max-width: 480px){
|
463 |
+
.mcnImageGroupBlockInner{
|
464 |
+
padding-top:0 !important;
|
465 |
+
padding-bottom:0 !important;
|
466 |
+
}
|
467 |
+
|
468 |
+
} @media only screen and (max-width: 480px){
|
469 |
+
.mcnImageGroupBlockOuter{
|
470 |
+
padding-top:9px !important;
|
471 |
+
padding-bottom:9px !important;
|
472 |
+
}
|
473 |
+
|
474 |
+
} @media only screen and (max-width: 480px){
|
475 |
+
.mcnTextContent,.mcnBoxedTextContentColumn{
|
476 |
+
padding-right:18px !important;
|
477 |
+
padding-left:18px !important;
|
478 |
+
}
|
479 |
+
|
480 |
+
} @media only screen and (max-width: 480px){
|
481 |
+
.mcnImageCardLeftImageContent,.mcnImageCardRightImageContent{
|
482 |
+
padding-right:18px !important;
|
483 |
+
padding-bottom:0 !important;
|
484 |
+
padding-left:18px !important;
|
485 |
+
}
|
486 |
+
|
487 |
+
} @media only screen and (max-width: 480px){
|
488 |
+
.mcpreview-image-uploader{
|
489 |
+
display:none !important;
|
490 |
+
width:100% !important;
|
491 |
+
}
|
492 |
+
|
493 |
+
} @media only screen and (max-width: 480px){
|
494 |
+
/*
|
495 |
+
@tab Mobile Styles
|
496 |
+
@section Heading 1
|
497 |
+
@tip Make the first-level headings larger in size for better readability on small screens.
|
498 |
+
*/
|
499 |
+
h1{
|
500 |
+
/*@editable*/font-size:36px !important;
|
501 |
+
/*@editable*/line-height:150% !important;
|
502 |
+
}
|
503 |
+
|
504 |
+
} @media only screen and (max-width: 480px){
|
505 |
+
/*
|
506 |
+
@tab Mobile Styles
|
507 |
+
@section Heading 2
|
508 |
+
@tip Make the second-level headings larger in size for better readability on small screens.
|
509 |
+
*/
|
510 |
+
h2{
|
511 |
+
/*@editable*/font-size:30px !important;
|
512 |
+
/*@editable*/line-height:150% !important;
|
513 |
+
}
|
514 |
+
|
515 |
+
} @media only screen and (max-width: 480px){
|
516 |
+
/*
|
517 |
+
@tab Mobile Styles
|
518 |
+
@section Heading 3
|
519 |
+
@tip Make the third-level headings larger in size for better readability on small screens.
|
520 |
+
*/
|
521 |
+
h3{
|
522 |
+
/*@editable*/font-size:24px !important;
|
523 |
+
/*@editable*/line-height:150% !important;
|
524 |
+
}
|
525 |
+
|
526 |
+
} @media only screen and (max-width: 480px){
|
527 |
+
/*
|
528 |
+
@tab Mobile Styles
|
529 |
+
@section Heading 4
|
530 |
+
@tip Make the fourth-level headings larger in size for better readability on small screens.
|
531 |
+
*/
|
532 |
+
h4{
|
533 |
+
/*@editable*/font-size:16px !important;
|
534 |
+
/*@editable*/line-height:150% !important;
|
535 |
+
}
|
536 |
+
|
537 |
+
} @media only screen and (max-width: 480px){
|
538 |
+
/*
|
539 |
+
@tab Mobile Styles
|
540 |
+
@section Boxed Text
|
541 |
+
@tip Make the boxed text larger in size for better readability on small screens. We recommend a font size of at least 16px.
|
542 |
+
*/
|
543 |
+
.mcnBoxedTextContentContainer .mcnTextContent,.mcnBoxedTextContentContainer .mcnTextContent p{
|
544 |
+
/*@editable*/font-size:12px !important;
|
545 |
+
/*@editable*/line-height:150% !important;
|
546 |
+
}
|
547 |
+
|
548 |
+
} @media only screen and (max-width: 480px){
|
549 |
+
/*
|
550 |
+
@tab Mobile Styles
|
551 |
+
@section Preheader Visibility
|
552 |
+
@tip Set the visibility of the email's preheader on small screens. You can hide it to save space.
|
553 |
+
*/
|
554 |
+
#templatePreheader{
|
555 |
+
/*@editable*/display:none !important;
|
556 |
+
}
|
557 |
+
|
558 |
+
} @media only screen and (max-width: 480px){
|
559 |
+
/*
|
560 |
+
@tab Mobile Styles
|
561 |
+
@section Preheader Text
|
562 |
+
@tip Make the preheader text larger in size for better readability on small screens.
|
563 |
+
*/
|
564 |
+
#templatePreheader .mcnTextContent,#templatePreheader .mcnTextContent p{
|
565 |
+
/*@editable*/font-size:14px !important;
|
566 |
+
/*@editable*/line-height:150% !important;
|
567 |
+
}
|
568 |
+
|
569 |
+
} @media only screen and (max-width: 480px){
|
570 |
+
/*
|
571 |
+
@tab Mobile Styles
|
572 |
+
@section Header Text
|
573 |
+
@tip Make the header text larger in size for better readability on small screens.
|
574 |
+
*/
|
575 |
+
#templateHeader .mcnTextContent,#templateHeader .mcnTextContent p{
|
576 |
+
/*@editable*/font-size:16px !important;
|
577 |
+
/*@editable*/line-height:150% !important;
|
578 |
+
}
|
579 |
+
|
580 |
+
} @media only screen and (max-width: 480px){
|
581 |
+
/*
|
582 |
+
@tab Mobile Styles
|
583 |
+
@section Upper Body Text
|
584 |
+
@tip Make the upper body text larger in size for better readability on small screens. We recommend a font size of at least 16px.
|
585 |
+
*/
|
586 |
+
#templateUpperBody .mcnTextContent,#templateUpperBody .mcnTextContent p{
|
587 |
+
/*@editable*/font-size:16px !important;
|
588 |
+
/*@editable*/line-height:150% !important;
|
589 |
+
}
|
590 |
+
|
591 |
+
} @media only screen and (max-width: 480px){
|
592 |
+
/*
|
593 |
+
@tab Mobile Styles
|
594 |
+
@section Column Text
|
595 |
+
@tip Make the column text larger in size for better readability on small screens. We recommend a font size of at least 16px.
|
596 |
+
*/
|
597 |
+
#templateColumns .columnContainer .mcnTextContent,#templateColumns .columnContainer .mcnTextContent p{
|
598 |
+
/*@editable*/font-size:16px !important;
|
599 |
+
/*@editable*/line-height:150% !important;
|
600 |
+
}
|
601 |
+
|
602 |
+
} @media only screen and (max-width: 480px){
|
603 |
+
/*
|
604 |
+
@tab Mobile Styles
|
605 |
+
@section Lower Body Text
|
606 |
+
@tip Make the lower body text larger in size for better readability on small screens. We recommend a font size of at least 16px.
|
607 |
+
*/
|
608 |
+
#templateLowerBody .mcnTextContent,#templateLowerBody .mcnTextContent p{
|
609 |
+
/*@editable*/font-size:16px !important;
|
610 |
+
/*@editable*/line-height:150% !important;
|
611 |
+
}
|
612 |
+
|
613 |
+
} @media only screen and (max-width: 480px){
|
614 |
+
/*
|
615 |
+
@tab Mobile Styles
|
616 |
+
@section Footer Text
|
617 |
+
@tip Make the footer content text larger in size for better readability on small screens.
|
618 |
+
*/
|
619 |
+
#templateFooter .mcnTextContent,#templateFooter .mcnTextContent p{
|
620 |
+
/*@editable*/font-size:14px !important;
|
621 |
+
/*@editable*/line-height:150% !important;
|
622 |
+
}
|
623 |
+
|
624 |
+
}</style></head>
|
625 |
+
<body>
|
626 |
+
<!--*|IF:MC_PREVIEW_TEXT|*-->
|
627 |
+
<!--[if !gte mso 9]><!----><span class="mcnPreviewText" style="display:none; font-size:0px; line-height:0px; max-height:0px; max-width:0px; opacity:0; overflow:hidden; visibility:hidden; mso-hide:all;">*|MC_PREVIEW_TEXT|*</span><!--<![endif]-->
|
628 |
+
<!--*|END:IF|*-->
|
629 |
+
<center>
|
630 |
+
<table align="center" border="0" cellpadding="0" cellspacing="0" height="100%" width="100%" id="bodyTable">
|
631 |
+
<tr>
|
632 |
+
<td align="center" valign="top" id="bodyCell">
|
633 |
+
<!-- BEGIN TEMPLATE // -->
|
634 |
+
<table border="0" cellpadding="0" cellspacing="0" width="100%">
|
635 |
+
<tr>
|
636 |
+
<td align="center" valign="top" id="templatePreheader">
|
637 |
+
<!--[if (gte mso 9)|(IE)]>
|
638 |
+
<table align="center" border="0" cellspacing="0" cellpadding="0" width="600" style="width:600px;">
|
639 |
+
<tr>
|
640 |
+
<td align="center" valign="top" width="600" style="width:600px;">
|
641 |
+
<![endif]-->
|
642 |
+
<table align="center" border="0" cellpadding="0" cellspacing="0" width="100%" class="templateContainer">
|
643 |
+
<tr>
|
644 |
+
<td valign="top" class="preheaderContainer"><table border="0" cellpadding="0" cellspacing="0" width="100%" class="mcnTextBlock" style="min-width:100%;">
|
645 |
+
<tbody class="mcnTextBlockOuter">
|
646 |
+
<tr>
|
647 |
+
<td valign="top" class="mcnTextBlockInner" style="padding-top:9px;">
|
648 |
+
<!--[if mso]>
|
649 |
+
<table align="left" border="0" cellspacing="0" cellpadding="0" width="100%" style="width:100%;">
|
650 |
+
<tr>
|
651 |
+
<![endif]-->
|
652 |
+
|
653 |
+
<!--[if mso]>
|
654 |
+
<td valign="top" width="600" style="width:600px;">
|
655 |
+
<![endif]-->
|
656 |
+
<table align="left" border="0" cellpadding="0" cellspacing="0" style="max-width:100%; min-width:100%;" width="100%" class="mcnTextContentContainer">
|
657 |
+
<tbody><tr>
|
658 |
+
|
659 |
+
<td valign="top" class="mcnTextContent" style="padding: 0px 18px 9px; text-align: center;">
|
660 |
+
|
661 |
+
|
662 |
+
</td>
|
663 |
+
</tr>
|
664 |
+
</tbody></table>
|
665 |
+
<!--[if mso]>
|
666 |
+
</td>
|
667 |
+
<![endif]-->
|
668 |
+
|
669 |
+
<!--[if mso]>
|
670 |
+
</tr>
|
671 |
+
</table>
|
672 |
+
<![endif]-->
|
673 |
+
</td>
|
674 |
+
</tr>
|
675 |
+
</tbody>
|
676 |
+
</table></td>
|
677 |
+
</tr>
|
678 |
+
</table>
|
679 |
+
<!--[if (gte mso 9)|(IE)]>
|
680 |
+
</td>
|
681 |
+
</tr>
|
682 |
+
</table>
|
683 |
+
<![endif]-->
|
684 |
+
</td>
|
685 |
+
</tr>
|
686 |
+
<tr>
|
687 |
+
<td align="center" valign="top" id="templateHeader">
|
688 |
+
<!--[if (gte mso 9)|(IE)]>
|
689 |
+
<table align="center" border="0" cellspacing="0" cellpadding="0" width="600" style="width:600px;">
|
690 |
+
<tr>
|
691 |
+
<td align="center" valign="top" width="600" style="width:600px;">
|
692 |
+
<![endif]-->
|
693 |
+
<table align="center" border="0" cellpadding="0" cellspacing="0" width="100%" class="templateContainer">
|
694 |
+
<tr>
|
695 |
+
<td valign="top" class="headerContainer"><table border="0" cellpadding="0" cellspacing="0" width="100%" class="mcnDividerBlock" style="min-width:100%;">
|
696 |
+
<tbody class="mcnDividerBlockOuter">
|
697 |
+
<tr>
|
698 |
+
<td class="mcnDividerBlockInner" style="min-width: 100%; padding: 10px 18px 18px;">
|
699 |
+
<table class="mcnDividerContent" border="0" cellpadding="0" cellspacing="0" width="100%" style="min-width:100%;">
|
700 |
+
<tbody><tr>
|
701 |
+
<td>
|
702 |
+
<span></span>
|
703 |
+
</td>
|
704 |
+
</tr>
|
705 |
+
</tbody></table>
|
706 |
+
<!--
|
707 |
+
<td class="mcnDividerBlockInner" style="padding: 18px;">
|
708 |
+
<hr class="mcnDividerContent" style="border-bottom-color:none; border-left-color:none; border-right-color:none; border-bottom-width:0; border-left-width:0; border-right-width:0; margin-top:0; margin-right:0; margin-bottom:0; margin-left:0;" />
|
709 |
+
-->
|
710 |
+
</td>
|
711 |
+
</tr>
|
712 |
+
</tbody>
|
713 |
+
</table><table border="0" cellpadding="0" cellspacing="0" width="100%" class="mcnImageBlock" style="min-width:100%;">
|
714 |
+
<tbody class="mcnImageBlockOuter">
|
715 |
+
<tr>
|
716 |
+
<td valign="top" style="padding:9px" class="mcnImageBlockInner">
|
717 |
+
<table align="left" width="100%" border="0" cellpadding="0" cellspacing="0" class="mcnImageContentContainer" style="min-width:100%;">
|
718 |
+
<tbody><tr>
|
719 |
+
<td class="mcnImageContent" valign="top" style="padding-right: 9px; padding-left: 9px; padding-top: 0; padding-bottom: 0; text-align:center;">
|
720 |
+
|
721 |
+
|
722 |
+
<img align="center" alt="" src="https://gallery.mailchimp.com/7acf83c7a47b32c740ad94a4e/images/35010455-0fe7-4f14-a9c4-253f6579937f.png" width="163" style="max-width:163px; padding-bottom: 0; display: inline !important; vertical-align: bottom;" class="mcnImage">
|
723 |
+
|
724 |
+
|
725 |
+
</td>
|
726 |
+
</tr>
|
727 |
+
</tbody></table>
|
728 |
+
</td>
|
729 |
+
</tr>
|
730 |
+
</tbody>
|
731 |
+
</table><table border="0" cellpadding="0" cellspacing="0" width="100%" class="mcnDividerBlock" style="min-width:100%;">
|
732 |
+
<tbody class="mcnDividerBlockOuter">
|
733 |
+
<tr>
|
734 |
+
<td class="mcnDividerBlockInner" style="min-width: 100%; padding: 10px 18px;">
|
735 |
+
<table class="mcnDividerContent" border="0" cellpadding="0" cellspacing="0" width="100%" style="min-width:100%;">
|
736 |
+
<tbody><tr>
|
737 |
+
<td>
|
738 |
+
<span></span>
|
739 |
+
</td>
|
740 |
+
</tr>
|
741 |
+
</tbody></table>
|
742 |
+
<!--
|
743 |
+
<td class="mcnDividerBlockInner" style="padding: 18px;">
|
744 |
+
<hr class="mcnDividerContent" style="border-bottom-color:none; border-left-color:none; border-right-color:none; border-bottom-width:0; border-left-width:0; border-right-width:0; margin-top:0; margin-right:0; margin-bottom:0; margin-left:0;" />
|
745 |
+
-->
|
746 |
+
</td>
|
747 |
+
</tr>
|
748 |
+
</tbody>
|
749 |
+
</table><table border="0" cellpadding="0" cellspacing="0" width="100%" class="mcnTextBlock" style="min-width:100%;">
|
750 |
+
<tbody class="mcnTextBlockOuter">
|
751 |
+
<tr>
|
752 |
+
<td valign="top" class="mcnTextBlockInner" style="padding-top:9px;">
|
753 |
+
<!--[if mso]>
|
754 |
+
<table align="left" border="0" cellspacing="0" cellpadding="0" width="100%" style="width:100%;">
|
755 |
+
<tr>
|
756 |
+
<![endif]-->
|
757 |
+
|
758 |
+
<!--[if mso]>
|
759 |
+
<td valign="top" width="600" style="width:600px;">
|
760 |
+
<![endif]-->
|
761 |
+
<table align="left" border="0" cellpadding="0" cellspacing="0" style="max-width:100%; min-width:100%;" width="100%" class="mcnTextContentContainer">
|
762 |
+
<tbody><tr>
|
763 |
+
|
764 |
+
<td valign="top" class="mcnTextContent" style="padding-top:0; padding-right:18px; padding-bottom:9px; padding-left:18px;">
|
765 |
+
|
766 |
+
<h1 class="null">Your Magic Link is Here!</h1>
|
767 |
+
|
768 |
+
</td>
|
769 |
+
</tr>
|
770 |
+
</tbody></table>
|
771 |
+
<!--[if mso]>
|
772 |
+
</td>
|
773 |
+
<![endif]-->
|
774 |
+
|
775 |
+
<!--[if mso]>
|
776 |
+
</tr>
|
777 |
+
</table>
|
778 |
+
<![endif]-->
|
779 |
+
</td>
|
780 |
+
</tr>
|
781 |
+
</tbody>
|
782 |
+
</table><table border="0" cellpadding="0" cellspacing="0" width="100%" class="mcnTextBlock" style="min-width:100%;">
|
783 |
+
<tbody class="mcnTextBlockOuter">
|
784 |
+
<tr>
|
785 |
+
<td valign="top" class="mcnTextBlockInner" style="padding-top:9px;">
|
786 |
+
<!--[if mso]>
|
787 |
+
<table align="left" border="0" cellspacing="0" cellpadding="0" width="100%" style="width:100%;">
|
788 |
+
<tr>
|
789 |
+
<![endif]-->
|
790 |
+
|
791 |
+
<!--[if mso]>
|
792 |
+
<td valign="top" width="600" style="width:600px;">
|
793 |
+
<![endif]-->
|
794 |
+
<table align="left" border="0" cellpadding="0" cellspacing="0" style="max-width:100%; min-width:100%;" width="100%" class="mcnTextContentContainer">
|
795 |
+
<tbody><tr>
|
796 |
+
|
797 |
+
<td valign="top" class="mcnTextContent" style="padding-top:0; padding-right:18px; padding-bottom:9px; padding-left:18px;">
|
798 |
+
|
799 |
+
<h4 class="null">Hi Saylor,<br>
|
800 |
+
Click the button to continue logging in.</h4>
|
801 |
+
|
802 |
+
</td>
|
803 |
+
</tr>
|
804 |
+
</tbody></table>
|
805 |
+
<!--[if mso]>
|
806 |
+
</td>
|
807 |
+
<![endif]-->
|
808 |
+
|
809 |
+
<!--[if mso]>
|
810 |
+
</tr>
|
811 |
+
</table>
|
812 |
+
<![endif]-->
|
813 |
+
</td>
|
814 |
+
</tr>
|
815 |
+
</tbody>
|
816 |
+
</table></td>
|
817 |
+
</tr>
|
818 |
+
</table>
|
819 |
+
<!--[if (gte mso 9)|(IE)]>
|
820 |
+
</td>
|
821 |
+
</tr>
|
822 |
+
</table>
|
823 |
+
<![endif]-->
|
824 |
+
</td>
|
825 |
+
</tr>
|
826 |
+
<tr>
|
827 |
+
<td align="center" valign="top" id="templateUpperBody">
|
828 |
+
<!--[if (gte mso 9)|(IE)]>
|
829 |
+
<table align="center" border="0" cellspacing="0" cellpadding="0" width="600" style="width:600px;">
|
830 |
+
<tr>
|
831 |
+
<td align="center" valign="top" width="600" style="width:600px;">
|
832 |
+
<![endif]-->
|
833 |
+
<table align="center" border="0" cellpadding="0" cellspacing="0" width="100%" class="templateContainer">
|
834 |
+
<tr>
|
835 |
+
<td valign="top" class="bodyContainer"><table border="0" cellpadding="0" cellspacing="0" width="100%" class="mcnButtonBlock" style="min-width:100%;">
|
836 |
+
<tbody class="mcnButtonBlockOuter">
|
837 |
+
<tr>
|
838 |
+
<td style="padding-top:0; padding-right:18px; padding-bottom:18px; padding-left:18px;" valign="top" align="center" class="mcnButtonBlockInner">
|
839 |
+
<table border="0" cellpadding="0" cellspacing="0" class="mcnButtonContentContainer" style="border-collapse: separate !important;border-radius: 3px;background-color: #F8D23A;">
|
840 |
+
<tbody>
|
841 |
+
<tr>
|
842 |
+
<td align="center" valign="middle" class="mcnButtonContent" style="font-family: Arial; font-size: 24px; padding: 22px;">
|
843 |
+
<a class="mcnButton " title="Login Now →" href="http://www.ithemes.com" target="_blank" style="font-weight: bold;letter-spacing: normal;line-height: 100%;text-align: center;text-decoration: none;color: #3C3A31;">Login Now →</a>
|
844 |
+
</td>
|
845 |
+
</tr>
|
846 |
+
</tbody>
|
847 |
+
</table>
|
848 |
+
</td>
|
849 |
+
</tr>
|
850 |
+
</tbody>
|
851 |
+
</table></td>
|
852 |
+
</tr>
|
853 |
+
</table>
|
854 |
+
<!--[if (gte mso 9)|(IE)]>
|
855 |
+
</td>
|
856 |
+
</tr>
|
857 |
+
</table>
|
858 |
+
<![endif]-->
|
859 |
+
</td>
|
860 |
+
</tr>
|
861 |
+
<tr>
|
862 |
+
<td align="center" valign="top" id="templateColumns">
|
863 |
+
<table border="0" cellpadding="0" cellspacing="0" width="100%" class="templateContainer">
|
864 |
+
<tr>
|
865 |
+
<td valign="top">
|
866 |
+
<!--[if (gte mso 9)|(IE)]>
|
867 |
+
<table align="center" border="0" cellspacing="0" cellpadding="0" width="600" style="width:600px;">
|
868 |
+
<tr>
|
869 |
+
<td align="center" valign="top" width="300" style="width:300px;">
|
870 |
+
<![endif]-->
|
871 |
+
<table align="left" border="0" cellpadding="0" cellspacing="0" width="300" class="columnWrapper">
|
872 |
+
<tr>
|
873 |
+
<td valign="top" class="columnContainer"></td>
|
874 |
+
</tr>
|
875 |
+
</table>
|
876 |
+
<!--[if (gte mso 9)|(IE)]>
|
877 |
+
</td>
|
878 |
+
<td align="center" valign="top" width="300" style="width:300px;">
|
879 |
+
<![endif]-->
|
880 |
+
<table align="left" border="0" cellpadding="0" cellspacing="0" width="300" class="columnWrapper">
|
881 |
+
<tr>
|
882 |
+
<td valign="top" class="columnContainer"></td>
|
883 |
+
</tr>
|
884 |
+
</table>
|
885 |
+
<!--[if (gte mso 9)|(IE)]>
|
886 |
+
</td>
|
887 |
+
</tr>
|
888 |
+
</table>
|
889 |
+
<![endif]-->
|
890 |
+
</td>
|
891 |
+
</tr>
|
892 |
+
</table>
|
893 |
+
</td>
|
894 |
+
</tr>
|
895 |
+
<tr>
|
896 |
+
<td align="center" valign="top" id="templateLowerBody">
|
897 |
+
<!--[if (gte mso 9)|(IE)]>
|
898 |
+
<table align="center" border="0" cellspacing="0" cellpadding="0" width="600" style="width:600px;">
|
899 |
+
<tr>
|
900 |
+
<td align="center" valign="top" width="600" style="width:600px;">
|
901 |
+
<![endif]-->
|
902 |
+
<table align="center" border="0" cellpadding="0" cellspacing="0" width="100%" class="templateContainer">
|
903 |
+
<tr>
|
904 |
+
<td valign="top" class="bodyContainer"></td>
|
905 |
+
</tr>
|
906 |
+
</table>
|
907 |
+
<!--[if (gte mso 9)|(IE)]>
|
908 |
+
</td>
|
909 |
+
</tr>
|
910 |
+
</table>
|
911 |
+
<![endif]-->
|
912 |
+
</td>
|
913 |
+
</tr>
|
914 |
+
<tr>
|
915 |
+
<td align="center" valign="top" id="templateFooter">
|
916 |
+
<!--[if (gte mso 9)|(IE)]>
|
917 |
+
<table align="center" border="0" cellspacing="0" cellpadding="0" width="600" style="width:600px;">
|
918 |
+
<tr>
|
919 |
+
<td align="center" valign="top" width="600" style="width:600px;">
|
920 |
+
<![endif]-->
|
921 |
+
<table align="center" border="0" cellpadding="0" cellspacing="0" width="100%" class="templateContainer">
|
922 |
+
<tr>
|
923 |
+
<td valign="top" class="footerContainer"><table border="0" cellpadding="0" cellspacing="0" width="100%" class="mcnImageBlock" style="min-width:100%;">
|
924 |
+
<tbody class="mcnImageBlockOuter">
|
925 |
+
<tr>
|
926 |
+
<td valign="top" style="padding:9px" class="mcnImageBlockInner">
|
927 |
+
<table align="left" width="100%" border="0" cellpadding="0" cellspacing="0" class="mcnImageContentContainer" style="min-width:100%;">
|
928 |
+
<tbody><tr>
|
929 |
+
<td class="mcnImageContent" valign="top" style="padding-right: 9px; padding-left: 9px; padding-top: 0; padding-bottom: 0; text-align:center;">
|
930 |
+
|
931 |
+
|
932 |
+
<img align="center" alt="" src="https://gallery.mailchimp.com/7acf83c7a47b32c740ad94a4e/images/39da2582-1fd8-4190-a085-eb45316d4cd9.png" width="50" style="max-width:50px; padding-bottom: 0; display: inline !important; vertical-align: bottom;" class="mcnImage">
|
933 |
+
|
934 |
+
|
935 |
+
</td>
|
936 |
+
</tr>
|
937 |
+
</tbody></table>
|
938 |
+
</td>
|
939 |
+
</tr>
|
940 |
+
</tbody>
|
941 |
+
</table><table border="0" cellpadding="0" cellspacing="0" width="100%" class="mcnTextBlock" style="min-width:100%;">
|
942 |
+
<tbody class="mcnTextBlockOuter">
|
943 |
+
<tr>
|
944 |
+
<td valign="top" class="mcnTextBlockInner" style="padding-top:9px;">
|
945 |
+
<!--[if mso]>
|
946 |
+
<table align="left" border="0" cellspacing="0" cellpadding="0" width="100%" style="width:100%;">
|
947 |
+
<tr>
|
948 |
+
<![endif]-->
|
949 |
+
|
950 |
+
<!--[if mso]>
|
951 |
+
<td valign="top" width="600" style="width:600px;">
|
952 |
+
<![endif]-->
|
953 |
+
<table align="left" border="0" cellpadding="0" cellspacing="0" style="max-width:100%; min-width:100%;" width="100%" class="mcnTextContentContainer">
|
954 |
+
<tbody><tr>
|
955 |
+
|
956 |
+
<td valign="top" class="mcnTextContent" style="padding-top:0; padding-right:18px; padding-bottom:9px; padding-left:18px;">
|
957 |
+
|
958 |
+
<span style="font-size:11px">This email was generated by the iThemes Security plugin on behalf of [website].<br>
|
959 |
+
To unsubscribe from these notifications, please <a data-saferedirecturl="https://www.google.com/url?q=https://saylor.dev.ithemes.com&source=gmail&ust=1564773724048000&usg=AFQjCNG0ZQUquLozba102rtW848UVn3YYQ" href="https://saylor.dev.ithemes.com/" target="_blank"><span style="color:#3366ff">contact the site administrator</span></a><span style="color:#3366ff">.</span></span>
|
960 |
+
</td>
|
961 |
+
</tr>
|
962 |
+
</tbody></table>
|
963 |
+
<!--[if mso]>
|
964 |
+
</td>
|
965 |
+
<![endif]-->
|
966 |
+
|
967 |
+
<!--[if mso]>
|
968 |
+
</tr>
|
969 |
+
</table>
|
970 |
+
<![endif]-->
|
971 |
+
</td>
|
972 |
+
</tr>
|
973 |
+
</tbody>
|
974 |
+
</table></td>
|
975 |
+
</tr>
|
976 |
+
</table>
|
977 |
+
<!--[if (gte mso 9)|(IE)]>
|
978 |
+
</td>
|
979 |
+
</tr>
|
980 |
+
</table>
|
981 |
+
<![endif]-->
|
982 |
+
</td>
|
983 |
+
</tr>
|
984 |
+
</table>
|
985 |
+
<!-- // END TEMPLATE -->
|
986 |
+
</td>
|
987 |
+
</tr>
|
988 |
+
</table>
|
989 |
+
</center>
|
990 |
+
</body>
|
991 |
+
</html>
|
core/lib/schema.php
CHANGED
@@ -51,6 +51,7 @@ CREATE TABLE {$wpdb->base_prefix}itsec_lockouts (
|
|
51 |
lockout_user bigint(20) UNSIGNED,
|
52 |
lockout_username varchar(60),
|
53 |
lockout_active int(1) NOT NULL DEFAULT 1,
|
|
|
54 |
PRIMARY KEY (lockout_id),
|
55 |
KEY lockout_expire_gmt (lockout_expire_gmt),
|
56 |
KEY lockout_host (lockout_host),
|
@@ -114,6 +115,16 @@ CREATE TABLE {$wpdb->base_prefix}itsec_fingerprints (
|
|
114 |
UNIQUE KEY fingerprint_user__hash (fingerprint_user,fingerprint_hash),
|
115 |
UNIQUE KEY fingerprint_uuid (fingerprint_uuid)
|
116 |
) $charset_collate;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
117 |
";
|
118 |
|
119 |
require_once( ABSPATH . 'wp-admin/includes/upgrade.php' );
|
51 |
lockout_user bigint(20) UNSIGNED,
|
52 |
lockout_username varchar(60),
|
53 |
lockout_active int(1) NOT NULL DEFAULT 1,
|
54 |
+
lockout_context TEXT,
|
55 |
PRIMARY KEY (lockout_id),
|
56 |
KEY lockout_expire_gmt (lockout_expire_gmt),
|
57 |
KEY lockout_host (lockout_host),
|
115 |
UNIQUE KEY fingerprint_user__hash (fingerprint_user,fingerprint_hash),
|
116 |
UNIQUE KEY fingerprint_uuid (fingerprint_uuid)
|
117 |
) $charset_collate;
|
118 |
+
|
119 |
+
CREATE TABLE {$wpdb->base_prefix}itsec_opaque_tokens (
|
120 |
+
token_id char(64) NOT NULL,
|
121 |
+
token_hashed char(64) NOT NULL,
|
122 |
+
token_type VARCHAR(32) NOT NULL,
|
123 |
+
token_data TEXT NOT NULL,
|
124 |
+
token_created_at DATETIME NOT NULL,
|
125 |
+
PRIMARY KEY (token_id),
|
126 |
+
UNIQUE KEY token_hashed (token_hashed)
|
127 |
+
) $charset_collate;
|
128 |
";
|
129 |
|
130 |
require_once( ABSPATH . 'wp-admin/includes/upgrade.php' );
|
core/lib/validator.php
CHANGED
@@ -112,7 +112,7 @@ abstract class ITSEC_Validator {
|
|
112 |
}
|
113 |
}
|
114 |
|
115 |
-
final protected function sanitize_setting( $type, $var, $name, $prevent_save_on_error = true, $trim_value = true ) {
|
116 |
$id = $this->get_id();
|
117 |
|
118 |
if ( ! isset( $this->settings[$var] ) ) {
|
@@ -470,6 +470,10 @@ abstract class ITSEC_Validator {
|
|
470 |
}
|
471 |
|
472 |
if ( false !== $error ) {
|
|
|
|
|
|
|
|
|
473 |
$this->add_error( $this->generate_error( $id, $var, $type, $error ) );
|
474 |
$this->vars_to_skip_validate_matching_types[] = $var;
|
475 |
|
112 |
}
|
113 |
}
|
114 |
|
115 |
+
final protected function sanitize_setting( $type, $var, $name, $prevent_save_on_error = true, $trim_value = true, $custom_error = '' ) {
|
116 |
$id = $this->get_id();
|
117 |
|
118 |
if ( ! isset( $this->settings[$var] ) ) {
|
470 |
}
|
471 |
|
472 |
if ( false !== $error ) {
|
473 |
+
if ( $custom_error ) {
|
474 |
+
$error = $custom_error;
|
475 |
+
}
|
476 |
+
|
477 |
$this->add_error( $this->generate_error( $id, $var, $type, $error ) );
|
478 |
$this->vars_to_skip_validate_matching_types[] = $var;
|
479 |
|
core/lockout.php
CHANGED
@@ -2,10 +2,20 @@
|
|
2 |
/**
|
3 |
* Handles lockouts for modules and core
|
4 |
*
|
5 |
-
* @package iThemes-Security
|
6 |
* @since 4.0
|
|
|
7 |
*/
|
8 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
9 |
/**
|
10 |
* Class ITSEC_Lockout
|
11 |
*
|
@@ -83,9 +93,6 @@ final class ITSEC_Lockout {
|
|
83 |
//Register all plugin modules
|
84 |
add_action( 'plugins_loaded', array( $this, 'register_modules' ) );
|
85 |
|
86 |
-
//Set an error message on improper logout
|
87 |
-
add_action( 'login_head', array( $this, 'set_lockout_error' ) );
|
88 |
-
|
89 |
add_action( 'ithemes_sync_register_verbs', array( $this, 'register_sync_verbs' ) );
|
90 |
add_filter( 'itsec-filter-itsec-get-everything-verbs', array( $this, 'register_sync_get_everything_verbs' ) );
|
91 |
|
@@ -135,8 +142,12 @@ final class ITSEC_Lockout {
|
|
135 |
|
136 |
$host = ITSEC_Lib::get_ip();
|
137 |
|
138 |
-
if (
|
139 |
-
$this->execute_lock();
|
|
|
|
|
|
|
|
|
140 |
}
|
141 |
}
|
142 |
|
@@ -145,88 +156,92 @@ final class ITSEC_Lockout {
|
|
145 |
*
|
146 |
* @since 4.0
|
147 |
*
|
148 |
-
* @param
|
149 |
-
* @param
|
150 |
-
* @param string
|
151 |
*
|
152 |
* @return void
|
153 |
*/
|
154 |
public function check_lockout( $user = false, $username = false, $type = '' ) {
|
155 |
-
|
|
|
156 |
|
157 |
-
$
|
158 |
-
|
159 |
-
$host = ITSEC_Lib::get_ip();
|
160 |
-
$username = sanitize_text_field( trim( $username ) );
|
161 |
-
$username_check = false;
|
162 |
-
$user_check = false;
|
163 |
-
$host_check = false;
|
164 |
-
|
165 |
-
if ( is_object( $user ) && is_a( $user, 'WP_User' ) ) {
|
166 |
|
|
|
167 |
$user_id = $user->ID;
|
168 |
-
|
169 |
-
|
170 |
-
|
171 |
-
$user = get_userdata( intval( $user ) );
|
172 |
-
$user_id = $user->ID;
|
173 |
-
|
174 |
} else {
|
175 |
-
|
176 |
$user = wp_get_current_user();
|
177 |
$user_id = $user->ID;
|
178 |
-
|
179 |
-
if ( $username !== false && $username != '' ) {
|
180 |
-
$username_check = $wpdb->get_results( $wpdb->prepare(
|
181 |
-
"SELECT `lockout_username`, `lockout_type` FROM `{$wpdb->base_prefix}itsec_lockouts` WHERE `lockout_active`=1 AND `lockout_expire_gmt` > %s AND `lockout_username`= %s;",
|
182 |
-
date( 'Y-m-d H:i:s', ITSEC_Core::get_current_time_gmt() ), $username
|
183 |
-
) );
|
184 |
-
}
|
185 |
-
|
186 |
-
$host_check = $wpdb->get_results( $wpdb->prepare(
|
187 |
-
"SELECT `lockout_host`, `lockout_type` FROM `{$wpdb->base_prefix}itsec_lockouts` WHERE `lockout_active`=1 AND `lockout_expire_gmt` > %s AND `lockout_host`= %s;",
|
188 |
-
date( 'Y-m-d H:i:s', ITSEC_Core::get_current_time_gmt() ), $host
|
189 |
-
) );
|
190 |
-
|
191 |
}
|
192 |
|
193 |
-
if ( $
|
194 |
-
|
195 |
-
$user_check = $wpdb->get_results( $wpdb->prepare(
|
196 |
-
"SELECT `lockout_user`, `lockout_type` FROM `{$wpdb->base_prefix}itsec_lockouts` WHERE `lockout_active`=1 AND `lockout_expire_gmt` > %s AND `lockout_user`= %d;",
|
197 |
-
date( 'Y-m-d H:i:s', ITSEC_Core::get_current_time_gmt() ), $user_id
|
198 |
-
) );
|
199 |
}
|
200 |
|
201 |
-
|
202 |
-
|
203 |
-
|
204 |
-
ITSEC_Lib::create_database_tables();
|
205 |
}
|
206 |
|
207 |
-
if ( $
|
|
|
|
|
208 |
|
209 |
-
|
210 |
-
$this->execute_lock( array( 'type' => $type ) );
|
211 |
|
212 |
-
|
|
|
|
|
213 |
|
214 |
-
|
215 |
-
|
216 |
-
}
|
217 |
|
218 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
219 |
|
220 |
-
|
221 |
-
|
222 |
-
|
223 |
-
|
224 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
225 |
|
226 |
-
|
|
|
|
|
|
|
|
|
227 |
|
|
|
|
|
228 |
}
|
229 |
|
|
|
|
|
|
|
|
|
|
|
230 |
}
|
231 |
|
232 |
/**
|
@@ -292,158 +307,468 @@ final class ITSEC_Lockout {
|
|
292 |
*
|
293 |
* @since 4.0
|
294 |
*
|
295 |
-
* @param
|
296 |
-
* @param string $username username of user
|
297 |
*
|
298 |
* @return void
|
299 |
*/
|
300 |
-
public function do_lockout( $
|
301 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
302 |
|
303 |
-
if ( ! isset( $this->lockout_modules[$module] ) ) {
|
304 |
return;
|
305 |
}
|
306 |
|
|
|
|
|
|
|
307 |
|
308 |
-
|
309 |
-
|
310 |
-
|
311 |
|
312 |
-
$
|
313 |
-
$
|
314 |
-
$lock_username = false;
|
315 |
-
$options = $this->lockout_modules[$module];
|
316 |
|
317 |
-
$
|
318 |
'temp_type' => $options['type'],
|
319 |
'temp_date' => date( 'Y-m-d H:i:s', ITSEC_Core::get_current_time() ),
|
320 |
'temp_date_gmt' => date( 'Y-m-d H:i:s', ITSEC_Core::get_current_time_gmt() ),
|
321 |
);
|
322 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
323 |
|
324 |
-
if (
|
325 |
-
$
|
|
|
|
|
326 |
|
327 |
-
|
328 |
-
|
329 |
|
330 |
-
|
331 |
|
332 |
-
|
333 |
-
$wpdb->prepare(
|
334 |
-
"SELECT COUNT(*) FROM `{$wpdb->base_prefix}itsec_temp` WHERE `temp_date_gmt` > %s AND `temp_host` = %s",
|
335 |
-
date( 'Y-m-d H:i:s', ITSEC_Core::get_current_time_gmt() - ( $options['period'] * MINUTE_IN_SECONDS ) ),
|
336 |
-
$host
|
337 |
-
)
|
338 |
-
);
|
339 |
|
340 |
-
|
341 |
-
|
342 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
343 |
}
|
344 |
|
345 |
-
|
346 |
-
|
347 |
-
|
|
|
348 |
|
349 |
-
|
350 |
-
$user_lockout_event_data = $lockout_event_data;
|
351 |
-
$user_lockout_event_data['temp_user'] = $user_id;
|
352 |
-
$user_lockout_event_data['temp_username'] = $username;
|
353 |
|
354 |
-
|
|
|
|
|
355 |
|
356 |
-
|
357 |
-
|
358 |
-
"SELECT COUNT(*) FROM `{$wpdb->base_prefix}itsec_temp` WHERE `temp_date_gmt` > %s AND (`temp_username` = %s OR `temp_user` = %d)",
|
359 |
-
date( 'Y-m-d H:i:s', ITSEC_Core::get_current_time_gmt() - ( $options['period'] * MINUTE_IN_SECONDS ) ),
|
360 |
-
$username,
|
361 |
-
$user_id
|
362 |
-
)
|
363 |
-
);
|
364 |
|
365 |
-
|
366 |
-
|
367 |
-
|
368 |
-
|
369 |
-
|
370 |
-
|
371 |
|
372 |
-
|
373 |
|
374 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
375 |
$wpdb->prepare(
|
376 |
-
"SELECT COUNT(*) FROM `{$wpdb->base_prefix}
|
377 |
-
date( 'Y-m-d H:i:s', ITSEC_Core::get_current_time_gmt() -
|
378 |
-
$
|
379 |
)
|
380 |
);
|
381 |
|
382 |
-
|
383 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
384 |
}
|
385 |
}
|
386 |
}
|
387 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
388 |
|
389 |
-
$
|
|
|
|
|
|
|
390 |
|
391 |
-
|
392 |
-
|
393 |
-
|
394 |
}
|
395 |
|
|
|
|
|
|
|
|
|
396 |
|
397 |
-
|
398 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
399 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
400 |
}
|
401 |
|
402 |
/**
|
403 |
* Executes lockout (locks user out)
|
404 |
*
|
405 |
-
* @param array $context
|
406 |
-
* @param bool $deprecated Deprecated argument. Previously whether this is a network lock.
|
407 |
*
|
408 |
* @return void
|
409 |
*/
|
410 |
-
public function execute_lock( $context = array()
|
|
|
|
|
411 |
|
412 |
-
|
413 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
414 |
}
|
415 |
|
416 |
-
if (
|
417 |
-
|
418 |
-
$
|
419 |
-
$network = $context['network_lock'];
|
420 |
-
} else {
|
421 |
-
$user = $context;
|
422 |
-
$network = $deprecated;
|
423 |
}
|
424 |
|
425 |
if ( ITSEC_Lib::is_ip_whitelisted( ITSEC_Lib::get_ip() ) ) {
|
426 |
return;
|
427 |
}
|
428 |
|
429 |
-
if (
|
|
|
|
|
|
|
|
|
430 |
|
431 |
$message = ITSEC_Modules::get_setting( 'global', 'community_lockout_message' );
|
432 |
|
433 |
if ( ! $message ) {
|
434 |
$message = __( 'Your IP address has been flagged as a threat by the iThemes Security network.', 'better-wp-security' );
|
435 |
}
|
436 |
-
|
437 |
-
} elseif ( $user === true ) { //lockout the user
|
438 |
|
439 |
$message = ITSEC_Modules::get_setting( 'global', 'user_lockout_message' );
|
440 |
|
441 |
if ( ! $message ) {
|
442 |
-
$message =
|
443 |
}
|
444 |
-
|
445 |
-
} else { //just lockout the host
|
446 |
-
|
447 |
$message = ITSEC_Modules::get_setting( 'global', 'lockout_message' );
|
448 |
|
449 |
if ( ! $message ) {
|
@@ -451,26 +776,23 @@ final class ITSEC_Lockout {
|
|
451 |
}
|
452 |
}
|
453 |
|
454 |
-
$
|
455 |
|
456 |
-
if ( $
|
457 |
-
|
458 |
-
|
459 |
-
|
460 |
-
|
461 |
-
* @param string $type
|
462 |
-
* @param array $context
|
463 |
-
*/
|
464 |
-
$message = apply_filters( "itsec_{$context['type']}_lockout_message", $message, $context );
|
465 |
|
|
|
466 |
/**
|
467 |
-
* Filter
|
468 |
*
|
469 |
-
* @param
|
470 |
-
* @param string
|
471 |
-
* @param
|
472 |
*/
|
473 |
-
$
|
474 |
}
|
475 |
|
476 |
$current_user = wp_get_current_user();
|
@@ -479,28 +801,20 @@ final class ITSEC_Lockout {
|
|
479 |
wp_logout();
|
480 |
}
|
481 |
|
482 |
-
|
483 |
-
|
484 |
-
} else {
|
485 |
-
@header( 'HTTP/1.0 403 Forbidden' );
|
486 |
-
@header( 'Cache-Control: no-cache, must-revalidate, max-age=0' );
|
487 |
-
@header( 'Expires: Thu, 22 Jun 1978 00:28:00 GMT' );
|
488 |
-
@header( 'Pragma: no-cache' );
|
489 |
|
490 |
-
|
491 |
-
add_filter( 'wp_die_ajax_handler', array( $this, 'apply_wp_die_handler' ) );
|
492 |
-
add_filter( 'wp_die_xmlrpc_handler', array( $this, 'apply_wp_die_handler' ) );
|
493 |
-
wp_die( $message, '', array( 'response' => 403 ) );
|
494 |
-
}
|
495 |
-
}
|
496 |
|
497 |
-
|
498 |
-
|
499 |
-
|
500 |
-
|
501 |
-
|
502 |
-
|
503 |
-
|
|
|
|
|
504 |
}
|
505 |
|
506 |
/**
|
@@ -517,7 +831,7 @@ final class ITSEC_Lockout {
|
|
517 |
$global_settings_url = add_query_arg( array( 'module_type', 'all' ), $global_settings_url );
|
518 |
}
|
519 |
|
520 |
-
$description
|
521 |
$description .= '<p>';
|
522 |
$description .= sprintf( __( 'Your lockout settings can be configured in <a href="%s" data-module-link="global">Global Settings</a>.', 'better-wp-security' ), esc_url( $global_settings_url ) );
|
523 |
$description .= '<br />';
|
@@ -545,8 +859,8 @@ final class ITSEC_Lockout {
|
|
545 |
*
|
546 |
* @since 4.0
|
547 |
*
|
548 |
-
* @param string $type
|
549 |
-
* @param array $args
|
550 |
*
|
551 |
* @return array all lockouts in the system
|
552 |
*/
|
@@ -667,7 +981,7 @@ final class ITSEC_Lockout {
|
|
667 |
|
668 |
if ( ! is_array( $whitelist ) ) {
|
669 |
$whitelist = array();
|
670 |
-
}
|
671 |
// Update old format
|
672 |
$whitelist = array(
|
673 |
$whitelist['ip'] => $whitelist['exp'] - ITSEC_Core::get_time_offset(),
|
@@ -702,11 +1016,11 @@ final class ITSEC_Lockout {
|
|
702 |
* @param string $ip
|
703 |
*/
|
704 |
public function add_to_temp_whitelist( $ip ) {
|
705 |
-
$whitelist
|
706 |
-
$expiration
|
707 |
$refresh_expiration = $expiration - HOUR_IN_SECONDS;
|
708 |
|
709 |
-
if ( isset( $whitelist[$ip] ) && $whitelist[$ip] > $refresh_expiration ) {
|
710 |
// An update is not needed yet.
|
711 |
return;
|
712 |
}
|
@@ -714,11 +1028,11 @@ final class ITSEC_Lockout {
|
|
714 |
// Remove expired entries.
|
715 |
foreach ( $whitelist as $cached_ip => $cached_expiration ) {
|
716 |
if ( $cached_expiration < ITSEC_Core::get_current_time_gmt() ) {
|
717 |
-
unset( $whitelist[$cached_ip] );
|
718 |
}
|
719 |
}
|
720 |
|
721 |
-
$whitelist[$ip] = $expiration;
|
722 |
|
723 |
update_site_option( 'itsec_temp_whitelist_ip', $whitelist );
|
724 |
}
|
@@ -731,11 +1045,11 @@ final class ITSEC_Lockout {
|
|
731 |
public function remove_from_temp_whitelist( $ip ) {
|
732 |
$whitelist = $this->get_temp_whitelist();
|
733 |
|
734 |
-
if ( ! isset( $whitelist[$ip] ) ) {
|
735 |
return;
|
736 |
}
|
737 |
|
738 |
-
unset( $whitelist[$ip] );
|
739 |
|
740 |
update_site_option( 'itsec_temp_whitelist_ip', $whitelist );
|
741 |
}
|
@@ -759,219 +1073,15 @@ final class ITSEC_Lockout {
|
|
759 |
}
|
760 |
|
761 |
$whitelist = $this->get_temp_whitelist();
|
762 |
-
$ip
|
763 |
|
764 |
-
if ( isset( $whitelist[$ip] ) && $whitelist[$ip] > ITSEC_Core::get_current_time() ) {
|
765 |
return true;
|
766 |
}
|
767 |
|
768 |
return false;
|
769 |
}
|
770 |
|
771 |
-
/**
|
772 |
-
* Create a lockout.
|
773 |
-
*
|
774 |
-
* @param array $args
|
775 |
-
*
|
776 |
-
* @return array
|
777 |
-
*/
|
778 |
-
public function create_lockout( $args = array() ) {
|
779 |
-
global $wpdb;
|
780 |
-
|
781 |
-
$args = wp_parse_args( $args, array(
|
782 |
-
'module' => '',
|
783 |
-
'host' => false,
|
784 |
-
'user_id' => false,
|
785 |
-
'username' => false,
|
786 |
-
) );
|
787 |
-
|
788 |
-
$module = $args['module'];
|
789 |
-
$host = $args['host'];
|
790 |
-
$user_id = $args['user_id'];
|
791 |
-
$username = $args['username'];
|
792 |
-
|
793 |
-
$module_details = $this->lockout_modules[ $module ];
|
794 |
-
|
795 |
-
$whitelisted = ITSEC_Lib::is_ip_whitelisted( $host );
|
796 |
-
$blacklisted = false;
|
797 |
-
|
798 |
-
$log_data = array(
|
799 |
-
'module' => $module,
|
800 |
-
'host' => $host,
|
801 |
-
'user_id' => $user_id,
|
802 |
-
'username' => $username,
|
803 |
-
'module_details' => $module_details,
|
804 |
-
'whitelisted' => $whitelisted,
|
805 |
-
'blacklisted' => false,
|
806 |
-
);
|
807 |
-
|
808 |
-
|
809 |
-
// Do a permanent ban if enabled and settings criteria are met.
|
810 |
-
if ( ITSEC_Modules::get_setting( 'global', 'blacklist' ) && false !== $host ) {
|
811 |
-
$blacklist_count = ITSEC_Modules::get_setting( 'global', 'blacklist_count' );
|
812 |
-
$blacklist_period = ITSEC_Modules::get_setting( 'global', 'blacklist_period', 7 );
|
813 |
-
$blacklist_seconds = $blacklist_period * DAY_IN_SECONDS;
|
814 |
-
|
815 |
-
$host_count = 1 + $wpdb->get_var(
|
816 |
-
$wpdb->prepare(
|
817 |
-
"SELECT COUNT(*) FROM `{$wpdb->base_prefix}itsec_lockouts` WHERE `lockout_expire_gmt` > %s AND `lockout_host`= %s",
|
818 |
-
date( 'Y-m-d H:i:s', ITSEC_Core::get_current_time_gmt() - $blacklist_seconds ),
|
819 |
-
$host
|
820 |
-
)
|
821 |
-
);
|
822 |
-
|
823 |
-
if ( $host_count >= $blacklist_count ) {
|
824 |
-
$blacklisted = true;
|
825 |
-
$log_data['blacklisted'] = true;
|
826 |
-
|
827 |
-
if ( $whitelisted ) {
|
828 |
-
ITSEC_Log::add_notice( 'lockout', 'whitelisted-host-triggered-blacklist', array_merge( $log_data, compact( 'blacklist_period', 'blacklist_count', 'host_count' ) ) );
|
829 |
-
} else {
|
830 |
-
$this->blacklist_ip( $host );
|
831 |
-
ITSEC_Log::add_action( 'lockout', 'host-triggered-blacklist', array_merge( $log_data, compact( 'blacklist_period', 'blacklist_count', 'host_count' ) ) );
|
832 |
-
}
|
833 |
-
}
|
834 |
-
}
|
835 |
-
|
836 |
-
|
837 |
-
$host_expiration = false;
|
838 |
-
$user_expiration = false;
|
839 |
-
$id = false;
|
840 |
-
|
841 |
-
$lockouts_data = array(
|
842 |
-
'lockout_type' => $module_details['type'],
|
843 |
-
'lockout_start' => date( 'Y-m-d H:i:s', ITSEC_Core::get_current_time() ),
|
844 |
-
'lockout_start_gmt' => date( 'Y-m-d H:i:s', ITSEC_Core::get_current_time_gmt() ),
|
845 |
-
);
|
846 |
-
|
847 |
-
if ( $whitelisted ) {
|
848 |
-
$lockouts_data['lockout_expire'] = date( 'Y-m-d H:i:s', 1 );
|
849 |
-
$lockouts_data['lockout_expire_gmt'] = date( 'Y-m-d H:i:s', 1 );
|
850 |
-
} else {
|
851 |
-
$exp_seconds = ITSEC_Modules::get_setting( 'global', 'lockout_period' ) * MINUTE_IN_SECONDS;
|
852 |
-
|
853 |
-
$lockouts_data['lockout_expire'] = date( 'Y-m-d H:i:s', ITSEC_Core::get_current_time() + $exp_seconds );
|
854 |
-
$lockouts_data['lockout_expire_gmt'] = date( 'Y-m-d H:i:s', ITSEC_Core::get_current_time_gmt() + $exp_seconds );
|
855 |
-
}
|
856 |
-
|
857 |
-
if ( false !== $host && ! $blacklisted ) {
|
858 |
-
$host_expiration = $lockouts_data['lockout_expire'];
|
859 |
-
$id = $this->add_lockout_to_db( 'host', $host, $whitelisted, $lockouts_data, $log_data );
|
860 |
-
}
|
861 |
-
|
862 |
-
if ( false !== $user_id ) {
|
863 |
-
$user_expiration = $lockouts_data['lockout_expire'];
|
864 |
-
$id = $this->add_lockout_to_db( 'user', $user_id, $whitelisted, $lockouts_data, $log_data );
|
865 |
-
}
|
866 |
-
|
867 |
-
if ( false !== $username ) {
|
868 |
-
$user_expiration = $lockouts_data['lockout_expire'];
|
869 |
-
$id = $this->add_lockout_to_db( 'username', $username, $whitelisted, $lockouts_data, $log_data );
|
870 |
-
}
|
871 |
-
|
872 |
-
return compact( 'id', 'host_expiration', 'user_expiration', 'whitelisted', 'blacklisted', 'module_details' );
|
873 |
-
}
|
874 |
-
|
875 |
-
/**
|
876 |
-
* Store a record of the locked out user/host or permanently ban the host.
|
877 |
-
*
|
878 |
-
* Permanently banned hosts will be forwarded to the ban-users module via the itsec-new-blacklisted-ip hook and
|
879 |
-
* not persisted to the database.
|
880 |
-
*
|
881 |
-
* If configured, notifies the configured email addresses of the lockout.
|
882 |
-
*
|
883 |
-
* @since 4.0
|
884 |
-
*
|
885 |
-
* @param string $module The module triggering the lockout.
|
886 |
-
* @param string|bool $host Host to lock out or false if the host should not be locked out.
|
887 |
-
* @param int|bool $user_id User ID to lockout or false if the host should not be locked out.
|
888 |
-
* @param string|bool $username Username to lockout or false if the host should not be locked out.
|
889 |
-
*
|
890 |
-
* @return void
|
891 |
-
*/
|
892 |
-
private function lockout( $module, $host, $user_id, $username ) {
|
893 |
-
global $wpdb;
|
894 |
-
|
895 |
-
|
896 |
-
$lock = "lockout_$host$user_id$username";
|
897 |
-
|
898 |
-
// Acquire a lock to prevent a lockout being created more than once by a particularly fast attacker.
|
899 |
-
if ( ! ITSEC_Lib::get_lock( $lock, 180 ) ) {
|
900 |
-
return;
|
901 |
-
}
|
902 |
-
|
903 |
-
$details = $this->create_lockout( compact( 'module', 'host', 'user_id', 'username' ) );
|
904 |
-
|
905 |
-
if ( $details['whitelisted'] ) {
|
906 |
-
// No need to send an email notice when the host is whitelisted.
|
907 |
-
ITSEC_Lib::release_lock( $lock );
|
908 |
-
|
909 |
-
return;
|
910 |
-
}
|
911 |
-
|
912 |
-
$this->send_lockout_email(
|
913 |
-
$host,
|
914 |
-
$user_id,
|
915 |
-
$username,
|
916 |
-
$details['host_expiration'],
|
917 |
-
$details['user_expiration'],
|
918 |
-
$details['module_details']['reason']
|
919 |
-
);
|
920 |
-
|
921 |
-
$lock_context = array(
|
922 |
-
'type' => $details['module_details']['type'],
|
923 |
-
);
|
924 |
-
|
925 |
-
if ( false !== $user_id ) {
|
926 |
-
$lock_context['user'] = get_userdata( $user_id );
|
927 |
-
} elseif ( false !== $username ) {
|
928 |
-
$lock_context['username'] = $username;
|
929 |
-
}
|
930 |
-
|
931 |
-
if ( false === $host ) {
|
932 |
-
$lock_context['user_lock'] = true;
|
933 |
-
}
|
934 |
-
|
935 |
-
ITSEC_Lib::release_lock( $lock );
|
936 |
-
$this->execute_lock( $lock_context );
|
937 |
-
}
|
938 |
-
|
939 |
-
/**
|
940 |
-
* Adds a record of a lockout event to the database and log the event.
|
941 |
-
*
|
942 |
-
* @param string $type The type of lockout: "host", "user", "username".
|
943 |
-
* @param string|int $id The value for the type: host's IP, user's ID, username.
|
944 |
-
* @param bool $whitelisted Whether or not the host triggering the event is whitelisted.
|
945 |
-
* @param array $lockout_data Array of base data to be inserted.
|
946 |
-
* @param array $log_data Array of data to be logged for the event.
|
947 |
-
*
|
948 |
-
* @return int|false
|
949 |
-
*/
|
950 |
-
private function add_lockout_to_db( $type, $id, $whitelisted, $lockout_data, $log_data ) {
|
951 |
-
global $wpdb;
|
952 |
-
|
953 |
-
$lockout_data["lockout_$type"] = $id;
|
954 |
-
|
955 |
-
$result = $wpdb->insert( "{$wpdb->base_prefix}itsec_lockouts", $lockout_data );
|
956 |
-
$insert_id = $result ? $wpdb->insert_id : false;
|
957 |
-
|
958 |
-
if ( $whitelisted ) {
|
959 |
-
ITSEC_Log::add_notice( 'lockout', "whitelisted-host-triggered-$type-lockout", array_merge( $log_data, $lockout_data ) );
|
960 |
-
} else {
|
961 |
-
if ( 'host' === $type ) {
|
962 |
-
$code = "host-lockout::{$log_data['host']}";
|
963 |
-
} else if ( 'user' === $type ) {
|
964 |
-
$code = "user-lockout::{$log_data['user_id']}";
|
965 |
-
} else if ( 'username' === $type ) {
|
966 |
-
$code = "username-lockout::{$log_data['username']}";
|
967 |
-
}
|
968 |
-
|
969 |
-
ITSEC_Log::add_action( 'lockout', $code, array_merge( $log_data, $lockout_data ) );
|
970 |
-
}
|
971 |
-
|
972 |
-
return $insert_id;
|
973 |
-
}
|
974 |
-
|
975 |
/**
|
976 |
* Inserts an IP address into the htaccess ban list.
|
977 |
*
|
@@ -1041,7 +1151,7 @@ final class ITSEC_Lockout {
|
|
1041 |
*
|
1042 |
* @since 3.6.0
|
1043 |
*
|
1044 |
-
* @param
|
1045 |
*
|
1046 |
* @return array Array of verbs.
|
1047 |
*/
|
@@ -1085,11 +1195,13 @@ final class ITSEC_Lockout {
|
|
1085 |
/**
|
1086 |
* Get lockout details.
|
1087 |
*
|
1088 |
-
* @param int
|
|
|
1089 |
*
|
1090 |
-
* @return array|false
|
|
|
1091 |
*/
|
1092 |
-
public function get_lockout( $id ) {
|
1093 |
global $wpdb;
|
1094 |
|
1095 |
$results = $wpdb->get_results( $wpdb->prepare(
|
@@ -1101,7 +1213,13 @@ final class ITSEC_Lockout {
|
|
1101 |
return false;
|
1102 |
}
|
1103 |
|
1104 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
1105 |
}
|
1106 |
|
1107 |
/**
|
@@ -1113,32 +1231,22 @@ final class ITSEC_Lockout {
|
|
1113 |
*
|
1114 |
* @return bool true on success or false
|
1115 |
*/
|
1116 |
-
public function release_lockout( $id =
|
1117 |
-
|
1118 |
global $wpdb;
|
1119 |
|
1120 |
-
if (
|
1121 |
-
|
1122 |
-
$lockout = $wpdb->get_results( $wpdb->prepare( "SELECT * FROM `{$wpdb->base_prefix}itsec_lockouts` WHERE lockout_id = %d;", $id ), ARRAY_A );
|
1123 |
-
|
1124 |
-
if ( is_array( $lockout ) && count( $lockout ) >= 1 ) {
|
1125 |
-
|
1126 |
-
$success = $wpdb->update(
|
1127 |
-
$wpdb->base_prefix . 'itsec_lockouts',
|
1128 |
-
array(
|
1129 |
-
'lockout_active' => 0,
|
1130 |
-
),
|
1131 |
-
array(
|
1132 |
-
'lockout_id' => (int) $id,
|
1133 |
-
)
|
1134 |
-
);
|
1135 |
-
|
1136 |
-
return (bool) $success;
|
1137 |
-
|
1138 |
-
}
|
1139 |
}
|
1140 |
|
1141 |
-
return
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1142 |
}
|
1143 |
|
1144 |
/**
|
@@ -1177,16 +1285,14 @@ final class ITSEC_Lockout {
|
|
1177 |
*
|
1178 |
* @since 4.0
|
1179 |
*
|
1180 |
-
* @param
|
1181 |
-
* @param
|
1182 |
-
* @param
|
1183 |
-
* @param
|
1184 |
-
* @param string $user_expiration when the user lockout expires
|
1185 |
-
* @param string $reason the reason for the lockout to show to the user
|
1186 |
*
|
1187 |
* @return void
|
1188 |
*/
|
1189 |
-
private function send_lockout_email(
|
1190 |
|
1191 |
$nc = ITSEC_Core::get_notification_center();
|
1192 |
|
@@ -1194,29 +1300,39 @@ final class ITSEC_Lockout {
|
|
1194 |
return;
|
1195 |
}
|
1196 |
|
1197 |
-
$lockouts
|
1198 |
-
$show_remove_ip_ban_message
|
1199 |
$show_remove_lockout_message = false;
|
1200 |
|
1201 |
-
if (
|
1202 |
-
$
|
1203 |
-
$
|
1204 |
-
|
1205 |
-
|
1206 |
-
if ( false !== $username ) {
|
1207 |
$show_remove_lockout_message = true;
|
1208 |
|
1209 |
$lockouts[] = array(
|
1210 |
'type' => 'user',
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1211 |
'id' => $username,
|
1212 |
'until' => $user_expiration,
|
1213 |
'reason' => $reason,
|
1214 |
);
|
1215 |
}
|
1216 |
|
1217 |
-
if (
|
1218 |
if ( false === $host_expiration ) {
|
1219 |
-
$host_expiration
|
1220 |
$show_remove_ip_ban_message = true;
|
1221 |
} else {
|
1222 |
$show_remove_lockout_message = true;
|
@@ -1224,13 +1340,12 @@ final class ITSEC_Lockout {
|
|
1224 |
|
1225 |
$lockouts[] = array(
|
1226 |
'type' => 'host',
|
1227 |
-
'id' => '<a href="' . esc_url( ITSEC_Lib::get_trace_ip_link( $
|
1228 |
'until' => $host_expiration,
|
1229 |
'reason' => $reason,
|
1230 |
);
|
1231 |
}
|
1232 |
|
1233 |
-
|
1234 |
$mail = $nc->mail();
|
1235 |
|
1236 |
$mail->add_header( esc_html__( 'Site Lockout Notification', 'better-wp-security' ), esc_html__( 'Site Lockout Notification', 'better-wp-security' ) );
|
@@ -1248,7 +1363,6 @@ final class ITSEC_Lockout {
|
|
1248 |
|
1249 |
$mail->add_footer();
|
1250 |
|
1251 |
-
|
1252 |
$subject = $mail->prepend_site_url_to_subject( $nc->get_subject( 'lockout' ) );
|
1253 |
$subject = apply_filters( 'itsec_lockout_email_subject', $subject );
|
1254 |
$mail->set_subject( $subject, false );
|
@@ -1256,38 +1370,24 @@ final class ITSEC_Lockout {
|
|
1256 |
$nc->send( 'lockout', $mail );
|
1257 |
}
|
1258 |
|
1259 |
-
/**
|
1260 |
-
* Sets an error message when a user has been forcibly logged out due to lockout
|
1261 |
-
*
|
1262 |
-
* @return string
|
1263 |
-
*/
|
1264 |
-
public function set_lockout_error() {
|
1265 |
-
|
1266 |
-
//check to see if it's the logout screen
|
1267 |
-
if ( isset( $_GET['itsec'] ) && $_GET['itsec'] == true ) {
|
1268 |
-
return '<div id="login_error">' . ITSEC_Modules::get_setting( 'global', 'user_lockout_message' ) . '</div>' . PHP_EOL;
|
1269 |
-
}
|
1270 |
-
|
1271 |
-
}
|
1272 |
-
|
1273 |
public function filter_entry_for_list_display( $entry, $code, $data ) {
|
1274 |
$entry['module_display'] = esc_html__( 'Lockout', 'better-wp-security' );
|
1275 |
|
1276 |
if ( 'whitelisted-host-triggered-blacklist' === $code ) {
|
1277 |
$entry['description'] = esc_html__( 'Whitelisted Host Triggered Blacklist', 'better-wp-security' );
|
1278 |
-
}
|
1279 |
$entry['description'] = esc_html__( 'Host Triggered Blacklist', 'better-wp-security' );
|
1280 |
-
}
|
1281 |
$entry['description'] = esc_html__( 'Whitelisted Host Triggered Host Lockout', 'better-wp-security' );
|
1282 |
-
}
|
1283 |
if ( isset( $data[0] ) ) {
|
1284 |
$entry['description'] = sprintf( wp_kses( __( 'Host Lockout: <code>%s</code>', 'better-wp-security' ), array( 'code' => array() ) ), $data[0] );
|
1285 |
} else {
|
1286 |
$entry['description'] = esc_html__( 'Host Lockout', 'better-wp-security' );
|
1287 |
}
|
1288 |
-
}
|
1289 |
$entry['description'] = esc_html__( 'Whitelisted Host Triggered User Lockout', 'better-wp-security' );
|
1290 |
-
}
|
1291 |
if ( isset( $data[0] ) ) {
|
1292 |
$user = get_user_by( 'id', $data[0] );
|
1293 |
}
|
@@ -1297,9 +1397,9 @@ final class ITSEC_Lockout {
|
|
1297 |
} else {
|
1298 |
$entry['description'] = esc_html__( 'User Lockout', 'better-wp-security' );
|
1299 |
}
|
1300 |
-
}
|
1301 |
$entry['description'] = esc_html__( 'Whitelisted Host Triggered Username Lockout', 'better-wp-security' );
|
1302 |
-
}
|
1303 |
if ( isset( $data[0] ) ) {
|
1304 |
$entry['description'] = sprintf( wp_kses( __( 'Username Lockout: <code>%s</code>', 'better-wp-security' ), array( 'code' => array() ) ), $data[0] );
|
1305 |
} else {
|
2 |
/**
|
3 |
* Handles lockouts for modules and core
|
4 |
*
|
|
|
5 |
* @since 4.0
|
6 |
+
* @package iThemes-Security
|
7 |
*/
|
8 |
|
9 |
+
use iThemesSecurity\Lib\Lockout;
|
10 |
+
use iThemesSecurity\Lib\Lockout\Execute_Lock;
|
11 |
+
use iThemesSecurity\Lib\Lockout\Execute_Lock\Source\Configurable;
|
12 |
+
use iThemesSecurity\Lib\Lockout\Execute_Lock\Source\Lockout_Module;
|
13 |
+
|
14 |
+
require_once( __DIR__ . '/lib/lockout/execute-lock/source/interface-source.php' );
|
15 |
+
require_once( __DIR__ . '/lib/lockout/execute-lock/abstract-context.php' );
|
16 |
+
require_once( __DIR__ . '/lib/lockout/class-lockout.php' );
|
17 |
+
require_once( __DIR__ . '/lib/lockout/abstract-context.php' );
|
18 |
+
|
19 |
/**
|
20 |
* Class ITSEC_Lockout
|
21 |
*
|
93 |
//Register all plugin modules
|
94 |
add_action( 'plugins_loaded', array( $this, 'register_modules' ) );
|
95 |
|
|
|
|
|
|
|
96 |
add_action( 'ithemes_sync_register_verbs', array( $this, 'register_sync_verbs' ) );
|
97 |
add_filter( 'itsec-filter-itsec-get-everything-verbs', array( $this, 'register_sync_get_everything_verbs' ) );
|
98 |
|
142 |
|
143 |
$host = ITSEC_Lib::get_ip();
|
144 |
|
145 |
+
if ( ITSEC_Lib::is_ip_blacklisted() ) {
|
146 |
+
$this->execute_lock( new Execute_Lock\Host_Context( new Configurable( 'blacklist' ), $host ) );
|
147 |
+
}
|
148 |
+
|
149 |
+
if ( $lockout = $this->find_lockout( 'host', $host ) ) {
|
150 |
+
$this->execute_lock( $lockout->make_execute_lock_context() );
|
151 |
}
|
152 |
}
|
153 |
|
156 |
*
|
157 |
* @since 4.0
|
158 |
*
|
159 |
+
* @param WP_User|int|false $user WordPress user object or false.
|
160 |
+
* @param string|false $username The username to check.
|
161 |
+
* @param string $type Lockout type asking for the check.
|
162 |
*
|
163 |
* @return void
|
164 |
*/
|
165 |
public function check_lockout( $user = false, $username = false, $type = '' ) {
|
166 |
+
$host = ITSEC_Lib::get_ip();
|
167 |
+
$username = sanitize_text_field( trim( $username ) );
|
168 |
|
169 |
+
$lockout = null;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
170 |
|
171 |
+
if ( $user instanceof WP_User ) {
|
172 |
$user_id = $user->ID;
|
173 |
+
} elseif ( $user ) {
|
174 |
+
$user = get_userdata( (int) $user );
|
175 |
+
$user_id = $user ? $user->ID : 0;
|
|
|
|
|
|
|
176 |
} else {
|
|
|
177 |
$user = wp_get_current_user();
|
178 |
$user_id = $user->ID;
|
179 |
+
$lockout = $this->find_lockout( 'host', $host );
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
180 |
}
|
181 |
|
182 |
+
if ( ! $lockout && $user_id ) {
|
183 |
+
$lockout = $this->find_lockout( 'user', $user_id );
|
|
|
|
|
|
|
|
|
184 |
}
|
185 |
|
186 |
+
// Only check for a username lockout if no user ID was passed.
|
187 |
+
if ( ! $lockout && ! $user_id && $username ) {
|
188 |
+
$lockout = $this->find_lockout( 'username', $username );
|
|
|
189 |
}
|
190 |
|
191 |
+
if ( ! $lockout ) {
|
192 |
+
return;
|
193 |
+
}
|
194 |
|
195 |
+
$context = $lockout->make_execute_lock_context();
|
|
|
196 |
|
197 |
+
if ( $type && $context->get_source()->get_source_slug() !== $type ) {
|
198 |
+
$context = $context->with_source( new Lockout_Module( $type ) );
|
199 |
+
}
|
200 |
|
201 |
+
$this->execute_lock( $context );
|
202 |
+
}
|
|
|
203 |
|
204 |
+
/**
|
205 |
+
* Find the lockout for a given type and identifier.
|
206 |
+
*
|
207 |
+
* @param string $type
|
208 |
+
* @param string $identifier
|
209 |
+
*
|
210 |
+
* @return Lockout\Lockout|null
|
211 |
+
*/
|
212 |
+
private function find_lockout( $type, $identifier ) {
|
213 |
+
/** @var wpdb $wpdb */
|
214 |
+
global $wpdb;
|
215 |
|
216 |
+
switch ( $type ) {
|
217 |
+
case 'username':
|
218 |
+
$field = 'lockout_username';
|
219 |
+
break;
|
220 |
+
case 'user':
|
221 |
+
$field = 'lockout_user';
|
222 |
+
break;
|
223 |
+
case 'host':
|
224 |
+
$field = 'lockout_host';
|
225 |
+
break;
|
226 |
+
default:
|
227 |
+
return null;
|
228 |
+
}
|
229 |
|
230 |
+
$data = $wpdb->get_row( $wpdb->prepare(
|
231 |
+
"SELECT * FROM `{$wpdb->base_prefix}itsec_lockouts` WHERE `lockout_active`=1 AND `lockout_expire_gmt` > %s AND `{$field}` = %s ORDER BY `lockout_start` DESC LIMIT 1;",
|
232 |
+
date( 'Y-m-d H:i:s', ITSEC_Core::get_current_time_gmt() ),
|
233 |
+
$identifier
|
234 |
+
), ARRAY_A );
|
235 |
|
236 |
+
if ( ! $data ) {
|
237 |
+
return null;
|
238 |
}
|
239 |
|
240 |
+
try {
|
241 |
+
return $this->hydrate_lockout_entity( $data['lockout_id'], $data );
|
242 |
+
} catch ( Exception $e ) {
|
243 |
+
return null;
|
244 |
+
}
|
245 |
}
|
246 |
|
247 |
/**
|
307 |
*
|
308 |
* @since 4.0
|
309 |
*
|
310 |
+
* @param Lockout\Context $context
|
|
|
311 |
*
|
312 |
* @return void
|
313 |
*/
|
314 |
+
public function do_lockout( $context ) {
|
315 |
+
if ( is_string( $context ) ) {
|
316 |
+
_deprecated_argument( __METHOD__, '5.3.0', '$context should be an iThemesSecurity\Lib\Lockout\Context object.' );
|
317 |
+
|
318 |
+
if ( func_num_args() > 1 && $username = func_get_arg( 1 ) ) {
|
319 |
+
$user_id = username_exists( $username );
|
320 |
+
|
321 |
+
if ( ! empty( $this->lockout_modules[ $context ]['host'] ) ) {
|
322 |
+
$context = new Lockout\Host_Context( $context, ITSEC_Lib::get_ip() );
|
323 |
+
|
324 |
+
if ( $user_id ) {
|
325 |
+
$context->set_login_user_id( $user_id );
|
326 |
+
} else {
|
327 |
+
$context->set_login_username( $username );
|
328 |
+
}
|
329 |
+
} elseif ( $user_id ) {
|
330 |
+
$context = new Lockout\User_Context( $context, $user_id );
|
331 |
+
} else {
|
332 |
+
$context = new Lockout\Username_Context( $context, $username );
|
333 |
+
}
|
334 |
+
} else {
|
335 |
+
$context = new Lockout\Host_Context( $context, ITSEC_Lib::get_ip() );
|
336 |
+
}
|
337 |
+
}
|
338 |
+
|
339 |
+
if ( ! $context instanceof Lockout\Context ) {
|
340 |
+
_doing_it_wrong( __METHOD__, '$context must be an iThemesSecurity\Lib\Lockout\Context object.', '5.3.0' );
|
341 |
|
|
|
342 |
return;
|
343 |
}
|
344 |
|
345 |
+
if ( ! isset( $this->lockout_modules[ $context->get_lockout_module() ] ) ) {
|
346 |
+
return;
|
347 |
+
}
|
348 |
|
349 |
+
if ( ! apply_filters( 'itsec_do_lockout', true, $context ) ) {
|
350 |
+
return;
|
351 |
+
}
|
352 |
|
353 |
+
$lockout = false;
|
354 |
+
$options = $this->lockout_modules[ $context->get_lockout_module() ];
|
|
|
|
|
355 |
|
356 |
+
$event_data = array(
|
357 |
'temp_type' => $options['type'],
|
358 |
'temp_date' => date( 'Y-m-d H:i:s', ITSEC_Core::get_current_time() ),
|
359 |
'temp_date_gmt' => date( 'Y-m-d H:i:s', ITSEC_Core::get_current_time_gmt() ),
|
360 |
);
|
361 |
|
362 |
+
if ( $context instanceof Lockout\Host_Context && ! empty( $options['host'] ) ) {
|
363 |
+
$lockout = $this->create_host_temp_event( $context->get_host(), $event_data, $options );
|
364 |
+
|
365 |
+
if ( ! empty( $options['user'] ) ) {
|
366 |
+
if ( $context->get_login_user_id() && $this->create_user_temp_event( $context->get_login_user_id(), $event_data, $options ) ) {
|
367 |
+
if ( $lockout ) {
|
368 |
+
$context->set_user_limit_triggered();
|
369 |
+
} else {
|
370 |
+
$context = new Lockout\User_Context( $context->get_lockout_module(), $context->get_login_user_id() );
|
371 |
+
}
|
372 |
+
|
373 |
+
$lockout = true;
|
374 |
+
} elseif ( $context->get_login_username() && $this->create_username_temp_event( $context->get_login_username(), $event_data, $options ) ) {
|
375 |
+
if ( $lockout ) {
|
376 |
+
$context->set_user_limit_triggered();
|
377 |
+
} else {
|
378 |
+
$context = new Lockout\Username_Context( $context->get_lockout_module(), $context->get_login_username() );
|
379 |
+
}
|
380 |
+
|
381 |
+
$lockout = true;
|
382 |
+
}
|
383 |
+
}
|
384 |
+
} elseif ( $context instanceof Lockout\User_Context && ! empty( $options['user'] ) ) {
|
385 |
+
$lockout = $this->create_user_temp_event( $context->get_user_id(), $event_data, $options );
|
386 |
+
} elseif ( $context instanceof Lockout\Username_Context && ! empty( $options['user'] ) ) {
|
387 |
+
$lockout = $this->create_username_temp_event( $context->get_username(), $event_data, $options );
|
388 |
+
}
|
389 |
|
390 |
+
if ( $lockout ) {
|
391 |
+
$this->lockout( $context );
|
392 |
+
}
|
393 |
+
}
|
394 |
|
395 |
+
private function create_host_temp_event( $host, $event_data, $options ) {
|
396 |
+
global $wpdb;
|
397 |
|
398 |
+
$event_data['temp_host'] = $host;
|
399 |
|
400 |
+
$wpdb->insert( "{$wpdb->base_prefix}itsec_temp", $event_data );
|
|
|
|
|
|
|
|
|
|
|
|
|
401 |
|
402 |
+
$host_count = $wpdb->get_var(
|
403 |
+
$wpdb->prepare(
|
404 |
+
"SELECT COUNT(*) FROM `{$wpdb->base_prefix}itsec_temp` WHERE `temp_date_gmt` > %s AND `temp_host` = %s",
|
405 |
+
date( 'Y-m-d H:i:s', ITSEC_Core::get_current_time_gmt() - ( $options['period'] * MINUTE_IN_SECONDS ) ),
|
406 |
+
$host
|
407 |
+
)
|
408 |
+
);
|
409 |
+
|
410 |
+
return $host_count >= $options['host'];
|
411 |
+
}
|
412 |
+
|
413 |
+
private function create_user_temp_event( $user_id, $event_data, $options ) {
|
414 |
+
global $wpdb;
|
415 |
+
|
416 |
+
$event_data['temp_user'] = $user_id;
|
417 |
+
$event_data['temp_username'] = sanitize_text_field( get_userdata( $user_id )->user_login );
|
418 |
+
|
419 |
+
$wpdb->insert( "{$wpdb->base_prefix}itsec_temp", $event_data );
|
420 |
+
|
421 |
+
$user_count = $wpdb->get_var(
|
422 |
+
$wpdb->prepare(
|
423 |
+
"SELECT COUNT(*) FROM `{$wpdb->base_prefix}itsec_temp` WHERE `temp_date_gmt` > %s AND (`temp_username` = %s OR `temp_user` = %d)",
|
424 |
+
date( 'Y-m-d H:i:s', ITSEC_Core::get_current_time_gmt() - ( $options['period'] * MINUTE_IN_SECONDS ) ),
|
425 |
+
$event_data['temp_username'],
|
426 |
+
$event_data['temp_user']
|
427 |
+
)
|
428 |
+
);
|
429 |
+
|
430 |
+
return $user_count >= $options['user'];
|
431 |
+
}
|
432 |
+
|
433 |
+
private function create_username_temp_event( $username, $event_data, $options ) {
|
434 |
+
global $wpdb;
|
435 |
+
|
436 |
+
$event_data['temp_username'] = sanitize_text_field( $username );
|
437 |
+
|
438 |
+
$wpdb->insert( "{$wpdb->base_prefix}itsec_temp", $event_data );
|
439 |
+
|
440 |
+
$user_count = $wpdb->get_var(
|
441 |
+
$wpdb->prepare(
|
442 |
+
"SELECT COUNT(*) FROM `{$wpdb->base_prefix}itsec_temp` WHERE `temp_date_gmt` > %s AND `temp_username` = %s",
|
443 |
+
date( 'Y-m-d H:i:s', ITSEC_Core::get_current_time_gmt() - ( $options['period'] * MINUTE_IN_SECONDS ) ),
|
444 |
+
$event_data['temp_username']
|
445 |
+
)
|
446 |
+
);
|
447 |
+
|
448 |
+
return $user_count >= $options['user'];
|
449 |
+
}
|
450 |
+
|
451 |
+
/**
|
452 |
+
* Store a record of the locked out user/host or permanently ban the host.
|
453 |
+
*
|
454 |
+
* Permanently banned hosts will be forwarded to the ban-users module via the itsec-new-blacklisted-ip hook and
|
455 |
+
* not persisted to the database.
|
456 |
+
*
|
457 |
+
* If configured, notifies the configured email addresses of the lockout.
|
458 |
+
*
|
459 |
+
* @since 4.0
|
460 |
+
*
|
461 |
+
* @param Lockout\Context $context
|
462 |
+
*
|
463 |
+
* @return void
|
464 |
+
*/
|
465 |
+
private function lockout( Lockout\Context $context ) {
|
466 |
+
$lock = $context->get_lockout_module() . '_';
|
467 |
+
|
468 |
+
if ( $context instanceof Lockout\Host_Context ) {
|
469 |
+
$lock .= $context->get_host();
|
470 |
+
} elseif ( $context instanceof Lockout\User_Context ) {
|
471 |
+
$lock .= $context->get_user_id();
|
472 |
+
} elseif ( $context instanceof Lockout\Username_Context ) {
|
473 |
+
$lock .= $context->get_username();
|
474 |
}
|
475 |
|
476 |
+
// Acquire a lock to prevent a lockout being created more than once by a particularly fast attacker.
|
477 |
+
if ( ! ITSEC_Lib::get_lock( $lock, 180 ) ) {
|
478 |
+
return;
|
479 |
+
}
|
480 |
|
481 |
+
$details = $this->create_lockout( $context );
|
|
|
|
|
|
|
482 |
|
483 |
+
if ( $details['whitelisted'] ) {
|
484 |
+
// No need to send an email notice when the host is whitelisted.
|
485 |
+
ITSEC_Lib::release_lock( $lock );
|
486 |
|
487 |
+
return;
|
488 |
+
}
|
|
|
|
|
|
|
|
|
|
|
|
|
489 |
|
490 |
+
$this->send_lockout_email(
|
491 |
+
$context,
|
492 |
+
$details['host_expiration'],
|
493 |
+
$details['user_expiration'],
|
494 |
+
$details['module_details']['reason']
|
495 |
+
);
|
496 |
|
497 |
+
ITSEC_Lib::release_lock( $lock );
|
498 |
|
499 |
+
if ( $details['blacklisted'] ) {
|
500 |
+
$this->execute_lock( new Execute_Lock\Host_Context( new Configurable( 'blacklist' ), $context->get_host() ) );
|
501 |
+
} elseif ( $details['lockout'] instanceof Lockout\Lockout ) {
|
502 |
+
$this->execute_lock( $details['lockout']->make_execute_lock_context() );
|
503 |
+
} else {
|
504 |
+
$this->execute_lock( $context->make_execute_lock_context() );
|
505 |
+
}
|
506 |
+
}
|
507 |
+
|
508 |
+
/**
|
509 |
+
* Create a lockout.
|
510 |
+
*
|
511 |
+
* @param Lockout\Context|array $args_or_context
|
512 |
+
*
|
513 |
+
* @return array
|
514 |
+
*/
|
515 |
+
public function create_lockout( $args_or_context = array() ) {
|
516 |
+
global $wpdb;
|
517 |
+
|
518 |
+
$host = $user_id = $username = false;
|
519 |
+
|
520 |
+
if ( $args_or_context instanceof Lockout\Context ) {
|
521 |
+
$context = $args_or_context;
|
522 |
+
$module = $context->get_lockout_module();
|
523 |
+
|
524 |
+
switch ( true ) {
|
525 |
+
case $context instanceof Lockout\Host_Context:
|
526 |
+
$host = $context->get_host();
|
527 |
+
|
528 |
+
if ( $context->is_user_limit_triggered() ) {
|
529 |
+
$user_id = $context->get_login_user_id() ?: $user_id;
|
530 |
+
$username = $context->get_login_username() ?: $username;
|
531 |
+
}
|
532 |
+
break;
|
533 |
+
case $context instanceof Lockout\User_Context:
|
534 |
+
$user_id = $context->get_user_id();
|
535 |
+
break;
|
536 |
+
case $context instanceof Lockout\Username_Context:
|
537 |
+
$username = $context->get_username();
|
538 |
+
break;
|
539 |
+
}
|
540 |
+
} else {
|
541 |
+
$args = $args_or_context;
|
542 |
+
$module = $args['module'];
|
543 |
+
$host = isset( $args['host'] ) ? $args['host'] : false;
|
544 |
+
$user_id = isset( $args['user_id'] ) ? $args['user_id'] : false;
|
545 |
+
$username = isset( $args['username'] ) ? $args['username'] : false;
|
546 |
+
$context = null;
|
547 |
+
}
|
548 |
+
|
549 |
+
$module_details = $this->lockout_modules[ $module ];
|
550 |
+
|
551 |
+
$whitelisted = ITSEC_Lib::is_ip_whitelisted( $host );
|
552 |
+
$blacklisted = false;
|
553 |
+
|
554 |
+
$log_data = array(
|
555 |
+
'module' => $module,
|
556 |
+
'host' => $host,
|
557 |
+
'user_id' => $user_id,
|
558 |
+
'username' => $username,
|
559 |
+
'module_details' => $module_details,
|
560 |
+
'whitelisted' => $whitelisted,
|
561 |
+
'blacklisted' => false,
|
562 |
+
);
|
563 |
+
|
564 |
+
// Do a permanent ban if enabled and settings criteria are met.
|
565 |
+
if ( false !== $host && ITSEC_Modules::get_setting( 'global', 'blacklist' ) ) {
|
566 |
+
$blacklist_count = ITSEC_Modules::get_setting( 'global', 'blacklist_count' );
|
567 |
+
$blacklist_period = ITSEC_Modules::get_setting( 'global', 'blacklist_period', 7 );
|
568 |
+
$blacklist_seconds = $blacklist_period * DAY_IN_SECONDS;
|
569 |
+
|
570 |
+
$host_count = 1 + $wpdb->get_var(
|
571 |
$wpdb->prepare(
|
572 |
+
"SELECT COUNT(*) FROM `{$wpdb->base_prefix}itsec_lockouts` WHERE `lockout_expire_gmt` > %s AND `lockout_host`= %s",
|
573 |
+
date( 'Y-m-d H:i:s', ITSEC_Core::get_current_time_gmt() - $blacklist_seconds ),
|
574 |
+
$host
|
575 |
)
|
576 |
);
|
577 |
|
578 |
+
if ( $host_count >= $blacklist_count ) {
|
579 |
+
$blacklisted = true;
|
580 |
+
$log_data['blacklisted'] = true;
|
581 |
+
|
582 |
+
if ( $whitelisted ) {
|
583 |
+
ITSEC_Log::add_notice( 'lockout', 'whitelisted-host-triggered-blacklist', array_merge( $log_data, compact( 'blacklist_period', 'blacklist_count', 'host_count' ) ) );
|
584 |
+
} else {
|
585 |
+
$this->blacklist_ip( $host );
|
586 |
+
ITSEC_Log::add_action( 'lockout', 'host-triggered-blacklist', array_merge( $log_data, compact( 'blacklist_period', 'blacklist_count', 'host_count' ) ) );
|
587 |
}
|
588 |
}
|
589 |
}
|
590 |
|
591 |
+
$host_expiration = false;
|
592 |
+
$user_expiration = false;
|
593 |
+
$lockout = null;
|
594 |
+
|
595 |
+
$lockouts_data = array(
|
596 |
+
'lockout_type' => $module_details['type'],
|
597 |
+
'lockout_start' => date( 'Y-m-d H:i:s', ITSEC_Core::get_current_time() ),
|
598 |
+
'lockout_start_gmt' => date( 'Y-m-d H:i:s', ITSEC_Core::get_current_time_gmt() ),
|
599 |
+
'lockout_context' => $context ? serialize( $context ) : null,
|
600 |
+
);
|
601 |
+
|
602 |
+
if ( $whitelisted ) {
|
603 |
+
$lockouts_data['lockout_expire'] = date( 'Y-m-d H:i:s', 1 );
|
604 |
+
$lockouts_data['lockout_expire_gmt'] = date( 'Y-m-d H:i:s', 1 );
|
605 |
+
} else {
|
606 |
+
$exp_seconds = ITSEC_Modules::get_setting( 'global', 'lockout_period' ) * MINUTE_IN_SECONDS;
|
607 |
+
|
608 |
+
$lockouts_data['lockout_expire'] = date( 'Y-m-d H:i:s', ITSEC_Core::get_current_time() + $exp_seconds );
|
609 |
+
$lockouts_data['lockout_expire_gmt'] = date( 'Y-m-d H:i:s', ITSEC_Core::get_current_time_gmt() + $exp_seconds );
|
610 |
+
}
|
611 |
+
|
612 |
+
if ( false !== $host && ! $blacklisted ) {
|
613 |
+
$host_expiration = $lockouts_data['lockout_expire'];
|
614 |
+
$lockout = $this->add_lockout_to_db( 'host', $host, $whitelisted, $lockouts_data, $log_data );
|
615 |
+
}
|
616 |
|
617 |
+
if ( false !== $user_id ) {
|
618 |
+
$user_expiration = $lockouts_data['lockout_expire'];
|
619 |
+
$lockout = $this->add_lockout_to_db( 'user', $user_id, $whitelisted, $lockouts_data, $log_data );
|
620 |
+
}
|
621 |
|
622 |
+
if ( false !== $username ) {
|
623 |
+
$user_expiration = $lockouts_data['lockout_expire'];
|
624 |
+
$lockout = $this->add_lockout_to_db( 'username', $username, $whitelisted, $lockouts_data, $log_data );
|
625 |
}
|
626 |
|
627 |
+
$id = $lockout ? $lockout->get_id() : false;
|
628 |
+
|
629 |
+
return compact( 'id', 'host_expiration', 'user_expiration', 'whitelisted', 'blacklisted', 'module_details', 'lockout' );
|
630 |
+
}
|
631 |
|
632 |
+
/**
|
633 |
+
* Adds a record of a lockout event to the database and log the event.
|
634 |
+
*
|
635 |
+
* @param string $type The type of lockout: "host", "user", "username".
|
636 |
+
* @param string|int $id The value for the type: host's IP, user's ID, username.
|
637 |
+
* @param bool $whitelisted Whether or not the host triggering the event is whitelisted.
|
638 |
+
* @param array $lockout_data Array of base data to be inserted.
|
639 |
+
* @param array $log_data Array of data to be logged for the event.
|
640 |
+
*
|
641 |
+
* @return Lockout\Lockout|null
|
642 |
+
*/
|
643 |
+
private function add_lockout_to_db( $type, $id, $whitelisted, $lockout_data, $log_data ) {
|
644 |
+
global $wpdb;
|
645 |
+
|
646 |
+
$lockout_data["lockout_$type"] = $id;
|
647 |
+
|
648 |
+
$result = $wpdb->insert( "{$wpdb->base_prefix}itsec_lockouts", $lockout_data );
|
649 |
+
$insert_id = $result ? $wpdb->insert_id : false;
|
650 |
+
|
651 |
+
if ( $whitelisted ) {
|
652 |
+
ITSEC_Log::add_notice( 'lockout', "whitelisted-host-triggered-$type-lockout", array_merge( $log_data, $lockout_data ) );
|
653 |
+
} else {
|
654 |
+
if ( 'host' === $type ) {
|
655 |
+
$code = "host-lockout::{$log_data['host']}";
|
656 |
+
} elseif ( 'user' === $type ) {
|
657 |
+
$code = "user-lockout::{$log_data['user_id']}";
|
658 |
+
} elseif ( 'username' === $type ) {
|
659 |
+
$code = "username-lockout::{$log_data['username']}";
|
660 |
+
}
|
661 |
+
|
662 |
+
ITSEC_Log::add_action( 'lockout', $code, array_merge( $log_data, $lockout_data ) );
|
663 |
}
|
664 |
+
|
665 |
+
if ( ! $insert_id ) {
|
666 |
+
return null;
|
667 |
+
}
|
668 |
+
|
669 |
+
try {
|
670 |
+
return $this->hydrate_lockout_entity( $insert_id, $lockout_data );
|
671 |
+
} catch ( Exception $e ) {
|
672 |
+
return null;
|
673 |
+
}
|
674 |
+
}
|
675 |
+
|
676 |
+
/**
|
677 |
+
* Hydrate a lockout entity from its DB data.
|
678 |
+
*
|
679 |
+
* @param int $id
|
680 |
+
* @param array $data
|
681 |
+
*
|
682 |
+
* @return Lockout\Lockout
|
683 |
+
* @throws Exception
|
684 |
+
*/
|
685 |
+
private function hydrate_lockout_entity( $id, array $data ) {
|
686 |
+
$context = null;
|
687 |
+
|
688 |
+
if ( $data['lockout_context'] && ! ( $context = unserialize( $data['lockout_context'] ) ) instanceof Lockout\Context ) {
|
689 |
+
$context = null;
|
690 |
+
}
|
691 |
+
|
692 |
+
return new Lockout\Lockout(
|
693 |
+
$id,
|
694 |
+
$data['lockout_type'],
|
695 |
+
new DateTime( $data['lockout_start_gmt'], new DateTimeZone( 'UTC' ) ),
|
696 |
+
new DateTime( $data['lockout_expire_gmt'], new DateTimeZone( 'UTC' ) ),
|
697 |
+
isset( $data['lockout_host'] ) ? $data['lockout_host'] : '',
|
698 |
+
isset( $data['lockout_user'] ) ? $data['lockout_user'] : 0,
|
699 |
+
isset( $data['lockout_username'] ) ? $data['lockout_username'] : '',
|
700 |
+
! empty( $data['lockout_active'] ),
|
701 |
+
$context
|
702 |
+
);
|
703 |
}
|
704 |
|
705 |
/**
|
706 |
* Executes lockout (locks user out)
|
707 |
*
|
708 |
+
* @param Execute_Lock\Context|array $context
|
|
|
709 |
*
|
710 |
* @return void
|
711 |
*/
|
712 |
+
public function execute_lock( $context = array() ) {
|
713 |
+
if ( is_array( $context ) ) {
|
714 |
+
_deprecated_argument( __METHOD__, '5.3.0', '$context should be a \iThemesSecurity\Lib\Lockout\Execute_Lock\Context object.' );
|
715 |
|
716 |
+
$legacy = wp_parse_args( $context, array( 'user_lock' => false, 'network_lock' => false, 'type' => '' ) );
|
717 |
+
$source = $legacy['type'] ? new Lockout_Module( $legacy['type'] ) : new Configurable( 'legacy' );
|
718 |
+
|
719 |
+
if ( ! empty( $legacy['host'] ) ) {
|
720 |
+
$context = new Execute_Lock\Host_Context( $source, $legacy['host'] );
|
721 |
+
|
722 |
+
if ( $legacy['network_lock'] ) {
|
723 |
+
$context->set_network_brute_force();
|
724 |
+
}
|
725 |
+
|
726 |
+
if ( ! empty( $legacy['user'] ) ) {
|
727 |
+
$context->set_login_user_id( $legacy['user'] );
|
728 |
+
}
|
729 |
+
} elseif ( ! empty( $legacy['user'] ) ) {
|
730 |
+
$context = new Execute_Lock\User_Context( $source, $legacy['user'] );
|
731 |
+
} elseif ( ! empty( $legacy['username'] ) ) {
|
732 |
+
$context = new Execute_Lock\Username_Context( $source, $legacy['username'] );
|
733 |
+
} elseif ( $legacy['user_lock'] ) {
|
734 |
+
$context = new Execute_Lock\Username_Context( $source, '' );
|
735 |
+
} else {
|
736 |
+
$context = new Execute_Lock\Host_Context( $source, ITSEC_Lib::get_ip() );
|
737 |
+
|
738 |
+
if ( $legacy['network_lock'] ) {
|
739 |
+
$context->set_network_brute_force();
|
740 |
+
}
|
741 |
+
}
|
742 |
}
|
743 |
|
744 |
+
if ( ! $context instanceof Execute_Lock\Context ) {
|
745 |
+
_deprecated_argument( __METHOD__, '5.3.0', '$context should be a \iThemesSecurity\Lib\Lockout\Execute_Lock\Context object.' );
|
746 |
+
$context = new Execute_Lock\Host_Context( new Configurable( 'legacy' ), ITSEC_Lib::get_ip() );
|
|
|
|
|
|
|
|
|
747 |
}
|
748 |
|
749 |
if ( ITSEC_Lib::is_ip_whitelisted( ITSEC_Lib::get_ip() ) ) {
|
750 |
return;
|
751 |
}
|
752 |
|
753 |
+
if ( ! apply_filters( 'itsec_execute_lock', true, $context ) ) {
|
754 |
+
return;
|
755 |
+
}
|
756 |
+
|
757 |
+
if ( $context instanceof Execute_Lock\Host_Context && $context->is_network_brute_force() ) {
|
758 |
|
759 |
$message = ITSEC_Modules::get_setting( 'global', 'community_lockout_message' );
|
760 |
|
761 |
if ( ! $message ) {
|
762 |
$message = __( 'Your IP address has been flagged as a threat by the iThemes Security network.', 'better-wp-security' );
|
763 |
}
|
764 |
+
} elseif ( $context instanceof Execute_Lock\User_Context || $context instanceof Execute_Lock\Username_Context ) {
|
|
|
765 |
|
766 |
$message = ITSEC_Modules::get_setting( 'global', 'user_lockout_message' );
|
767 |
|
768 |
if ( ! $message ) {
|
769 |
+
$message = __( 'You have been locked out due to too many invalid login attempts.', 'better-wp-security' );
|
770 |
}
|
771 |
+
} else {
|
|
|
|
|
772 |
$message = ITSEC_Modules::get_setting( 'global', 'lockout_message' );
|
773 |
|
774 |
if ( ! $message ) {
|
776 |
}
|
777 |
}
|
778 |
|
779 |
+
$source = $context->get_source();
|
780 |
|
781 |
+
if ( $source instanceof Lockout\Lockout ) {
|
782 |
+
$slug = $source->get_module();
|
783 |
+
} else {
|
784 |
+
$slug = $source->get_source_slug();
|
785 |
+
}
|
|
|
|
|
|
|
|
|
786 |
|
787 |
+
if ( $slug ) {
|
788 |
/**
|
789 |
+
* Filter the lockout message displayed to the user.
|
790 |
*
|
791 |
+
* @param string $message
|
792 |
+
* @param string $type
|
793 |
+
* @param Execute_Lock\Context $context
|
794 |
*/
|
795 |
+
$message = apply_filters( "itsec_{$slug}_lockout_message", $message, $context );
|
796 |
}
|
797 |
|
798 |
$current_user = wp_get_current_user();
|
801 |
wp_logout();
|
802 |
}
|
803 |
|
804 |
+
@header( 'HTTP/1.0 403 Forbidden' );
|
805 |
+
ITSEC_Lib::no_cache();
|
|
|
|
|
|
|
|
|
|
|
806 |
|
807 |
+
$actions = apply_filters( 'itsec_lockout_action_links', array(), $context );
|
|
|
|
|
|
|
|
|
|
|
808 |
|
809 |
+
ob_start();
|
810 |
+
call_user_func( function () use ( $context, $message, $actions ) {
|
811 |
+
require( dirname( __FILE__ ) . '/templates/lockout/lockout.php' );
|
812 |
+
} );
|
813 |
+
|
814 |
+
add_filter( 'wp_die_handler', function () {
|
815 |
+
return '_scalar_wp_die_handler';
|
816 |
+
} );
|
817 |
+
wp_die( ob_get_clean() );
|
818 |
}
|
819 |
|
820 |
/**
|
831 |
$global_settings_url = add_query_arg( array( 'module_type', 'all' ), $global_settings_url );
|
832 |
}
|
833 |
|
834 |
+
$description = '<h4>' . __( 'About Lockouts', 'better-wp-security' ) . '</h4>';
|
835 |
$description .= '<p>';
|
836 |
$description .= sprintf( __( 'Your lockout settings can be configured in <a href="%s" data-module-link="global">Global Settings</a>.', 'better-wp-security' ), esc_url( $global_settings_url ) );
|
837 |
$description .= '<br />';
|
859 |
*
|
860 |
* @since 4.0
|
861 |
*
|
862 |
+
* @param string $type 'all', 'host', 'user' or 'username'.
|
863 |
+
* @param array $args Additional arguments.
|
864 |
*
|
865 |
* @return array all lockouts in the system
|
866 |
*/
|
981 |
|
982 |
if ( ! is_array( $whitelist ) ) {
|
983 |
$whitelist = array();
|
984 |
+
} elseif ( isset( $whitelist['ip'] ) ) {
|
985 |
// Update old format
|
986 |
$whitelist = array(
|
987 |
$whitelist['ip'] => $whitelist['exp'] - ITSEC_Core::get_time_offset(),
|
1016 |
* @param string $ip
|
1017 |
*/
|
1018 |
public function add_to_temp_whitelist( $ip ) {
|
1019 |
+
$whitelist = $this->get_temp_whitelist();
|
1020 |
+
$expiration = ITSEC_Core::get_current_time_gmt() + DAY_IN_SECONDS;
|
1021 |
$refresh_expiration = $expiration - HOUR_IN_SECONDS;
|
1022 |
|
1023 |
+
if ( isset( $whitelist[ $ip ] ) && $whitelist[ $ip ] > $refresh_expiration ) {
|
1024 |
// An update is not needed yet.
|
1025 |
return;
|
1026 |
}
|
1028 |
// Remove expired entries.
|
1029 |
foreach ( $whitelist as $cached_ip => $cached_expiration ) {
|
1030 |
if ( $cached_expiration < ITSEC_Core::get_current_time_gmt() ) {
|
1031 |
+
unset( $whitelist[ $cached_ip ] );
|
1032 |
}
|
1033 |
}
|
1034 |
|
1035 |
+
$whitelist[ $ip ] = $expiration;
|
1036 |
|
1037 |
update_site_option( 'itsec_temp_whitelist_ip', $whitelist );
|
1038 |
}
|
1045 |
public function remove_from_temp_whitelist( $ip ) {
|
1046 |
$whitelist = $this->get_temp_whitelist();
|
1047 |
|
1048 |
+
if ( ! isset( $whitelist[ $ip ] ) ) {
|
1049 |
return;
|
1050 |
}
|
1051 |
|
1052 |
+
unset( $whitelist[ $ip ] );
|
1053 |
|
1054 |
update_site_option( 'itsec_temp_whitelist_ip', $whitelist );
|
1055 |
}
|
1073 |
}
|
1074 |
|
1075 |
$whitelist = $this->get_temp_whitelist();
|
1076 |
+
$ip = ITSEC_Lib::get_ip();
|
1077 |
|
1078 |
+
if ( isset( $whitelist[ $ip ] ) && $whitelist[ $ip ] > ITSEC_Core::get_current_time() ) {
|
1079 |
return true;
|
1080 |
}
|
1081 |
|
1082 |
return false;
|
1083 |
}
|
1084 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1085 |
/**
|
1086 |
* Inserts an IP address into the htaccess ban list.
|
1087 |
*
|
1151 |
*
|
1152 |
* @since 3.6.0
|
1153 |
*
|
1154 |
+
* @param array $verbs of verbs.
|
1155 |
*
|
1156 |
* @return array Array of verbs.
|
1157 |
*/
|
1195 |
/**
|
1196 |
* Get lockout details.
|
1197 |
*
|
1198 |
+
* @param int $id
|
1199 |
+
* @param string $return
|
1200 |
*
|
1201 |
+
* @return Lockout\Lockout|array|false
|
1202 |
+
* @throws Exception
|
1203 |
*/
|
1204 |
+
public function get_lockout( $id, $return = ARRAY_A ) {
|
1205 |
global $wpdb;
|
1206 |
|
1207 |
$results = $wpdb->get_results( $wpdb->prepare(
|
1213 |
return false;
|
1214 |
}
|
1215 |
|
1216 |
+
$data = $results[0];
|
1217 |
+
|
1218 |
+
if ( $return === OBJECT ) {
|
1219 |
+
return $this->hydrate_lockout_entity( $id, $data );
|
1220 |
+
}
|
1221 |
+
|
1222 |
+
return $data;
|
1223 |
}
|
1224 |
|
1225 |
/**
|
1231 |
*
|
1232 |
* @return bool true on success or false
|
1233 |
*/
|
1234 |
+
public function release_lockout( $id = 0 ) {
|
|
|
1235 |
global $wpdb;
|
1236 |
|
1237 |
+
if ( ! $id ) {
|
1238 |
+
return false;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1239 |
}
|
1240 |
|
1241 |
+
return (bool) $wpdb->update(
|
1242 |
+
$wpdb->base_prefix . 'itsec_lockouts',
|
1243 |
+
array(
|
1244 |
+
'lockout_active' => 0,
|
1245 |
+
),
|
1246 |
+
array(
|
1247 |
+
'lockout_id' => (int) $id,
|
1248 |
+
)
|
1249 |
+
);
|
1250 |
}
|
1251 |
|
1252 |
/**
|
1285 |
*
|
1286 |
* @since 4.0
|
1287 |
*
|
1288 |
+
* @param Lockout\Context $context
|
1289 |
+
* @param string $host_expiration when the host login expires
|
1290 |
+
* @param string $user_expiration when the user lockout expires
|
1291 |
+
* @param string $reason the reason for the lockout to show to the user
|
|
|
|
|
1292 |
*
|
1293 |
* @return void
|
1294 |
*/
|
1295 |
+
private function send_lockout_email( Lockout\Context $context, $host_expiration, $user_expiration, $reason ) {
|
1296 |
|
1297 |
$nc = ITSEC_Core::get_notification_center();
|
1298 |
|
1300 |
return;
|
1301 |
}
|
1302 |
|
1303 |
+
$lockouts = array();
|
1304 |
+
$show_remove_ip_ban_message = false;
|
1305 |
$show_remove_lockout_message = false;
|
1306 |
|
1307 |
+
if (
|
1308 |
+
( $context instanceof Lockout\User_Context && $user_id = $context->get_user_id() ) ||
|
1309 |
+
( $context instanceof Lockout\Host_Context && $context->is_user_limit_triggered() && $user_id = $context->get_login_user_id() )
|
1310 |
+
) {
|
|
|
|
|
1311 |
$show_remove_lockout_message = true;
|
1312 |
|
1313 |
$lockouts[] = array(
|
1314 |
'type' => 'user',
|
1315 |
+
'id' => get_userdata( $user_id )->user_login,
|
1316 |
+
'until' => $user_expiration,
|
1317 |
+
'reason' => $reason,
|
1318 |
+
);
|
1319 |
+
}
|
1320 |
+
|
1321 |
+
if (
|
1322 |
+
( $context instanceof Lockout\Username_Context && $username = $context->get_username() ) ||
|
1323 |
+
( $context instanceof Lockout\Host_Context && $context->is_user_limit_triggered() && $username = $context->get_login_username() )
|
1324 |
+
) {
|
1325 |
+
$lockouts[] = array(
|
1326 |
+
'type' => 'username',
|
1327 |
'id' => $username,
|
1328 |
'until' => $user_expiration,
|
1329 |
'reason' => $reason,
|
1330 |
);
|
1331 |
}
|
1332 |
|
1333 |
+
if ( $context instanceof Lockout\Host_Context ) {
|
1334 |
if ( false === $host_expiration ) {
|
1335 |
+
$host_expiration = __( 'Permanently', 'better-wp-security' );
|
1336 |
$show_remove_ip_ban_message = true;
|
1337 |
} else {
|
1338 |
$show_remove_lockout_message = true;
|
1340 |
|
1341 |
$lockouts[] = array(
|
1342 |
'type' => 'host',
|
1343 |
+
'id' => '<a href="' . esc_url( ITSEC_Lib::get_trace_ip_link( $context->get_host() ) ) . '">' . $context->get_host() . '</a>',
|
1344 |
'until' => $host_expiration,
|
1345 |
'reason' => $reason,
|
1346 |
);
|
1347 |
}
|
1348 |
|
|
|
1349 |
$mail = $nc->mail();
|
1350 |
|
1351 |
$mail->add_header( esc_html__( 'Site Lockout Notification', 'better-wp-security' ), esc_html__( 'Site Lockout Notification', 'better-wp-security' ) );
|
1363 |
|
1364 |
$mail->add_footer();
|
1365 |
|
|
|
1366 |
$subject = $mail->prepend_site_url_to_subject( $nc->get_subject( 'lockout' ) );
|
1367 |
$subject = apply_filters( 'itsec_lockout_email_subject', $subject );
|
1368 |
$mail->set_subject( $subject, false );
|
1370 |
$nc->send( 'lockout', $mail );
|
1371 |
}
|
1372 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1373 |
public function filter_entry_for_list_display( $entry, $code, $data ) {
|
1374 |
$entry['module_display'] = esc_html__( 'Lockout', 'better-wp-security' );
|
1375 |
|
1376 |
if ( 'whitelisted-host-triggered-blacklist' === $code ) {
|
1377 |
$entry['description'] = esc_html__( 'Whitelisted Host Triggered Blacklist', 'better-wp-security' );
|
1378 |
+
} elseif ( 'host-triggered-blacklist' === $code ) {
|
1379 |
$entry['description'] = esc_html__( 'Host Triggered Blacklist', 'better-wp-security' );
|
1380 |
+
} elseif ( 'whitelisted-host-triggered-host-lockout' === $code ) {
|
1381 |
$entry['description'] = esc_html__( 'Whitelisted Host Triggered Host Lockout', 'better-wp-security' );
|
1382 |
+
} elseif ( 'host-lockout' === $code ) {
|
1383 |
if ( isset( $data[0] ) ) {
|
1384 |
$entry['description'] = sprintf( wp_kses( __( 'Host Lockout: <code>%s</code>', 'better-wp-security' ), array( 'code' => array() ) ), $data[0] );
|
1385 |
} else {
|
1386 |
$entry['description'] = esc_html__( 'Host Lockout', 'better-wp-security' );
|
1387 |
}
|
1388 |
+
} elseif ( 'whitelisted-host-triggered-user-lockout' === $code ) {
|
1389 |
$entry['description'] = esc_html__( 'Whitelisted Host Triggered User Lockout', 'better-wp-security' );
|
1390 |
+
} elseif ( 'user-lockout' === $code ) {
|
1391 |
if ( isset( $data[0] ) ) {
|
1392 |
$user = get_user_by( 'id', $data[0] );
|
1393 |
}
|
1397 |
} else {
|
1398 |
$entry['description'] = esc_html__( 'User Lockout', 'better-wp-security' );
|
1399 |
}
|
1400 |
+
} elseif ( 'whitelisted-host-triggered-username-lockout' === $code ) {
|
1401 |
$entry['description'] = esc_html__( 'Whitelisted Host Triggered Username Lockout', 'better-wp-security' );
|
1402 |
+
} elseif ( 'username-lockout' === $code ) {
|
1403 |
if ( isset( $data[0] ) ) {
|
1404 |
$entry['description'] = sprintf( wp_kses( __( 'Username Lockout: <code>%s</code>', 'better-wp-security' ), array( 'code' => array() ) ), $data[0] );
|
1405 |
} else {
|
core/modules/404-detection/class-itsec-four-oh-four.php
CHANGED
@@ -1,5 +1,7 @@
|
|
1 |
<?php
|
2 |
|
|
|
|
|
3 |
class ITSEC_Four_Oh_Four {
|
4 |
|
5 |
private $settings;
|
@@ -34,7 +36,7 @@ class ITSEC_Four_Oh_Four {
|
|
34 |
! in_array( '.' . pathinfo( $uri[0], PATHINFO_EXTENSION ), $this->settings['types'], true )
|
35 |
) {
|
36 |
ITSEC_Log::add_notice( 'four_oh_four', 'found_404', array( 'SERVER' => $_SERVER ) );
|
37 |
-
$itsec_lockout->do_lockout( 'four_oh_four' );
|
38 |
} else {
|
39 |
do_action( 'itsec_four_oh_four_whitelisted', $uri );
|
40 |
}
|
@@ -43,9 +45,9 @@ class ITSEC_Four_Oh_Four {
|
|
43 |
/**
|
44 |
* Register 404 detection for lockout
|
45 |
*
|
46 |
-
* @param
|
47 |
*
|
48 |
-
* @return array
|
49 |
*/
|
50 |
public function register_lockout( $lockout_modules ) {
|
51 |
|
1 |
<?php
|
2 |
|
3 |
+
use iThemesSecurity\Lib\Lockout\Host_Context;
|
4 |
+
|
5 |
class ITSEC_Four_Oh_Four {
|
6 |
|
7 |
private $settings;
|
36 |
! in_array( '.' . pathinfo( $uri[0], PATHINFO_EXTENSION ), $this->settings['types'], true )
|
37 |
) {
|
38 |
ITSEC_Log::add_notice( 'four_oh_four', 'found_404', array( 'SERVER' => $_SERVER ) );
|
39 |
+
$itsec_lockout->do_lockout( new Host_Context( 'four_oh_four' ) );
|
40 |
} else {
|
41 |
do_action( 'itsec_four_oh_four_whitelisted', $uri );
|
42 |
}
|
45 |
/**
|
46 |
* Register 404 detection for lockout
|
47 |
*
|
48 |
+
* @param array $lockout_modules array of lockout modules
|
49 |
*
|
50 |
+
* @return array
|
51 |
*/
|
52 |
public function register_lockout( $lockout_modules ) {
|
53 |
|
core/modules/brute-force/class-itsec-brute-force.php
CHANGED
@@ -1,16 +1,16 @@
|
|
1 |
<?php
|
2 |
|
|
|
|
|
3 |
class ITSEC_Brute_Force {
|
4 |
|
5 |
-
private
|
6 |
-
$settings,
|
7 |
-
$username;
|
8 |
|
9 |
function run() {
|
10 |
|
11 |
$this->settings = ITSEC_Modules::get_settings( 'brute-force' );
|
12 |
|
13 |
-
add_filter( 'authenticate', array( $this, 'authenticate' ), 10000,
|
14 |
add_filter( 'itsec_lockout_modules', array( $this, 'itsec_lockout_modules' ) );
|
15 |
add_filter( 'jetpack_get_default_modules', array( $this, 'jetpack_get_default_modules' ) ); //disable jetpack protect via Geoge Stephanis
|
16 |
|
@@ -23,11 +23,10 @@ class ITSEC_Brute_Force {
|
|
23 |
*
|
24 |
* @param object $user user or wordpress error
|
25 |
* @param string $username username attempted
|
26 |
-
* @param string $password password attempted
|
27 |
*
|
28 |
* @return WP_User|WP_Error|null
|
29 |
*/
|
30 |
-
public function authenticate( $user, $username = ''
|
31 |
/** @var ITSEC_Lockout $itsec_lockout */
|
32 |
global $itsec_lockout;
|
33 |
|
@@ -40,37 +39,44 @@ class ITSEC_Brute_Force {
|
|
40 |
// Failed authentication.
|
41 |
|
42 |
$details = ITSEC_Lib::get_login_details();
|
43 |
-
$SERVER
|
44 |
|
45 |
if ( 'admin' === $username && $this->settings['auto_ban_admin'] ) {
|
46 |
ITSEC_Log::add_notice( 'brute_force', 'auto-ban-admin-username', compact( 'details', 'user', 'username', 'SERVER' ) );
|
47 |
|
48 |
-
$
|
|
|
|
|
49 |
} else {
|
|
|
50 |
$user_id = false;
|
51 |
-
$code
|
52 |
|
53 |
if ( empty( $username ) ) {
|
54 |
-
$itsec_lockout->check_lockout( false, false, '
|
55 |
} else {
|
56 |
-
|
|
|
|
|
57 |
|
58 |
-
if (
|
59 |
-
$
|
|
|
60 |
$code = "invalid-login::username-{$username}";
|
61 |
} else {
|
62 |
-
$
|
|
|
63 |
$code = "invalid-login::user-{$user_id}";
|
64 |
}
|
65 |
}
|
66 |
|
67 |
ITSEC_Log::add_notice( 'brute_force', $code, compact( 'details', 'user', 'username', 'user_id', 'SERVER' ) );
|
68 |
|
69 |
-
$itsec_lockout->do_lockout(
|
70 |
}
|
71 |
} else {
|
72 |
// Successful authentication. Check to ensure that they are not locked out.
|
73 |
-
$itsec_lockout->check_lockout( $user, false, '
|
74 |
}
|
75 |
|
76 |
return $user;
|
@@ -82,7 +88,7 @@ class ITSEC_Brute_Force {
|
|
82 |
*
|
83 |
* @since 4.0
|
84 |
*
|
85 |
-
* @param
|
86 |
*
|
87 |
* @return array array of lockout modules
|
88 |
*/
|
1 |
<?php
|
2 |
|
3 |
+
use iThemesSecurity\Lib\Lockout\Host_Context;
|
4 |
+
|
5 |
class ITSEC_Brute_Force {
|
6 |
|
7 |
+
private $settings;
|
|
|
|
|
8 |
|
9 |
function run() {
|
10 |
|
11 |
$this->settings = ITSEC_Modules::get_settings( 'brute-force' );
|
12 |
|
13 |
+
add_filter( 'authenticate', array( $this, 'authenticate' ), 10000, 2 ); // Set a very late priority so that we run after actual authentication takes place.
|
14 |
add_filter( 'itsec_lockout_modules', array( $this, 'itsec_lockout_modules' ) );
|
15 |
add_filter( 'jetpack_get_default_modules', array( $this, 'jetpack_get_default_modules' ) ); //disable jetpack protect via Geoge Stephanis
|
16 |
|
23 |
*
|
24 |
* @param object $user user or wordpress error
|
25 |
* @param string $username username attempted
|
|
|
26 |
*
|
27 |
* @return WP_User|WP_Error|null
|
28 |
*/
|
29 |
+
public function authenticate( $user, $username = '' ) {
|
30 |
/** @var ITSEC_Lockout $itsec_lockout */
|
31 |
global $itsec_lockout;
|
32 |
|
39 |
// Failed authentication.
|
40 |
|
41 |
$details = ITSEC_Lib::get_login_details();
|
42 |
+
$SERVER = $_SERVER;
|
43 |
|
44 |
if ( 'admin' === $username && $this->settings['auto_ban_admin'] ) {
|
45 |
ITSEC_Log::add_notice( 'brute_force', 'auto-ban-admin-username', compact( 'details', 'user', 'username', 'SERVER' ) );
|
46 |
|
47 |
+
$context = new Host_Context( 'brute_force_admin_user' );
|
48 |
+
$context->set_login_username( 'admin' );
|
49 |
+
$itsec_lockout->do_lockout( $context );
|
50 |
} else {
|
51 |
+
$context = new Host_Context( 'brute_force' );
|
52 |
$user_id = false;
|
53 |
+
$code = 'invalid-login';
|
54 |
|
55 |
if ( empty( $username ) ) {
|
56 |
+
$itsec_lockout->check_lockout( false, false, 'brute_force' );
|
57 |
} else {
|
58 |
+
ITSEC_Lib::load( 'login' );
|
59 |
+
$found_user = ITSEC_Lib_Login::get_user( $username );
|
60 |
+
$user_id = $found_user ? $found_user->ID : 0;
|
61 |
|
62 |
+
if ( ! $user_id ) {
|
63 |
+
$context->set_login_username( $username );
|
64 |
+
$itsec_lockout->check_lockout( false, $username, 'brute_force' );
|
65 |
$code = "invalid-login::username-{$username}";
|
66 |
} else {
|
67 |
+
$context->set_login_user_id( $user_id );
|
68 |
+
$itsec_lockout->check_lockout( $user_id, false, 'brute_force' );
|
69 |
$code = "invalid-login::user-{$user_id}";
|
70 |
}
|
71 |
}
|
72 |
|
73 |
ITSEC_Log::add_notice( 'brute_force', $code, compact( 'details', 'user', 'username', 'user_id', 'SERVER' ) );
|
74 |
|
75 |
+
$itsec_lockout->do_lockout( $context );
|
76 |
}
|
77 |
} else {
|
78 |
// Successful authentication. Check to ensure that they are not locked out.
|
79 |
+
$itsec_lockout->check_lockout( $user, false, 'brute_force' );
|
80 |
}
|
81 |
|
82 |
return $user;
|
88 |
*
|
89 |
* @since 4.0
|
90 |
*
|
91 |
+
* @param array $lockout_modules array of lockout modules
|
92 |
*
|
93 |
* @return array array of lockout modules
|
94 |
*/
|
core/modules/core/class-rest-core-admin-notices-controller.php
CHANGED
@@ -9,7 +9,7 @@ class ITSEC_REST_Core_Admin_Notices_Controller extends WP_REST_Controller {
|
|
9 |
'permission_callback' => array( $this, 'get_items_permissions_check' ),
|
10 |
) );
|
11 |
|
12 |
-
register_rest_route( 'ithemes-security/v1', 'admin-notices/(?P<notice>[\w
|
13 |
'methods' => WP_REST_Server::EDITABLE,
|
14 |
'callback' => array( $this, 'update_item' ),
|
15 |
'permission_callback' => array( $this, 'update_item_permissions_check' ),
|
@@ -104,7 +104,7 @@ class ITSEC_REST_Core_Admin_Notices_Controller extends WP_REST_Controller {
|
|
104 |
return $error;
|
105 |
}
|
106 |
|
107 |
-
return null;
|
108 |
}
|
109 |
|
110 |
public function update_item_permissions_check( $request ) {
|
9 |
'permission_callback' => array( $this, 'get_items_permissions_check' ),
|
10 |
) );
|
11 |
|
12 |
+
register_rest_route( 'ithemes-security/v1', 'admin-notices/(?P<notice>[\w\-\.]+)/(?P<action>[\w\-]+)', array(
|
13 |
'methods' => WP_REST_Server::EDITABLE,
|
14 |
'callback' => array( $this, 'update_item' ),
|
15 |
'permission_callback' => array( $this, 'update_item_permissions_check' ),
|
104 |
return $error;
|
105 |
}
|
106 |
|
107 |
+
return new WP_REST_Response( null, WP_Http::NO_CONTENT );
|
108 |
}
|
109 |
|
110 |
public function update_item_permissions_check( $request ) {
|
core/modules/core/img/security-ebook.png
CHANGED
Binary file
|
core/modules/core/img/sync-logo.png
CHANGED
Binary file
|
core/modules/core/sidebar-widget-mail-list-signup.php
CHANGED
@@ -29,7 +29,7 @@ class ITSEC_Settings_Page_Sidebar_Widget_Mail_List_Signup extends ITSEC_Settings
|
|
29 |
method="post" id="mc-embedded-subscribe-form" name="mc-embedded-subscribe-form" class="validate"
|
30 |
target="_blank" novalidate>
|
31 |
<div style="text-align: center;">
|
32 |
-
<img src="<?php echo plugins_url( 'img/security-ebook.png', __FILE__ ) ?>"
|
33 |
</div>
|
34 |
<p><?php _e( 'Get tips for securing your site + the latest WordPress security updates, news and releases from iThemes.', 'better-wp-security' ); ?></p>
|
35 |
|
29 |
method="post" id="mc-embedded-subscribe-form" name="mc-embedded-subscribe-form" class="validate"
|
30 |
target="_blank" novalidate>
|
31 |
<div style="text-align: center;">
|
32 |
+
<img src="<?php echo plugins_url( 'img/security-ebook.png', __FILE__ ) ?>" style="max-width: 100%" alt="WordPress Security - A Pocket Guide">
|
33 |
</div>
|
34 |
<p><?php _e( 'Get tips for securing your site + the latest WordPress security updates, news and releases from iThemes.', 'better-wp-security' ); ?></p>
|
35 |
|
core/modules/core/sidebar-widget-sync-cross-promo.php
CHANGED
@@ -12,15 +12,14 @@ class ITSEC_Settings_Page_Sidebar_Widget_Sync_Cross_Promo extends ITSEC_Settings
|
|
12 |
public function render( $form ) {
|
13 |
?>
|
14 |
<div style="text-align: center;">
|
15 |
-
<img src="<?php echo plugins_url( 'img/sync-logo.png', __FILE__ ) ?>" width="
|
16 |
-
height="65" alt="Manage Your Sites Remotely">
|
17 |
</div>
|
18 |
<?php
|
19 |
|
20 |
-
echo '<p>' . __( 'Manage updates
|
21 |
-
echo '<p>' . __( 'Integrated with iThemes Security, so you can release lockouts and turn Away Mode on or off right from your Sync dashboard
|
22 |
echo '<div style="text-align: center;">';
|
23 |
-
echo '<p><a class="button-primary" href="
|
24 |
echo '</div>';
|
25 |
}
|
26 |
|
12 |
public function render( $form ) {
|
13 |
?>
|
14 |
<div style="text-align: center;">
|
15 |
+
<img src="<?php echo plugins_url( 'img/sync-logo.png', __FILE__ ) ?>" style="max-width: 100%" alt="Manage Your Sites Remotely">
|
|
|
16 |
</div>
|
17 |
<?php
|
18 |
|
19 |
+
echo '<p>' . __( 'Manage updates (and much more!) for your WordPress websites all in one place. Save time logging in to multiple websites to perform WordPress admin tasks.', 'better-wp-security' ) . '</p>';
|
20 |
+
echo '<p>' . __( 'Integrated with iThemes Security, so you can release lockouts, whitelist IPs, and turn Away Mode on or off right from your Sync dashboard.', 'better-wp-security' ) . '</p>';
|
21 |
echo '<div style="text-align: center;">';
|
22 |
+
echo '<p><a class="button-primary" href="https://ithemes.com/member/cart.php?action=add&id=523" target="_blank" rel="noopener noreferrer">' . __( 'Free 30 Day Trial', 'better-wp-security' ) . '</a></p>';
|
23 |
echo '</div>';
|
24 |
}
|
25 |
|
core/modules/email-confirmation/active.php
ADDED
@@ -0,0 +1,4 @@
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
require_once __DIR__ . '/class-itsec-email-confirmation.php';
|
3 |
+
$module = new ITSEC_Email_Confirmation();
|
4 |
+
$module->run();
|
core/modules/email-confirmation/class-itsec-email-confirmation.php
ADDED
@@ -0,0 +1,51 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
class ITSEC_Email_Confirmation {
|
4 |
+
|
5 |
+
public function run() {
|
6 |
+
add_action( 'after_password_reset', array( $this, 'after_password_reset' ) );
|
7 |
+
add_action( 'profile_update', array( $this, 'on_user_update' ), 10, 2 );
|
8 |
+
}
|
9 |
+
|
10 |
+
/**
|
11 |
+
* When a user's password has been reset, mark the user's email address as confirmed.
|
12 |
+
*
|
13 |
+
* @param WP_User $user
|
14 |
+
*/
|
15 |
+
public function after_password_reset( $user ) {
|
16 |
+
ITSEC_Lib::load( 'email-confirmation' );
|
17 |
+
ITSEC_Lib_Email_Confirmation::mark_email_as_confirmed( $user );
|
18 |
+
}
|
19 |
+
|
20 |
+
/**
|
21 |
+
* When a user's email is updated, mark their email confirmation as not confirmed.
|
22 |
+
*
|
23 |
+
* @param int $user_id User id.
|
24 |
+
* @param object $old_data Old data.
|
25 |
+
*/
|
26 |
+
public function on_user_update( $user_id, $old_data ) {
|
27 |
+
$user = get_userdata( $user_id );
|
28 |
+
|
29 |
+
if ( $user->user_email === $old_data->user_email ) {
|
30 |
+
return;
|
31 |
+
}
|
32 |
+
|
33 |
+
ITSEC_Lib::load( 'email-confirmation' );
|
34 |
+
$change = get_user_meta( $user->ID, '_new_email', true );
|
35 |
+
|
36 |
+
if (
|
37 |
+
IS_PROFILE_PAGE &&
|
38 |
+
! empty( $_GET['newuseremail'] ) &&
|
39 |
+
is_array( $change ) &&
|
40 |
+
isset( $change['hash'], $change['newemail'] ) &&
|
41 |
+
$change['newemail'] === $user->user_email &&
|
42 |
+
hash_equals( $change['hash'], $_GET['newuseremail'] )
|
43 |
+
) {
|
44 |
+
ITSEC_Lib_Email_Confirmation::mark_email_as_confirmed( $user );
|
45 |
+
|
46 |
+
return;
|
47 |
+
}
|
48 |
+
|
49 |
+
ITSEC_Lib_Email_Confirmation::mark_email_as_confirmed( $user, false );
|
50 |
+
}
|
51 |
+
}
|
core/modules/email-confirmation/index.php
ADDED
@@ -0,0 +1 @@
|
|
|
1 |
+
<?php // Silence is golden.
|
core/modules/global/settings-page.php
CHANGED
@@ -265,7 +265,7 @@ final class ITSEC_Global_Settings_Page extends ITSEC_Module_Settings_Page {
|
|
265 |
<td>
|
266 |
<?php if ( $proxy_header ) : ?>
|
267 |
<p class="description">
|
268 |
-
<?php printf( esc_html__( 'Security Check Pro has automatically determined the correct header, %s.', 'better-wp-security' ), '<code>' .
|
269 |
</p>
|
270 |
<?php else: ?>
|
271 |
<?php $form->add_select( 'proxy', $proxy ); ?>
|
265 |
<td>
|
266 |
<?php if ( $proxy_header ) : ?>
|
267 |
<p class="description">
|
268 |
+
<?php printf( esc_html__( 'Security Check Pro has automatically determined the correct header, %s.', 'better-wp-security' ), '<code>' . esc_html( is_array( $proxy_header ) ? implode( ', ', $proxy_header ) : $proxy_header ) . '</code>' ); ?>
|
269 |
</p>
|
270 |
<?php else: ?>
|
271 |
<?php $form->add_select( 'proxy', $proxy ); ?>
|
core/modules/global/settings.php
CHANGED
@@ -84,6 +84,12 @@ final class ITSEC_Global_Settings_New extends ITSEC_Settings {
|
|
84 |
|
85 |
$new->uninstall();
|
86 |
|
|
|
|
|
|
|
|
|
|
|
|
|
87 |
foreach ( $current->get_recurring_events() as $event ) {
|
88 |
$new->schedule( $event['schedule'], $event['id'], $event['data'], array(
|
89 |
'fire_at' => $event['fire_at'],
|
84 |
|
85 |
$new->uninstall();
|
86 |
|
87 |
+
foreach ( $current->get_custom_schedules() as $slug => $interval ) {
|
88 |
+
$new->register_custom_schedule( $slug, $interval );
|
89 |
+
}
|
90 |
+
|
91 |
+
$new->run();
|
92 |
+
|
93 |
foreach ( $current->get_recurring_events() as $event ) {
|
94 |
$new->schedule( $event['schedule'], $event['id'], $event['data'], array(
|
95 |
'fire_at' => $event['fire_at'],
|
core/modules/ipcheck/class-itsec-ipcheck.php
CHANGED
@@ -1,5 +1,8 @@
|
|
1 |
<?php
|
2 |
|
|
|
|
|
|
|
3 |
/**
|
4 |
* iThemes IPCheck API Wrapper.
|
5 |
*
|
@@ -28,14 +31,23 @@ class ITSEC_IPCheck {
|
|
28 |
|
29 |
$enable_ban = ITSEC_Modules::get_setting( 'network-brute-force', 'enable_ban' );
|
30 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
31 |
if ( is_wp_error( $user ) || null == $user ) {
|
32 |
if ( ITSEC_Network_Brute_Force_Utilities::report_ip() && $enable_ban ) {
|
33 |
ITSEC_Log::add_notice( 'ipcheck', 'failed-login-by-blocked-ip', array( 'details' => ITSEC_Lib::get_login_details() ) );
|
34 |
-
$itsec_lockout->execute_lock(
|
35 |
}
|
36 |
} elseif ( $enable_ban && ITSEC_Network_Brute_Force_Utilities::is_ip_banned() ) {
|
37 |
ITSEC_Log::add_critical_issue( 'ipcheck', 'successful-login-by-blocked-ip', array( 'details' => ITSEC_Lib::get_login_details() ) );
|
38 |
-
$itsec_lockout->execute_lock(
|
39 |
}
|
40 |
|
41 |
return $user;
|
1 |
<?php
|
2 |
|
3 |
+
use iThemesSecurity\Lib\Lockout\Execute_Lock\Host_Context;
|
4 |
+
use iThemesSecurity\Lib\Lockout\Execute_Lock\Source\Configurable;
|
5 |
+
|
6 |
/**
|
7 |
* iThemes IPCheck API Wrapper.
|
8 |
*
|
31 |
|
32 |
$enable_ban = ITSEC_Modules::get_setting( 'network-brute-force', 'enable_ban' );
|
33 |
|
34 |
+
$context = new Host_Context( new Configurable( 'network-brute-force' ), ITSEC_Lib::get_ip() );
|
35 |
+
$context->set_network_brute_force();
|
36 |
+
|
37 |
+
if ( $user instanceof WP_User ) {
|
38 |
+
$context->set_login_user_id( $user->ID );
|
39 |
+
} elseif ( $exists = username_exists( $username ) ) {
|
40 |
+
$context->set_login_user_id( $exists );
|
41 |
+
}
|
42 |
+
|
43 |
if ( is_wp_error( $user ) || null == $user ) {
|
44 |
if ( ITSEC_Network_Brute_Force_Utilities::report_ip() && $enable_ban ) {
|
45 |
ITSEC_Log::add_notice( 'ipcheck', 'failed-login-by-blocked-ip', array( 'details' => ITSEC_Lib::get_login_details() ) );
|
46 |
+
$itsec_lockout->execute_lock( $context );
|
47 |
}
|
48 |
} elseif ( $enable_ban && ITSEC_Network_Brute_Force_Utilities::is_ip_banned() ) {
|
49 |
ITSEC_Log::add_critical_issue( 'ipcheck', 'successful-login-by-blocked-ip', array( 'details' => ITSEC_Lib::get_login_details() ) );
|
50 |
+
$itsec_lockout->execute_lock( $context );
|
51 |
}
|
52 |
|
53 |
return $user;
|
core/modules/pro/settings-page.php
CHANGED
@@ -149,3 +149,18 @@ final class ITSEC_Version_Management_Settings_Page extends ITSEC_Module_Settings
|
|
149 |
}
|
150 |
}
|
151 |
new ITSEC_Version_Management_Settings_Page();
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
149 |
}
|
150 |
}
|
151 |
new ITSEC_Version_Management_Settings_Page();
|
152 |
+
|
153 |
+
final class ITSEC_Passwordless_Login_Settings_Page extends ITSEC_Module_Settings_Page {
|
154 |
+
public function __construct() {
|
155 |
+
$this->id = 'passwordless-login';
|
156 |
+
$this->title = __( 'Passwordless Login', 'better-wp-security' );
|
157 |
+
$this->description = __( 'Enable logging in without a password.', 'better-wp-security' );
|
158 |
+
$this->type = 'recommended';
|
159 |
+
$this->pro = true;
|
160 |
+
$this->upsell = true;
|
161 |
+
$this->upsell_url = 'https://ithemes.com/new-wordpress-passwordless-login-ithemes-security/?utm_source=wordpressadmin&utm_medium=widget&utm_campaign=itsecfreecta';
|
162 |
+
|
163 |
+
parent::__construct();
|
164 |
+
}
|
165 |
+
}
|
166 |
+
new ITSEC_Passwordless_Login_Settings_Page();
|
core/modules/security-check/scanner.php
CHANGED
@@ -18,6 +18,7 @@ final class ITSEC_Security_Check_Scanner {
|
|
18 |
'magic-links' => __( 'Magic Links', 'better-wp-security' ),
|
19 |
'malware-scheduling' => __( 'Malware Scan Scheduling', 'better-wp-security' ),
|
20 |
'network-brute-force' => __( 'Network Brute Force Protection', 'better-wp-security' ),
|
|
|
21 |
'strong-passwords' => __( 'Strong Passwords', 'better-wp-security' ),
|
22 |
'two-factor' => __( 'Two-Factor Authentication', 'better-wp-security' ),
|
23 |
'user-logging' => __( 'User Logging', 'better-wp-security' ),
|
@@ -78,6 +79,7 @@ final class ITSEC_Security_Check_Scanner {
|
|
78 |
|
79 |
self::add_network_brute_force_signup();
|
80 |
|
|
|
81 |
self::enforce_password_requirement_enabled( 'strength', __( 'Strong Password Enforcement', 'better-wp-security' ) );
|
82 |
self::enforce_activation( 'two-factor', __( 'Two-Factor Authentication', 'better-wp-security' ) );
|
83 |
self::enforce_setting( 'two-factor', 'available_methods', 'all', esc_html__( 'Changed the Authentication Methods Available to Users setting in Two-Factor Authentication to "All Methods".', 'better-wp-security' ) );
|
18 |
'magic-links' => __( 'Magic Links', 'better-wp-security' ),
|
19 |
'malware-scheduling' => __( 'Malware Scan Scheduling', 'better-wp-security' ),
|
20 |
'network-brute-force' => __( 'Network Brute Force Protection', 'better-wp-security' ),
|
21 |
+
'passwordless-login' => __( 'Passwordless Login', 'better-wp-security' ),
|
22 |
'strong-passwords' => __( 'Strong Passwords', 'better-wp-security' ),
|
23 |
'two-factor' => __( 'Two-Factor Authentication', 'better-wp-security' ),
|
24 |
'user-logging' => __( 'User Logging', 'better-wp-security' ),
|
79 |
|
80 |
self::add_network_brute_force_signup();
|
81 |
|
82 |
+
self::enforce_activation( 'passwordless-login', __( 'Passwordless Login', 'better-wp-security' ) );
|
83 |
self::enforce_password_requirement_enabled( 'strength', __( 'Strong Password Enforcement', 'better-wp-security' ) );
|
84 |
self::enforce_activation( 'two-factor', __( 'Two-Factor Authentication', 'better-wp-security' ) );
|
85 |
self::enforce_setting( 'two-factor', 'available_methods', 'all', esc_html__( 'Changed the Authentication Methods Available to Users setting in Two-Factor Authentication to "All Methods".', 'better-wp-security' ) );
|
core/modules/system-tweaks/class-itsec-system-tweaks.php
CHANGED
@@ -100,6 +100,10 @@ final class ITSEC_System_Tweaks {
|
|
100 |
return;
|
101 |
}
|
102 |
|
|
|
|
|
|
|
|
|
103 |
@header( 'HTTP/1.1 414 Request-URI Too Long' );
|
104 |
@header( 'Status: 414 Request-URI Too Long' );
|
105 |
@header( 'Cache-Control: no-cache, must-revalidate' );
|
100 |
return;
|
101 |
}
|
102 |
|
103 |
+
if ( $_SERVER['REQUEST_METHOD'] === 'GET' && isset( $GLOBALS['pagenow'], $_GET['action'] ) && 'wp-login.php' === $GLOBALS['pagenow'] ) {
|
104 |
+
return;
|
105 |
+
}
|
106 |
+
|
107 |
@header( 'HTTP/1.1 414 Request-URI Too Long' );
|
108 |
@header( 'Status: 414 Request-URI Too Long' );
|
109 |
@header( 'Cache-Control: no-cache, must-revalidate' );
|
core/package.json
CHANGED
@@ -109,6 +109,8 @@
|
|
109 |
"test-unit:coverage": "npm run test-unit -- --coverage",
|
110 |
"test-unit:update": "npm run test-unit -- --updateSnapshot",
|
111 |
"test-unit:watch": "npm run test-unit -- --watch",
|
112 |
-
"watch": "./node_modules/.bin/webpack --watch"
|
|
|
|
|
113 |
}
|
114 |
}
|
109 |
"test-unit:coverage": "npm run test-unit -- --coverage",
|
110 |
"test-unit:update": "npm run test-unit -- --updateSnapshot",
|
111 |
"test-unit:watch": "npm run test-unit -- --watch",
|
112 |
+
"watch": "./node_modules/.bin/webpack --watch",
|
113 |
+
"test-acceptance": "docker-compose exec -w /bitnami/wordpress/wp-content/plugins/ithemes-security-pro wordpress ./vendor/bin/codecept run acceptance",
|
114 |
+
"test-acceptance:build": "docker-compose exec -w /bitnami/wordpress/wp-content/plugins/ithemes-security-pro wordpress ./vendor/bin/codecept build"
|
115 |
}
|
116 |
}
|
core/templates/index.php
ADDED
@@ -0,0 +1 @@
|
|
|
1 |
+
<?php // Silence is golden.
|
core/templates/lockout/icon.svg
ADDED
@@ -0,0 +1 @@
|
|
|
1 |
+
<svg xmlns="http://www.w3.org/2000/svg" width="547" height="305.817" viewBox="0 0 547 305.817"><defs><style>.a{fill:#0061a8;}.b{fill:#e1f2fc;}.b,.f,.g{stroke:#0083e3;}.c{fill:#0083e3;}.d{fill:#fff;}.e,.f,.g{fill:none;}.f,.g{stroke-linecap:round;}.f{stroke-width:2px;}.g{stroke-width:3px;}</style></defs><g transform="translate(-152 -220.183)"><ellipse class="a" cx="273.5" cy="56.5" rx="273.5" ry="56.5" transform="translate(152 413)"/><g transform="translate(226.992 220.683)"><g transform="translate(0 0)"><path class="b" d="M356.159,304.5A11.65,11.65,0,0,0,344.5,316.14V524.175a7.765,7.765,0,0,0,7.773,7.759h322.6a7.765,7.765,0,0,0,7.772-7.759V316.14a11.649,11.649,0,0,0-11.659-11.64Z" transform="translate(-307.906 -304.5)"/><path class="c" d="M371,529.473H687.369V334H371Z" transform="translate(-322.132 -320.336)"/><path class="d" d="M373,529.62H687.517V336H373Z" transform="translate(-323.205 -321.409)"/><path class="e" d="M281.281,803.654c-5.169,0-11.636-3.094-14.4-4.414-.394-.19-.672-.324-.88-.417V793H676.863v5.823c-.208.1-.486.227-.88.417-2.742,1.32-9.162,4.415-14.29,4.415H281.281Z" transform="translate(-265.768 -566.724)"/><path class="b" d="M677.753,792.5H265.5v7.008c.477-.009,9.273,5.035,16,5.035H661.861c6.684,0,15.415-5.049,15.874-5.049.014,0,.019-.042.019-.042Z" transform="translate(-265.5 -566.456)"/><path class="c" d="M266.633,805l-.463.926h410.4l.463-.926Z" transform="translate(-265.86 -573.166)"/><path class="c" d="M696.416,796.632h-46.9A4.548,4.548,0,0,1,645,792.047V792h56.048v.047a4.549,4.549,0,0,1-4.516,4.586" transform="translate(-469.214 -566.188)"/><path class="c" d="M705.412,319.951a1.621,1.621,0,1,1-1.621-1.621,1.623,1.623,0,0,1,1.621,1.621" transform="translate(-499.902 -311.924)"/><circle class="c" cx="4.725" cy="4.725" r="4.725" transform="translate(162.607 100.885)"/><circle class="c" cx="4.725" cy="4.725" r="4.725" transform="translate(234.106 99.885)"/><path class="c" d="M21.4,10.325,58.811,9.113c2.11,0,3.82-2.04,3.82-4.557S60.92,0,58.811,0L-9.755,8.147l10,.966Z" transform="matrix(-0.998, 0.07, -0.07, -0.998, 262.782, 83.763)"/><path class="c" d="M21.952-.07l15.42-5.575c2.357,0,33.408,8.876,33.408,10.98a4.059,4.059,0,0,1-4.268,3.81L38.491,1.465C35.036-.316-3.517,9.848-2.523,9.848S21.952-.07,21.952-.07Z" transform="translate(109.095 70.919) rotate(8)"/><line class="f" x2="50" transform="translate(127.508 100.817)"/><line class="f" x2="50" transform="translate(199.508 100.817)"/><line class="g" x2="32" transform="translate(199.508 150.817)"/></g></g></g></svg>
|
core/templates/lockout/index.php
ADDED
@@ -0,0 +1 @@
|
|
|
1 |
+
<?php // Silence is golden.
|
core/templates/lockout/lamp-light.svg
ADDED
@@ -0,0 +1 @@
|
|
|
1 |
+
<svg xmlns="http://www.w3.org/2000/svg" width="544.535" height="653" viewBox="0 0 544.535 653"><defs><style>.a{fill:#f7fcfe;}.b{fill:#ffcd0c;}.c{fill:#d5d5d5;}.d{filter:url(#c);}.e{filter:url(#a);}</style><filter id="a" x="0" y="0" width="544.535" height="653" filterUnits="userSpaceOnUse"><feOffset dy="3" input="SourceAlpha"/><feGaussianBlur result="b"/><feFlood flood-opacity="0.161"/><feComposite operator="in" in2="b"/><feComposite in="SourceGraphic"/></filter><filter id="c" x="110" y="0" width="314.535" height="80.329" filterUnits="userSpaceOnUse"><feOffset dy="3" input="SourceAlpha"/><feGaussianBlur result="d"/><feFlood flood-opacity="0.161"/><feComposite operator="in" in2="d"/><feComposite in="SourceGraphic"/></filter></defs><g transform="translate(-684)"><g class="e" transform="matrix(1, 0, 0, 1, 684, 0)"><path class="a" d="M-3.694,0H274.229L412.535,650H-132Z" transform="translate(132)"/></g><g transform="translate(794)"><path class="b" d="M61,60.67a60.626,60.626,0,0,1-23.673-4.763,61,61,0,0,1-10.338-5.59,61.41,61.41,0,0,1-9.015-7.4,61.431,61.431,0,0,1-7.45-8.977A61.042,61.042,0,0,1,4.883,23.629,60.6,60.6,0,0,1,0,0H122a60.6,60.6,0,0,1-4.883,23.629,61.042,61.042,0,0,1-5.641,10.306,61.439,61.439,0,0,1-7.45,8.977,61.4,61.4,0,0,1-9.015,7.4,61,61,0,0,1-10.338,5.59A60.63,60.63,0,0,1,61,60.67Z" transform="translate(97 57.33)"/><g class="d" transform="matrix(1, 0, 0, 1, -110, 0)"><path class="c" d="M-3.694,0H274.229l18.306,77.329H-22Z" transform="translate(132)"/></g></g></g></svg>
|
core/templates/lockout/lockout.css
ADDED
@@ -0,0 +1,163 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
body {
|
2 |
+
margin: 0;
|
3 |
+
}
|
4 |
+
|
5 |
+
#lock_out_screen {
|
6 |
+
position: relative;
|
7 |
+
background: #0083E3;
|
8 |
+
box-sizing: border-box;
|
9 |
+
height: 100%;
|
10 |
+
}
|
11 |
+
|
12 |
+
#lockout-text {
|
13 |
+
text-align: center;
|
14 |
+
z-index: 1;
|
15 |
+
position: relative;
|
16 |
+
background: url(./lamp-light.svg) no-repeat top left;
|
17 |
+
background-size: cover;
|
18 |
+
display: block;
|
19 |
+
}
|
20 |
+
|
21 |
+
#lockout-text h1 {
|
22 |
+
font-weight: 700;
|
23 |
+
text-transform: uppercase;
|
24 |
+
color: #0083e3;
|
25 |
+
max-width: 40%;
|
26 |
+
margin: 4em auto 1em;
|
27 |
+
font-family: 'Open Sans', Helvetica, Arial, sans-serif;
|
28 |
+
}
|
29 |
+
|
30 |
+
#lockout-text p {
|
31 |
+
max-width: 45%;
|
32 |
+
margin: 1em auto;
|
33 |
+
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen-Sans, Ubuntu, Cantarell, "Helvetica Neue", sans-serif;
|
34 |
+
font-weight: 100;
|
35 |
+
line-height: 1.5em;
|
36 |
+
}
|
37 |
+
|
38 |
+
#lockout-text img {
|
39 |
+
position: absolute;
|
40 |
+
top: -1px;
|
41 |
+
left: 0;
|
42 |
+
right: 0;
|
43 |
+
margin: 0px auto;
|
44 |
+
}
|
45 |
+
|
46 |
+
#lockout-computer {
|
47 |
+
position: relative;
|
48 |
+
}
|
49 |
+
|
50 |
+
#lockout-computer img {
|
51 |
+
float: left;
|
52 |
+
width: 93%;
|
53 |
+
top: 30%;
|
54 |
+
position: absolute;
|
55 |
+
max-height: 300px;
|
56 |
+
}
|
57 |
+
#spacer {
|
58 |
+
background: linear-gradient(75deg, #ffffff 30%, rgba(0, 0, 0, 0) 30%), linear-gradient(65deg, #0083e3 60%, #0083e3 60%);
|
59 |
+
}
|
60 |
+
.container {
|
61 |
+
display: flex;
|
62 |
+
flex-wrap: wrap;
|
63 |
+
flex-direction: row;
|
64 |
+
margin: 0% 4%;
|
65 |
+
padding: 0;
|
66 |
+
justify-content: center;
|
67 |
+
background: #0083E3;
|
68 |
+
}
|
69 |
+
.box {
|
70 |
+
flex-grow: 1;
|
71 |
+
font-size: 18px;
|
72 |
+
text-align: center;
|
73 |
+
}
|
74 |
+
.box2 {
|
75 |
+
flex-basis: 50%;
|
76 |
+
}
|
77 |
+
.box3 {
|
78 |
+
flex-basis: 50%;
|
79 |
+
}
|
80 |
+
.box4 {
|
81 |
+
flex-basis: 25%;
|
82 |
+
}
|
83 |
+
#lock_out_screen .btn {
|
84 |
+
text-decoration: none;
|
85 |
+
margin: 1% 2% 10%;
|
86 |
+
display: inline-block;
|
87 |
+
padding: 2% 4%;
|
88 |
+
background: #0083e3;
|
89 |
+
color: #ffff;
|
90 |
+
border-radius: 4px;
|
91 |
+
border: solid 2px transparent;
|
92 |
+
cursor: pointer;
|
93 |
+
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen-Sans, Ubuntu, Cantarell, "Helvetica Neue", sans-serif;
|
94 |
+
}
|
95 |
+
#lock_out_screen .secondary-btn {
|
96 |
+
border: solid 2px #0083e3;
|
97 |
+
background: transparent;
|
98 |
+
color: #0083e3;
|
99 |
+
}
|
100 |
+
#lock_out_screen .btn:hover {
|
101 |
+
background: #0061a8;
|
102 |
+
color: #fff;
|
103 |
+
border-color: transparent;
|
104 |
+
}
|
105 |
+
|
106 |
+
/* Media Queries */
|
107 |
+
|
108 |
+
@media only screen and (max-width: 1200px) {
|
109 |
+
#lockout-computer.flex.box3 {
|
110 |
+
flex-basis: 35%
|
111 |
+
}
|
112 |
+
#lockout-text.flex.box3 {
|
113 |
+
flex-basis: 65%
|
114 |
+
}
|
115 |
+
#lockout-text h1 {
|
116 |
+
margin: 6em auto 1em;
|
117 |
+
}
|
118 |
+
}
|
119 |
+
@media only screen and (max-width: 1000px) {
|
120 |
+
.container {
|
121 |
+
display: flex;
|
122 |
+
flex-wrap: nowrap;
|
123 |
+
flex-direction: column;
|
124 |
+
margin: 0% 4%;
|
125 |
+
padding: 0;
|
126 |
+
justify-content: center;
|
127 |
+
height: 100%;
|
128 |
+
}
|
129 |
+
#lockout-computer img {
|
130 |
+
float: unset;
|
131 |
+
width: 100%;
|
132 |
+
position: unset;
|
133 |
+
margin-top: 100px;
|
134 |
+
}
|
135 |
+
#lockout-text.flex.box3 {
|
136 |
+
flex-basis: 100%;
|
137 |
+
background: none;
|
138 |
+
background-size: unset;
|
139 |
+
z-index: unset;
|
140 |
+
position: unset;
|
141 |
+
}
|
142 |
+
#lockout-text h1 {
|
143 |
+
margin: 2em auto 1em;
|
144 |
+
max-width: 80%;
|
145 |
+
color: #fff;
|
146 |
+
}
|
147 |
+
#lockout-text p {
|
148 |
+
max-width: 65%;
|
149 |
+
color: #fdfdfd;
|
150 |
+
}
|
151 |
+
#lockout-text p {
|
152 |
+
max-width: 75%;
|
153 |
+
}
|
154 |
+
#lock_out_screen .btn {
|
155 |
+
background: #ffcd0c;
|
156 |
+
color: #1d1d1e;
|
157 |
+
}
|
158 |
+
}
|
159 |
+
@media only screen and (min-width: 1300px) {
|
160 |
+
#lockout-text h1 {
|
161 |
+
margin: 7em auto 1em;
|
162 |
+
}
|
163 |
+
}
|
core/templates/lockout/lockout.php
ADDED
@@ -0,0 +1,36 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
/**
|
3 |
+
* @var \iThemesSecurity\Lib\Lockout\Context $context
|
4 |
+
* @var string $message
|
5 |
+
* @var array[] $actions
|
6 |
+
*/
|
7 |
+
?>
|
8 |
+
<!DOCTYPE html>
|
9 |
+
<html>
|
10 |
+
<head>
|
11 |
+
<meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
|
12 |
+
<meta name="viewport" content="width=device-width, initial-scale=1">
|
13 |
+
<link href="<?php echo plugin_dir_url( __FILE__ ) . 'lockout.css'; ?>" type="text/css" rel="stylesheet">
|
14 |
+
<?php wp_no_robots(); ?>
|
15 |
+
<title><?php esc_html_e( 'Forbidden', 'better-wp-security' ); ?></title>
|
16 |
+
</head>
|
17 |
+
<body id="error-page">
|
18 |
+
<div id="lock_out_screen">
|
19 |
+
<div class="container">
|
20 |
+
<div id="lockout-computer" class="flex box3">
|
21 |
+
<img src="<?php echo plugin_dir_url( __FILE__ ) . 'icon.svg'; ?>" alt=""/>
|
22 |
+
</div>
|
23 |
+
<div id="lockout-text" class="flex box3">
|
24 |
+
<h1><?php esc_html_e( 'You have been locked out.', 'better-wp-security' ) ?></h1>
|
25 |
+
<p style="font-weight:bold;"><?php echo $message; ?></p>
|
26 |
+
<?php do_action( 'itsec_lockout_template_before_actions', $context ); ?>
|
27 |
+
<?php foreach ( $actions as $action ): ?>
|
28 |
+
<a class="btn <?php echo empty( $action['secondary'] ) ? '' : 'secondary' ?>" href="<?php echo esc_url( $action['uri'] ); ?>">
|
29 |
+
<?php echo $action['label']; ?>
|
30 |
+
</a>
|
31 |
+
<?php endforeach; ?>
|
32 |
+
</div>
|
33 |
+
</div>
|
34 |
+
</div>
|
35 |
+
</body>
|
36 |
+
</html>
|
history.txt
CHANGED
@@ -847,3 +847,15 @@
|
|
847 |
Bug Fix: Hide Backend Bypass.
|
848 |
Bug Fix: Strict Standards error during Sync request.
|
849 |
Bug Fix: wp_die() if a login interstitial session fails to be created instead of throwing a fatal error.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
847 |
Bug Fix: Hide Backend Bypass.
|
848 |
Bug Fix: Strict Standards error during Sync request.
|
849 |
Bug Fix: wp_die() if a login interstitial session fails to be created instead of throwing a fatal error.
|
850 |
+
7.5.0 - 2019-11-13 - Timothy Jacobs
|
851 |
+
Breaking Change: iThemes Security requires PHP 5.4 or later.
|
852 |
+
Enhancement: New Lockout Template screen.
|
853 |
+
Enhancement: Add confirmation button to Login Interstitial Async Actions when on a different device.
|
854 |
+
Enhancement: Add filter to "Lookup IP" link.
|
855 |
+
Developer Note: There were significant changes to the internals of the iThemes Security Lockout API in this release. If you are using the ITSEC_Lockout class directly, all the API functions will continue to work, but will emit deprecation notices when legacy behavior is being used. Please update any integrations.
|
856 |
+
Bug Fix: Brute Force module reporting invalid logins using an email address incorrectly.
|
857 |
+
Bug Fix: Improve lockout compatibility with caching plugins.
|
858 |
+
Bug Fix: Fix admin notice not being dismissed due to a REST API route that was more narrowly defined than necessary.
|
859 |
+
Bug Fix: Admin Notices list did not refresh after dismissing a notice.
|
860 |
+
Bug Fix: Strong Passwords zxcvbn Library was not evaluating penalty strings correctly.
|
861 |
+
Bug Fix: Fix PHP warning if there are multiple detected proxy headers.
|
package.json
CHANGED
@@ -109,6 +109,8 @@
|
|
109 |
"test-unit:coverage": "npm run test-unit -- --coverage",
|
110 |
"test-unit:update": "npm run test-unit -- --updateSnapshot",
|
111 |
"test-unit:watch": "npm run test-unit -- --watch",
|
112 |
-
"watch": "./node_modules/.bin/webpack --watch"
|
|
|
|
|
113 |
}
|
114 |
}
|
109 |
"test-unit:coverage": "npm run test-unit -- --coverage",
|
110 |
"test-unit:update": "npm run test-unit -- --updateSnapshot",
|
111 |
"test-unit:watch": "npm run test-unit -- --watch",
|
112 |
+
"watch": "./node_modules/.bin/webpack --watch",
|
113 |
+
"test-acceptance": "docker-compose exec -w /bitnami/wordpress/wp-content/plugins/ithemes-security-pro wordpress ./vendor/bin/codecept run acceptance",
|
114 |
+
"test-acceptance:build": "docker-compose exec -w /bitnami/wordpress/wp-content/plugins/ithemes-security-pro wordpress ./vendor/bin/codecept build"
|
115 |
}
|
116 |
}
|
readme.txt
CHANGED
@@ -2,9 +2,9 @@
|
|
2 |
Contributors: ithemes, chrisjean, mattdanner, timothyblynjacobs
|
3 |
Tags: security, security plugin, malware, hack, secure, block, SSL, admin, htaccess, lockdown, login, protect, protection, anti virus, attack, injection, login security, maintenance, permissions, prevention, authentication, administration, password, brute force, ban, permissions, bots, user agents, xml rpc, security log
|
4 |
Requires at least: 4.7
|
5 |
-
Tested up to: 5.
|
6 |
-
Stable tag: 7.
|
7 |
-
Requires PHP: 5.
|
8 |
License: GPLv2 or later
|
9 |
License URI: http://www.gnu.org/licenses/gpl-2.0.html
|
10 |
|
@@ -189,6 +189,19 @@ Free support may be available with the help of the community in the <a href="htt
|
|
189 |
|
190 |
== Changelog ==
|
191 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
192 |
= 7.4.1 =
|
193 |
* Enhancement: New iThemes Sync Verb support for File Change.
|
194 |
* Tweak: Add additional information about the login attempt when calling the Network Brute Force API.
|
@@ -547,5 +560,5 @@ Free support may be available with the help of the community in the <a href="htt
|
|
547 |
|
548 |
== Upgrade Notice ==
|
549 |
|
550 |
-
= 7.
|
551 |
-
Version 7.
|
2 |
Contributors: ithemes, chrisjean, mattdanner, timothyblynjacobs
|
3 |
Tags: security, security plugin, malware, hack, secure, block, SSL, admin, htaccess, lockdown, login, protect, protection, anti virus, attack, injection, login security, maintenance, permissions, prevention, authentication, administration, password, brute force, ban, permissions, bots, user agents, xml rpc, security log
|
4 |
Requires at least: 4.7
|
5 |
+
Tested up to: 5.3.0
|
6 |
+
Stable tag: 7.5.0
|
7 |
+
Requires PHP: 5.4
|
8 |
License: GPLv2 or later
|
9 |
License URI: http://www.gnu.org/licenses/gpl-2.0.html
|
10 |
|
189 |
|
190 |
== Changelog ==
|
191 |
|
192 |
+
= 7.5.0 =
|
193 |
+
* Breaking Change: iThemes Security requires PHP 5.4 or later.
|
194 |
+
* Enhancement: New Lockout Template screen.
|
195 |
+
* Enhancement: Add confirmation button to Login Interstitial Async Actions when on a different device.
|
196 |
+
* Enhancement: Add filter to "Lookup IP" link.
|
197 |
+
* Developer Note: There were significant changes to the internals of the iThemes Security Lockout API in this release. If you are using the ITSEC_Lockout class directly, all the API functions will continue to work, but will emit deprecation notices when legacy behavior is being used. Please update any integrations.
|
198 |
+
* Bug Fix: Brute Force module reporting invalid logins using an email address incorrectly.
|
199 |
+
* Bug Fix: Improve lockout compatibility with caching plugins.
|
200 |
+
* Bug Fix: Fix admin notice not being dismissed due to a REST API route that was more narrowly defined than necessary.
|
201 |
+
* Bug Fix: Admin Notices list did not refresh after dismissing a notice.
|
202 |
+
* Bug Fix: Strong Passwords zxcvbn Library was not evaluating penalty strings correctly.
|
203 |
+
* Bug Fix: Fix PHP warning if there are multiple detected proxy headers.
|
204 |
+
|
205 |
= 7.4.1 =
|
206 |
* Enhancement: New iThemes Sync Verb support for File Change.
|
207 |
* Tweak: Add additional information about the login attempt when calling the Network Brute Force API.
|
560 |
|
561 |
== Upgrade Notice ==
|
562 |
|
563 |
+
= 7.5.0 =
|
564 |
+
Version 7.5.0 contains new features and bug fixes. It is recommended for all users.
|