Version Description
- Bug Fix: Throw a real 403 instead of a faked 404 for hide backend - Fixes compatability with certain plugins including WordPress SEO. Hat tip to Joost de Valk (@jdevalk) and the @Yoast team for bringing this issue to our attention.
Download this release
Release Info
Developer | aaroncampbell |
Plugin | iThemes Security (formerly Better WP Security) |
Version | 5.3.7 |
Comparing to | |
See all releases |
Code changes from version 5.2.1 to 5.3.7
- better-wp-security.php +2 -2
- core/class-ithemes-sync-verb-itsec-set-temp-whitelist.php +6 -1
- core/class-itsec-core.php +17 -8
- core/class-itsec-files.php +7 -2
- core/class-itsec-global-settings.php +18 -18
- core/class-itsec-lib.php +39 -220
- core/class-itsec-lockout.php +9 -129
- core/class-itsec-logger-all-logs.php +7 -1
- core/class-itsec-logger.php +127 -77
- core/class-itsec-setup.php +45 -1
- core/history.txt +42 -0
- core/js/admin-dashboard.js +1 -1
- core/lib/class-itsec-lib-config-file.php +10 -0
- core/lib/class-itsec-lib-ip-tools.php +649 -0
- core/lib/class-itsec-lib-utility.php +2 -0
- core/modules/away-mode/class-ithemes-sync-verb-itsec-get-away-mode.php +3 -0
- core/modules/backup/class-itsec-backup.php +9 -10
- core/modules/ban-users/class-itsec-ban-users-admin.php +36 -25
- core/modules/ban-users/class-itsec-ban-users.php +1 -97
- core/modules/brute-force/class-itsec-brute-force-log.php +7 -1
- core/modules/content-directory/class-itsec-content-directory-admin.php +6 -1
- core/modules/core/class-itsec-core-admin.php +2 -2
- core/modules/file-change/class-itsec-file-change-admin.php +22 -14
- core/modules/hide-backend/class-itsec-hide-backend-admin.php +5 -5
- core/modules/hide-backend/class-itsec-hide-backend.php +3 -15
- core/modules/ipcheck/class-itsec-ipcheck.php +4 -4
- core/modules/malware/class-itsec-malware-admin.php +2 -2
- core/modules/malware/class-itsec-malware-scan-results-template.php +2 -1
- core/modules/malware/class-itsec-malware-scanner.php +9 -11
- core/modules/malware/css/malware.css +2 -0
- core/modules/tweaks/class-itsec-tweaks.php +9 -20
- history.txt +38 -0
- readme.txt +84 -24
better-wp-security.php
CHANGED
@@ -3,10 +3,10 @@
|
|
3 |
/*
|
4 |
* Plugin Name: iThemes Security
|
5 |
* Plugin URI: https://ithemes.com/security
|
6 |
-
* Description:
|
7 |
* Author: iThemes
|
8 |
* Author URI: https://ithemes.com
|
9 |
-
* Version: 5.
|
10 |
* Text Domain: better-wp-security
|
11 |
* Network: True
|
12 |
* License: GPLv2
|
3 |
/*
|
4 |
* Plugin Name: iThemes Security
|
5 |
* Plugin URI: https://ithemes.com/security
|
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: 5.3.7
|
10 |
* Text Domain: better-wp-security
|
11 |
* Network: True
|
12 |
* License: GPLv2
|
core/class-ithemes-sync-verb-itsec-set-temp-whitelist.php
CHANGED
@@ -24,7 +24,12 @@ class Ithemes_Sync_Verb_ITSEC_Set_Temp_Whitelist extends Ithemes_Sync_Verb {
|
|
24 |
|
25 |
$ip = sanitize_text_field( $arguments['ip'] );
|
26 |
|
27 |
-
if (
|
|
|
|
|
|
|
|
|
|
|
28 |
|
29 |
$response = array(
|
30 |
'ip' => $ip,
|
24 |
|
25 |
$ip = sanitize_text_field( $arguments['ip'] );
|
26 |
|
27 |
+
if ( ! class_exists( 'ITSEC_Lib_IP_Tools' ) ) {
|
28 |
+
$itsec_core = ITSEC_Core::get_instance();
|
29 |
+
require_once( dirname( $itsec_core->get_plugin_file() ) . '/core/lib/class-itsec-lib-ip-tools.php' );
|
30 |
+
}
|
31 |
+
|
32 |
+
if ( ITSEC_Lib_IP_Tools::validate( $ip ) ) {
|
33 |
|
34 |
$response = array(
|
35 |
'ip' => $ip,
|
core/class-itsec-core.php
CHANGED
@@ -29,11 +29,11 @@ if ( ! class_exists( 'ITSEC_Core' ) ) {
|
|
29 |
$pages,
|
30 |
$pro_toc_items,
|
31 |
$tracking_vars,
|
32 |
-
$toc_items
|
33 |
-
$_plugin_file;
|
34 |
|
35 |
public
|
36 |
-
$available_pages
|
|
|
37 |
|
38 |
/**
|
39 |
* Private constructor to make this a singleton
|
@@ -58,7 +58,7 @@ if ( ! class_exists( 'ITSEC_Core' ) ) {
|
|
58 |
*
|
59 |
*/
|
60 |
public function init( $plugin_file, $plugin_name ) {
|
61 |
-
$this->
|
62 |
|
63 |
global $itsec_globals, $itsec_files, $itsec_logger, $itsec_lockout, $itsec_notify, $itsec_sync;
|
64 |
|
@@ -91,7 +91,7 @@ if ( ! class_exists( 'ITSEC_Core' ) ) {
|
|
91 |
|
92 |
//Set plugin defaults
|
93 |
$itsec_globals = array(
|
94 |
-
'plugin_build' =>
|
95 |
'plugin_access_lvl' => 'manage_options', //Access level required to access plugin options
|
96 |
'plugin_name' => sanitize_text_field( $plugin_name ), //the name of the plugin
|
97 |
'plugin_base' => str_replace( WP_PLUGIN_DIR . '/', '', $plugin_file ),
|
@@ -755,9 +755,10 @@ if ( ! class_exists( 'ITSEC_Core' ) ) {
|
|
755 |
wp_enqueue_script( 'jquery-ui-dialog' );
|
756 |
wp_enqueue_style( 'jquery-ui-tabs' );
|
757 |
wp_enqueue_style( 'wp-jquery-ui-dialog' );
|
758 |
-
wp_enqueue_script( 'itsec_dashboard_js', $itsec_globals['plugin_url'] . 'core/js/admin-dashboard.js', array( 'jquery' ) );
|
759 |
wp_localize_script( 'itsec_dashboard_js', 'itsec_dashboard', array(
|
760 |
'text' => __( 'Show Intro', 'better-wp-security' ),
|
|
|
761 |
) );
|
762 |
wp_enqueue_script( 'itsec_footer', $itsec_globals['plugin_url'] . 'core/js/admin-dashboard-footer.js', array( 'jquery' ), $itsec_globals['plugin_build'], true );
|
763 |
|
@@ -1122,7 +1123,7 @@ if ( ! class_exists( 'ITSEC_Core' ) ) {
|
|
1122 |
|
1123 |
foreach ( $active_plugins as $active_plugin ) {
|
1124 |
$file = basename( $active_plugin );
|
1125 |
-
|
1126 |
if ( in_array( $file, array( 'better-wp-security.php', 'ithemes-security-pro.php' ) ) ) {
|
1127 |
return;
|
1128 |
}
|
@@ -1562,7 +1563,15 @@ if ( ! class_exists( 'ITSEC_Core' ) ) {
|
|
1562 |
}
|
1563 |
|
1564 |
public function get_plugin_file() {
|
1565 |
-
return $this->
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1566 |
}
|
1567 |
|
1568 |
}
|
29 |
$pages,
|
30 |
$pro_toc_items,
|
31 |
$tracking_vars,
|
32 |
+
$toc_items;
|
|
|
33 |
|
34 |
public
|
35 |
+
$available_pages,
|
36 |
+
$plugin_file;
|
37 |
|
38 |
/**
|
39 |
* Private constructor to make this a singleton
|
58 |
*
|
59 |
*/
|
60 |
public function init( $plugin_file, $plugin_name ) {
|
61 |
+
$this->plugin_file = $plugin_file;
|
62 |
|
63 |
global $itsec_globals, $itsec_files, $itsec_logger, $itsec_lockout, $itsec_notify, $itsec_sync;
|
64 |
|
91 |
|
92 |
//Set plugin defaults
|
93 |
$itsec_globals = array(
|
94 |
+
'plugin_build' => 4040, //plugin build number - used to trigger updates
|
95 |
'plugin_access_lvl' => 'manage_options', //Access level required to access plugin options
|
96 |
'plugin_name' => sanitize_text_field( $plugin_name ), //the name of the plugin
|
97 |
'plugin_base' => str_replace( WP_PLUGIN_DIR . '/', '', $plugin_file ),
|
755 |
wp_enqueue_script( 'jquery-ui-dialog' );
|
756 |
wp_enqueue_style( 'jquery-ui-tabs' );
|
757 |
wp_enqueue_style( 'wp-jquery-ui-dialog' );
|
758 |
+
wp_enqueue_script( 'itsec_dashboard_js', $itsec_globals['plugin_url'] . 'core/js/admin-dashboard.js', array( 'jquery' ), '20160322' );
|
759 |
wp_localize_script( 'itsec_dashboard_js', 'itsec_dashboard', array(
|
760 |
'text' => __( 'Show Intro', 'better-wp-security' ),
|
761 |
+
'url' => esc_url( add_query_arg( array( 'show_admin_modal' => 'true' ) ) ),
|
762 |
) );
|
763 |
wp_enqueue_script( 'itsec_footer', $itsec_globals['plugin_url'] . 'core/js/admin-dashboard-footer.js', array( 'jquery' ), $itsec_globals['plugin_build'], true );
|
764 |
|
1123 |
|
1124 |
foreach ( $active_plugins as $active_plugin ) {
|
1125 |
$file = basename( $active_plugin );
|
1126 |
+
|
1127 |
if ( in_array( $file, array( 'better-wp-security.php', 'ithemes-security-pro.php' ) ) ) {
|
1128 |
return;
|
1129 |
}
|
1563 |
}
|
1564 |
|
1565 |
public function get_plugin_file() {
|
1566 |
+
return $this->plugin_file;
|
1567 |
+
}
|
1568 |
+
|
1569 |
+
public static function required_cap() {
|
1570 |
+
return apply_filters( 'itsec_cap_required', is_multisite() ? 'manage_network_options' : 'manage_options' );
|
1571 |
+
}
|
1572 |
+
|
1573 |
+
public static function current_user_can_manage() {
|
1574 |
+
return current_user_can( self::required_cap() );
|
1575 |
}
|
1576 |
|
1577 |
}
|
core/class-itsec-files.php
CHANGED
@@ -400,8 +400,13 @@ final class ITSEC_Files {
|
|
400 |
*/
|
401 |
public static function quick_ban( $host ) {
|
402 |
$host = trim( $host );
|
403 |
-
|
404 |
-
if ( !
|
|
|
|
|
|
|
|
|
|
|
405 |
return false;
|
406 |
}
|
407 |
|
400 |
*/
|
401 |
public static function quick_ban( $host ) {
|
402 |
$host = trim( $host );
|
403 |
+
|
404 |
+
if ( ! class_exists( 'ITSEC_Lib_IP_Tools' ) ) {
|
405 |
+
$itsec_core = ITSEC_Core::get_instance();
|
406 |
+
require_once( dirname( $itsec_core->get_plugin_file() ) . '/core/lib/class-itsec-lib-ip-tools.php' );
|
407 |
+
}
|
408 |
+
|
409 |
+
if ( ! ITSEC_Lib_IP_Tools::validate( $host ) ) {
|
410 |
return false;
|
411 |
}
|
412 |
|
core/class-itsec-global-settings.php
CHANGED
@@ -626,13 +626,18 @@ class ITSEC_Global_Settings {
|
|
626 |
$bad_white_listed_ips = array();
|
627 |
$raw_white_listed_ips = array();
|
628 |
|
629 |
-
|
|
|
|
|
|
|
630 |
|
631 |
-
|
|
|
|
|
632 |
|
633 |
if ( strlen( trim( $address ) ) > 0 ) {
|
634 |
|
635 |
-
if (
|
636 |
|
637 |
$bad_white_listed_ips[] = filter_var( $address, FILTER_SANITIZE_STRING );
|
638 |
|
@@ -653,20 +658,12 @@ class ITSEC_Global_Settings {
|
|
653 |
if ( sizeof( $bad_white_listed_ips ) > 0 ) {
|
654 |
|
655 |
$type = 'error';
|
656 |
-
$message = '';
|
657 |
|
658 |
-
$message
|
659 |
-
'%s<br /><br />',
|
660 |
-
__( 'There is a problem with an IP address in the white list:', 'better-wp-security' )
|
661 |
-
);
|
662 |
|
663 |
foreach ( $bad_white_listed_ips as $bad_ip ) {
|
664 |
|
665 |
-
$message .= sprintf(
|
666 |
-
'%s %s<br />',
|
667 |
-
$bad_ip,
|
668 |
-
__( 'is not a valid address in the white list users box.', 'better-wp-security' )
|
669 |
-
);
|
670 |
|
671 |
}
|
672 |
|
@@ -1097,13 +1094,16 @@ class ITSEC_Global_Settings {
|
|
1097 |
echo '<p class="submit"><a href="' . PHP_EOL . ITSEC_Lib::get_ip() . '" class="itsec_add_ip_to_whitelist button-primary">' . __( 'Add my current IP to Whitelist', 'better-wp-security' ) . '</a></p>';
|
1098 |
echo '<p class="description">' . __( 'Use the guidelines below to enter hosts that will not be locked out from your site. This will keep you from locking yourself out of any features if you should trigger a lockout. Please note this does not override away mode and will only prevent a temporary ban. Should a permanent ban be triggered you will still be added to the "Ban Users" list unless the IP address is also white listed in that section.', 'better-wp-security' ) . '</p>';
|
1099 |
echo '<ul>';
|
1100 |
-
echo '<li>' . __( 'You may white list users by individual IP address or IP address range.', 'better-wp-security' ) . '</li>';
|
1101 |
-
echo '<
|
1102 |
-
echo '<li>' . __( '
|
1103 |
-
echo '<li
|
|
|
|
|
1104 |
echo '<li>' . __( 'Enter only 1 IP address or 1 IP address range per line.', 'better-wp-security' ) . '</li>';
|
1105 |
echo '</ul>';
|
1106 |
-
echo '<p
|
|
|
1107 |
|
1108 |
}
|
1109 |
|
626 |
$bad_white_listed_ips = array();
|
627 |
$raw_white_listed_ips = array();
|
628 |
|
629 |
+
if ( ! class_exists( 'ITSEC_Lib_IP_Tools' ) ) {
|
630 |
+
$itsec_core = ITSEC_Core::get_instance();
|
631 |
+
require_once( dirname( $itsec_core->get_plugin_file() ) . '/core/lib/class-itsec-lib-ip-tools.php' );
|
632 |
+
}
|
633 |
|
634 |
+
foreach ( $white_listed_addresses as $index => $address ) {
|
635 |
+
// Convert wildcard IPs to CIDR notation
|
636 |
+
$address = ITSEC_Lib_IP_Tools::ip_wild_to_ip_cidr( trim( $address ) );
|
637 |
|
638 |
if ( strlen( trim( $address ) ) > 0 ) {
|
639 |
|
640 |
+
if ( ITSEC_Lib_IP_Tools::validate( $address ) === false ) {
|
641 |
|
642 |
$bad_white_listed_ips[] = filter_var( $address, FILTER_SANITIZE_STRING );
|
643 |
|
658 |
if ( sizeof( $bad_white_listed_ips ) > 0 ) {
|
659 |
|
660 |
$type = 'error';
|
|
|
661 |
|
662 |
+
$message = __( 'There is a problem with an IP address in the white list:', 'better-wp-security' ) . '<br /><br />';
|
|
|
|
|
|
|
663 |
|
664 |
foreach ( $bad_white_listed_ips as $bad_ip ) {
|
665 |
|
666 |
+
$message .= sprintf( __( '%s is not a valid address in the white list users box.', 'better-wp-security' ), $bad_ip ) . '<br />';
|
|
|
|
|
|
|
|
|
667 |
|
668 |
}
|
669 |
|
1094 |
echo '<p class="submit"><a href="' . PHP_EOL . ITSEC_Lib::get_ip() . '" class="itsec_add_ip_to_whitelist button-primary">' . __( 'Add my current IP to Whitelist', 'better-wp-security' ) . '</a></p>';
|
1095 |
echo '<p class="description">' . __( 'Use the guidelines below to enter hosts that will not be locked out from your site. This will keep you from locking yourself out of any features if you should trigger a lockout. Please note this does not override away mode and will only prevent a temporary ban. Should a permanent ban be triggered you will still be added to the "Ban Users" list unless the IP address is also white listed in that section.', 'better-wp-security' ) . '</p>';
|
1096 |
echo '<ul>';
|
1097 |
+
echo '<li>' . __( 'You may white list users by individual IP address or IP address range using wildcards or CIDR notation.', 'better-wp-security' ) . '</li>';
|
1098 |
+
echo '<ul>';
|
1099 |
+
echo '<li>' . __( 'Individual IP addresses must be in IPv4 or IPv6 standard format (###.###.###.### or ####:####:####:####:####:####:####:####).', 'better-wp-security' ) . '</li>';
|
1100 |
+
echo '<li>' . __( 'CIDR notation is allowed to specify a range of IP addresses (###.###.###.###/## or ####:####:####:####:####:####:####:####/###).', 'better-wp-security' ) . '</li>';
|
1101 |
+
echo '<li>' . __( 'Wildcards are also supported with some limitations. If using wildcards (*), you must start with the right-most chunk in the IP address. For example ###.###.###.* and ###.###.*.* are permitted but ###.###.*.### is not. Wildcards are only for convenient entering of IP addresses, and will be automatically converted to their appropriate CIDR notation format on save.', 'better-wp-security' ) . '</li>';
|
1102 |
+
echo '</ul>';
|
1103 |
echo '<li>' . __( 'Enter only 1 IP address or 1 IP address range per line.', 'better-wp-security' ) . '</li>';
|
1104 |
echo '</ul>';
|
1105 |
+
echo '<p><a href="http://ip-lookup.net/domain-lookup.php" target="_blank">' . __( 'Lookup IP Address.', 'better-wp-security' ) . '</a></p>';
|
1106 |
+
echo '<p class="description"><strong>' . __( 'This white list will prevent any IP listed from triggering an automatic lockout. You can still block the IP address manually in the banned users settings.', 'better-wp-security' ) . '</strong></p>';
|
1107 |
|
1108 |
}
|
1109 |
|
core/class-itsec-lib.php
CHANGED
@@ -10,40 +10,6 @@
|
|
10 |
* @since 4.0.0
|
11 |
*/
|
12 |
final class ITSEC_Lib {
|
13 |
-
|
14 |
-
/**
|
15 |
-
* Converts CIDR to ip range.
|
16 |
-
*
|
17 |
-
* Modified from function at http://stackoverflow.com/questions/4931721/getting-list-ips-from-cidr-notation-in-php
|
18 |
-
* as it was far more elegant than my own solution
|
19 |
-
*
|
20 |
-
* @since 4.0.0.0
|
21 |
-
*
|
22 |
-
* @param string $cidr cidr notation to convert
|
23 |
-
*
|
24 |
-
* @return array range of ips returned
|
25 |
-
*/
|
26 |
-
public static function cidr_to_range( $cidr ) {
|
27 |
-
|
28 |
-
$range = array();
|
29 |
-
|
30 |
-
if ( strpos( $cidr, '/' ) ) {
|
31 |
-
|
32 |
-
$cidr = explode( '/', $cidr );
|
33 |
-
|
34 |
-
$range[] = long2ip( ( ip2long( $cidr[0] ) ) & ( ( - 1 << ( 32 - (int) $cidr[1] ) ) ) );
|
35 |
-
$range[] = long2ip( ( ip2long( $cidr[0] ) ) + pow( 2, ( 32 - (int) $cidr[1] ) ) - 1 );
|
36 |
-
|
37 |
-
} else { //if not a range just return the original ip
|
38 |
-
|
39 |
-
$range[] = $cidr;
|
40 |
-
|
41 |
-
}
|
42 |
-
|
43 |
-
return $range;
|
44 |
-
|
45 |
-
}
|
46 |
-
|
47 |
/**
|
48 |
* Clear caches.
|
49 |
*
|
@@ -112,8 +78,8 @@ final class ITSEC_Lib {
|
|
112 |
log_priority int(2) NOT NULL DEFAULT 1,
|
113 |
log_date datetime NOT NULL DEFAULT '0000-00-00 00:00:00',
|
114 |
log_date_gmt datetime NOT NULL DEFAULT '0000-00-00 00:00:00',
|
115 |
-
log_host varchar(
|
116 |
-
log_username varchar(
|
117 |
log_user bigint(20) UNSIGNED,
|
118 |
log_url varchar(255),
|
119 |
log_referrer varchar(255),
|
@@ -131,9 +97,9 @@ final class ITSEC_Lib {
|
|
131 |
lockout_start_gmt datetime NOT NULL,
|
132 |
lockout_expire datetime NOT NULL,
|
133 |
lockout_expire_gmt datetime NOT NULL,
|
134 |
-
lockout_host varchar(
|
135 |
lockout_user bigint(20) UNSIGNED,
|
136 |
-
lockout_username varchar(
|
137 |
lockout_active int(1) NOT NULL DEFAULT 1,
|
138 |
PRIMARY KEY (lockout_id),
|
139 |
KEY lockout_expire_gmt (lockout_expire_gmt),
|
@@ -149,9 +115,9 @@ final class ITSEC_Lib {
|
|
149 |
temp_type varchar(20) NOT NULL,
|
150 |
temp_date datetime NOT NULL,
|
151 |
temp_date_gmt datetime NOT NULL,
|
152 |
-
temp_host varchar(
|
153 |
temp_user bigint(20) UNSIGNED,
|
154 |
-
temp_username varchar(
|
155 |
PRIMARY KEY (temp_id),
|
156 |
KEY temp_date_gmt (temp_date_gmt),
|
157 |
KEY temp_host (temp_host),
|
@@ -433,41 +399,6 @@ final class ITSEC_Lib {
|
|
433 |
|
434 |
}
|
435 |
|
436 |
-
/**
|
437 |
-
* Returns a psuedo-random string of requested length.
|
438 |
-
*
|
439 |
-
* Builds a random string similar to the WordPress password functions.
|
440 |
-
*
|
441 |
-
* @since 4.0.0
|
442 |
-
*
|
443 |
-
* @param int $length how long the string should be (max 62)
|
444 |
-
* @param bool $base32 true if use only base32 characters to generate
|
445 |
-
* @param bool $special_chars whether to include special characters in generation
|
446 |
-
*
|
447 |
-
* @return string
|
448 |
-
*/
|
449 |
-
public static function get_random( $length, $base32 = false, $special_chars = false ) {
|
450 |
-
|
451 |
-
if ( true === $base32 ) {
|
452 |
-
|
453 |
-
$string = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ234567';
|
454 |
-
|
455 |
-
} else {
|
456 |
-
|
457 |
-
$string = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789';
|
458 |
-
|
459 |
-
if ( true === $special_chars ) {
|
460 |
-
|
461 |
-
$string .= '_)(*&^%$#@!~`:;<>,.?/{}[]|';
|
462 |
-
|
463 |
-
}
|
464 |
-
|
465 |
-
}
|
466 |
-
|
467 |
-
return substr( str_shuffle( $string ), mt_rand( 0, strlen( $string ) - $length ), $length );
|
468 |
-
|
469 |
-
}
|
470 |
-
|
471 |
/**
|
472 |
* Returns the server type of the plugin user.
|
473 |
*
|
@@ -478,35 +409,9 @@ final class ITSEC_Lib {
|
|
478 |
* @return string|bool server type the user is using of false if undetectable.
|
479 |
*/
|
480 |
public static function get_server() {
|
481 |
-
|
482 |
-
|
483 |
-
|
484 |
-
if ( defined( 'ITSEC_SERVER_OVERRIDE' ) ) {
|
485 |
-
return ITSEC_SERVER_OVERRIDE;
|
486 |
-
}
|
487 |
-
// @codeCoverageIgnoreEnd
|
488 |
-
|
489 |
-
$server_raw = strtolower( filter_var( $_SERVER['SERVER_SOFTWARE'], FILTER_SANITIZE_STRING ) );
|
490 |
-
|
491 |
-
//figure out what server they're using
|
492 |
-
if ( false !== strpos( $server_raw, 'apache' ) ) {
|
493 |
-
|
494 |
-
return 'apache';
|
495 |
-
|
496 |
-
} elseif ( false !== strpos( $server_raw, 'nginx' ) ) {
|
497 |
-
|
498 |
-
return 'nginx';
|
499 |
-
|
500 |
-
} elseif ( false !== strpos( $server_raw, 'litespeed' ) ) {
|
501 |
-
|
502 |
-
return 'litespeed';
|
503 |
-
|
504 |
-
} else { //unsupported server
|
505 |
-
|
506 |
-
return false;
|
507 |
-
|
508 |
-
}
|
509 |
-
|
510 |
}
|
511 |
|
512 |
/**
|
@@ -567,88 +472,53 @@ final class ITSEC_Lib {
|
|
567 |
}
|
568 |
|
569 |
/**
|
570 |
-
*
|
571 |
*
|
572 |
-
*
|
|
|
|
|
573 |
*
|
574 |
-
* @
|
575 |
-
*
|
576 |
-
* @param string $ip ip to convert
|
577 |
-
*
|
578 |
-
* @return string the converted ip
|
579 |
*/
|
580 |
-
public static function
|
581 |
-
|
582 |
-
|
583 |
-
|
584 |
-
|
585 |
-
$octets = array_reverse( explode( '.', trim( $parts[0] ) ) );
|
586 |
-
|
587 |
-
if ( isset( $parts[1] ) && 0 < intval( $parts[1] ) ) {
|
588 |
|
589 |
-
|
590 |
|
591 |
-
|
592 |
|
593 |
-
|
594 |
|
595 |
-
|
596 |
|
597 |
-
|
|
|
|
|
598 |
|
|
|
|
|
|
|
|
|
|
|
|
|
599 |
} else {
|
600 |
-
|
601 |
-
|
602 |
-
|
603 |
}
|
604 |
-
|
605 |
}
|
606 |
|
607 |
-
|
608 |
-
|
609 |
-
}
|
610 |
-
|
611 |
-
/**
|
612 |
-
* Converts IP with * wildcards to one with a netmask instead
|
613 |
-
*
|
614 |
-
* Attempts to create a standardized CIDR block from an IP using wildcards.
|
615 |
-
*
|
616 |
-
* @since 4.0.0
|
617 |
-
*
|
618 |
-
* @param string $ip ip to convert
|
619 |
-
*
|
620 |
-
* @return string the converted ip
|
621 |
-
*/
|
622 |
-
public static function ip_wild_to_mask( $ip ) {
|
623 |
-
|
624 |
-
$host_parts = array_reverse( explode( '.', trim( $ip ) ) );
|
625 |
-
|
626 |
-
if ( strpos( $ip, '*' ) ) {
|
627 |
-
|
628 |
-
$mask = 32; //used to calculate netmask with wildcards
|
629 |
-
$converted_host = str_replace( '*', '0', $ip );
|
630 |
-
|
631 |
-
//convert hosts with wildcards to host with netmask and create rule lines
|
632 |
-
foreach ( $host_parts as $part ) {
|
633 |
-
|
634 |
-
if ( '*' === $part ) {
|
635 |
-
$mask = $mask - 8;
|
636 |
-
}
|
637 |
-
|
638 |
-
}
|
639 |
-
|
640 |
-
$converted_host = trim( $converted_host );
|
641 |
|
642 |
-
|
643 |
-
if (
|
644 |
-
|
645 |
}
|
646 |
-
|
647 |
-
return $converted_host;
|
648 |
-
|
649 |
}
|
650 |
|
651 |
-
return
|
652 |
|
653 |
}
|
654 |
|
@@ -824,57 +694,6 @@ final class ITSEC_Lib {
|
|
824 |
|
825 |
}
|
826 |
|
827 |
-
/**
|
828 |
-
* Validates a list of ip addresses.
|
829 |
-
*
|
830 |
-
* Makes sure that the provided IP addresses are in fact valid IPV4 addresses.
|
831 |
-
*
|
832 |
-
* @since 4.0.0
|
833 |
-
*
|
834 |
-
* @param string $ip string of hosts to check
|
835 |
-
*
|
836 |
-
* @return array array of good hosts or false
|
837 |
-
*/
|
838 |
-
public static function validates_ip_address( $ip ) {
|
839 |
-
$ip = trim( filter_var( $ip, FILTER_SANITIZE_STRING ) );
|
840 |
-
|
841 |
-
if ( substr_count( $ip, '.' ) !== 3 ) {
|
842 |
-
return false;
|
843 |
-
}
|
844 |
-
|
845 |
-
$has_cidr = ( false !== strpos( $ip, '/' ) );
|
846 |
-
$has_wildcard = ( false !== strpos( $ip, '*' ) );
|
847 |
-
|
848 |
-
if ( $has_cidr && $has_wildcard ) {
|
849 |
-
return false;
|
850 |
-
}
|
851 |
-
|
852 |
-
$ip_digit_regex = '(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)';
|
853 |
-
$cidr_digit_regex = '(?:3[0-2]|2[0-9]|1[1-9]|[148])';
|
854 |
-
|
855 |
-
$ip_regex = "(?:$ip_digit_regex\.){3}$ip_digit_regex";
|
856 |
-
|
857 |
-
if ( $has_cidr ) {
|
858 |
-
return (boolean) preg_match( "{^$ip_regex/$cidr_digit_regex$}", $ip );
|
859 |
-
}
|
860 |
-
|
861 |
-
if ( $has_wildcard ) {
|
862 |
-
$wildcard_count = substr_count( $ip, '*' );
|
863 |
-
|
864 |
-
if ( 1 === $wildcard_count ) {
|
865 |
-
return (boolean) preg_match( "{^(?:$ip_digit_regex\.){3}\*$}", $ip );
|
866 |
-
} else if ( 2 === $wildcard_count ) {
|
867 |
-
return (boolean) preg_match( "{^(?:$ip_digit_regex\.){2}\*\.\*$}", $ip );
|
868 |
-
} else if ( 3 === $wildcard_count ) {
|
869 |
-
return (boolean) preg_match( "{^(?:$ip_digit_regex\.)\*\.\*\.\*$}", $ip );
|
870 |
-
}
|
871 |
-
|
872 |
-
return false;
|
873 |
-
}
|
874 |
-
|
875 |
-
return (boolean) preg_match( "{^$ip_regex$}", $ip );
|
876 |
-
}
|
877 |
-
|
878 |
/**
|
879 |
* Validates a file path
|
880 |
*
|
10 |
* @since 4.0.0
|
11 |
*/
|
12 |
final class ITSEC_Lib {
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
13 |
/**
|
14 |
* Clear caches.
|
15 |
*
|
78 |
log_priority int(2) NOT NULL DEFAULT 1,
|
79 |
log_date datetime NOT NULL DEFAULT '0000-00-00 00:00:00',
|
80 |
log_date_gmt datetime NOT NULL DEFAULT '0000-00-00 00:00:00',
|
81 |
+
log_host varchar(40),
|
82 |
+
log_username varchar(60),
|
83 |
log_user bigint(20) UNSIGNED,
|
84 |
log_url varchar(255),
|
85 |
log_referrer varchar(255),
|
97 |
lockout_start_gmt datetime NOT NULL,
|
98 |
lockout_expire datetime NOT NULL,
|
99 |
lockout_expire_gmt datetime NOT NULL,
|
100 |
+
lockout_host varchar(40),
|
101 |
lockout_user bigint(20) UNSIGNED,
|
102 |
+
lockout_username varchar(60),
|
103 |
lockout_active int(1) NOT NULL DEFAULT 1,
|
104 |
PRIMARY KEY (lockout_id),
|
105 |
KEY lockout_expire_gmt (lockout_expire_gmt),
|
115 |
temp_type varchar(20) NOT NULL,
|
116 |
temp_date datetime NOT NULL,
|
117 |
temp_date_gmt datetime NOT NULL,
|
118 |
+
temp_host varchar(40),
|
119 |
temp_user bigint(20) UNSIGNED,
|
120 |
+
temp_username varchar(60),
|
121 |
PRIMARY KEY (temp_id),
|
122 |
KEY temp_date_gmt (temp_date_gmt),
|
123 |
KEY temp_host (temp_host),
|
399 |
|
400 |
}
|
401 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
402 |
/**
|
403 |
* Returns the server type of the plugin user.
|
404 |
*
|
409 |
* @return string|bool server type the user is using of false if undetectable.
|
410 |
*/
|
411 |
public static function get_server() {
|
412 |
+
require_once( trailingslashit( $GLOBALS['itsec_globals']['plugin_dir'] ) . 'core/lib/class-itsec-lib-utility.php' );
|
413 |
+
|
414 |
+
return ITSEC_Lib_Utility::get_web_server();
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
415 |
}
|
416 |
|
417 |
/**
|
472 |
}
|
473 |
|
474 |
/**
|
475 |
+
* Determines whether a given IP address is whitelisted
|
476 |
*
|
477 |
+
* @param string $ip_to_check ip to check (can be in CIDR notation)
|
478 |
+
* @param array $white_ips ip list to compare to if not yet saved to options
|
479 |
+
* @param boolean $current whether to whitelist the current ip or not (due to saving, etc)
|
480 |
*
|
481 |
+
* @return boolean true if whitelisted or false
|
|
|
|
|
|
|
|
|
482 |
*/
|
483 |
+
public static function is_ip_whitelisted( $ip_to_check, $white_ips = null, $current = false ) {
|
484 |
+
if ( ! class_exists( 'ITSEC_Lib_IP_Tools' ) ) {
|
485 |
+
$itsec_core = ITSEC_Core::get_instance();
|
486 |
+
require_once( dirname( $itsec_core->get_plugin_file() ) . '/core/lib/class-itsec-lib-ip-tools.php' );
|
487 |
+
}
|
|
|
|
|
|
|
488 |
|
489 |
+
if ( $white_ips === null ) {
|
490 |
|
491 |
+
$global_settings = get_site_option( 'itsec_global' );
|
492 |
|
493 |
+
$white_ips = ( isset( $global_settings['lockout_white_list'] ) ? $global_settings['lockout_white_list'] : array() );
|
494 |
|
495 |
+
}
|
496 |
|
497 |
+
if ( $current === true ) {
|
498 |
+
$white_ips[] = ITSEC_Lib::get_ip(); //add current user ip to whitelist to check automatically
|
499 |
+
}
|
500 |
|
501 |
+
// Check to see if we have a temporarily white listed IP
|
502 |
+
$temp = get_site_option( 'itsec_temp_whitelist_ip' );
|
503 |
+
if ( false !== $temp ) {
|
504 |
+
// If the temporary white list is expired, delete the option we store it in
|
505 |
+
if ( $temp['exp'] < current_time( 'timestamp' ) ) {
|
506 |
+
delete_site_option( 'itsec_temp_whitelist_ip' );
|
507 |
} else {
|
508 |
+
// If the temporary white list is still valid, add the IP to our list of white IPs
|
509 |
+
$white_ips[] = $temp['ip'];
|
|
|
510 |
}
|
|
|
511 |
}
|
512 |
|
513 |
+
$white_ips = apply_filters( 'itsec_white_ips', $white_ips );
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
514 |
|
515 |
+
foreach ( $white_ips as $white_ip ) {
|
516 |
+
if ( ITSEC_Lib_IP_Tools::intersect( $ip_to_check, ITSEC_Lib_IP_Tools::ip_wild_to_ip_cidr( $white_ip ) ) ) {
|
517 |
+
return true;
|
518 |
}
|
|
|
|
|
|
|
519 |
}
|
520 |
|
521 |
+
return false;
|
522 |
|
523 |
}
|
524 |
|
694 |
|
695 |
}
|
696 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
697 |
/**
|
698 |
* Validates a file path
|
699 |
*
|
core/class-itsec-lockout.php
CHANGED
@@ -308,7 +308,7 @@ class ITSEC_Lockout {
|
|
308 |
ITSEC_Lib::create_database_tables();
|
309 |
}
|
310 |
|
311 |
-
if ( !
|
312 |
|
313 |
$this->lockout( $options['type'], $options['reason'], $lock_host, $lock_user, $lock_username );
|
314 |
|
@@ -316,7 +316,7 @@ class ITSEC_Lockout {
|
|
316 |
|
317 |
global $itsec_logger;
|
318 |
|
319 |
-
$itsec_logger->log_event(
|
320 |
|
321 |
}
|
322 |
|
@@ -331,7 +331,7 @@ class ITSEC_Lockout {
|
|
331 |
*/
|
332 |
protected function execute_lock( $user = false, $network = false ) {
|
333 |
|
334 |
-
if (
|
335 |
return;
|
336 |
}
|
337 |
|
@@ -418,7 +418,7 @@ class ITSEC_Lockout {
|
|
418 |
__( 'User lockout message', 'better-wp-security' ),
|
419 |
$settings['user_lockout_message'],
|
420 |
__( 'Is this computer white-listed', 'better-wp-security' ),
|
421 |
-
(
|
422 |
);
|
423 |
|
424 |
return $description;
|
@@ -493,126 +493,6 @@ class ITSEC_Lockout {
|
|
493 |
|
494 |
}
|
495 |
|
496 |
-
/**
|
497 |
-
* Determines whether a given IP address is whitelisted.
|
498 |
-
*
|
499 |
-
* @since 4.0
|
500 |
-
*
|
501 |
-
* @access private
|
502 |
-
*
|
503 |
-
* @param string $ip_to_check ip to check
|
504 |
-
*
|
505 |
-
* @return boolean true if whitelisted or false
|
506 |
-
*/
|
507 |
-
protected function is_ip_whitelisted( $ip_to_check, $current = false ) {
|
508 |
-
|
509 |
-
global $itsec_globals;
|
510 |
-
|
511 |
-
$white_ips = isset( $itsec_globals['settings']['lockout_white_list'] ) ? $itsec_globals['settings']['lockout_white_list'] : array();
|
512 |
-
|
513 |
-
if ( ! is_array( $white_ips ) ) {
|
514 |
-
$white_ips = explode( PHP_EOL, $white_ips );
|
515 |
-
}
|
516 |
-
|
517 |
-
//Add the server IP address
|
518 |
-
if ( isset( $_SERVER['LOCAL_ADDR'] ) ) {
|
519 |
-
|
520 |
-
$white_ips[] = $_SERVER['LOCAL_ADDR'];
|
521 |
-
|
522 |
-
} elseif ( isset( $_SERVER['SERVER_ADDR'] ) ) {
|
523 |
-
|
524 |
-
$white_ips[] = $_SERVER['SERVER_ADDR'];
|
525 |
-
|
526 |
-
}
|
527 |
-
|
528 |
-
if ( $current === true ) {
|
529 |
-
$white_ips[] = ITSEC_Lib::get_ip(); //add current user ip to whitelist to check automatically
|
530 |
-
}
|
531 |
-
|
532 |
-
$temp = get_site_option( 'itsec_temp_whitelist_ip' );
|
533 |
-
|
534 |
-
if ( $temp !== false ) {
|
535 |
-
|
536 |
-
if ( $temp['exp'] < $itsec_globals['current_time'] ) {
|
537 |
-
|
538 |
-
delete_site_option( 'itsec_temp_whitelist_ip' );
|
539 |
-
|
540 |
-
} else {
|
541 |
-
|
542 |
-
$white_ips[] = filter_var( $temp['ip'],
|
543 |
-
FILTER_VALIDATE_IP,
|
544 |
-
FILTER_FLAG_IPV4 );
|
545 |
-
|
546 |
-
}
|
547 |
-
|
548 |
-
}
|
549 |
-
|
550 |
-
if ( is_array( $white_ips ) && sizeof( $white_ips > 0 ) ) {
|
551 |
-
|
552 |
-
foreach ( $white_ips as $white_ip ) {
|
553 |
-
|
554 |
-
$converted_white_ip = ITSEC_Lib::ip_wild_to_mask( $white_ip );
|
555 |
-
|
556 |
-
$check_range = ITSEC_Lib::cidr_to_range( $converted_white_ip );
|
557 |
-
$ip_range = ITSEC_Lib::cidr_to_range( $ip_to_check );
|
558 |
-
|
559 |
-
if ( sizeof( $check_range ) === 2 ) { //range to check
|
560 |
-
|
561 |
-
$check_min = ip2long( $check_range[0] );
|
562 |
-
$check_max = ip2long( $check_range[1] );
|
563 |
-
|
564 |
-
if ( sizeof( $ip_range ) === 2 ) {
|
565 |
-
|
566 |
-
$ip_min = ip2long( $ip_range[0] );
|
567 |
-
$ip_max = ip2long( $ip_range[1] );
|
568 |
-
|
569 |
-
if ( ( $check_min < $ip_min && $ip_min < $check_max ) || ( $check_min < $ip_max && $ip_max < $check_max ) ) {
|
570 |
-
return true;
|
571 |
-
}
|
572 |
-
|
573 |
-
} else {
|
574 |
-
|
575 |
-
$ip = ip2long( $ip_range[0] );
|
576 |
-
|
577 |
-
if ( $check_min < $ip && $ip < $check_max ) {
|
578 |
-
return true;
|
579 |
-
}
|
580 |
-
|
581 |
-
}
|
582 |
-
|
583 |
-
} else { //single ip to check
|
584 |
-
|
585 |
-
$check = ip2long( $check_range[0] );
|
586 |
-
|
587 |
-
if ( sizeof( $ip_range ) === 2 ) {
|
588 |
-
|
589 |
-
$ip_min = ip2long( $ip_range[0] );
|
590 |
-
$ip_max = ip2long( $ip_range[1] );
|
591 |
-
|
592 |
-
if ( $ip_min < $check && $check < $ip_max ) {
|
593 |
-
return true;
|
594 |
-
}
|
595 |
-
|
596 |
-
} else {
|
597 |
-
|
598 |
-
$ip = ip2long( $ip_range[0] );
|
599 |
-
|
600 |
-
if ( $check == $ip ) {
|
601 |
-
return true;
|
602 |
-
}
|
603 |
-
|
604 |
-
}
|
605 |
-
|
606 |
-
}
|
607 |
-
|
608 |
-
}
|
609 |
-
|
610 |
-
}
|
611 |
-
|
612 |
-
return false;
|
613 |
-
|
614 |
-
}
|
615 |
-
|
616 |
/**
|
617 |
* Process ajax request to set temp whitelist
|
618 |
*
|
@@ -711,7 +591,7 @@ class ITSEC_Lockout {
|
|
711 |
if ( $itsec_files->get_file_lock( 'lockout_' . $host . $user . $username ) ) {
|
712 |
|
713 |
//Do we have a good host to lock out or not
|
714 |
-
if ( $host
|
715 |
$good_host = sanitize_text_field( $host );
|
716 |
} else {
|
717 |
$good_host = false;
|
@@ -769,7 +649,7 @@ class ITSEC_Lockout {
|
|
769 |
//We have temp bans to perform
|
770 |
if ( $good_host !== false || $good_user !== false || $good_username || $good_username !== false ) {
|
771 |
|
772 |
-
if (
|
773 |
|
774 |
$whitelisted = true;
|
775 |
$expiration = date( 'Y-m-d H:i:s', 1 );
|
@@ -800,7 +680,7 @@ class ITSEC_Lockout {
|
|
800 |
)
|
801 |
);
|
802 |
|
803 |
-
$itsec_logger->log_event(
|
804 |
'expires' => $expiration, 'expires_gmt' => $expiration_gmt, 'type' => $type
|
805 |
), sanitize_text_field( $host ) );
|
806 |
|
@@ -926,7 +806,7 @@ class ITSEC_Lockout {
|
|
926 |
id="lo_<?php echo $host['lockout_id']; ?>"
|
927 |
value="<?php echo $host['lockout_id']; ?>"/>
|
928 |
<label
|
929 |
-
for="lo_<?php echo $host['lockout_id']; ?>"><strong><?php echo
|
930 |
- <?php _e( 'Expires in', 'better-wp-security' ); ?>
|
931 |
<em> <?php echo human_time_diff( $itsec_globals['current_time_gmt'], strtotime( $host['lockout_expire_gmt'] ) ); ?></em></label>
|
932 |
</li>
|
@@ -1216,7 +1096,7 @@ class ITSEC_Lockout {
|
|
1216 |
//Tell which host was locked out
|
1217 |
if ( $host !== false ) {
|
1218 |
|
1219 |
-
$host_text = sprintf( '%s, <a href="http://
|
1220 |
|
1221 |
$host_expiration_text = __( 'The host has been locked out ', 'better-wp-security' );
|
1222 |
|
308 |
ITSEC_Lib::create_database_tables();
|
309 |
}
|
310 |
|
311 |
+
if ( ! ITSEC_Lib::is_ip_whitelisted( $host ) && ( $lock_host !== null || $lock_user !== null || $lock_username !== null ) ) {
|
312 |
|
313 |
$this->lockout( $options['type'], $options['reason'], $lock_host, $lock_user, $lock_username );
|
314 |
|
316 |
|
317 |
global $itsec_logger;
|
318 |
|
319 |
+
$itsec_logger->log_event( 'lockout', 10, array( __( 'A whitelisted host has triggered a lockout condition but was not locked out.', 'better-wp-security' ) ), sanitize_text_field( $host ) );
|
320 |
|
321 |
}
|
322 |
|
331 |
*/
|
332 |
protected function execute_lock( $user = false, $network = false ) {
|
333 |
|
334 |
+
if ( ITSEC_Lib::is_ip_whitelisted( ITSEC_Lib::get_ip() ) ) {
|
335 |
return;
|
336 |
}
|
337 |
|
418 |
__( 'User lockout message', 'better-wp-security' ),
|
419 |
$settings['user_lockout_message'],
|
420 |
__( 'Is this computer white-listed', 'better-wp-security' ),
|
421 |
+
( ITSEC_Lib::is_ip_whitelisted( ITSEC_Lib::get_ip() ) === true ? __( 'yes', 'better-wp-security' ) : __( 'no', 'better-wp-security' ) )
|
422 |
);
|
423 |
|
424 |
return $description;
|
493 |
|
494 |
}
|
495 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
496 |
/**
|
497 |
* Process ajax request to set temp whitelist
|
498 |
*
|
591 |
if ( $itsec_files->get_file_lock( 'lockout_' . $host . $user . $username ) ) {
|
592 |
|
593 |
//Do we have a good host to lock out or not
|
594 |
+
if ( ! is_null( $host ) && ITSEC_Lib::is_ip_whitelisted( sanitize_text_field( $host ) ) === false && ITSEC_Lib_IP_Tools::validate( $host ) ) {
|
595 |
$good_host = sanitize_text_field( $host );
|
596 |
} else {
|
597 |
$good_host = false;
|
649 |
//We have temp bans to perform
|
650 |
if ( $good_host !== false || $good_user !== false || $good_username || $good_username !== false ) {
|
651 |
|
652 |
+
if ( ITSEC_Lib::is_ip_whitelisted( sanitize_text_field( $host ) ) ) {
|
653 |
|
654 |
$whitelisted = true;
|
655 |
$expiration = date( 'Y-m-d H:i:s', 1 );
|
680 |
)
|
681 |
);
|
682 |
|
683 |
+
$itsec_logger->log_event( 'lockout', 10, array(
|
684 |
'expires' => $expiration, 'expires_gmt' => $expiration_gmt, 'type' => $type
|
685 |
), sanitize_text_field( $host ) );
|
686 |
|
806 |
id="lo_<?php echo $host['lockout_id']; ?>"
|
807 |
value="<?php echo $host['lockout_id']; ?>"/>
|
808 |
<label
|
809 |
+
for="lo_<?php echo $host['lockout_id']; ?>"><strong><?php echo esc_html( $host['lockout_host'] ); ?></strong>
|
810 |
- <?php _e( 'Expires in', 'better-wp-security' ); ?>
|
811 |
<em> <?php echo human_time_diff( $itsec_globals['current_time_gmt'], strtotime( $host['lockout_expire_gmt'] ) ); ?></em></label>
|
812 |
</li>
|
1096 |
//Tell which host was locked out
|
1097 |
if ( $host !== false ) {
|
1098 |
|
1099 |
+
$host_text = sprintf( '%s, <a href="http://www.traceip.net/?query=%s"><strong>%s</strong></a>, ', __( 'host', 'better-wp-security' ), urlencode( $host ), sanitize_text_field( $host ) );
|
1100 |
|
1101 |
$host_expiration_text = __( 'The host has been locked out ', 'better-wp-security' );
|
1102 |
|
core/class-itsec-logger-all-logs.php
CHANGED
@@ -72,13 +72,19 @@ final class ITSEC_Logger_All_Logs extends ITSEC_WP_List_Table {
|
|
72 |
*
|
73 |
**/
|
74 |
function column_host( $item ) {
|
|
|
|
|
|
|
|
|
75 |
|
76 |
$r = array();
|
77 |
if ( ! is_array( $item['host'] ) ) {
|
78 |
$item['host'] = array( $item['host'] );
|
79 |
}
|
80 |
foreach ( $item['host'] as $host ) {
|
81 |
-
|
|
|
|
|
82 |
}
|
83 |
$return = implode( '<br />', $r );
|
84 |
|
72 |
*
|
73 |
**/
|
74 |
function column_host( $item ) {
|
75 |
+
if ( ! class_exists( 'ITSEC_Lib_IP_Tools' ) ) {
|
76 |
+
$itsec_core = ITSEC_Core::get_instance();
|
77 |
+
require_once( dirname( $itsec_core->get_plugin_file() ) . '/core/lib/class-itsec-lib-ip-tools.php' );
|
78 |
+
}
|
79 |
|
80 |
$r = array();
|
81 |
if ( ! is_array( $item['host'] ) ) {
|
82 |
$item['host'] = array( $item['host'] );
|
83 |
}
|
84 |
foreach ( $item['host'] as $host ) {
|
85 |
+
if ( ITSEC_Lib_IP_Tools::validate( $host ) ) {
|
86 |
+
$r[] = '<a href="http://www.traceip.net/?query=' . urlencode( $host ) . '" target="_blank">' . esc_html( $host ) . '</a>';
|
87 |
+
}
|
88 |
}
|
89 |
$return = implode( '<br />', $r );
|
90 |
|
core/class-itsec-logger.php
CHANGED
@@ -14,37 +14,23 @@ final class ITSEC_Logger {
|
|
14 |
$logger_modules,
|
15 |
$module_path;
|
16 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
17 |
function __construct() {
|
18 |
|
19 |
global $itsec_globals;
|
20 |
|
21 |
-
//make sure the log file info is there or generate it. This should only affect beta users.
|
22 |
-
if ( ! isset( $itsec_globals['settings']['log_info'] ) ) {
|
23 |
-
|
24 |
-
$itsec_globals['settings']['log_info'] = substr( sanitize_title( get_bloginfo( 'name' ) ), 0, 20 ) . '-' . ITSEC_Lib::get_random( mt_rand( 0, 10 ) );
|
25 |
-
|
26 |
-
update_site_option( 'itsec_global', $itsec_globals['settings'] );
|
27 |
-
|
28 |
-
}
|
29 |
-
|
30 |
-
//Make sure the logs directory was created
|
31 |
-
if ( ! is_dir( $itsec_globals['ithemes_log_dir'] ) ) {
|
32 |
-
@mkdir( trailingslashit( $itsec_globals['ithemes_dir'] ) . 'logs' );
|
33 |
-
}
|
34 |
-
|
35 |
-
//don't create a log file if we don't need it.
|
36 |
-
if ( isset( $itsec_globals['settings']['log_type'] ) && $itsec_globals['settings']['log_type'] !== 0 ) {
|
37 |
-
|
38 |
-
$this->log_file = $itsec_globals['ithemes_log_dir'] . '/event-log-' . $itsec_globals['settings']['log_info'] . '.log';
|
39 |
-
$this->start_log(); //create a log file if we don't have one
|
40 |
-
|
41 |
-
}
|
42 |
-
|
43 |
$this->logger_modules = array(); //array to hold information on modules using this feature
|
44 |
$this->logger_displays = array(); //array to hold metabox information
|
45 |
$this->module_path = ITSEC_Lib::get_module_path( __FILE__ );
|
46 |
|
47 |
add_action( 'plugins_loaded', array( $this, 'register_modules' ) );
|
|
|
48 |
|
49 |
add_action( 'admin_enqueue_scripts', array( $this, 'admin_script' ) ); //enqueue scripts for admin page
|
50 |
|
@@ -301,68 +287,98 @@ final class ITSEC_Logger {
|
|
301 |
*/
|
302 |
public function log_event( $module, $priority = 5, $data = array(), $host = '', $username = '', $user = '', $url = '', $referrer = '' ) {
|
303 |
global $wpdb, $itsec_globals;
|
304 |
-
|
305 |
if ( isset( $this->logger_modules[ $module ] ) ) {
|
306 |
-
$options = $this->logger_modules[ $module ];
|
307 |
-
|
308 |
if ( ! isset( $itsec_globals['settings']['log_type'] ) || $itsec_globals['settings']['log_type'] === 0 || $itsec_globals['settings']['log_type'] == 2 ) {
|
309 |
-
$
|
310 |
-
'log_type' => $options['type'],
|
311 |
-
'log_priority' => intval( $priority ),
|
312 |
-
'log_function' => $options['function'],
|
313 |
-
'log_date' => date( 'Y-m-d H:i:s', $itsec_globals['current_time'] ),
|
314 |
-
'log_date_gmt' => date( 'Y-m-d H:i:s', $itsec_globals['current_time_gmt'] ),
|
315 |
-
'log_host' => sanitize_text_field( $host ),
|
316 |
-
'log_username' => sanitize_text_field( $username ),
|
317 |
-
'log_user' => intval( $user ),
|
318 |
-
'log_url' => $url,
|
319 |
-
'log_referrer' => $referrer,
|
320 |
-
'log_data' => serialize( $data ),
|
321 |
-
);
|
322 |
-
|
323 |
-
$columns = '`' . implode( '`, `', array_keys( $values ) ) . '`';
|
324 |
-
$placeholders = '%s, %d, %s, %s, %s, %s, %s, %s, %s, %s, %s';
|
325 |
-
|
326 |
-
$query_format = "INSERT INTO `{$wpdb->base_prefix}itsec_log` ($columns) VALUES ($placeholders)";
|
327 |
-
|
328 |
-
$cached_show_errors_setting = $wpdb->hide_errors();
|
329 |
-
$result = $wpdb->query( $wpdb->prepare( $query_format, $values ) );
|
330 |
-
|
331 |
-
if ( ! $result ) {
|
332 |
-
$wpdb->show_errors();
|
333 |
-
|
334 |
-
ITSEC_Lib::create_database_tables();
|
335 |
-
|
336 |
-
// Attempt the query again. Since errors will now be shown, a remaining issue will be display an error.
|
337 |
-
$result = $wpdb->query( $wpdb->prepare( $query_format, $values ) );
|
338 |
-
}
|
339 |
-
|
340 |
-
// Set $wpdb->show_errors back to its original setting.
|
341 |
-
$wpdb->show_errors( $cached_show_errors_setting );
|
342 |
}
|
343 |
|
344 |
if ( isset( $itsec_globals['settings']['log_type'] ) && ( $itsec_globals['settings']['log_type'] === 1 || $itsec_globals['settings']['log_type'] == 2 ) ) {
|
345 |
-
$
|
346 |
-
|
347 |
-
$message =
|
348 |
-
$options['type'] . ',' .
|
349 |
-
intval( $priority ) . ',' .
|
350 |
-
$options['function'] . ',' .
|
351 |
-
date( 'Y-m-d H:i:s', $itsec_globals['current_time'] ) . ',' .
|
352 |
-
date( 'Y-m-d H:i:s', $itsec_globals['current_time_gmt'] ) . ',' .
|
353 |
-
sanitize_text_field( $host ) . ',' .
|
354 |
-
sanitize_text_field( $username ) . ',' .
|
355 |
-
( intval( $user ) === 0 ? '' : intval( $user ) ) . ',' .
|
356 |
-
esc_sql( $url ) . ',' .
|
357 |
-
esc_sql( $referrer ) . ',' .
|
358 |
-
maybe_serialize( $file_data );
|
359 |
-
|
360 |
-
error_log( $message . PHP_EOL, 3, $this->log_file );
|
361 |
-
|
362 |
}
|
363 |
|
364 |
}
|
365 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
366 |
}
|
367 |
|
368 |
/**
|
@@ -532,6 +548,8 @@ final class ITSEC_Logger {
|
|
532 |
|
533 |
}
|
534 |
|
|
|
|
|
535 |
if ( ( @file_exists( $this->log_file ) && @filesize( $this->log_file ) >= 10485760 ) ) {
|
536 |
$this->rotate_log();
|
537 |
}
|
@@ -597,7 +615,7 @@ final class ITSEC_Logger {
|
|
597 |
|
598 |
}
|
599 |
|
600 |
-
$this->
|
601 |
|
602 |
}
|
603 |
|
@@ -643,14 +661,46 @@ final class ITSEC_Logger {
|
|
643 |
|
644 |
}
|
645 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
646 |
/**
|
647 |
* Creates a new log file and adds header information (if needed)
|
648 |
*
|
649 |
* @return void
|
650 |
*/
|
651 |
-
private function
|
|
|
|
|
|
|
|
|
652 |
|
653 |
if ( file_exists( $this->log_file ) !== true ) { //only if current log file doesn't exist
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
654 |
|
655 |
$header = 'log_type,log_priority,log_function,log_date,log_date_gmt,log_host,log_username,log_user,log_url,log_referrer,log_data' . PHP_EOL;
|
656 |
|
14 |
$logger_modules,
|
15 |
$module_path;
|
16 |
|
17 |
+
/**
|
18 |
+
* @access private
|
19 |
+
*
|
20 |
+
* @var array Events that need to be logged to a file but couldn't
|
21 |
+
*/
|
22 |
+
private $_events_to_log_to_file = array();
|
23 |
+
|
24 |
function __construct() {
|
25 |
|
26 |
global $itsec_globals;
|
27 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
28 |
$this->logger_modules = array(); //array to hold information on modules using this feature
|
29 |
$this->logger_displays = array(); //array to hold metabox information
|
30 |
$this->module_path = ITSEC_Lib::get_module_path( __FILE__ );
|
31 |
|
32 |
add_action( 'plugins_loaded', array( $this, 'register_modules' ) );
|
33 |
+
add_action( 'plugins_loaded', array( $this, 'write_pending_events_to_file' ) );
|
34 |
|
35 |
add_action( 'admin_enqueue_scripts', array( $this, 'admin_script' ) ); //enqueue scripts for admin page
|
36 |
|
287 |
*/
|
288 |
public function log_event( $module, $priority = 5, $data = array(), $host = '', $username = '', $user = '', $url = '', $referrer = '' ) {
|
289 |
global $wpdb, $itsec_globals;
|
290 |
+
|
291 |
if ( isset( $this->logger_modules[ $module ] ) ) {
|
|
|
|
|
292 |
if ( ! isset( $itsec_globals['settings']['log_type'] ) || $itsec_globals['settings']['log_type'] === 0 || $itsec_globals['settings']['log_type'] == 2 ) {
|
293 |
+
$this->_log_event_to_db( $module, $priority, $data, $host, $username, $user, $url, $referrer );
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
294 |
}
|
295 |
|
296 |
if ( isset( $itsec_globals['settings']['log_type'] ) && ( $itsec_globals['settings']['log_type'] === 1 || $itsec_globals['settings']['log_type'] == 2 ) ) {
|
297 |
+
$this->_log_event_to_file( $module, $priority, $data, $host, $username, $user, $url, $referrer );
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
298 |
}
|
299 |
|
300 |
}
|
301 |
|
302 |
+
do_action( 'itsec_log_event', $module, $priority, $data, $host, $username, $user, $url, $referrer );
|
303 |
+
|
304 |
+
}
|
305 |
+
|
306 |
+
private function _log_event_to_db( $module, $priority = 5, $data = array(), $host = '', $username = '', $user = '', $url = '', $referrer = '' ) {
|
307 |
+
global $wpdb, $itsec_globals;
|
308 |
+
|
309 |
+
$options = $this->logger_modules[ $module ];
|
310 |
+
|
311 |
+
$values = array(
|
312 |
+
'log_type' => $options['type'],
|
313 |
+
'log_priority' => intval( $priority ),
|
314 |
+
'log_function' => $options['function'],
|
315 |
+
'log_date' => date( 'Y-m-d H:i:s', $itsec_globals['current_time'] ),
|
316 |
+
'log_date_gmt' => date( 'Y-m-d H:i:s', $itsec_globals['current_time_gmt'] ),
|
317 |
+
'log_host' => sanitize_text_field( $host ),
|
318 |
+
'log_username' => sanitize_text_field( $username ),
|
319 |
+
'log_user' => intval( $user ),
|
320 |
+
'log_url' => $url,
|
321 |
+
'log_referrer' => $referrer,
|
322 |
+
'log_data' => serialize( $data ),
|
323 |
+
);
|
324 |
+
|
325 |
+
$columns = '`' . implode( '`, `', array_keys( $values ) ) . '`';
|
326 |
+
$placeholders = '%s, %d, %s, %s, %s, %s, %s, %s, %s, %s, %s';
|
327 |
+
|
328 |
+
$query_format = "INSERT INTO `{$wpdb->base_prefix}itsec_log` ($columns) VALUES ($placeholders)";
|
329 |
+
|
330 |
+
$cached_show_errors_setting = $wpdb->hide_errors();
|
331 |
+
$result = $wpdb->query( $wpdb->prepare( $query_format, $values ) );
|
332 |
+
|
333 |
+
if ( ! $result ) {
|
334 |
+
$wpdb->show_errors();
|
335 |
+
|
336 |
+
ITSEC_Lib::create_database_tables();
|
337 |
+
|
338 |
+
// Attempt the query again. Since errors will now be shown, a remaining issue will be display an error.
|
339 |
+
$result = $wpdb->query( $wpdb->prepare( $query_format, $values ) );
|
340 |
+
}
|
341 |
+
|
342 |
+
// Set $wpdb->show_errors back to its original setting.
|
343 |
+
$wpdb->show_errors( $cached_show_errors_setting );
|
344 |
+
}
|
345 |
+
|
346 |
+
private function _log_event_to_file( $module, $priority = 5, $data = array(), $host = '', $username = '', $user = '', $url = '', $referrer = '' ) {
|
347 |
+
global $itsec_globals;
|
348 |
+
|
349 |
+
// If the file can't be prepared, store the events up to write later (at plugins_loaded)
|
350 |
+
if ( false === $this->_prepare_log_file() ) {
|
351 |
+
$this->_events_to_log_to_file[] = compact( 'module', 'priority', 'data', 'host', 'username', 'user', 'url', 'referrer' );
|
352 |
+
return;
|
353 |
+
}
|
354 |
+
|
355 |
+
$options = $this->logger_modules[ $module ];
|
356 |
+
|
357 |
+
$file_data = $this->sanitize_array( $data, true );
|
358 |
+
|
359 |
+
$message =
|
360 |
+
$options['type'] . ',' .
|
361 |
+
intval( $priority ) . ',' .
|
362 |
+
$options['function'] . ',' .
|
363 |
+
date( 'Y-m-d H:i:s', $itsec_globals['current_time'] ) . ',' .
|
364 |
+
date( 'Y-m-d H:i:s', $itsec_globals['current_time_gmt'] ) . ',' .
|
365 |
+
sanitize_text_field( $host ) . ',' .
|
366 |
+
sanitize_text_field( $username ) . ',' .
|
367 |
+
( intval( $user ) === 0 ? '' : intval( $user ) ) . ',' .
|
368 |
+
esc_sql( $url ) . ',' .
|
369 |
+
esc_sql( $referrer ) . ',' .
|
370 |
+
maybe_serialize( $file_data );
|
371 |
+
|
372 |
+
error_log( $message . PHP_EOL, 3, $this->log_file );
|
373 |
+
|
374 |
+
}
|
375 |
+
|
376 |
+
public function write_pending_events_to_file() {
|
377 |
+
if ( ! empty( $this->_events_to_log_to_file ) ) {
|
378 |
+
foreach ( $this->_events_to_log_to_file as $event ) {
|
379 |
+
call_user_func_array( array( $this, '_log_event_to_file' ), $event );
|
380 |
+
}
|
381 |
+
}
|
382 |
}
|
383 |
|
384 |
/**
|
548 |
|
549 |
}
|
550 |
|
551 |
+
$this->get_log_file();
|
552 |
+
|
553 |
if ( ( @file_exists( $this->log_file ) && @filesize( $this->log_file ) >= 10485760 ) ) {
|
554 |
$this->rotate_log();
|
555 |
}
|
615 |
|
616 |
}
|
617 |
|
618 |
+
$this->_prepare_log_file();
|
619 |
|
620 |
}
|
621 |
|
661 |
|
662 |
}
|
663 |
|
664 |
+
private function get_log_file() {
|
665 |
+
global $itsec_globals;
|
666 |
+
|
667 |
+
//make sure the log file info is there or generate it. This should only affect beta users.
|
668 |
+
if ( ! isset( $itsec_globals['settings']['log_info'] ) ) {
|
669 |
+
|
670 |
+
// We need wp_generate_password() to create a cryptographically secure file name
|
671 |
+
if ( ! function_exists( 'wp_generate_password' ) ) {
|
672 |
+
return false;
|
673 |
+
}
|
674 |
+
$itsec_globals['settings']['log_info'] = substr( sanitize_title( get_bloginfo( 'name' ) ), 0, 20 ) . '-' . wp_generate_password( 30, false );
|
675 |
+
|
676 |
+
update_site_option( 'itsec_global', $itsec_globals['settings'] );
|
677 |
+
|
678 |
+
}
|
679 |
+
$this->log_file = $itsec_globals['ithemes_log_dir'] . '/event-log-' . $itsec_globals['settings']['log_info'] . '.log';
|
680 |
+
return $this->log_file;
|
681 |
+
}
|
682 |
+
|
683 |
/**
|
684 |
* Creates a new log file and adds header information (if needed)
|
685 |
*
|
686 |
* @return void
|
687 |
*/
|
688 |
+
private function _prepare_log_file() {
|
689 |
+
// We can't prepare a file if we can't get the file name
|
690 |
+
if ( false === $this->get_log_file() ) {
|
691 |
+
return false;
|
692 |
+
}
|
693 |
|
694 |
if ( file_exists( $this->log_file ) !== true ) { //only if current log file doesn't exist
|
695 |
+
global $itsec_globals;
|
696 |
+
|
697 |
+
//Make sure the logs directory was created
|
698 |
+
if ( ! is_dir( $itsec_globals['ithemes_log_dir'] ) ) {
|
699 |
+
if ( wp_mkdir_p( $itsec_globals['ithemes_log_dir'] ) ) {
|
700 |
+
// Make sure we have an index file to block directory listing
|
701 |
+
file_put_contents( path_join( $itsec_globals['ithemes_log_dir'], 'index.php' ), "<?php\n// Silence is golden." );
|
702 |
+
}
|
703 |
+
}
|
704 |
|
705 |
$header = 'log_type,log_priority,log_function,log_date,log_date_gmt,log_host,log_username,log_user,log_url,log_referrer,log_data' . PHP_EOL;
|
706 |
|
core/class-itsec-setup.php
CHANGED
@@ -169,6 +169,10 @@ class ITSEC_Setup {
|
|
169 |
if ( ! is_dir( $itsec_globals['ithemes_log_dir'] ) ) {
|
170 |
|
171 |
@mkdir( $itsec_globals['ithemes_log_dir'] );
|
|
|
|
|
|
|
|
|
172 |
$handle = @fopen( $itsec_globals['ithemes_log_dir'] . '/.htaccess', 'w+' );
|
173 |
@fwrite( $handle, 'Deny from all' );
|
174 |
@fclose( $handle );
|
@@ -178,6 +182,10 @@ class ITSEC_Setup {
|
|
178 |
if ( ! is_dir( $itsec_globals['ithemes_backup_dir'] ) ) {
|
179 |
|
180 |
@mkdir( $itsec_globals['ithemes_backup_dir'] );
|
|
|
|
|
|
|
|
|
181 |
$handle = @fopen( $itsec_globals['ithemes_backup_dir'] . '/.htaccess', 'w+' );
|
182 |
@fwrite( $handle, 'Deny from all' );
|
183 |
@fclose( $handle );
|
@@ -200,7 +208,7 @@ class ITSEC_Setup {
|
|
200 |
|
201 |
if ( $options === false || ( isset( $options['log_info'] ) && sizeof( $options ) <= 2 ) ) {
|
202 |
|
203 |
-
$this->defaults['log_info'] = substr( sanitize_title( get_bloginfo( 'name' ) ), 0, 20 ) . '-' .
|
204 |
|
205 |
$itsec_globals['settings'] = $this->defaults;
|
206 |
|
@@ -231,6 +239,7 @@ class ITSEC_Setup {
|
|
231 |
private function upgrade_execute( $upgrade = false ) {
|
232 |
|
233 |
global $itsec_old_version, $itsec_globals, $wpdb, $itsec_setup_action;
|
|
|
234 |
|
235 |
$itsec_setup_action = 'upgrade';
|
236 |
$itsec_old_version = $upgrade;
|
@@ -330,6 +339,7 @@ class ITSEC_Setup {
|
|
330 |
if ( $itsec_old_version < 4030 ) {
|
331 |
|
332 |
ITSEC_Lib::create_database_tables(); //adds username field to lockouts and temp
|
|
|
333 |
add_site_option( 'itsec_rewrites_changed', true );
|
334 |
|
335 |
}
|
@@ -370,6 +380,40 @@ class ITSEC_Setup {
|
|
370 |
|
371 |
}
|
372 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
373 |
}
|
374 |
|
375 |
/**
|
169 |
if ( ! is_dir( $itsec_globals['ithemes_log_dir'] ) ) {
|
170 |
|
171 |
@mkdir( $itsec_globals['ithemes_log_dir'] );
|
172 |
+
// Make sure we have an index file to block directory listing
|
173 |
+
if ( ! file_exists( path_join( $itsec_globals['ithemes_log_dir'], 'index.php' ) ) ) {
|
174 |
+
file_put_contents( path_join( $itsec_globals['ithemes_log_dir'], 'index.php' ), "<?php\n// Silence is golden." );
|
175 |
+
}
|
176 |
$handle = @fopen( $itsec_globals['ithemes_log_dir'] . '/.htaccess', 'w+' );
|
177 |
@fwrite( $handle, 'Deny from all' );
|
178 |
@fclose( $handle );
|
182 |
if ( ! is_dir( $itsec_globals['ithemes_backup_dir'] ) ) {
|
183 |
|
184 |
@mkdir( $itsec_globals['ithemes_backup_dir'] );
|
185 |
+
// Make sure we have an index file to block directory listing
|
186 |
+
if ( ! file_exists( path_join( $itsec_globals['ithemes_backup_dir'], 'index.php' ) ) ) {
|
187 |
+
file_put_contents( path_join( $itsec_globals['ithemes_backup_dir'], 'index.php' ), "<?php\n// Silence is golden." );
|
188 |
+
}
|
189 |
$handle = @fopen( $itsec_globals['ithemes_backup_dir'] . '/.htaccess', 'w+' );
|
190 |
@fwrite( $handle, 'Deny from all' );
|
191 |
@fclose( $handle );
|
208 |
|
209 |
if ( $options === false || ( isset( $options['log_info'] ) && sizeof( $options ) <= 2 ) ) {
|
210 |
|
211 |
+
$this->defaults['log_info'] = substr( sanitize_title( get_bloginfo( 'name' ) ), 0, 20 ) . '-' . wp_generate_password( 30, false );
|
212 |
|
213 |
$itsec_globals['settings'] = $this->defaults;
|
214 |
|
239 |
private function upgrade_execute( $upgrade = false ) {
|
240 |
|
241 |
global $itsec_old_version, $itsec_globals, $wpdb, $itsec_setup_action;
|
242 |
+
$tables_updated = false;
|
243 |
|
244 |
$itsec_setup_action = 'upgrade';
|
245 |
$itsec_old_version = $upgrade;
|
339 |
if ( $itsec_old_version < 4030 ) {
|
340 |
|
341 |
ITSEC_Lib::create_database_tables(); //adds username field to lockouts and temp
|
342 |
+
$tables_updated = true;
|
343 |
add_site_option( 'itsec_rewrites_changed', true );
|
344 |
|
345 |
}
|
380 |
|
381 |
}
|
382 |
|
383 |
+
//IPv6 support was added in 4039
|
384 |
+
if ( $itsec_old_version < 4039 && ! $tables_updated ) {
|
385 |
+
ITSEC_Lib::create_database_tables();
|
386 |
+
$tables_updated = true;
|
387 |
+
}
|
388 |
+
|
389 |
+
if ( $itsec_old_version < 4040 ) {
|
390 |
+
$options = get_site_option( 'itsec_global' );
|
391 |
+
|
392 |
+
if ( $options['log_info'] ) {
|
393 |
+
$new_log_info = substr( sanitize_title( get_bloginfo( 'name' ) ), 0, 20 ) . '-' . wp_generate_password( 30, false );
|
394 |
+
$old_file = path_join( $options['log_location'], 'event-log-' . $options['log_info'] . '.log' );
|
395 |
+
$new_file = path_join( $options['log_location'], 'event-log-' . $new_log_info . '.log' );
|
396 |
+
|
397 |
+
// If the file exists already, don't update the location unless we successfully move it.
|
398 |
+
if ( file_exists( $old_file ) && rename( $old_file, $new_file ) ) {
|
399 |
+
$options['log_info'] = $new_log_info;
|
400 |
+
}
|
401 |
+
}
|
402 |
+
|
403 |
+
// Make sure we have an index files to block directory listing in logs directory
|
404 |
+
if ( is_dir( $options['log_location'] ) && ! file_exists( path_join( $options['log_location'], 'index.php' ) ) ) {
|
405 |
+
file_put_contents( path_join( $options['log_location'], 'index.php' ), "<?php\n// Silence is golden." );
|
406 |
+
}
|
407 |
+
|
408 |
+
$backup_options = get_site_option( 'itsec_backup' );
|
409 |
+
// Make sure we have an index files to block directory listing in backups directory
|
410 |
+
if ( is_dir( $backup_options['location'] ) && ! file_exists( path_join( $backup_options['location'], 'index.php' ) ) ) {
|
411 |
+
file_put_contents( path_join( $backup_options['location'], 'index.php' ), "<?php\n// Silence is golden." );
|
412 |
+
}
|
413 |
+
|
414 |
+
update_site_option( 'itsec_global', $options );
|
415 |
+
}
|
416 |
+
|
417 |
}
|
418 |
|
419 |
/**
|
core/history.txt
CHANGED
@@ -326,3 +326,45 @@
|
|
326 |
Bug Fix: All data added to the options table by iThemes Security is removed on uninstall.
|
327 |
Bug Fix: Fixed the cause of the following warning: call_user_func_array() expects parameter 1 to be a valid callback, class 'ITSEC_SSL_Setup' does not have a method 'execute_deactivate'
|
328 |
Enhancement: Improved code that ensures that tables and options table entries created by iThemes Security are removed on uninstall only when no other iThemes Security plugin is active.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
326 |
Bug Fix: All data added to the options table by iThemes Security is removed on uninstall.
|
327 |
Bug Fix: Fixed the cause of the following warning: call_user_func_array() expects parameter 1 to be a valid callback, class 'ITSEC_SSL_Setup' does not have a method 'execute_deactivate'
|
328 |
Enhancement: Improved code that ensures that tables and options table entries created by iThemes Security are removed on uninstall only when no other iThemes Security plugin is active.
|
329 |
+
2.2.0 - 2016-02-11 - Chris Jean & Aaron D. Campbell
|
330 |
+
New Feature: Added support for IPv6 addresses. This includes support for IPv6 in lockouts, ban hosts, and white lists.
|
331 |
+
Bug Fix: Fixed issue that could cause username-based lockouts to fail for long usernames.
|
332 |
+
Enhancement: Updated descriptions of valid IP and IP range formats for the Lockout White List and the Ban Hosts settings.
|
333 |
+
2.2.1 - 2016-02-15 - Chris Jean & Aaron D. Campbell
|
334 |
+
Bug Fix: Fixed issue that prevented wildcard IP ranges from being blacklisted or whitelisted.
|
335 |
+
Bug Fix: Removed warnings generated when the Away Mode module is disabled and iThemes Sync contacts the site.
|
336 |
+
Enhancement: Updated host entries in log details to link to traceip.net rather than ip-adress.com. This is because ip-adress.com does not support IPv6 addresses.
|
337 |
+
Enhancement: Updated some translatable strings relating to blacklisting and whitelisting to allow for better translations.
|
338 |
+
Enhancement: Added details about how wildcard IP ranges are converted to CIDR format (this improves performance).
|
339 |
+
2.2.2 - 2016-02-18 - Chris Jean & Aaron D. Campbell
|
340 |
+
Bug Fix: Fixed formatting issue that could cause raw HTML output in the malware scan logs.
|
341 |
+
Enhancement: Improved error handling and reporting for malware scan issues.
|
342 |
+
2.2.3 - 2016-02-29 - Chris Jean & Aaron D. Campbell
|
343 |
+
Security Fix: Hardened the created backups and logs directories. Thanks to Nicolas Chatelain (SYSDREAM IT Security Services) for notifying us of this issue.
|
344 |
+
Security Fix: More secure backup and log file names. Thanks to Nicolas Chatelain (SYSDREAM IT Security Services) for notifying us of this issue.
|
345 |
+
Bug Fix: The "NGINX Conf File" setting is now properly respected, causing the generated NGINX configuration file to be stored in that location.
|
346 |
+
Enhancement: Generated database backup file names now contain a human-readable timestamp in the format of YYYYMMDD-HHMMSS.
|
347 |
+
Enhancement: Zipped database backup files no longer contain a deeply nested directory structure. Instead, they only contain the sql file.
|
348 |
+
Enhancement: When the "Force Unique Nickname" feature is enabled, the generated display name now uses an improved randomization function.
|
349 |
+
Enhancement: Improved tabbing of rules in generated nginx.conf files.
|
350 |
+
Enhancement: Removed the "See what's new button" as it has fulfilled its purpose.
|
351 |
+
2.2.4 - 2016-03-01 - Chris Jean & Aaron D. Campbell
|
352 |
+
Bug Fix: Updated code that generates the backups and logs directories to ensure that it attempts to create the parent directory if it does not exist yet.
|
353 |
+
Bug Fix: Removed warnings that could be generated if the logs directory could not be created.
|
354 |
+
Bug Fix: Database backup files sent via email no longer have a name without an extension if zipping up the file fails.
|
355 |
+
2.2.5 - 2016-03-03 - Chris Jean & Aaron D. Campbell
|
356 |
+
Bug Fix: Fixed temporary whitelisting by preventing a temporarily whitelisted IP from being locked out.
|
357 |
+
2.2.8 - 2016-03-17 - Chris Jean & Aaron D. Campbell
|
358 |
+
Bug Fix: Fixed issue that could cause a fatal error after changing the content directory.
|
359 |
+
Bug Fix: Updated the link to sign up for security guide download to point to a https address. This is better security and prevents warnings when submitting from a http site in some browsers.
|
360 |
+
Bug Fix: If a cryptographically secure log file name can't be generated, queue up log file writes until we can.
|
361 |
+
2.2.9 - 2016-03-29 - Chris Jean & Aaron D. Campbell
|
362 |
+
Security Fix: No longer using document.location to build 'Show Intro' link in admin - Thanks to David Lodge (Pen Test Partners) for notifying us of this issue.
|
363 |
+
Bug Fix: Fixed some notices when certain multisite options are used on BuddyPress
|
364 |
+
Enhancement: New itsec_white_ips filter to allow plugins that work with external services to whitelist service IPs
|
365 |
+
2.2.10 - 2016-04-19 - Chris Jean & Aaron D. Campbell
|
366 |
+
Security Fix: Better caps checks for dismissal of changed file dialog - Thanks to Julio Potier for notifying us of this issue.
|
367 |
+
Bug Fix: Make file change warning dialog text properly translatable
|
368 |
+
Enhancement: Adding 'itsec_log_event' action for logged events
|
369 |
+
2.2.11 - 2016-05-02 - Chris Jean & Aaron D. Campbell
|
370 |
+
Bug Fix: Throw a real 403 instead of a faked 404 for hide backend - Fixes compatability with certain plugins including WordPress SEO. Hat tip to Joost de Valk (@jdevalk) and the @Yoast team for bringing this issue to our attention.
|
core/js/admin-dashboard.js
CHANGED
@@ -2,7 +2,7 @@ jQuery( document ).ready( function () {
|
|
2 |
|
3 |
jQuery( '#screen-meta-links' ).append(
|
4 |
'<div id="itsec-meta-link-wrap" class="hide-if-no-js screen-meta-toggle">' +
|
5 |
-
'<a href="' +
|
6 |
'</div>'
|
7 |
);
|
8 |
|
2 |
|
3 |
jQuery( '#screen-meta-links' ).append(
|
4 |
'<div id="itsec-meta-link-wrap" class="hide-if-no-js screen-meta-toggle">' +
|
5 |
+
'<a href="' + itsec_dashboard.url + '" class="show-settings">' + itsec_dashboard.text + '</a>' +
|
6 |
'</div>'
|
7 |
);
|
8 |
|
core/lib/class-itsec-lib-config-file.php
CHANGED
@@ -678,6 +678,16 @@ class ITSEC_Lib_Config_File {
|
|
678 |
* @return string Full path to the server config file or a blank string if modifications for the file are disabled.
|
679 |
*/
|
680 |
public static function get_server_config_file_path() {
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
681 |
$file = self::get_default_server_config_file_name();
|
682 |
|
683 |
if ( empty( $file ) ) {
|
678 |
* @return string Full path to the server config file or a blank string if modifications for the file are disabled.
|
679 |
*/
|
680 |
public static function get_server_config_file_path() {
|
681 |
+
global $itsec_globals;
|
682 |
+
|
683 |
+
|
684 |
+
$server = ITSEC_Lib_Utility::get_web_server();
|
685 |
+
|
686 |
+
if ( 'nginx' === $server && ! empty( $itsec_globals['settings']['nginx_file'] ) ) {
|
687 |
+
return $itsec_globals['settings']['nginx_file'];
|
688 |
+
}
|
689 |
+
|
690 |
+
|
691 |
$file = self::get_default_server_config_file_name();
|
692 |
|
693 |
if ( empty( $file ) ) {
|
core/lib/class-itsec-lib-ip-tools.php
ADDED
@@ -0,0 +1,649 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
/**
|
3 |
+
* iThemes Security IP tools library.
|
4 |
+
*
|
5 |
+
* Contains the ITSEC_Lib_IP_Tools class.
|
6 |
+
*
|
7 |
+
* @package iThemes_Security
|
8 |
+
*/
|
9 |
+
|
10 |
+
/**
|
11 |
+
* iThemes Security IP Tools Library class.
|
12 |
+
*
|
13 |
+
* Utility class for validating and comparing IPs, as well as converting ranges. Supports IPv4 and IPv6.
|
14 |
+
*
|
15 |
+
* @package iThemes_Security
|
16 |
+
* @since 2.2.0
|
17 |
+
*/
|
18 |
+
class ITSEC_Lib_IP_Tools {
|
19 |
+
/**
|
20 |
+
* Stores max cidr (number of bits) for each IP version.
|
21 |
+
*
|
22 |
+
* @static
|
23 |
+
* @access private
|
24 |
+
*
|
25 |
+
* @var array
|
26 |
+
*/
|
27 |
+
private static $_max_cidr = array(
|
28 |
+
4 => 32,
|
29 |
+
6 => 128,
|
30 |
+
);
|
31 |
+
|
32 |
+
/**
|
33 |
+
* Validates an IP or an IP Range using CIDR notation
|
34 |
+
*
|
35 |
+
* @since 2.2.0
|
36 |
+
*
|
37 |
+
* @static
|
38 |
+
* @access public
|
39 |
+
*
|
40 |
+
* @param string $ip The IP address to validate, can be given in CIDR notation
|
41 |
+
*
|
42 |
+
* @return bool|int False for an invalid IP or range, and the IP version (4 or 6) on for a valid one
|
43 |
+
*/
|
44 |
+
public static function validate( $ip ) {
|
45 |
+
$ip_parts = self::_ip_cidr( $ip );
|
46 |
+
|
47 |
+
if ( filter_var( $ip_parts->ip, FILTER_VALIDATE_IP, FILTER_FLAG_IPV4 ) ) {
|
48 |
+
if ( ! isset( $ip_parts->cidr ) || self::_is_valid_cidr( $ip_parts->cidr, 4 ) ) {
|
49 |
+
return 4;
|
50 |
+
}
|
51 |
+
|
52 |
+
// Invalid CIDR
|
53 |
+
return false;
|
54 |
+
} elseif ( filter_var( $ip_parts->ip, FILTER_VALIDATE_IP, FILTER_FLAG_IPV6 ) ) {
|
55 |
+
if ( ! isset( $ip_parts->cidr ) || self::_is_valid_cidr( $ip_parts->cidr, 6 ) ) {
|
56 |
+
return 6;
|
57 |
+
}
|
58 |
+
|
59 |
+
// Invalid CIDR
|
60 |
+
return false;
|
61 |
+
}
|
62 |
+
|
63 |
+
// IP is not valid v4 or v6 IP
|
64 |
+
return false;
|
65 |
+
}
|
66 |
+
|
67 |
+
/**
|
68 |
+
* Converts an IP or an IP Range using CIDR notation, to it's parts (IP and CIDR)
|
69 |
+
*
|
70 |
+
* @since 2.2.0
|
71 |
+
*
|
72 |
+
* @static
|
73 |
+
* @access private
|
74 |
+
*
|
75 |
+
* @param string $ip The IP address, can be given in CIDR notation
|
76 |
+
*
|
77 |
+
* @return object IP parts, ->ip and ->cidr
|
78 |
+
*/
|
79 |
+
private static function _ip_cidr( $ip ) {
|
80 |
+
$ip_parts = new stdClass();
|
81 |
+
if ( strpos( $ip, '/' ) ) {
|
82 |
+
list( $ip_parts->ip, $ip_parts->cidr ) = explode( '/', $ip );
|
83 |
+
} else {
|
84 |
+
$ip_parts->ip = $ip;
|
85 |
+
}
|
86 |
+
|
87 |
+
return $ip_parts;
|
88 |
+
}
|
89 |
+
|
90 |
+
/**
|
91 |
+
* Validates a CIDR value for an IP version
|
92 |
+
*
|
93 |
+
* @since 2.2.0
|
94 |
+
*
|
95 |
+
* @static
|
96 |
+
* @access private
|
97 |
+
*
|
98 |
+
* @param string $cidr The CIDR value to validate
|
99 |
+
* @param int $version The IP version to validate the CIDR for (4 or 6)
|
100 |
+
*
|
101 |
+
* @return bool
|
102 |
+
*/
|
103 |
+
private static function _is_valid_cidr( $cidr, $version ) {
|
104 |
+
// $version needs to be valid
|
105 |
+
if ( ! in_array( $version, array_keys( self::$_max_cidr ) ) ) {
|
106 |
+
return false;
|
107 |
+
}
|
108 |
+
|
109 |
+
// The cidr needs to be numeric and between 0 and the max
|
110 |
+
if ( isset( $cidr ) && ( ! ctype_digit( $cidr ) || $cidr > self::$_max_cidr[ $version ] ) ) {
|
111 |
+
return false;
|
112 |
+
}
|
113 |
+
|
114 |
+
return true;
|
115 |
+
}
|
116 |
+
|
117 |
+
/**
|
118 |
+
* Checks to see if a given IP/CIDR is a range
|
119 |
+
*
|
120 |
+
* @since 2.2.0
|
121 |
+
*
|
122 |
+
* @static
|
123 |
+
* @access public
|
124 |
+
*
|
125 |
+
* @param string $ip The IP address, can be given in CIDR notation
|
126 |
+
* @param int $version The IP version (4 or 6). This needs to be supplied if skipping validation (for efficiency)
|
127 |
+
* @param bool $validate True to validate the IP, and false to skip (version must be supplied to skip) (Default true)
|
128 |
+
*
|
129 |
+
* @return bool
|
130 |
+
*/
|
131 |
+
public static function is_range( $ip, $version = null, $validate = true ) {
|
132 |
+
if ( $validate || ! isset( $version ) ) {
|
133 |
+
$version = self::validate( $ip );
|
134 |
+
|
135 |
+
// If the IP isn't valid, it's not a range.
|
136 |
+
if ( ! $version ) {
|
137 |
+
return false;
|
138 |
+
}
|
139 |
+
}
|
140 |
+
|
141 |
+
$ip_parts = self::_ip_cidr( $ip );
|
142 |
+
|
143 |
+
// If there is no cidr specified or if it's the max for this IP version, then this is not a range.
|
144 |
+
return !( ! isset( $ip_parts->cidr ) || $ip_parts->cidr == self::$_max_cidr[ $version ] );
|
145 |
+
}
|
146 |
+
|
147 |
+
/**
|
148 |
+
* Gets the start and end IPs for a given range
|
149 |
+
*
|
150 |
+
* @static
|
151 |
+
* @access public
|
152 |
+
*
|
153 |
+
* @param string $ip The IP address, can be given in CIDR notation
|
154 |
+
*
|
155 |
+
* @return bool|array False if the IP is invalid, and an array containing start and end IPs for the range specified otherwise
|
156 |
+
*/
|
157 |
+
public static function get_ip_range( $ip ) {
|
158 |
+
$version = self::validate( $ip );
|
159 |
+
if ( ! $version ) {
|
160 |
+
return false;
|
161 |
+
}
|
162 |
+
|
163 |
+
$ip_parts = self::_ip_cidr( $ip );
|
164 |
+
|
165 |
+
// If this isn't a range, return a single address
|
166 |
+
if ( ! self::is_range( $ip, $version, false ) ) {
|
167 |
+
return array(
|
168 |
+
'start' => $ip_parts->ip,
|
169 |
+
'end' => $ip_parts->ip,
|
170 |
+
);
|
171 |
+
}
|
172 |
+
|
173 |
+
$mask = self::get_mask( $ip_parts->cidr, $version );
|
174 |
+
|
175 |
+
$range = array();
|
176 |
+
$range['start'] = inet_ntop( inet_pton( $ip_parts->ip ) & inet_pton( $mask ) );
|
177 |
+
$range['end'] = inet_ntop( inet_pton( $ip_parts->ip ) | ~ inet_pton( $mask ) );
|
178 |
+
return $range;
|
179 |
+
}
|
180 |
+
|
181 |
+
/**
|
182 |
+
* Gets the mask from CIDR and IP version
|
183 |
+
*
|
184 |
+
* @static
|
185 |
+
* @access public
|
186 |
+
*
|
187 |
+
* @param string $cidr The CIDR value to validate
|
188 |
+
* @param int $version The IP version to validate the CIDR for (4 or 6)
|
189 |
+
*
|
190 |
+
* @return string IP Mask
|
191 |
+
*/
|
192 |
+
public static function get_mask( $cidr, $version ) {
|
193 |
+
if ( ! in_array( $version, array( 4, 6 ) ) ) {
|
194 |
+
return false;
|
195 |
+
}
|
196 |
+
$bin_mask = str_repeat( '1', $cidr ) . str_repeat( '0', self::$_max_cidr[ $version ] - $cidr );
|
197 |
+
|
198 |
+
$bin2mask_method = '_bin2mask_v' . $version;
|
199 |
+
|
200 |
+
return call_user_func( array( 'self', $bin2mask_method ), $bin_mask );
|
201 |
+
}
|
202 |
+
|
203 |
+
/**
|
204 |
+
* Gets the IPv4 mask from the binary representation
|
205 |
+
*
|
206 |
+
* @static
|
207 |
+
* @access private
|
208 |
+
*
|
209 |
+
* @param string $bin_mask The binary representation of the mask
|
210 |
+
*
|
211 |
+
* @return string IP Mask
|
212 |
+
*/
|
213 |
+
private static function _bin2mask_v4( $bin_mask ) {
|
214 |
+
$mask = array();
|
215 |
+
// Eight binary bits per number
|
216 |
+
foreach ( str_split( $bin_mask, 8 ) as $num ) {
|
217 |
+
// Convert from bin to dec and append
|
218 |
+
$mask[] = base_convert( $num, 2, 10 );
|
219 |
+
}
|
220 |
+
|
221 |
+
// Explode our new hex mask into 4 character segments and implode with colons
|
222 |
+
return implode( '.', $mask );
|
223 |
+
}
|
224 |
+
|
225 |
+
/**
|
226 |
+
* Gets the IPv6 mask from the binary representation
|
227 |
+
*
|
228 |
+
* @static
|
229 |
+
* @access private
|
230 |
+
*
|
231 |
+
* @param string $bin_mask The binary representation of the mask
|
232 |
+
*
|
233 |
+
* @return string IP Mask
|
234 |
+
*/
|
235 |
+
private static function _bin2mask_v6( $bin_mask ) {
|
236 |
+
$mask = '';
|
237 |
+
// Four binary bits per hex character
|
238 |
+
foreach ( str_split( $bin_mask, 4 ) as $char ) {
|
239 |
+
// Convert from bin to hex and append
|
240 |
+
$mask .= base_convert( $char, 2, 16 );
|
241 |
+
}
|
242 |
+
|
243 |
+
// Explode our new hex mask into 4 character segments and implode with colons
|
244 |
+
return implode( ':', str_split( $mask, 4 ) );
|
245 |
+
}
|
246 |
+
|
247 |
+
/**
|
248 |
+
* Checks to see if an IP or range is within another IP or range
|
249 |
+
*
|
250 |
+
* @static
|
251 |
+
* @access public
|
252 |
+
*
|
253 |
+
* @param string $ip The IP address to check to see if is contained, can be given in CIDR notation
|
254 |
+
* @param string $range The IP address to check to see if contains, can be given in CIDR notation
|
255 |
+
*
|
256 |
+
* @return bool False if the given IP or range is not completely contained in the supplied range. True if it is
|
257 |
+
*/
|
258 |
+
public static function in_range( $ip, $range ) {
|
259 |
+
$ip_version = self::validate( $ip );
|
260 |
+
// If the IP isn't valid, it's not in the range
|
261 |
+
if ( ! $ip_version ) {
|
262 |
+
return false;
|
263 |
+
}
|
264 |
+
|
265 |
+
$range_version = self::validate( $range );
|
266 |
+
// If the range isn't valid or isn't the same IP version as the first IP, it's not in the range
|
267 |
+
if ( $ip_version != $range_version ) {
|
268 |
+
return false;
|
269 |
+
}
|
270 |
+
|
271 |
+
if ( ! self::is_range( $range, $range_version, false ) ) {
|
272 |
+
if ( ! self::is_range( $ip, $ip_version, false ) ) {
|
273 |
+
$ip_parts = self::_ip_cidr( $ip );
|
274 |
+
$range_parts = self::_ip_cidr( $ip );
|
275 |
+
|
276 |
+
// If neither is a range, just compare and return
|
277 |
+
return $ip_parts->ip == $range_parts->ip;
|
278 |
+
} else {
|
279 |
+
// If the IP is a range and the specified range isn't, then return false
|
280 |
+
return false;
|
281 |
+
}
|
282 |
+
}
|
283 |
+
|
284 |
+
$ip_range = array_map( 'inet_pton', self::get_ip_range( $ip ) );
|
285 |
+
$in_range = array_map( 'inet_pton', self::get_ip_range( $range ) );
|
286 |
+
|
287 |
+
return ( $in_range['start'] <= $ip_range['start'] && $ip_range['end'] <= $in_range['end'] );
|
288 |
+
}
|
289 |
+
|
290 |
+
/**
|
291 |
+
* Checks to see if an IP or range intersects with another IP or range
|
292 |
+
*
|
293 |
+
* @static
|
294 |
+
* @access public
|
295 |
+
*
|
296 |
+
* @param string $ip1 IP address, can be given in CIDR notation
|
297 |
+
* @param string $ip2 IP address, can be given in CIDR notation
|
298 |
+
*
|
299 |
+
* @return bool
|
300 |
+
*/
|
301 |
+
public static function intersect( $ip1, $ip2 ) {
|
302 |
+
$ip1_version = self::validate( $ip1 );
|
303 |
+
// If the first IP isn't valid, there is no intersection
|
304 |
+
if ( ! $ip1_version ) {
|
305 |
+
return false;
|
306 |
+
}
|
307 |
+
|
308 |
+
$ip2_version = self::validate( $ip2 );
|
309 |
+
// If the second IP isn't valid or isn't the same IP version as the first IP, there is no intersection
|
310 |
+
if ( $ip1_version != $ip2_version ) {
|
311 |
+
return false;
|
312 |
+
}
|
313 |
+
|
314 |
+
// If neither is a range, just compare and return
|
315 |
+
if ( ! self::is_range( $ip1, $ip1_version, false ) && ! self::is_range( $ip2, $ip2_version, false ) ) {
|
316 |
+
$ip1_parts = self::_ip_cidr( $ip1 );
|
317 |
+
$ip2_parts = self::_ip_cidr( $ip2 );
|
318 |
+
|
319 |
+
return $ip1_parts->ip == $ip2_parts->ip;
|
320 |
+
}
|
321 |
+
|
322 |
+
$ip1_range = array_map( 'inet_pton', self::get_ip_range( $ip1 ) );
|
323 |
+
$ip2_range = array_map( 'inet_pton', self::get_ip_range( $ip2 ) );
|
324 |
+
|
325 |
+
return (
|
326 |
+
// $ip1_range start is in $ip2_range
|
327 |
+
( $ip2_range['start'] <= $ip1_range['start'] && $ip1_range['start'] <= $ip2_range['end'] ) ||
|
328 |
+
// $ip1_range end is in $ip2_range
|
329 |
+
( $ip2_range['start'] <= $ip1_range['end'] && $ip1_range['end'] <= $ip2_range['end'] ) ||
|
330 |
+
// $ip2_range start is in $ip1_range
|
331 |
+
( $ip1_range['start'] <= $ip2_range['start'] && $ip2_range['start'] <= $ip1_range['end'] ) ||
|
332 |
+
// $ip2_range end is in $ip1_range
|
333 |
+
( $ip1_range['start'] <= $ip2_range['end'] && $ip2_range['end'] <= $ip1_range['end'] )
|
334 |
+
);
|
335 |
+
}
|
336 |
+
|
337 |
+
/**
|
338 |
+
* Converts IP with * wildcards to CIDR format
|
339 |
+
*
|
340 |
+
* Limited to only contiguous wildcards at the end of an IP, and wildcards represent a whole segment not a single character or digit
|
341 |
+
*
|
342 |
+
* @since 2.2.0
|
343 |
+
*
|
344 |
+
* @static
|
345 |
+
* @access public
|
346 |
+
*
|
347 |
+
* @param string $ip The IP address, can be given in CIDR notation
|
348 |
+
* @param int $version The IP version (4 or 6). This needs to be supplied if skipping validation (for efficiency)
|
349 |
+
* @param bool $validate True to validate the IP, and false to skip (version must be supplied to skip) (Default true)
|
350 |
+
*
|
351 |
+
* @return string IP in CIDR format
|
352 |
+
*/
|
353 |
+
public static function ip_wild_to_ip_cidr( $ip, $version = null, $validate = true ) {
|
354 |
+
if ( $validate || ! isset( $version ) ) {
|
355 |
+
// Replace the wildcards with zeroes and test to get version
|
356 |
+
$version = self::validate( self::_clean_wildcards( $ip ) );
|
357 |
+
|
358 |
+
// If the IP isn't valid, it's not a range.
|
359 |
+
if ( ! $version ) {
|
360 |
+
return false;
|
361 |
+
}
|
362 |
+
}
|
363 |
+
|
364 |
+
// Not meant for IPs already using CIDR notation and only works on wildcards
|
365 |
+
if ( strpos( $ip, '/' ) || false === strpos( $ip, '*' ) ) {
|
366 |
+
return $ip;
|
367 |
+
}
|
368 |
+
|
369 |
+
$wild_to_cidr_method = "_ipv{$version}_wild_to_ip_cidr";
|
370 |
+
|
371 |
+
return call_user_func( array( 'self', $wild_to_cidr_method ), $ip );
|
372 |
+
}
|
373 |
+
|
374 |
+
/**
|
375 |
+
* Converts IPv4 IP with * wildcards to CIDR format
|
376 |
+
*
|
377 |
+
* Limited to only contiguous wildcards at the end of an IP, and wildcards represent a whole segment not a single character or digit
|
378 |
+
*
|
379 |
+
* @since 2.2.0
|
380 |
+
*
|
381 |
+
* @static
|
382 |
+
* @access private
|
383 |
+
*
|
384 |
+
* @param string $ip The IP address, can be given in CIDR notation
|
385 |
+
*
|
386 |
+
* @return string IP in CIDR format
|
387 |
+
*/
|
388 |
+
private static function _ipv4_wild_to_ip_cidr( $ip ) {
|
389 |
+
$host_parts = array_reverse( explode( '.', trim( $ip ) ) );
|
390 |
+
|
391 |
+
$mask = self::$_max_cidr[4];
|
392 |
+
$ip = self::_clean_wildcards( $ip );
|
393 |
+
|
394 |
+
//convert hosts with wildcards to host with netmask and create rule lines
|
395 |
+
foreach ( $host_parts as $part ) {
|
396 |
+
if ( '*' === $part ) {
|
397 |
+
$mask -= 8;
|
398 |
+
} else {
|
399 |
+
break; // We only want to deal with contiguous wildcards at the end of an IP
|
400 |
+
}
|
401 |
+
}
|
402 |
+
|
403 |
+
return "{$ip}/{$mask}";
|
404 |
+
}
|
405 |
+
|
406 |
+
/**
|
407 |
+
* Converts IPv6 IP with * wildcards to CIDR format
|
408 |
+
*
|
409 |
+
* Limited to only contiguous wildcards at the end of an IP, and wildcards represent a whole segment not a single character or digit
|
410 |
+
*
|
411 |
+
* @since 2.2.0
|
412 |
+
*
|
413 |
+
* @static
|
414 |
+
* @access private
|
415 |
+
*
|
416 |
+
* @param string $ip The IP address, can be given in CIDR notation
|
417 |
+
*
|
418 |
+
* @return string IP in CIDR format
|
419 |
+
*/
|
420 |
+
private static function _ipv6_wild_to_ip_cidr( $ip ) {
|
421 |
+
$host_parts = array_reverse( explode( ':', trim( $ip ) ) );
|
422 |
+
|
423 |
+
$mask = self::$_max_cidr[6];
|
424 |
+
$ip = self::_clean_wildcards( $ip );
|
425 |
+
|
426 |
+
//convert hosts with wildcards to host with netmask and create rule lines
|
427 |
+
foreach ( $host_parts as $part ) {
|
428 |
+
if ( '*' === $part ) {
|
429 |
+
$mask -= 16;
|
430 |
+
} else {
|
431 |
+
break; // We only want to deal with contiguous wildcards at the end of an IP
|
432 |
+
}
|
433 |
+
}
|
434 |
+
|
435 |
+
return "{$ip}/{$mask}";
|
436 |
+
}
|
437 |
+
|
438 |
+
/**
|
439 |
+
* Remove wildcards, but only those that represent an entire chunk or octets
|
440 |
+
*
|
441 |
+
* @param string $ip The IP to clean
|
442 |
+
*
|
443 |
+
* @return string IP address with wildcards replaced with 0s
|
444 |
+
*/
|
445 |
+
private static function _clean_wildcards( $ip ) {
|
446 |
+
$search = array(
|
447 |
+
'/([:\.])(\*(\1|$))+/', // Match all whole chunks in the middle with wildcards, or a wildcard as the whole chunk at the end
|
448 |
+
'/^\*([:\.])/', // Match a wildcard as the whole first chunk
|
449 |
+
);
|
450 |
+
return preg_replace_callback( $search, array( 'self', 'clean_wildcards_preg_replace_callback' ), $ip );
|
451 |
+
}
|
452 |
+
|
453 |
+
/**
|
454 |
+
* Used with preg_replace_callback() to replace wildcards with 0 ONLY in cases where the wildcard is the whole chunk
|
455 |
+
*
|
456 |
+
* @param array $matches The matches found by preg_replace_callback()
|
457 |
+
*
|
458 |
+
* @return string Replacement string
|
459 |
+
*/
|
460 |
+
public static function clean_wildcards_preg_replace_callback( $matches ) {
|
461 |
+
return str_replace( '*', '0', $matches[0] );
|
462 |
+
}
|
463 |
+
|
464 |
+
/**
|
465 |
+
* Converts IP in CIDR notation to a regex
|
466 |
+
*
|
467 |
+
* @since 2.2.0
|
468 |
+
*
|
469 |
+
* @static
|
470 |
+
* @access public
|
471 |
+
*
|
472 |
+
* @param string $ip The IP address, can be given in CIDR notation
|
473 |
+
* @param int $version The IP version (4 or 6). This needs to be supplied if skipping validation (for efficiency)
|
474 |
+
* @param bool $validate True to validate the IP, and false to skip (version must be supplied to skip) (Default true)
|
475 |
+
*
|
476 |
+
* @return string The IP in regex format
|
477 |
+
*/
|
478 |
+
public static function ip_cidr_to_ip_regex( $ip, $version = null, $validate = true ) {
|
479 |
+
// Not meant for IPs already using wildcards
|
480 |
+
if ( strpos( $ip, '*' ) ) {
|
481 |
+
return $ip;
|
482 |
+
}
|
483 |
+
|
484 |
+
if ( $validate || ! isset( $version ) ) {
|
485 |
+
$version = self::validate( $ip );
|
486 |
+
|
487 |
+
// If the IP isn't valid, it's not a range.
|
488 |
+
if ( ! $version ) {
|
489 |
+
return false;
|
490 |
+
}
|
491 |
+
}
|
492 |
+
|
493 |
+
$ip_parts = self::_ip_cidr( $ip );
|
494 |
+
|
495 |
+
$cidr_to_wild_method = "_ipv{$version}_cidr_to_ip_regex";
|
496 |
+
|
497 |
+
return call_user_func( array( 'self', $cidr_to_wild_method ), $ip_parts );
|
498 |
+
}
|
499 |
+
|
500 |
+
/**
|
501 |
+
* Converts IPv4 in CIDR notation to a regex
|
502 |
+
*
|
503 |
+
* @since 2.2.0
|
504 |
+
*
|
505 |
+
* @static
|
506 |
+
* @access private
|
507 |
+
*
|
508 |
+
* @param object $ip_parts The IP address parts (->ip and ->cidr), generated using self::_ip_cidr()
|
509 |
+
*
|
510 |
+
* @return string The IP in regex format
|
511 |
+
*/
|
512 |
+
private static function _ipv4_cidr_to_ip_regex( $ip_parts ) {
|
513 |
+
// Explode IP into octets and reverse them to work backwards
|
514 |
+
$octets = array_reverse( explode( '.', $ip_parts->ip ) );
|
515 |
+
|
516 |
+
if ( ! isset( $ip_parts->cidr ) ) {
|
517 |
+
$ip_parts->cidr = self::$_max_cidr[4];
|
518 |
+
}
|
519 |
+
|
520 |
+
// How many bits are actually masked
|
521 |
+
$masked_bits = self::$_max_cidr[4] - $ip_parts->cidr;
|
522 |
+
|
523 |
+
$i = 0;
|
524 |
+
// For each set of 8 masked bits, we match a whole octet (3 digits is good enough here)
|
525 |
+
while ( $masked_bits >= 8 ) {
|
526 |
+
$octets[ $i ] = '[0-9]{1,3}';
|
527 |
+
$masked_bits -= 8;
|
528 |
+
++$i;
|
529 |
+
}
|
530 |
+
|
531 |
+
// If there are still masked bits to deal with after handling all whole octets
|
532 |
+
if ( $masked_bits ) {
|
533 |
+
// The step is the gap between the low and high values for this octet
|
534 |
+
$step = base_convert( str_repeat( '1', $masked_bits ), 2, 10 ) + 1;
|
535 |
+
// $low is the low value for this octect, based on the step
|
536 |
+
$low = $octets[ $i ] - ( $octets[ $i ] % $step );
|
537 |
+
// The regex we use is simply a valid range of numbers in a group with alternation, ex: (0|1|2|3|4|5|6|7)
|
538 |
+
$octets[ $i ] = '(' . implode( '|', range( $low, $low + $step - 1 ) ) . ')';
|
539 |
+
}
|
540 |
+
|
541 |
+
// Re-reverse the octets array to set things straight, and put the pieces back together
|
542 |
+
// Escape the . for a literal .
|
543 |
+
return implode( '\.', array_reverse( $octets ) );
|
544 |
+
}
|
545 |
+
|
546 |
+
/**
|
547 |
+
* Converts IPv6 in CIDR notation to a regex
|
548 |
+
*
|
549 |
+
* @since 2.2.0
|
550 |
+
*
|
551 |
+
* @static
|
552 |
+
* @access private
|
553 |
+
*
|
554 |
+
* @param object $ip_parts The IP address parts (->ip and ->cidr), generated using self::_ip_cidr()
|
555 |
+
*
|
556 |
+
* @return string The IP in regex format
|
557 |
+
*/
|
558 |
+
private static function _ipv6_cidr_to_ip_regex( $ip_parts ) {
|
559 |
+
// If the IP address has a :: in it, we need to expand that out so we have all eight chuks to work with
|
560 |
+
$colons = substr_count( $ip_parts->ip, ':' );
|
561 |
+
if ( $colons < 7 ) {
|
562 |
+
// Fill out all the chunks so we can properly mask them all
|
563 |
+
$ip_parts->ip = str_replace( '::', str_repeat( ':0', 7 - $colons + 1 ) . ':', $ip_parts->ip );
|
564 |
+
}
|
565 |
+
|
566 |
+
// Explode IP into chunks and reverse them to work backwards
|
567 |
+
$chunks = array_reverse( explode( ':', $ip_parts->ip ) );
|
568 |
+
|
569 |
+
if ( ! isset( $ip_parts->cidr ) ) {
|
570 |
+
$ip_parts->cidr = self::$_max_cidr[6];
|
571 |
+
}
|
572 |
+
$masked_bits = self::$_max_cidr[6] - $ip_parts->cidr;
|
573 |
+
|
574 |
+
$i = 0;
|
575 |
+
// For each set of 16 masked bits, we match a whole chunk (1-4 hex characters)
|
576 |
+
while ( $masked_bits >= 16 ) {
|
577 |
+
$chunks[ $i ] = '[0-f]{1,4}';
|
578 |
+
$masked_bits -= 16;
|
579 |
+
++$i;
|
580 |
+
}
|
581 |
+
|
582 |
+
|
583 |
+
// If there are still masked bits to deal with after handling all whole chunks, we start working in single hex characters
|
584 |
+
if ( $masked_bits ) {
|
585 |
+
// Explode the chunk into characters and reverse them to work backwards
|
586 |
+
$characters = array_reverse( str_split( str_pad( $chunks[ $i ], 4, '0', STR_PAD_LEFT ) ) );
|
587 |
+
|
588 |
+
$j = 0;
|
589 |
+
// For each set of 4 masked bits, we match a single hex character
|
590 |
+
while ( $masked_bits >= 4 ) {
|
591 |
+
$characters[ $j ] = '[0-f]';
|
592 |
+
$masked_bits -= 4;
|
593 |
+
++$j;
|
594 |
+
}
|
595 |
+
|
596 |
+
// If there are still masked bits to deal with after handling all whole characters
|
597 |
+
if ( $masked_bits ) {
|
598 |
+
// $step is the gap between the low and high values for this hex character (we want this in base 10 for use in operations)
|
599 |
+
$step = base_convert( str_repeat( '1', $masked_bits ), 2, 10 ) + 1;
|
600 |
+
// $value is the current value of the character in base 10
|
601 |
+
$value = base_convert( $characters[ $j ], 16, 10 );
|
602 |
+
// $low is the base 10 representation of the low value for this character based on the step
|
603 |
+
$low = $value - ( $value % $step );
|
604 |
+
// $high is the hex value (redy for our regex) of the high value for this character
|
605 |
+
$high = base_convert( $low + $step - 1, 10, 16 );
|
606 |
+
// Convert $low to hex for our
|
607 |
+
$low = base_convert( $low, 10, 16 );
|
608 |
+
// For our regex we use a character set from low to high, ex: [4-7] or [8-b]
|
609 |
+
$characters[ $j ] = "[{$low}-{$high}]";
|
610 |
+
}
|
611 |
+
|
612 |
+
// Re-reverse the characters array to set things straight, and put the pieces back together
|
613 |
+
$chunks[ $i ] = implode( array_reverse( $characters ) );
|
614 |
+
$zeroes = strlen( $chunks[ $i ] ) - strlen( ltrim( $chunks[ $i ], '0' ) );
|
615 |
+
if ( $zeroes ) {
|
616 |
+
$chunks[ $i ] = str_repeat( '0?', $zeroes ) . ltrim( $chunks[ $i ], '0' );
|
617 |
+
}
|
618 |
+
}
|
619 |
+
|
620 |
+
for ( $i; $i < count( $chunks ); $i++ ) {
|
621 |
+
$chunks[ $i ] = ltrim( $chunks[ $i ], '0' );
|
622 |
+
$num_chars = strlen( $chunks[ $i ] );
|
623 |
+
if ( $num_chars < 4 ) {
|
624 |
+
$chunks[ $i ] = str_repeat( '0?', 4 - $num_chars ) . $chunks[ $i ];
|
625 |
+
}
|
626 |
+
}
|
627 |
+
|
628 |
+
// Re-reverse the chunks array to set things straight, and put the pieces back together
|
629 |
+
$regex = implode( ':', array_reverse( $chunks ) );
|
630 |
+
|
631 |
+
// Replace multiple chunks of all zeros with a regular expression that makes them optional but still enforces accurate matching
|
632 |
+
$regex = preg_replace_callback( '/0\?0\?0\?0\?(\:0\?0\?0\?0\?)+/', array( 'self', 'ipv6_regex_preg_replace_callback' ), $regex );
|
633 |
+
|
634 |
+
return $regex;
|
635 |
+
}
|
636 |
+
|
637 |
+
/**
|
638 |
+
* Used with preg_replace_callback() to make chunks of all zeroes optional while still enforcing accurate matching
|
639 |
+
*
|
640 |
+
* @param array $matches The matches found by preg_replace_callback()
|
641 |
+
*
|
642 |
+
* @return string Replacement string
|
643 |
+
*/
|
644 |
+
public static function ipv6_regex_preg_replace_callback( $matches ) {
|
645 |
+
// Get the number of colons (chunks - 1) that we are replacing so we make sure to match no more than the original number of chunks
|
646 |
+
$colons = substr_count( $matches[0], ':' );
|
647 |
+
return sprintf( '(0{0,4}:){0,%d}(0{0,4})?', $colons );
|
648 |
+
}
|
649 |
+
}
|
core/lib/class-itsec-lib-utility.php
CHANGED
@@ -63,9 +63,11 @@ class ITSEC_Lib_Utility {
|
|
63 |
* @return string Returns apache, nginx, litespeed, or iis. Defaults to apache when the server cannot be identified.
|
64 |
*/
|
65 |
public static function get_web_server() {
|
|
|
66 |
if ( defined( 'ITSEC_SERVER_OVERRIDE' ) ) {
|
67 |
return ITSEC_SERVER_OVERRIDE;
|
68 |
}
|
|
|
69 |
|
70 |
|
71 |
if ( isset( $_SERVER['SERVER_SOFTWARE'] ) ) {
|
63 |
* @return string Returns apache, nginx, litespeed, or iis. Defaults to apache when the server cannot be identified.
|
64 |
*/
|
65 |
public static function get_web_server() {
|
66 |
+
// @codeCoverageIgnoreStart
|
67 |
if ( defined( 'ITSEC_SERVER_OVERRIDE' ) ) {
|
68 |
return ITSEC_SERVER_OVERRIDE;
|
69 |
}
|
70 |
+
// @codeCoverageIgnoreEnd
|
71 |
|
72 |
|
73 |
if ( isset( $_SERVER['SERVER_SOFTWARE'] ) ) {
|
core/modules/away-mode/class-ithemes-sync-verb-itsec-get-away-mode.php
CHANGED
@@ -21,6 +21,9 @@ class Ithemes_Sync_Verb_ITSEC_Get_Away_Mode extends Ithemes_Sync_Verb {
|
|
21 |
$away_enabled = false;
|
22 |
$next = absint( $away );
|
23 |
|
|
|
|
|
|
|
24 |
}
|
25 |
|
26 |
return array(
|
21 |
$away_enabled = false;
|
22 |
$next = absint( $away );
|
23 |
|
24 |
+
} else {
|
25 |
+
$away_enabled = null;
|
26 |
+
$next = null;
|
27 |
}
|
28 |
|
29 |
return array(
|
core/modules/backup/class-itsec-backup.php
CHANGED
@@ -238,15 +238,18 @@ class ITSEC_Backup {
|
|
238 |
|
239 |
$return .= PHP_EOL . PHP_EOL;
|
240 |
|
241 |
-
$current_time = current_time( 'timestamp' );
|
242 |
-
|
243 |
//save file
|
244 |
-
$file = 'backup-' . substr( sanitize_title( get_bloginfo( 'name' ) ), 0, 20 ) . '-' .
|
|
|
|
|
245 |
|
246 |
-
|
247 |
-
|
|
|
248 |
}
|
249 |
|
|
|
|
|
250 |
$handle = @fopen( $itsec_globals['ithemes_backup_dir'] . '/' . $file . '.sql', 'w+' );
|
251 |
|
252 |
@fwrite( $handle, $return );
|
@@ -261,7 +264,7 @@ class ITSEC_Backup {
|
|
261 |
|
262 |
$zip = new PclZip( $itsec_globals['ithemes_backup_dir'] . '/' . $file . '.zip' );
|
263 |
|
264 |
-
if ( 0 != $zip->create( $itsec_globals['ithemes_backup_dir'] . '/' . $file . '.sql' ) ) {
|
265 |
|
266 |
//delete .sql and keep zip
|
267 |
@unlink( $itsec_globals['ithemes_backup_dir'] . '/' . $file . '.sql' );
|
@@ -270,10 +273,6 @@ class ITSEC_Backup {
|
|
270 |
|
271 |
}
|
272 |
|
273 |
-
} else {
|
274 |
-
|
275 |
-
$fileext = '.sql';
|
276 |
-
|
277 |
}
|
278 |
|
279 |
if ( 2 !== $this->settings['method'] || true === $one_time ) {
|
238 |
|
239 |
$return .= PHP_EOL . PHP_EOL;
|
240 |
|
|
|
|
|
241 |
//save file
|
242 |
+
$file = 'backup-' . substr( sanitize_title( get_bloginfo( 'name' ) ), 0, 20 ) . '-' . current_time( 'Ymd-His' ) . '-' . wp_generate_password( 30, false );
|
243 |
+
|
244 |
+
wp_mkdir_p( $itsec_globals['ithemes_backup_dir'] );
|
245 |
|
246 |
+
// Make sure we have an index file to block directory listing
|
247 |
+
if ( ! file_exists( path_join( $itsec_globals['ithemes_backup_dir'], 'index.php' ) ) ) {
|
248 |
+
file_put_contents( path_join( $itsec_globals['ithemes_backup_dir'], 'index.php' ), "<?php\n// Silence is golden." );
|
249 |
}
|
250 |
|
251 |
+
$fileext = '.sql';
|
252 |
+
|
253 |
$handle = @fopen( $itsec_globals['ithemes_backup_dir'] . '/' . $file . '.sql', 'w+' );
|
254 |
|
255 |
@fwrite( $handle, $return );
|
264 |
|
265 |
$zip = new PclZip( $itsec_globals['ithemes_backup_dir'] . '/' . $file . '.zip' );
|
266 |
|
267 |
+
if ( 0 != $zip->create( $itsec_globals['ithemes_backup_dir'] . '/' . $file . '.sql', PCLZIP_OPT_REMOVE_PATH, $itsec_globals['ithemes_backup_dir'] ) ) {
|
268 |
|
269 |
//delete .sql and keep zip
|
270 |
@unlink( $itsec_globals['ithemes_backup_dir'] . '/' . $file . '.sql' );
|
273 |
|
274 |
}
|
275 |
|
|
|
|
|
|
|
|
|
276 |
}
|
277 |
|
278 |
if ( 2 !== $this->settings['method'] || true === $one_time ) {
|
core/modules/ban-users/class-itsec-ban-users-admin.php
CHANGED
@@ -177,14 +177,18 @@ class ITSEC_Ban_Users_Admin {
|
|
177 |
}
|
178 |
|
179 |
echo '<textarea id="itsec_ban_users_host_list" name="itsec_ban_users[host_list]" rows="10" cols="50">' . $host_list . PHP_EOL . '</textarea>';
|
180 |
-
echo '<p>' . __( 'Use the guidelines below to enter hosts that will not be allowed access to your site.
|
181 |
echo '<ul>';
|
182 |
-
echo '<li>' . __( 'You may ban users by individual IP address or IP address range.', 'better-wp-security' ) . '</li>';
|
183 |
-
echo '<
|
184 |
-
echo '<li>' . __( '
|
185 |
-
echo '<li
|
|
|
|
|
186 |
echo '<li>' . __( 'Enter only 1 IP address or 1 IP address range per line.', 'better-wp-security' ) . '</li>';
|
|
|
187 |
echo '</ul>';
|
|
|
188 |
|
189 |
}
|
190 |
|
@@ -240,6 +244,10 @@ class ITSEC_Ban_Users_Admin {
|
|
240 |
}
|
241 |
|
242 |
protected function get_server_config_ban_hosts_rules( $server_type ) {
|
|
|
|
|
|
|
|
|
243 |
if ( true !== $this->settings['enabled'] ) {
|
244 |
return '';
|
245 |
}
|
@@ -261,14 +269,13 @@ class ITSEC_Ban_Users_Admin {
|
|
261 |
|
262 |
// process hosts list
|
263 |
foreach ( $this->settings['host_list'] as $host ) {
|
264 |
-
$host =
|
265 |
-
$host = trim( $host );
|
266 |
|
267 |
if ( empty( $host ) ) {
|
268 |
continue;
|
269 |
}
|
270 |
|
271 |
-
if (
|
272 |
/**
|
273 |
* @todo warn the user the ip to be banned is whitelisted
|
274 |
*/
|
@@ -277,26 +284,19 @@ class ITSEC_Ban_Users_Admin {
|
|
277 |
|
278 |
|
279 |
if ( in_array( $server_type, array( 'apache', 'litespeed' ) ) ) {
|
280 |
-
$converted_host =
|
281 |
-
$converted_host = trim( $converted_host );
|
282 |
|
283 |
if ( empty( $converted_host ) ) {
|
284 |
continue;
|
285 |
}
|
286 |
|
287 |
-
|
288 |
-
$
|
289 |
-
|
290 |
-
$set_env_rules .= "\tSetEnvIF REMOTE_ADDR \"^$set_env_host$\" DenyAccess\n"; // Ban IP
|
291 |
-
$set_env_rules .= "\tSetEnvIF X-FORWARDED-FOR \"^$set_env_host$\" DenyAccess\n"; // Ban IP from a proxy
|
292 |
-
$set_env_rules .= "\tSetEnvIF X-CLUSTER-CLIENT-IP \"^$set_env_host$\" DenyAccess\n"; // Ban IP from a load balancer
|
293 |
$set_env_rules .= "\n";
|
294 |
|
295 |
-
|
296 |
-
$
|
297 |
-
|
298 |
-
$require_rules .= "\t\t\tRequire not ip $require_host\n";
|
299 |
-
$deny_rules .= "\t\tDeny from $require_host\n";
|
300 |
} else if ( 'nginx' === $server_type ) {
|
301 |
$host_rules .= "\tdeny $host;\n";
|
302 |
}
|
@@ -339,7 +339,7 @@ class ITSEC_Ban_Users_Admin {
|
|
339 |
} else if ( 'nginx' === $server_type ) {
|
340 |
if ( ! empty( $host_rules ) ) {
|
341 |
$rules .= "\n";
|
342 |
-
$rules .= "# " . __( 'Ban Hosts - Security > Settings > Banned Users', 'better-wp-security' ) . "\n";
|
343 |
$rules .= $host_rules;
|
344 |
}
|
345 |
}
|
@@ -374,7 +374,7 @@ class ITSEC_Ban_Users_Admin {
|
|
374 |
$rewrite_rules .= "\t\tRewriteCond %{HTTP_USER_AGENT} ^$agent [NC,OR]\n";
|
375 |
} else if ( 'nginx' === $server_type ) {
|
376 |
$agent = str_replace( '"', '\\"', $agent );
|
377 |
-
$agent_rules .= "
|
378 |
}
|
379 |
}
|
380 |
|
@@ -526,6 +526,10 @@ class ITSEC_Ban_Users_Admin {
|
|
526 |
* @return Array Sanitized array
|
527 |
*/
|
528 |
public function sanitize_module_input( $input ) {
|
|
|
|
|
|
|
|
|
529 |
|
530 |
global $itsec_globals;
|
531 |
|
@@ -579,11 +583,18 @@ class ITSEC_Ban_Users_Admin {
|
|
579 |
continue;
|
580 |
}
|
581 |
|
582 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
583 |
$bad_ips[] = trim( filter_var( $address, FILTER_SANITIZE_STRING ) );
|
584 |
}
|
585 |
|
586 |
-
if (
|
587 |
$white_ips[] = trim( filter_var( $address, FILTER_SANITIZE_STRING ) );
|
588 |
}
|
589 |
|
177 |
}
|
178 |
|
179 |
echo '<textarea id="itsec_ban_users_host_list" name="itsec_ban_users[host_list]" rows="10" cols="50">' . $host_list . PHP_EOL . '</textarea>';
|
180 |
+
echo '<p>' . __( 'Use the guidelines below to enter hosts that will not be allowed access to your site.', 'better-wp-security' ) . '</p>';
|
181 |
echo '<ul>';
|
182 |
+
echo '<li>' . __( 'You may ban users by individual IP address or IP address range using wildcards or CIDR notation.', 'better-wp-security' ) . '</li>';
|
183 |
+
echo '<ul>';
|
184 |
+
echo '<li>' . __( 'Individual IP addresses must be in IPv4 or IPv6 standard format (###.###.###.### or ####:####:####:####:####:####:####:####).', 'better-wp-security' ) . '</li>';
|
185 |
+
echo '<li>' . __( 'CIDR notation is allowed to specify a range of IP addresses (###.###.###.###/## or ####:####:####:####:####:####:####:####/###).', 'better-wp-security' ) . '</li>';
|
186 |
+
echo '<li>' . __( 'Wildcards are also supported with some limitations. If using wildcards (*), you must start with the right-most chunk in the IP address. For example ###.###.###.* and ###.###.*.* are permitted but ###.###.*.### is not. Wildcards are only for convenient entering of IP addresses, and will be automatically converted to their appropriate CIDR notation format on save.', 'better-wp-security' ) . '</li>';
|
187 |
+
echo '</ul>';
|
188 |
echo '<li>' . __( 'Enter only 1 IP address or 1 IP address range per line.', 'better-wp-security' ) . '</li>';
|
189 |
+
echo '<li>' . __( 'Note: You cannot ban yourself.', 'better-wp-security' ) . '</li>';
|
190 |
echo '</ul>';
|
191 |
+
echo '<p><a href="http://ip-lookup.net/domain-lookup.php" target="_blank">' . __( 'Lookup IP Address.', 'better-wp-security' ) . '</a></p>';
|
192 |
|
193 |
}
|
194 |
|
244 |
}
|
245 |
|
246 |
protected function get_server_config_ban_hosts_rules( $server_type ) {
|
247 |
+
if ( ! class_exists( 'ITSEC_Lib_IP_Tools' ) ) {
|
248 |
+
$itsec_core = ITSEC_Core::get_instance();
|
249 |
+
require_once( dirname( $itsec_core->get_plugin_file() ) . '/core/lib/class-itsec-lib-ip-tools.php' );
|
250 |
+
}
|
251 |
if ( true !== $this->settings['enabled'] ) {
|
252 |
return '';
|
253 |
}
|
269 |
|
270 |
// process hosts list
|
271 |
foreach ( $this->settings['host_list'] as $host ) {
|
272 |
+
$host = ITSEC_Lib_IP_Tools::ip_wild_to_ip_cidr( trim( $host ) );
|
|
|
273 |
|
274 |
if ( empty( $host ) ) {
|
275 |
continue;
|
276 |
}
|
277 |
|
278 |
+
if ( ITSEC_Lib::is_ip_whitelisted( $host ) ) {
|
279 |
/**
|
280 |
* @todo warn the user the ip to be banned is whitelisted
|
281 |
*/
|
284 |
|
285 |
|
286 |
if ( in_array( $server_type, array( 'apache', 'litespeed' ) ) ) {
|
287 |
+
$converted_host = ITSEC_Lib_IP_Tools::ip_cidr_to_ip_regex( $host );
|
|
|
288 |
|
289 |
if ( empty( $converted_host ) ) {
|
290 |
continue;
|
291 |
}
|
292 |
|
293 |
+
$set_env_rules .= "\tSetEnvIF REMOTE_ADDR \"^$converted_host$\" DenyAccess\n"; // Ban IP
|
294 |
+
$set_env_rules .= "\tSetEnvIF X-FORWARDED-FOR \"^$converted_host$\" DenyAccess\n"; // Ban IP from a proxy
|
295 |
+
$set_env_rules .= "\tSetEnvIF X-CLUSTER-CLIENT-IP \"^$converted_host$\" DenyAccess\n"; // Ban IP from a load balancer
|
|
|
|
|
|
|
296 |
$set_env_rules .= "\n";
|
297 |
|
298 |
+
$require_rules .= "\t\t\tRequire not ip $host\n";
|
299 |
+
$deny_rules .= "\t\tDeny from $host\n";
|
|
|
|
|
|
|
300 |
} else if ( 'nginx' === $server_type ) {
|
301 |
$host_rules .= "\tdeny $host;\n";
|
302 |
}
|
339 |
} else if ( 'nginx' === $server_type ) {
|
340 |
if ( ! empty( $host_rules ) ) {
|
341 |
$rules .= "\n";
|
342 |
+
$rules .= "\t# " . __( 'Ban Hosts - Security > Settings > Banned Users', 'better-wp-security' ) . "\n";
|
343 |
$rules .= $host_rules;
|
344 |
}
|
345 |
}
|
374 |
$rewrite_rules .= "\t\tRewriteCond %{HTTP_USER_AGENT} ^$agent [NC,OR]\n";
|
375 |
} else if ( 'nginx' === $server_type ) {
|
376 |
$agent = str_replace( '"', '\\"', $agent );
|
377 |
+
$agent_rules .= "\tif (\$http_user_agent ~* \"^$agent\") { return 403; }\n";
|
378 |
}
|
379 |
}
|
380 |
|
526 |
* @return Array Sanitized array
|
527 |
*/
|
528 |
public function sanitize_module_input( $input ) {
|
529 |
+
if ( ! class_exists( 'ITSEC_Lib_IP_Tools' ) ) {
|
530 |
+
$itsec_core = ITSEC_Core::get_instance();
|
531 |
+
require_once( dirname( $itsec_core->get_plugin_file() ) . '/core/lib/class-itsec-lib-ip-tools.php' );
|
532 |
+
}
|
533 |
|
534 |
global $itsec_globals;
|
535 |
|
583 |
continue;
|
584 |
}
|
585 |
|
586 |
+
//Store the original user supplied IP for use in error messages or to fill back into the list if invalid
|
587 |
+
$original_address = $address;
|
588 |
+
|
589 |
+
// This checks validity and converts wildcard notation to standard CIDR notation
|
590 |
+
$address = ITSEC_Lib_IP_Tools::ip_wild_to_ip_cidr( $address );
|
591 |
+
if ( ! $address ) {
|
592 |
+
// Put the address back to the original so it's not removed from the list
|
593 |
+
$address = $original_address;
|
594 |
$bad_ips[] = trim( filter_var( $address, FILTER_SANITIZE_STRING ) );
|
595 |
}
|
596 |
|
597 |
+
if ( ITSEC_Lib::is_ip_whitelisted( $address, null, true ) ) {
|
598 |
$white_ips[] = trim( filter_var( $address, FILTER_SANITIZE_STRING ) );
|
599 |
}
|
600 |
|
core/modules/ban-users/class-itsec-ban-users.php
CHANGED
@@ -38,7 +38,7 @@ class ITSEC_Ban_Users {
|
|
38 |
|
39 |
}
|
40 |
|
41 |
-
if ( ! in_array( $host, $ban_list ) && !
|
42 |
|
43 |
$ban_list[] = $host;
|
44 |
$settings['host_list'] = $ban_list;
|
@@ -49,100 +49,4 @@ class ITSEC_Ban_Users {
|
|
49 |
}
|
50 |
|
51 |
}
|
52 |
-
|
53 |
-
/**
|
54 |
-
* Determines whether a given IP address is whitelisted
|
55 |
-
*
|
56 |
-
* @param string $ip_to_check ip to check
|
57 |
-
* @param array $white_ips ip list to compare to if not yet saved to options
|
58 |
-
* @param boolean $current whether to whitelist the current ip or not (due to saving, etc)
|
59 |
-
*
|
60 |
-
* @return boolean true if whitelisted or false
|
61 |
-
*/
|
62 |
-
public static function is_ip_whitelisted( $ip_to_check, $white_ips = null, $current = false ) {
|
63 |
-
|
64 |
-
$ip_to_check = trim( $ip_to_check );
|
65 |
-
|
66 |
-
if ( $white_ips === null ) {
|
67 |
-
|
68 |
-
$global_settings = get_site_option( 'itsec_global' );
|
69 |
-
|
70 |
-
$white_ips = ( isset( $global_settings['lockout_white_list'] ) ? $global_settings['lockout_white_list'] : array() );
|
71 |
-
|
72 |
-
}
|
73 |
-
|
74 |
-
if ( $current === true ) {
|
75 |
-
$white_ips[] = ITSEC_Lib::get_ip(); //add current user ip to whitelist to check automatically
|
76 |
-
}
|
77 |
-
|
78 |
-
foreach ( $white_ips as $white_ip ) {
|
79 |
-
|
80 |
-
$converted_white_ip = ITSEC_Lib::ip_wild_to_mask( $white_ip );
|
81 |
-
|
82 |
-
$check_range = ITSEC_Lib::cidr_to_range( $converted_white_ip );
|
83 |
-
$ip_range = ITSEC_Lib::cidr_to_range( $ip_to_check );
|
84 |
-
|
85 |
-
if ( sizeof( $check_range ) === 2 ) { //range to check
|
86 |
-
|
87 |
-
$check_min = ip2long( $check_range[0] );
|
88 |
-
$check_max = ip2long( $check_range[1] );
|
89 |
-
|
90 |
-
if ( sizeof( $ip_range ) === 2 ) {
|
91 |
-
|
92 |
-
$ip_min = ip2long( $ip_range[0] );
|
93 |
-
$ip_max = ip2long( $ip_range[1] );
|
94 |
-
|
95 |
-
/**
|
96 |
-
* Checks cover the following scenarios:
|
97 |
-
* - min-a, min-b, max-a, max-b : min-b is in a range and min-a is in b range
|
98 |
-
* - min-b, min-a, max-b, max-a : max-b is in a range and max-a is in b range
|
99 |
-
* - min-a, min-b, max-b, max-a : range b is encapsulated by range a
|
100 |
-
* - min-b, min-a, max-a, max-b : range a is encapsulated by range b
|
101 |
-
*/
|
102 |
-
if ( ( $check_min <= $ip_min && $ip_min <= $check_max ) || ( $check_min <= $ip_max && $ip_max <= $check_max ) ||
|
103 |
-
( $ip_min <= $check_min && $check_min <= $ip_max ) || ( $ip_min <= $check_max && $check_max <= $ip_max ) ) {
|
104 |
-
return true;
|
105 |
-
}
|
106 |
-
|
107 |
-
} else {
|
108 |
-
|
109 |
-
$ip = ip2long( $ip_range[0] );
|
110 |
-
|
111 |
-
if ( $check_min <= $ip && $ip <= $check_max ) {
|
112 |
-
return true;
|
113 |
-
}
|
114 |
-
|
115 |
-
}
|
116 |
-
|
117 |
-
} else { //single ip to check
|
118 |
-
|
119 |
-
$check = ip2long( $check_range[0] );
|
120 |
-
|
121 |
-
if ( sizeof( $ip_range ) === 2 ) {
|
122 |
-
|
123 |
-
$ip_min = ip2long( $ip_range[0] );
|
124 |
-
$ip_max = ip2long( $ip_range[1] );
|
125 |
-
|
126 |
-
if ( $ip_min <= $check && $check <= $ip_max ) {
|
127 |
-
return true;
|
128 |
-
}
|
129 |
-
|
130 |
-
} else {
|
131 |
-
|
132 |
-
$ip = ip2long( $ip_range[0] );
|
133 |
-
|
134 |
-
if ( $check == $ip ) {
|
135 |
-
return true;
|
136 |
-
}
|
137 |
-
|
138 |
-
}
|
139 |
-
|
140 |
-
}
|
141 |
-
|
142 |
-
}
|
143 |
-
|
144 |
-
return false;
|
145 |
-
|
146 |
-
}
|
147 |
-
|
148 |
}
|
38 |
|
39 |
}
|
40 |
|
41 |
+
if ( ! in_array( $host, $ban_list ) && ! ITSEC_Lib::is_ip_whitelisted( $host, $white_list ) ) {
|
42 |
|
43 |
$ban_list[] = $host;
|
44 |
$settings['host_list'] = $ban_list;
|
49 |
}
|
50 |
|
51 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
52 |
}
|
core/modules/brute-force/class-itsec-brute-force-log.php
CHANGED
@@ -44,13 +44,19 @@ final class ITSEC_Brute_Force_Log extends ITSEC_WP_List_Table {
|
|
44 |
*
|
45 |
**/
|
46 |
function column_host( $item ) {
|
|
|
|
|
|
|
|
|
47 |
|
48 |
$r = array();
|
49 |
if ( ! is_array( $item['host'] ) ) {
|
50 |
$item['host'] = array( $item['host'] );
|
51 |
}
|
52 |
foreach ( $item['host'] as $host ) {
|
53 |
-
|
|
|
|
|
54 |
}
|
55 |
$return = implode( '<br />', $r );
|
56 |
|
44 |
*
|
45 |
**/
|
46 |
function column_host( $item ) {
|
47 |
+
if ( ! class_exists( 'ITSEC_Lib_IP_Tools' ) ) {
|
48 |
+
$itsec_core = ITSEC_Core::get_instance();
|
49 |
+
require_once( dirname( $itsec_core->get_plugin_file() ) . '/core/lib/class-itsec-lib-ip-tools.php' );
|
50 |
+
}
|
51 |
|
52 |
$r = array();
|
53 |
if ( ! is_array( $item['host'] ) ) {
|
54 |
$item['host'] = array( $item['host'] );
|
55 |
}
|
56 |
foreach ( $item['host'] as $host ) {
|
57 |
+
if ( ITSEC_Lib_IP_Tools::validate( $host ) ) {
|
58 |
+
$r[] = '<a href="http://www.traceip.net/?query=' . urlencode( $host ) . '" target="_blank">' . esc_html( $host ) . '</a>';
|
59 |
+
}
|
60 |
}
|
61 |
$return = implode( '<br />', $r );
|
62 |
|
core/modules/content-directory/class-itsec-content-directory-admin.php
CHANGED
@@ -291,7 +291,12 @@ class ITSEC_Content_Directory_Admin {
|
|
291 |
|
292 |
return;
|
293 |
}
|
294 |
-
|
|
|
|
|
|
|
|
|
|
|
295 |
$new_permissions = ITSEC_Lib_Directory::get_permissions( $new_dir );
|
296 |
|
297 |
if ( is_int( $old_permissions) && is_int( $new_permissions ) && ( $old_permissions != $new_permissions ) ) {
|
291 |
|
292 |
return;
|
293 |
}
|
294 |
+
|
295 |
+
// Make sure ITSEC_Core knows it's in a different place
|
296 |
+
$itsec_core = ITSEC_Core::get_instance();
|
297 |
+
$itsec_core->plugin_file = str_replace( $old_name, $new_name, $itsec_core->get_plugin_file() );
|
298 |
+
|
299 |
+
|
300 |
$new_permissions = ITSEC_Lib_Directory::get_permissions( $new_dir );
|
301 |
|
302 |
if ( is_int( $old_permissions) && is_int( $new_permissions ) && ( $old_permissions != $new_permissions ) ) {
|
core/modules/core/class-itsec-core-admin.php
CHANGED
@@ -171,7 +171,7 @@ class ITSEC_Core_Admin {
|
|
171 |
|
172 |
<div id="mc_embed_signup">
|
173 |
<form
|
174 |
-
action="
|
175 |
method="post" id="mc-embedded-subscribe-form" name="mc-embedded-subscribe-form" class="validate"
|
176 |
target="_blank" novalidate>
|
177 |
<div style="text-align: center;">
|
@@ -251,7 +251,7 @@ class ITSEC_Core_Admin {
|
|
251 |
global $itsec_globals;
|
252 |
|
253 |
echo '<div class="updated" id="itsec_setup_notice"><span class="it-icon-itsec"></span>'
|
254 |
-
. $itsec_globals['plugin_name'] . ' ' . __( 'is almost ready.', 'better-wp-security' ) . '<a href="#" class="itsec-notice-button" onclick="document.location.href=\'?itsec_setup=yes&_wpnonce=' . wp_create_nonce( 'itsec-nag' ) . '\';">' . __( 'Secure Your Site Now', 'better-wp-security' ) . '</a><a
|
255 |
</div>';
|
256 |
|
257 |
}
|
171 |
|
172 |
<div id="mc_embed_signup">
|
173 |
<form
|
174 |
+
action="https://ithemes.us2.list-manage.com/subscribe/post?u=7acf83c7a47b32c740ad94a4e&id=5176bfed9e"
|
175 |
method="post" id="mc-embedded-subscribe-form" name="mc-embedded-subscribe-form" class="validate"
|
176 |
target="_blank" novalidate>
|
177 |
<div style="text-align: center;">
|
251 |
global $itsec_globals;
|
252 |
|
253 |
echo '<div class="updated" id="itsec_setup_notice"><span class="it-icon-itsec"></span>'
|
254 |
+
. $itsec_globals['plugin_name'] . ' ' . __( 'is almost ready.', 'better-wp-security' ) . '<a href="#" class="itsec-notice-button" onclick="document.location.href=\'?itsec_setup=yes&_wpnonce=' . wp_create_nonce( 'itsec-nag' ) . '\';">' . __( 'Secure Your Site Now', 'better-wp-security' ) . '</a><a href="#" class="itsec-notice-hide" onclick="document.location.href=\'?itsec_setup=no&_wpnonce=' . wp_create_nonce( 'itsec-nag' ) . '\';">×</a>
|
255 |
</div>';
|
256 |
|
257 |
}
|
core/modules/file-change/class-itsec-file-change-admin.php
CHANGED
@@ -86,16 +86,18 @@ class ITSEC_File_Change_Admin {
|
|
86 |
|
87 |
global $itsec_globals;
|
88 |
|
89 |
-
|
90 |
-
|
91 |
-
|
92 |
-
|
93 |
-
|
94 |
-
|
95 |
-
|
96 |
-
|
97 |
-
|
98 |
-
|
|
|
|
|
99 |
|
100 |
if ( isset( get_current_screen()->id ) && ( false !== strpos( get_current_screen()->id, 'security_page_toplevel_page_itsec_settings' ) || false !== strpos( get_current_screen()->id, 'security_page_toplevel_page_itsec_logs' ) || false !== strpos( get_current_screen()->id, 'dashboard' ) ) ) {
|
101 |
|
@@ -149,7 +151,7 @@ class ITSEC_File_Change_Admin {
|
|
149 |
|
150 |
global $blog_id; //get the current blog id
|
151 |
|
152 |
-
if (
|
153 |
return;
|
154 |
}
|
155 |
|
@@ -172,9 +174,12 @@ class ITSEC_File_Change_Admin {
|
|
172 |
global $itsec_globals;
|
173 |
|
174 |
printf(
|
175 |
-
'<div id="itsec_file_change_warning_dialog" class="error"><p>%s
|
176 |
-
|
177 |
-
|
|
|
|
|
|
|
178 |
__( 'View Logs', 'better-wp-security' ),
|
179 |
__( 'Dismiss Warning', 'better-wp-security' )
|
180 |
|
@@ -863,6 +868,9 @@ class ITSEC_File_Change_Admin {
|
|
863 |
* @return void
|
864 |
*/
|
865 |
public function wp_ajax_itsec_file_change_warning_ajax() {
|
|
|
|
|
|
|
866 |
|
867 |
if ( ! wp_verify_nonce( sanitize_text_field( $_POST['nonce'] ), 'itsec_file_change_warning' ) ) {
|
868 |
die( __( 'Security error!', 'better-wp-security' ) );
|
86 |
|
87 |
global $itsec_globals;
|
88 |
|
89 |
+
if ( ITSEC_Core::current_user_can_manage() ) {
|
90 |
+
wp_register_script( 'itsec_file_change_warning_js', $this->module_path . 'js/admin-file-change-warning.js', array( 'jquery' ), $itsec_globals['plugin_build'] );
|
91 |
+
wp_enqueue_script( 'itsec_file_change_warning_js' );
|
92 |
+
wp_localize_script(
|
93 |
+
'itsec_file_change_warning_js',
|
94 |
+
'itsec_file_change_warning',
|
95 |
+
array(
|
96 |
+
'nonce' => wp_create_nonce( 'itsec_file_change_warning' ),
|
97 |
+
'url' => admin_url() . 'admin.php?page=toplevel_page_itsec_logs&itsec_log_filter=file_change',
|
98 |
+
)
|
99 |
+
);
|
100 |
+
}
|
101 |
|
102 |
if ( isset( get_current_screen()->id ) && ( false !== strpos( get_current_screen()->id, 'security_page_toplevel_page_itsec_settings' ) || false !== strpos( get_current_screen()->id, 'security_page_toplevel_page_itsec_logs' ) || false !== strpos( get_current_screen()->id, 'dashboard' ) ) ) {
|
103 |
|
151 |
|
152 |
global $blog_id; //get the current blog id
|
153 |
|
154 |
+
if ( ! ITSEC_Core::current_user_can_manage() ) {
|
155 |
return;
|
156 |
}
|
157 |
|
174 |
global $itsec_globals;
|
175 |
|
176 |
printf(
|
177 |
+
'<div id="itsec_file_change_warning_dialog" class="error"><p>%s</p> <p><input type="button" id="itsec_go_to_logs" class="button-primary" value="%s"> <input type="button" id="itsec_dismiss_file_change_warning" class="button-secondary" value="%s"></p></div>',
|
178 |
+
sprintf(
|
179 |
+
/* translators: 1: Plugin name */
|
180 |
+
__( '%1$s has noticed a change to some files in your WordPress site. Please review the logs to make sure your system has not been compromised.', 'better-wp-security' ),
|
181 |
+
$itsec_globals['plugin_name']
|
182 |
+
),
|
183 |
__( 'View Logs', 'better-wp-security' ),
|
184 |
__( 'Dismiss Warning', 'better-wp-security' )
|
185 |
|
868 |
* @return void
|
869 |
*/
|
870 |
public function wp_ajax_itsec_file_change_warning_ajax() {
|
871 |
+
if ( ! ITSEC_Core::current_user_can_manage() ) {
|
872 |
+
die( __( 'You do not have permissions to do this!', 'better-wp-security' ) );
|
873 |
+
}
|
874 |
|
875 |
if ( ! wp_verify_nonce( sanitize_text_field( $_POST['nonce'] ), 'itsec_file_change_warning' ) ) {
|
876 |
die( __( 'Security error!', 'better-wp-security' ) );
|
core/modules/hide-backend/class-itsec-hide-backend-admin.php
CHANGED
@@ -230,8 +230,8 @@ class ITSEC_Hide_Backend_Admin {
|
|
230 |
$slug = sanitize_title( isset( $this->settings['theme_compat_slug'] ) ? $this->settings['theme_compat_slug'] : 'not_found' );
|
231 |
|
232 |
$content = '<input name="itsec_hide_backend[theme_compat_slug]" id="itsec_hide_backend_strong_passwords_theme_compat_slug" value="' . $slug . '" type="text"><br />';
|
233 |
-
$content .= '<label for="itsec_hide_backend_strong_passwords_theme_compat_slug">' . __( '
|
234 |
-
$content .= '<p class="description">' . __( 'The slug to redirect
|
235 |
|
236 |
}
|
237 |
|
@@ -290,7 +290,7 @@ class ITSEC_Hide_Backend_Admin {
|
|
290 |
}
|
291 |
|
292 |
$content = '<input type="checkbox" id="itsec_hide_backend_theme_compat" name="itsec_hide_backend[theme_compat]" value="1" ' . checked( 1, $enabled, false ) . '/>';
|
293 |
-
$content .= '<label for="itsec_hide_backend_theme_compat"> ' . __( '
|
294 |
|
295 |
}
|
296 |
|
@@ -375,7 +375,7 @@ class ITSEC_Hide_Backend_Admin {
|
|
375 |
|
376 |
add_settings_field(
|
377 |
'itsec_hide_backend[theme_compat]',
|
378 |
-
__( 'Enable
|
379 |
array( $this, 'hide_backend_theme_compat' ),
|
380 |
'security_page_toplevel_page_itsec_settings',
|
381 |
'hide_backend-settings'
|
@@ -383,7 +383,7 @@ class ITSEC_Hide_Backend_Admin {
|
|
383 |
|
384 |
add_settings_field(
|
385 |
'itsec_hide_backend[theme_compat_slug]',
|
386 |
-
__( '
|
387 |
array( $this, 'hide_backend_theme_compat_slug' ),
|
388 |
'security_page_toplevel_page_itsec_settings',
|
389 |
'hide_backend-settings'
|
230 |
$slug = sanitize_title( isset( $this->settings['theme_compat_slug'] ) ? $this->settings['theme_compat_slug'] : 'not_found' );
|
231 |
|
232 |
$content = '<input name="itsec_hide_backend[theme_compat_slug]" id="itsec_hide_backend_strong_passwords_theme_compat_slug" value="' . $slug . '" type="text"><br />';
|
233 |
+
$content .= '<label for="itsec_hide_backend_strong_passwords_theme_compat_slug">' . __( 'Redirect Location:', 'better-wp-security' ) . ' ' . trailingslashit( get_option( 'siteurl' ) ) . '<span style="color: #4AA02C">' . $slug . '</span></label>';
|
234 |
+
$content .= '<p class="description">' . __( 'The slug to redirect users to when they attempt to access wp-admin while not logged in.', 'better-wp-security' ) . '</p>';
|
235 |
|
236 |
}
|
237 |
|
290 |
}
|
291 |
|
292 |
$content = '<input type="checkbox" id="itsec_hide_backend_theme_compat" name="itsec_hide_backend[theme_compat]" value="1" ' . checked( 1, $enabled, false ) . '/>';
|
293 |
+
$content .= '<label for="itsec_hide_backend_theme_compat"> ' . __( 'Redirect users to a custom location on your site, instead of throwing a 403 (forbidden) error.', 'better-wp-security' ) . '</label>';
|
294 |
|
295 |
}
|
296 |
|
375 |
|
376 |
add_settings_field(
|
377 |
'itsec_hide_backend[theme_compat]',
|
378 |
+
__( 'Enable Redirection', 'better-wp-security' ),
|
379 |
array( $this, 'hide_backend_theme_compat' ),
|
380 |
'security_page_toplevel_page_itsec_settings',
|
381 |
'hide_backend-settings'
|
383 |
|
384 |
add_settings_field(
|
385 |
'itsec_hide_backend[theme_compat_slug]',
|
386 |
+
__( 'Redirection Slug', 'better-wp-security' ),
|
387 |
array( $this, 'hide_backend_theme_compat_slug' ),
|
388 |
'security_page_toplevel_page_itsec_settings',
|
389 |
'hide_backend-settings'
|
core/modules/hide-backend/class-itsec-hide-backend.php
CHANGED
@@ -154,9 +154,10 @@ class ITSEC_Hide_Backend {
|
|
154 |
wp_redirect( ITSEC_Lib::get_home_root() . sanitize_title( isset( $this->settings['theme_compat_slug'] ) ? $this->settings['theme_compat_slug'] : 'not_found' ), 302 );
|
155 |
exit;
|
156 |
|
157 |
-
} else {
|
158 |
|
159 |
-
|
|
|
160 |
|
161 |
}
|
162 |
|
@@ -340,17 +341,4 @@ class ITSEC_Hide_Backend {
|
|
340 |
|
341 |
}
|
342 |
|
343 |
-
/**
|
344 |
-
* Sets 404 error at later time.
|
345 |
-
*
|
346 |
-
* @since 4.0.6
|
347 |
-
*
|
348 |
-
* @return void
|
349 |
-
*/
|
350 |
-
public function set_404() {
|
351 |
-
|
352 |
-
ITSEC_Lib::set_404();
|
353 |
-
|
354 |
-
}
|
355 |
-
|
356 |
}
|
154 |
wp_redirect( ITSEC_Lib::get_home_root() . sanitize_title( isset( $this->settings['theme_compat_slug'] ) ? $this->settings['theme_compat_slug'] : 'not_found' ), 302 );
|
155 |
exit;
|
156 |
|
157 |
+
} else {
|
158 |
|
159 |
+
// Throw a 403 forbidden
|
160 |
+
wp_die( __( 'This has been disabled.', 'better-wp-security' ), 403 );
|
161 |
|
162 |
}
|
163 |
|
341 |
|
342 |
}
|
343 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
344 |
}
|
core/modules/ipcheck/class-itsec-ipcheck.php
CHANGED
@@ -108,7 +108,7 @@ class ITSEC_IPCheck extends ITSEC_Lockout {
|
|
108 |
|
109 |
}
|
110 |
|
111 |
-
if (
|
112 |
return false;
|
113 |
}
|
114 |
|
@@ -121,7 +121,7 @@ class ITSEC_IPCheck extends ITSEC_Lockout {
|
|
121 |
|
122 |
$action = 'check-ip';
|
123 |
|
124 |
-
if (
|
125 |
|
126 |
if ( ! isset( $this->settings['api_key'] ) || ! isset( $this->settings['api_s'] ) ) {
|
127 |
return false; //invalid key or secret
|
@@ -281,11 +281,11 @@ class ITSEC_IPCheck extends ITSEC_Lockout {
|
|
281 |
|
282 |
}
|
283 |
|
284 |
-
if (
|
285 |
return 0;
|
286 |
}
|
287 |
|
288 |
-
if (
|
289 |
|
290 |
if ( ! isset( $this->settings['api_key'] ) || ! isset( $this->settings['api_s'] ) ) {
|
291 |
return -1; //invalid key or secret
|
108 |
|
109 |
}
|
110 |
|
111 |
+
if ( ITSEC_Lib::is_ip_whitelisted( $ip ) ) {
|
112 |
return false;
|
113 |
}
|
114 |
|
121 |
|
122 |
$action = 'check-ip';
|
123 |
|
124 |
+
if ( ITSEC_Lib_IP_Tools::validate( $ip ) ) { //verify IP address is valid
|
125 |
|
126 |
if ( ! isset( $this->settings['api_key'] ) || ! isset( $this->settings['api_s'] ) ) {
|
127 |
return false; //invalid key or secret
|
281 |
|
282 |
}
|
283 |
|
284 |
+
if ( ITSEC_Lib::is_ip_whitelisted( $ip ) ) {
|
285 |
return 0;
|
286 |
}
|
287 |
|
288 |
+
if ( ITSEC_Lib_IP_Tools::validate( $ip ) ) { //verify IP address is valid
|
289 |
|
290 |
if ( ! isset( $this->settings['api_key'] ) || ! isset( $this->settings['api_s'] ) ) {
|
291 |
return -1; //invalid key or secret
|
core/modules/malware/class-itsec-malware-admin.php
CHANGED
@@ -86,7 +86,7 @@ class ITSEC_Malware_Admin {
|
|
86 |
$style_id = 'itsec-malware-scan-style';
|
87 |
$style_url = plugins_url( 'css/malware.css', __FILE__ );
|
88 |
|
89 |
-
wp_enqueue_style( $style_id, $style_url );
|
90 |
}
|
91 |
|
92 |
protected function get_scan_details() {
|
@@ -94,7 +94,7 @@ class ITSEC_Malware_Admin {
|
|
94 |
require_once( dirname( __FILE__ ) . '/class-itsec-malware-scan-results-template.php' );
|
95 |
|
96 |
$results = ITSEC_Malware_Scanner::scan();
|
97 |
-
$html = ITSEC_Malware_Scan_Results_Template::get_html( $results );
|
98 |
|
99 |
return $html;
|
100 |
}
|
86 |
$style_id = 'itsec-malware-scan-style';
|
87 |
$style_url = plugins_url( 'css/malware.css', __FILE__ );
|
88 |
|
89 |
+
wp_enqueue_style( $style_id, $style_url, array(), 2 );
|
90 |
}
|
91 |
|
92 |
protected function get_scan_details() {
|
94 |
require_once( dirname( __FILE__ ) . '/class-itsec-malware-scan-results-template.php' );
|
95 |
|
96 |
$results = ITSEC_Malware_Scanner::scan();
|
97 |
+
$html = ITSEC_Malware_Scan_Results_Template::get_html( $results, true );
|
98 |
|
99 |
return $html;
|
100 |
}
|
core/modules/malware/class-itsec-malware-scan-results-template.php
CHANGED
@@ -29,7 +29,8 @@ class ITSEC_Malware_Scan_Results_Template {
|
|
29 |
|
30 |
if ( ! empty( $data ) ) {
|
31 |
$details .= '<p>' . __( 'If you contact support about this error, please provide the following debug details:', 'better-wp-security' ) . "</p>\n";
|
32 |
-
|
|
|
33 |
}
|
34 |
}
|
35 |
|
29 |
|
30 |
if ( ! empty( $data ) ) {
|
31 |
$details .= '<p>' . __( 'If you contact support about this error, please provide the following debug details:', 'better-wp-security' ) . "</p>\n";
|
32 |
+
|
33 |
+
$details .= '<pre>' . esc_html( print_r( array( 'code' => $results->get_error_code(), 'data' => $data ), true ) ) . "</pre>\n";
|
34 |
}
|
35 |
}
|
36 |
|
core/modules/malware/class-itsec-malware-scanner.php
CHANGED
@@ -87,20 +87,14 @@ final class ITSEC_Malware_Scanner {
|
|
87 |
}
|
88 |
|
89 |
|
90 |
-
if (
|
91 |
-
|
92 |
-
! array_key_exists( 'body', $response ) ||
|
93 |
-
! array_key_exists( 'headers', $response ) ||
|
94 |
-
! array_key_exists( 'response', $response ) ||
|
95 |
-
! isset( $response['headers']['content-type'] )
|
96 |
-
) {
|
97 |
-
return new WP_Error( 'itsec-malware-scanner-malformed-wp-remote-get-response', __( 'The response from the wp_remote_get function was malformed. This could indicate an issue with WordPress.', 'better-wp-security' ) );
|
98 |
}
|
99 |
|
100 |
|
101 |
$body = @json_decode( $response['body'], true );
|
102 |
|
103 |
-
if ( is_null( $body ) ) {
|
104 |
if ( 'application/json' === $response['headers']['content-type'] ) {
|
105 |
return new WP_Error( 'itsec-malware-scanner-invalid-json-data-in-scan-response', __( 'The scan did not complete successfully. The Sucuri server should send its response in JSON encoding. The response indicates that the encoding is JSON, but the data could not be decoded. This problem could be due to a temporary Sucuri server issue or a compatibility issue on your server. If the problem continues, please contact iThemes Security support.', 'better-wp-security' ), $response );
|
106 |
} else {
|
@@ -108,10 +102,14 @@ final class ITSEC_Malware_Scanner {
|
|
108 |
}
|
109 |
} else if ( ! is_array( $body ) ) {
|
110 |
if ( 'ERROR' === substr( $response['body'], 0, 5 ) ) {
|
111 |
-
return new WP_Error( 'itsec-malware-scanner-error-received', sprintf( __( 'The scan did not complete successfully. Sucuri sent the following error: %s', 'better-wp-security' ), $response['body'] ), $response );
|
112 |
}
|
113 |
|
114 |
-
|
|
|
|
|
|
|
|
|
115 |
}
|
116 |
|
117 |
|
87 |
}
|
88 |
|
89 |
|
90 |
+
if ( ! is_array( $response ) || empty( $response['body'] ) ) {
|
91 |
+
return new WP_Error( 'itsec-malware-scanner-wp-remote-get-response-missing-body', __( 'The scan failed due to an unexpected technical error. The response from the wp_remote_get function does not contain a body entry. Since the body entry contains the response for the request to Sucuri\'s servers, the response cannot be processed. This could indicate a plugin/theme compatibility issue or a problem in WordPress.', 'better-wp-security' ), $response );
|
|
|
|
|
|
|
|
|
|
|
|
|
92 |
}
|
93 |
|
94 |
|
95 |
$body = @json_decode( $response['body'], true );
|
96 |
|
97 |
+
if ( is_null( $body ) && isset( $response['headers'] ) && isset( $response['headers']['content-type'] ) ) {
|
98 |
if ( 'application/json' === $response['headers']['content-type'] ) {
|
99 |
return new WP_Error( 'itsec-malware-scanner-invalid-json-data-in-scan-response', __( 'The scan did not complete successfully. The Sucuri server should send its response in JSON encoding. The response indicates that the encoding is JSON, but the data could not be decoded. This problem could be due to a temporary Sucuri server issue or a compatibility issue on your server. If the problem continues, please contact iThemes Security support.', 'better-wp-security' ), $response );
|
100 |
} else {
|
102 |
}
|
103 |
} else if ( ! is_array( $body ) ) {
|
104 |
if ( 'ERROR' === substr( $response['body'], 0, 5 ) ) {
|
105 |
+
return new WP_Error( 'itsec-malware-scanner-error-received', sprintf( __( 'The scan did not complete successfully. Sucuri sent the following error: %s', 'better-wp-security' ), '<code>' . $response['body'] . '</code>' ), $response );
|
106 |
}
|
107 |
|
108 |
+
if ( ! empty( $response['response'] ) && ! empty( $response['response']['code'] ) ) {
|
109 |
+
return new WP_Error( 'itsec-malware-scanner-unknown-scan-error', sprintf( __( 'An unknown error prevented the scan from completing successfully. The Sucuri server responded with a <code>%s</code> error code.', 'better-wp-security' ), $response['response']['code'] ), $response );
|
110 |
+
}
|
111 |
+
|
112 |
+
return new WP_Error( 'itsec-malware-scanner-wp-remote-get-response-malformed', __( 'The scan failed due to an unexpected technical error. The response from the wp_remote_get function is missing some critical information that is needed in order to properly process the response from Sucuri\'s servers. This could indicate a plugin/theme compatibility issue or a problem in WordPress.', 'better-wp-security' ), $response );
|
113 |
}
|
114 |
|
115 |
|
core/modules/malware/css/malware.css
CHANGED
@@ -9,6 +9,8 @@
|
|
9 |
display: none;
|
10 |
}
|
11 |
.itsec-malware-scan-details pre {
|
|
|
|
|
12 |
white-space: pre-wrap;
|
13 |
}
|
14 |
.itsec-malware-scan-results-section {
|
9 |
display: none;
|
10 |
}
|
11 |
.itsec-malware-scan-details pre {
|
12 |
+
background-color: #eaeaea;
|
13 |
+
padding: 1em;
|
14 |
white-space: pre-wrap;
|
15 |
}
|
16 |
.itsec-malware-scan-results-section {
|
core/modules/tweaks/class-itsec-tweaks.php
CHANGED
@@ -28,7 +28,7 @@ class ITSEC_Tweaks {
|
|
28 |
//Disable XML-RPC
|
29 |
if ( isset( $this->settings['disable_xmlrpc'] ) && $this->settings['disable_xmlrpc'] == 2 ) {
|
30 |
|
31 |
-
add_filter( 'xmlrpc_enabled',
|
32 |
add_filter( 'bloginfo_url', array( $this, 'remove_pingback_url' ), 10, 2 );
|
33 |
|
34 |
}
|
@@ -56,17 +56,17 @@ class ITSEC_Tweaks {
|
|
56 |
|
57 |
//remove theme update notifications if turned on
|
58 |
if ( ( ! isset( $itsec_globals['is_iwp_call'] ) || $itsec_globals['is_iwp_call'] === false ) && isset( $this->settings['theme_updates'] ) && $this->settings['theme_updates'] == true ) {
|
59 |
-
add_action( '
|
60 |
}
|
61 |
|
62 |
//remove plugin update notifications if turned on
|
63 |
if ( ( ! isset( $itsec_globals['is_iwp_call'] ) || $itsec_globals['is_iwp_call'] === false ) && isset( $this->settings['plugin_updates'] ) && $this->settings['plugin_updates'] == true ) {
|
64 |
-
add_action( '
|
65 |
}
|
66 |
|
67 |
//remove core update notifications if turned on
|
68 |
if ( ( ! isset( $itsec_globals['is_iwp_call'] ) || $itsec_globals['is_iwp_call'] === false ) && isset( $this->settings['core_updates'] ) && $this->settings['core_updates'] == true ) {
|
69 |
-
add_action( '
|
70 |
}
|
71 |
|
72 |
//Execute jQuery check
|
@@ -78,7 +78,7 @@ class ITSEC_Tweaks {
|
|
78 |
|
79 |
//Process remove login errors
|
80 |
if ( isset( $this->settings['login_errors'] ) && $this->settings['login_errors'] === true ) {
|
81 |
-
add_filter( 'login_errors',
|
82 |
}
|
83 |
|
84 |
//Process remove extra author archives
|
@@ -151,7 +151,7 @@ class ITSEC_Tweaks {
|
|
151 |
if ( ! current_user_can( 'manage_options' ) ) {
|
152 |
|
153 |
remove_action( 'admin_notices', 'update_nag', 3 );
|
154 |
-
add_filter( 'pre_site_transient_update_core',
|
155 |
wp_clear_scheduled_hook( 'wp_version_check' );
|
156 |
|
157 |
}
|
@@ -177,17 +177,6 @@ class ITSEC_Tweaks {
|
|
177 |
|
178 |
}
|
179 |
|
180 |
-
/**
|
181 |
-
* Returns null
|
182 |
-
*
|
183 |
-
* @return null
|
184 |
-
*/
|
185 |
-
public function empty_return_function() {
|
186 |
-
|
187 |
-
return null;
|
188 |
-
|
189 |
-
}
|
190 |
-
|
191 |
/**
|
192 |
* Requires a unique nicename on profile update or activate.
|
193 |
*
|
@@ -197,7 +186,7 @@ class ITSEC_Tweaks {
|
|
197 |
*/
|
198 |
public function force_unique_nicename( &$errors, $update, &$user ) {
|
199 |
|
200 |
-
$display_name = isset( $user->display_name ) ? $user->display_name :
|
201 |
|
202 |
if ( ! empty( $user->nickname ) ) {
|
203 |
|
@@ -257,7 +246,7 @@ class ITSEC_Tweaks {
|
|
257 |
if ( ! current_user_can( 'manage_options' ) ) {
|
258 |
|
259 |
remove_action( 'load-update-core.php', 'wp_update_plugins' );
|
260 |
-
add_filter( 'pre_site_transient_update_plugins',
|
261 |
wp_clear_scheduled_hook( 'wp_update_plugins' );
|
262 |
|
263 |
}
|
@@ -308,7 +297,7 @@ class ITSEC_Tweaks {
|
|
308 |
if ( ! current_user_can( 'manage_options' ) ) {
|
309 |
|
310 |
remove_action( 'load-update-core.php', 'wp_update_themes' );
|
311 |
-
add_filter( 'pre_site_transient_update_themes',
|
312 |
wp_clear_scheduled_hook( 'wp_update_themes' );
|
313 |
|
314 |
}
|
28 |
//Disable XML-RPC
|
29 |
if ( isset( $this->settings['disable_xmlrpc'] ) && $this->settings['disable_xmlrpc'] == 2 ) {
|
30 |
|
31 |
+
add_filter( 'xmlrpc_enabled', '__return_null' );
|
32 |
add_filter( 'bloginfo_url', array( $this, 'remove_pingback_url' ), 10, 2 );
|
33 |
|
34 |
}
|
56 |
|
57 |
//remove theme update notifications if turned on
|
58 |
if ( ( ! isset( $itsec_globals['is_iwp_call'] ) || $itsec_globals['is_iwp_call'] === false ) && isset( $this->settings['theme_updates'] ) && $this->settings['theme_updates'] == true ) {
|
59 |
+
add_action( 'init', array( $this, 'theme_updates' ) );
|
60 |
}
|
61 |
|
62 |
//remove plugin update notifications if turned on
|
63 |
if ( ( ! isset( $itsec_globals['is_iwp_call'] ) || $itsec_globals['is_iwp_call'] === false ) && isset( $this->settings['plugin_updates'] ) && $this->settings['plugin_updates'] == true ) {
|
64 |
+
add_action( 'init', array( $this, 'public_updates' ) );
|
65 |
}
|
66 |
|
67 |
//remove core update notifications if turned on
|
68 |
if ( ( ! isset( $itsec_globals['is_iwp_call'] ) || $itsec_globals['is_iwp_call'] === false ) && isset( $this->settings['core_updates'] ) && $this->settings['core_updates'] == true ) {
|
69 |
+
add_action( 'init', array( $this, 'core_updates' ) );
|
70 |
}
|
71 |
|
72 |
//Execute jQuery check
|
78 |
|
79 |
//Process remove login errors
|
80 |
if ( isset( $this->settings['login_errors'] ) && $this->settings['login_errors'] === true ) {
|
81 |
+
add_filter( 'login_errors', '__return_null' );
|
82 |
}
|
83 |
|
84 |
//Process remove extra author archives
|
151 |
if ( ! current_user_can( 'manage_options' ) ) {
|
152 |
|
153 |
remove_action( 'admin_notices', 'update_nag', 3 );
|
154 |
+
add_filter( 'pre_site_transient_update_core', '__return_null' );
|
155 |
wp_clear_scheduled_hook( 'wp_version_check' );
|
156 |
|
157 |
}
|
177 |
|
178 |
}
|
179 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
180 |
/**
|
181 |
* Requires a unique nicename on profile update or activate.
|
182 |
*
|
186 |
*/
|
187 |
public function force_unique_nicename( &$errors, $update, &$user ) {
|
188 |
|
189 |
+
$display_name = isset( $user->display_name ) ? $user->display_name : wp_generate_password( 14, false );
|
190 |
|
191 |
if ( ! empty( $user->nickname ) ) {
|
192 |
|
246 |
if ( ! current_user_can( 'manage_options' ) ) {
|
247 |
|
248 |
remove_action( 'load-update-core.php', 'wp_update_plugins' );
|
249 |
+
add_filter( 'pre_site_transient_update_plugins', '__return_null' );
|
250 |
wp_clear_scheduled_hook( 'wp_update_plugins' );
|
251 |
|
252 |
}
|
297 |
if ( ! current_user_can( 'manage_options' ) ) {
|
298 |
|
299 |
remove_action( 'load-update-core.php', 'wp_update_themes' );
|
300 |
+
add_filter( 'pre_site_transient_update_themes', '__return_null' );
|
301 |
wp_clear_scheduled_hook( 'wp_update_themes' );
|
302 |
|
303 |
}
|
history.txt
CHANGED
@@ -461,3 +461,41 @@
|
|
461 |
Enhancement: Added "Use MySQLi" entry to the "System Information" section of Security > Dashboard to show whether the MySQLi driver is enabled.
|
462 |
Enhancement: Updated the "SQL Mode" entry in the "System Information" section of Security > Dashboard to show the full details if that value is set.
|
463 |
Enhancement: Improved code that ensures that tables and options table entries created by iThemes Security are removed on uninstall only when no other iThemes Security plugin is active.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
461 |
Enhancement: Added "Use MySQLi" entry to the "System Information" section of Security > Dashboard to show whether the MySQLi driver is enabled.
|
462 |
Enhancement: Updated the "SQL Mode" entry in the "System Information" section of Security > Dashboard to show the full details if that value is set.
|
463 |
Enhancement: Improved code that ensures that tables and options table entries created by iThemes Security are removed on uninstall only when no other iThemes Security plugin is active.
|
464 |
+
5.3.0 - 2016-02-17 - Chris Jean & Aaron D. Campbell
|
465 |
+
New Feature: Added support for IPv6 addresses. This includes support for IPv6 in lockouts, ban hosts, and white lists.
|
466 |
+
Bug Fix: Fixed issue that could cause username-based lockouts to fail for long usernames.
|
467 |
+
Bug Fix: Fixed issue that prevented wildcard IP ranges from being blacklisted or whitelisted.
|
468 |
+
Bug Fix: Removed warnings generated when the Away Mode module is disabled and iThemes Sync contacts the site.
|
469 |
+
Enhancement: Updated descriptions of valid IP and IP range formats for the Lockout White List and the Ban Hosts settings.
|
470 |
+
Enhancement: Updated host entries in log details to link to traceip.net rather than ip-adress.com. This is because ip-adress.com does not support IPv6 addresses.
|
471 |
+
Enhancement: Updated some translatable strings relating to blacklisting and whitelisting to allow for better translations.
|
472 |
+
Enhancement: Added details about how wildcard IP ranges are converted to CIDR format (this improves performance).
|
473 |
+
5.3.1 - 2016-02-29 - Chris Jean & Aaron D. Campbell
|
474 |
+
Security Fix: Hardened the created backups and logs directories. Thanks to Nicolas Chatelain (SYSDREAM IT Security Services) for notifying us of this issue.
|
475 |
+
Security Fix: More secure backup and log file names. Thanks to Nicolas Chatelain (SYSDREAM IT Security Services) for notifying us of this issue.
|
476 |
+
Bug Fix: The "NGINX Conf File" setting is now properly respected, causing the generated NGINX configuration file to be stored in that location.
|
477 |
+
Enhancement: Generated database backup file names now contain a human-readable timestamp in the format of YYYYMMDD-HHMMSS.
|
478 |
+
Enhancement: Zipped database backup files no longer contain a deeply nested directory structure. Instead, they only contain the sql file.
|
479 |
+
Enhancement: When the "Force Unique Nickname" feature is enabled, the generated display name now uses an improved randomization function.
|
480 |
+
Enhancement: Improved tabbing of rules in generated nginx.conf files.
|
481 |
+
Enhancement: Removed the "See what's new button" as it has fulfilled its purpose.
|
482 |
+
5.3.2 - 2016-03-01 - Chris Jean & Aaron D. Campbell
|
483 |
+
Bug Fix: Updated code that generates the backups and logs directories to ensure that it attempts to create the parent directory if it does not exist yet.
|
484 |
+
Bug Fix: Removed warnings that could be generated if the logs directory could not be created.
|
485 |
+
Bug Fix: Database backup files sent via email no longer have a name without an extension if zipping up the file fails.
|
486 |
+
5.3.3 - 2016-03-03 - Chris Jean & Aaron D. Campbell
|
487 |
+
Bug Fix: Fixed temporary whitelisting by preventing a temporarily whitelisted IP from being locked out.
|
488 |
+
5.3.4 - 2016-03-17 - Chris Jean & Aaron D. Campbell
|
489 |
+
Bug Fix: Fixed issue that could cause a fatal error after changing the content directory.
|
490 |
+
Bug Fix: Updated the link to sign up for security guide download to point to a https address. This is better security and prevents warnings when submitting from a http site in some browsers.
|
491 |
+
Bug Fix: If a cryptographically secure log file name can't be generated, queue up log file writes until we can.
|
492 |
+
5.3.5 - 2016-03-29 - Chris Jean & Aaron D. Campbell
|
493 |
+
Security Fix: No longer using document.location to build 'Show Intro' link in admin - Thanks to David Lodge (Pen Test Partners) for notifying us of this issue.
|
494 |
+
Bug Fix: Fixed some notices when certain multisite options are used on BuddyPress
|
495 |
+
Enhancement: New itsec_white_ips filter to allow plugins that work with external services to whitelist service IPs
|
496 |
+
5.3.6 - 2016-04-19 - Chris Jean & Aaron D. Campbell
|
497 |
+
Security Fix: Better caps checks for dismissal of changed file dialog - Thanks to Julio Potier for notifying us of this issue.
|
498 |
+
Bug Fix: Make file change warning dialog text properly translatable
|
499 |
+
Enhancement: Adding 'itsec_log_event' action for logged events
|
500 |
+
5.3.7 - 2016-05-02 - Chris Jean & Aaron D. Campbell
|
501 |
+
Bug Fix: Throw a real 403 instead of a faked 404 for hide backend - Fixes compatability with certain plugins including WordPress SEO. Hat tip to Joost de Valk (@jdevalk) and the @Yoast team for bringing this issue to our attention.
|
readme.txt
CHANGED
@@ -1,9 +1,9 @@
|
|
1 |
=== iThemes Security (formerly Better WP Security) ===
|
2 |
Contributors: ithemes, chrisjean, aaroncampbell, gerroald, mattdanner
|
3 |
-
Tags: security,
|
4 |
Requires at least: 4.1
|
5 |
-
Tested up to: 4.
|
6 |
-
Stable tag: 5.
|
7 |
License: GPLv2 or later
|
8 |
License URI: http://www.gnu.org/licenses/gpl-2.0.html
|
9 |
|
@@ -12,49 +12,51 @@ Released under the terms of the GNU General Public License.
|
|
12 |
|
13 |
== Description ==
|
14 |
|
15 |
-
= iThemes Security
|
16 |
|
17 |
iThemes Security (formerly Better WP Security) gives you over 30+ ways to secure and protect your WordPress site. On average, 30,000 new websites are hacked each day. WordPress sites can be an easy target for attacks because of plugin vulnerabilities, weak passwords and obsolete software.
|
18 |
|
19 |
-
Most WordPress admins don't know they're vulnerable, but iThemes Security works to fix common holes, stop automated attacks and strengthen user credentials. With one-click activation for most features, as well as advanced features for experienced users,
|
20 |
|
21 |
= Maintained and Supported by iThemes =
|
22 |
|
23 |
-
iThemes has been building and supporting WordPress tools since 2008. With our full range of WordPress <a href="http://ithemes.com/find/plugins/">plugins</a>, <a href="http://ithemes.com/find/themes/">themes</a> and <a href="http://
|
24 |
|
25 |
-
= Get Support and Pro Features =
|
26 |
|
27 |
Get added peace of mind with professional support from our expert team and pro features to take your site's security to the next level with <a href="http://ithemes.com/security">iThemes Security Pro</a>.
|
28 |
|
29 |
Pro Features:
|
30 |
|
31 |
-
* User Action Logging - Track when user's edit content, login or logout.
|
32 |
* Two-Factor Authentication - Use a mobile app such as Google Authenticator or Authy to generate a code or have a generated code emailed to you.
|
33 |
-
*
|
34 |
* Malware Scan Scheduling - Have your site scanned for malware automatically each day. If an issue is found, an email is sent with the details.
|
35 |
-
* Password
|
36 |
-
*
|
|
|
|
|
|
|
37 |
* Dashboard Widget - Manage important tasks such as user banning and system scans right from the WordPress dashboard.
|
38 |
* Online File Comparison - When a file change is detected it will scan the origin of the files to determine if the change was malicious or not. Currently works only in WordPress core but plugins and themes are coming.
|
39 |
* Temporary Privilege Escalation - give a contractor or someone else temporary admin or editor access to your site that will automatically reset itself.
|
40 |
* wp-cli Integration - Manage your site's security from the command line.
|
41 |
-
|
42 |
|
43 |
= iThemes Sync Integration =
|
44 |
|
45 |
-
Manage more than one site? Manage
|
46 |
|
47 |
-
= iThemes Brute Force Protection Network =
|
48 |
|
49 |
-
|
50 |
|
51 |
= Protect =
|
52 |
|
53 |
-
|
54 |
|
|
|
55 |
* Scans your site to instantly report where vulnerabilities exist and fixes them in seconds
|
56 |
* Bans troublesome user agents, bots and other hosts
|
57 |
-
* Prevents brute force attacks by banning hosts and users with too many invalid login attempts
|
58 |
* Strengthens server security
|
59 |
* Enforces strong passwords for all accounts of a configurable minimum role
|
60 |
* Forces SSL for admin pages (on supporting servers)
|
@@ -90,16 +92,16 @@ iThemes Security hides common WordPress security vulnerabilities, preventing att
|
|
90 |
|
91 |
iThemes Security makes regular backups of your WordPress database, allowing you to get back online quickly in the event of an attack. Use iThemes Security to create and email database backups on a customizable schedule.
|
92 |
|
93 |
-
For complete site backups and the ability to restore or move WordPress
|
94 |
|
95 |
-
= Other Benefits =
|
96 |
|
97 |
* Makes it easier for users not accustomed to WordPress to remember login and admin URLs by customizing default admin URLs
|
98 |
* Detects hidden 404 errors on your site that can affect your SEO such as bad links and missing images
|
99 |
|
100 |
-
= Tutorials =
|
101 |
|
102 |
-
Learn how to use
|
103 |
|
104 |
* <a href="http://ithemes.com/tutorials/getting-started-ithemes-security-part-1/">Getting Started</a>
|
105 |
* <a href="http://ithemes.com/tutorials/getting-started-ithemes-security-part-2-global-settings/">Global Settings</a>
|
@@ -107,7 +109,7 @@ Learn how to use iThemes Security with our series of <a href="http://ithemes.com
|
|
107 |
* <a href="http://ithemes.com/tutorials/getting-started-ithemes-security-part-4-away-mode/">Away Mode</a>
|
108 |
* <a href="http://ithemes.com/tutorials/getting-started-ithemes-security-part-5-banned-users/">Banned Users</a>
|
109 |
* <a href="http://ithemes.com/tutorials/getting-started-ithemes-security-part-6-brute-force-protection/">Brute Force Protection</a>
|
110 |
-
|
111 |
|
112 |
= Compatibility =
|
113 |
|
@@ -123,7 +125,7 @@ Please <a href="http://ithemes.com/contact" target="_blank">let us know</a> if y
|
|
123 |
|
124 |
= Warning =
|
125 |
|
126 |
-
Please read the installation instructions and FAQ before installing this plugin. iThemes Security makes significant changes to your database and other site files which can be problematic, so a backup is strongly recommended before making any changes to your site with this plugin. While problems are rare, most support requests involve the failure to make a proper backup before installation.
|
127 |
|
128 |
== Installation ==
|
129 |
|
@@ -189,6 +191,52 @@ Free support may be available with the help of the community in the <a href="htt
|
|
189 |
|
190 |
== Changelog ==
|
191 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
192 |
= 5.2.1 =
|
193 |
* Bug Fix: Comparisons of IPv4 addresses and ranges now include the IP's at the edge of the ranges.
|
194 |
* Bug Fix: IPv4 tests now work as expected when deciding if a blacklisted IP or range overlaps a whitelisted IP's and ranges.
|
@@ -290,7 +338,7 @@ Free support may be available with the help of the community in the <a href="htt
|
|
290 |
|
291 |
= 4.6.10 =
|
292 |
* Bug Fix: Fixed regression that prevented adding wildcard IP's in the form of 'XXX.XXX.XXX.*' to Ban Hosts.
|
293 |
-
* Bug Fix: When a file scan is run from iThemes Sync, a warning will no longer be added to the site's error log.
|
294 |
|
295 |
= 4.6.8 =
|
296 |
* Enhancement: Minor refactoring for performance and scalability.
|
@@ -1471,6 +1519,18 @@ This release is a complete rewrite from the ground up. Special thanks to Cory Mi
|
|
1471 |
|
1472 |
== Upgrade Notice ==
|
1473 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1474 |
= 4.6.8 =
|
1475 |
Version 4.6.8 contains minor bugfixes and enhancements and is recommended for all users.
|
1476 |
|
1 |
=== iThemes Security (formerly Better WP Security) ===
|
2 |
Contributors: ithemes, chrisjean, aaroncampbell, gerroald, mattdanner
|
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.1
|
5 |
+
Tested up to: 4.5.1
|
6 |
+
Stable tag: 5.3.7
|
7 |
License: GPLv2 or later
|
8 |
License URI: http://www.gnu.org/licenses/gpl-2.0.html
|
9 |
|
12 |
|
13 |
== Description ==
|
14 |
|
15 |
+
= iThemes Security is the #1 WordPress Security Plugin =
|
16 |
|
17 |
iThemes Security (formerly Better WP Security) gives you over 30+ ways to secure and protect your WordPress site. On average, 30,000 new websites are hacked each day. WordPress sites can be an easy target for attacks because of plugin vulnerabilities, weak passwords and obsolete software.
|
18 |
|
19 |
+
Most WordPress admins don't know they're vulnerable, but iThemes Security works to lock down Wordpress, fix common holes, stop automated attacks and strengthen user credentials. With one-click activation for most features, as well as advanced features for experienced users, our WordPress security plugin can help harden WordPress.
|
20 |
|
21 |
= Maintained and Supported by iThemes =
|
22 |
|
23 |
+
iThemes has been building and supporting WordPress tools since 2008 like BackupBuddy, our <a href="http://ithemes.com/purchase/backupbuddy">WordPress backup plugin</a>. With our full range of WordPress <a href="http://ithemes.com/find/plugins/">plugins</a>, <a href="http://ithemes.com/find/themes/">themes</a> and <a href="http://ithemes.com/training">training</a>, WordPress security is the next step in providing you with everything you need to build the WordPress web.
|
24 |
|
25 |
+
= Get Plugin Support and Pro Features =
|
26 |
|
27 |
Get added peace of mind with professional support from our expert team and pro features to take your site's security to the next level with <a href="http://ithemes.com/security">iThemes Security Pro</a>.
|
28 |
|
29 |
Pro Features:
|
30 |
|
|
|
31 |
* Two-Factor Authentication - Use a mobile app such as Google Authenticator or Authy to generate a code or have a generated code emailed to you.
|
32 |
+
* WordPress Salts & Security Keys - The iThemes Security plugin makes updating your WordPress keys and salts easy.
|
33 |
* Malware Scan Scheduling - Have your site scanned for malware automatically each day. If an issue is found, an email is sent with the details.
|
34 |
+
* Password Security - Generate strong passwords right from your profile screen.
|
35 |
+
* Password Expiration - Set a maximum password age and force users to choose a new password. You can also force all users to choose a new password immediately (if needed).
|
36 |
+
* Google reCAPTCHA - Protect your site against spammers.
|
37 |
+
* User Action Logging - Track when users edit content, login or logout.
|
38 |
+
* Import/Export Settings - Saves time setting up multiple WordPress sites.
|
39 |
* Dashboard Widget - Manage important tasks such as user banning and system scans right from the WordPress dashboard.
|
40 |
* Online File Comparison - When a file change is detected it will scan the origin of the files to determine if the change was malicious or not. Currently works only in WordPress core but plugins and themes are coming.
|
41 |
* Temporary Privilege Escalation - give a contractor or someone else temporary admin or editor access to your site that will automatically reset itself.
|
42 |
* wp-cli Integration - Manage your site's security from the command line.
|
43 |
+
|
44 |
|
45 |
= iThemes Sync Integration =
|
46 |
|
47 |
+
Manage more than one WordPress site? Manage Away Mode, release lockouts and keep your themes, plugins and WordPress core up to date from one dashboard with iThemes Sync. <a href="http://ithemes.com/sync/">Start managing 10 WordPress sites for free with iThemes Sync</a>.
|
48 |
|
49 |
+
= iThemes Brute Force Attack Protection Network =
|
50 |
|
51 |
+
iThemes Security takes brute force attack protection to the next level by banning users who have tried to break into other sites from breaking into yours. The iThemes Brute Force Attack Protection Network will automatically report IP addresses of failed login attempts and will block them for a length of time necessary to protect your site based on the number of sites that have seen a similar attack.
|
52 |
|
53 |
= Protect =
|
54 |
|
55 |
+
iThemes Security works to protect your site by blocking bad users and increasing the security of passwords and other vital information.
|
56 |
|
57 |
+
* Prevents brute force attacks by banning hosts and users with too many invalid login attempts
|
58 |
* Scans your site to instantly report where vulnerabilities exist and fixes them in seconds
|
59 |
* Bans troublesome user agents, bots and other hosts
|
|
|
60 |
* Strengthens server security
|
61 |
* Enforces strong passwords for all accounts of a configurable minimum role
|
62 |
* Forces SSL for admin pages (on supporting servers)
|
92 |
|
93 |
iThemes Security makes regular backups of your WordPress database, allowing you to get back online quickly in the event of an attack. Use iThemes Security to create and email database backups on a customizable schedule.
|
94 |
|
95 |
+
For complete site backups and the ability to restore or move WordPress to a new host or domain, check out <a href="http://ithemes.com/purchase/backupbuddy">BackupBuddy</a>.
|
96 |
|
97 |
+
= Other WordPress Security Benefits =
|
98 |
|
99 |
* Makes it easier for users not accustomed to WordPress to remember login and admin URLs by customizing default admin URLs
|
100 |
* Detects hidden 404 errors on your site that can affect your SEO such as bad links and missing images
|
101 |
|
102 |
+
= WordPress Security Tutorials =
|
103 |
|
104 |
+
Learn how to use our WordPress security plugin with our series of <a href="http://ithemes.com/tutorial/category/ithemes-security/">in-depth tutorial videos</a>:
|
105 |
|
106 |
* <a href="http://ithemes.com/tutorials/getting-started-ithemes-security-part-1/">Getting Started</a>
|
107 |
* <a href="http://ithemes.com/tutorials/getting-started-ithemes-security-part-2-global-settings/">Global Settings</a>
|
109 |
* <a href="http://ithemes.com/tutorials/getting-started-ithemes-security-part-4-away-mode/">Away Mode</a>
|
110 |
* <a href="http://ithemes.com/tutorials/getting-started-ithemes-security-part-5-banned-users/">Banned Users</a>
|
111 |
* <a href="http://ithemes.com/tutorials/getting-started-ithemes-security-part-6-brute-force-protection/">Brute Force Protection</a>
|
112 |
+
|
113 |
|
114 |
= Compatibility =
|
115 |
|
125 |
|
126 |
= Warning =
|
127 |
|
128 |
+
Please read the installation instructions and FAQ before installing this WordPress security plugin. iThemes Security makes significant changes to your database and other site files which can be problematic, so a backup is strongly recommended before making any changes to your site with this plugin. While problems are rare, most support requests involve the failure to make a proper backup before installation.
|
129 |
|
130 |
== Installation ==
|
131 |
|
191 |
|
192 |
== Changelog ==
|
193 |
|
194 |
+
= 5.3.7 =
|
195 |
+
* Bug Fix: Throw a real 403 instead of a faked 404 for hide backend - Fixes compatability with certain plugins including WordPress SEO. Hat tip to Joost de Valk (@jdevalk) and the @Yoast team for bringing this issue to our attention.
|
196 |
+
|
197 |
+
= 5.3.6 =
|
198 |
+
* Security Fix: Better caps checks for dismissal of changed file dialog - Thanks to Julio Potier for notifying us of this issue.
|
199 |
+
* Bug Fix: Make file change warning dialog text properly translatable
|
200 |
+
* Enhancement: Adding 'itsec_log_event' action for logged events
|
201 |
+
|
202 |
+
= 5.3.5 =
|
203 |
+
* Security Fix: No longer using document.location to build 'Show Intro' link in admin - Thanks to David Lodge (Pen Test Partners) for notifying us of this issue.
|
204 |
+
* Bug Fix: Fixed some notices when certain multisite options are used on BuddyPress
|
205 |
+
* Enhancement: New itsec_white_ips filter to allow plugins that work with external services to whitelist service IPs
|
206 |
+
|
207 |
+
= 5.3.4 =
|
208 |
+
* Bug Fix: Fixed issue that could cause a fatal error after changing the content directory.
|
209 |
+
* Bug Fix: Updated the link to sign up for security guide download to point to a https address. This is better security and prevents warnings when submitting from a http site in some browsers.
|
210 |
+
* Bug Fix: If a cryptographically secure log file name can't be generated, queue up log file writes until we can.
|
211 |
+
|
212 |
+
= 5.3.3 =
|
213 |
+
* Bug Fix: Fixed temporary whitelisting by preventing a temporarily whitelisted IP from being locked out.
|
214 |
+
|
215 |
+
= 5.3.2 =
|
216 |
+
* Bug Fix: Updated code that generates the backups and logs directories to ensure that it attempts to create the parent directory if it does not exist yet.
|
217 |
+
* Bug Fix: Removed warnings that could be generated if the logs directory could not be created.
|
218 |
+
* Bug Fix: Database backup files sent via email no longer have a name without an extension if zipping up the file fails.
|
219 |
+
|
220 |
+
= 5.3.1 =
|
221 |
+
* Security Fix: Hardened the created backups and logs directories. Thanks to Nicolas Chatelain (SYSDREAM IT Security Services) for notifying us of this issue.
|
222 |
+
* Security Fix: More secure backup and log file names. Thanks to Nicolas Chatelain (SYSDREAM IT Security Services) for notifying us of this issue.
|
223 |
+
* Bug Fix: The "NGINX Conf File" setting is now properly respected, causing the generated NGINX configuration file to be stored in that location.
|
224 |
+
* Enhancement: Generated database backup file names now contain a human-readable timestamp in the format of YYYYMMDD-HHMMSS.
|
225 |
+
* Enhancement: Zipped database backup files no longer contain a deeply nested directory structure. Instead, they only contain the sql file.
|
226 |
+
* Enhancement: When the "Force Unique Nickname" feature is enabled, the generated display name now uses an improved randomization function.
|
227 |
+
* Enhancement: Improved tabbing of rules in generated nginx.conf files.
|
228 |
+
* Enhancement: Removed the "See what's new button" as it has fulfilled its purpose.
|
229 |
+
|
230 |
+
= 5.3.0 =
|
231 |
+
* New Feature: Added support for IPv6 addresses. This includes support for IPv6 in lockouts, ban hosts, and white lists.
|
232 |
+
* Bug Fix: Fixed issue that could cause username-based lockouts to fail for long usernames.
|
233 |
+
* Bug Fix: Fixed issue that prevented wildcard IP ranges from being blacklisted or whitelisted.
|
234 |
+
* Bug Fix: Removed warnings generated when the Away Mode module is disabled and iThemes Sync contacts the site.
|
235 |
+
* Enhancement: Updated descriptions of valid IP and IP range formats for the Lockout White List and the Ban Hosts settings.
|
236 |
+
* Enhancement: Updated host entries in log details to link to traceip.net rather than ip-adress.com. This is because ip-adress.com does not support IPv6 addresses.
|
237 |
+
* Enhancement: Updated some translatable strings relating to blacklisting and whitelisting to allow for better translations.
|
238 |
+
* Enhancement: Added details about how wildcard IP ranges are converted to CIDR format (this improves performance).
|
239 |
+
|
240 |
= 5.2.1 =
|
241 |
* Bug Fix: Comparisons of IPv4 addresses and ranges now include the IP's at the edge of the ranges.
|
242 |
* Bug Fix: IPv4 tests now work as expected when deciding if a blacklisted IP or range overlaps a whitelisted IP's and ranges.
|
338 |
|
339 |
= 4.6.10 =
|
340 |
* Bug Fix: Fixed regression that prevented adding wildcard IP's in the form of 'XXX.XXX.XXX.*' to Ban Hosts.
|
341 |
+
* Bug Fix: When a file scan is run from iThemes Sync, a warning will no longer be added to the site's error log.
|
342 |
|
343 |
= 4.6.8 =
|
344 |
* Enhancement: Minor refactoring for performance and scalability.
|
1519 |
|
1520 |
== Upgrade Notice ==
|
1521 |
|
1522 |
+
= 5.3.7 =
|
1523 |
+
Version 5.3.6 contains a bugfix that fixes compatability with WordPress SEO and is recommended for all users.
|
1524 |
+
|
1525 |
+
= 5.3.6 =
|
1526 |
+
Version 5.3.6 contains minor bugfixes and a small security fix and is recommended for all users.
|
1527 |
+
|
1528 |
+
= 5.3.5 =
|
1529 |
+
Version 5.3.5 contains minor bugfixes and enhancements and is recommended for all users.
|
1530 |
+
|
1531 |
+
= 5.3.4 =
|
1532 |
+
Version 5.3.4 contains minor bugfixes and enhancements and is recommended for all users.
|
1533 |
+
|
1534 |
= 4.6.8 =
|
1535 |
Version 4.6.8 contains minor bugfixes and enhancements and is recommended for all users.
|
1536 |
|