Version Description
- Initial version
Download this release
Release Info
Developer | Jan-Paul Kleemans |
Plugin | Brute Force Login Protection |
Version | 1.0 |
Comparing to | |
See all releases |
Version 1.0
- brute-force-login-protection.php +387 -0
- readme.txt +43 -0
- settings-page.php +81 -0
brute-force-login-protection.php
ADDED
@@ -0,0 +1,387 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
require_once ABSPATH . '/wp-admin/includes/misc.php';
|
4 |
+
require_once ABSPATH . '/wp-admin/includes/file.php';
|
5 |
+
|
6 |
+
/**
|
7 |
+
* Plugin Name: Brute Force Login Protection
|
8 |
+
* Description: Protects your website against brute force login attacks using .htaccess
|
9 |
+
* Text Domain: brute-force-login-protection
|
10 |
+
* Author: Jan-Paul Kleemans
|
11 |
+
* Version: 1.0
|
12 |
+
* License: GPL2
|
13 |
+
*
|
14 |
+
* Copyright 2014 Jan-Paul Kleemans
|
15 |
+
*
|
16 |
+
* This program is free software; you can redistribute it and/or modify
|
17 |
+
* it under the terms of the GNU General Public License, version 2, as
|
18 |
+
* published by the Free Software Foundation.
|
19 |
+
*
|
20 |
+
* This program is distributed in the hope that it will be useful,
|
21 |
+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
22 |
+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
23 |
+
* GNU General Public License for more details.
|
24 |
+
*
|
25 |
+
* You should have received a copy of the GNU General Public License
|
26 |
+
* along with this program; if not, write to the Free Software
|
27 |
+
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
28 |
+
*/
|
29 |
+
class BruteForceLoginProtection {
|
30 |
+
|
31 |
+
private $__options;
|
32 |
+
|
33 |
+
public function __construct() {
|
34 |
+
//Default options
|
35 |
+
$this->__options = array(
|
36 |
+
'allowed_attempts' => 20, //Allowed login attempts before deny,
|
37 |
+
'reset_time' => 60, //Minutes before resetting login attempts count
|
38 |
+
'htaccess_dir' => get_home_path() //.htaccess file location
|
39 |
+
);
|
40 |
+
|
41 |
+
//Activation and deactivation hooks
|
42 |
+
register_activation_hook(__FILE__, array($this, 'activate'));
|
43 |
+
register_deactivation_hook(__FILE__, array($this, 'deactivate'));
|
44 |
+
|
45 |
+
//Init hooks
|
46 |
+
add_action('plugins_loaded', array($this, 'init'));
|
47 |
+
add_action('admin_init', array($this, 'adminInit'));
|
48 |
+
add_action('admin_menu', array($this, 'menuInit'));
|
49 |
+
|
50 |
+
//Login hooks
|
51 |
+
add_action('wp_login_failed', array($this, 'loginFailed'));
|
52 |
+
add_action('wp_login', array($this, 'loginSucceeded'));
|
53 |
+
}
|
54 |
+
|
55 |
+
/**
|
56 |
+
* Called once any activated plugins have been loaded.
|
57 |
+
*
|
58 |
+
* @return void
|
59 |
+
*/
|
60 |
+
public function init() {
|
61 |
+
//Load textdomain for i18n
|
62 |
+
load_plugin_textdomain('brute-force-login-protection', false, dirname(plugin_basename(__FILE__)) . '/languages/');
|
63 |
+
|
64 |
+
//Overrule default $__options with database options
|
65 |
+
$this->__fillOptions();
|
66 |
+
|
67 |
+
//Call checkRequirements to check for .htaccess errors
|
68 |
+
add_action('admin_notices', array($this, 'checkRequirements'));
|
69 |
+
}
|
70 |
+
|
71 |
+
/**
|
72 |
+
* Called when a user accesses the admin area.
|
73 |
+
*
|
74 |
+
* @return void
|
75 |
+
*/
|
76 |
+
public function adminInit() {
|
77 |
+
$this->__registerOptions();
|
78 |
+
}
|
79 |
+
|
80 |
+
/**
|
81 |
+
* Called after the basic admin panel menu structure is in place.
|
82 |
+
*
|
83 |
+
* @return void
|
84 |
+
*/
|
85 |
+
public function menuInit() {
|
86 |
+
add_options_page(__('Brute Force Login Protection Settings', 'brute-force-login-protection'), 'Brute Force Login Protection', 'manage_options', 'brute-force-login-protection', array($this, 'showSettingsPage'));
|
87 |
+
}
|
88 |
+
|
89 |
+
/**
|
90 |
+
* Adds base lines to .htaccess and resets commented denies.
|
91 |
+
*
|
92 |
+
* @return boolean
|
93 |
+
*/
|
94 |
+
public function activate() {
|
95 |
+
$lines = extract_from_markers($this->__options['htaccess_dir'] . '/.htaccess', 'Brute Force Login Protection');
|
96 |
+
|
97 |
+
$insertion[] = '<Files "*">';
|
98 |
+
$insertion[] = 'order allow,deny';
|
99 |
+
foreach ($lines as $line) {
|
100 |
+
if (substr($line, 0, 10) === "#deny from") {
|
101 |
+
$insertion[] = 'deny from ' . substr($line, 11);
|
102 |
+
}
|
103 |
+
}
|
104 |
+
$insertion[] = 'allow from all';
|
105 |
+
$insertion[] = '</Files>';
|
106 |
+
|
107 |
+
return insert_with_markers($this->__options['htaccess_dir'] . '/.htaccess', 'Brute Force Login Protection', $insertion);
|
108 |
+
}
|
109 |
+
|
110 |
+
/**
|
111 |
+
* Comments out all denies in .htaccess.
|
112 |
+
*
|
113 |
+
* @return boolean
|
114 |
+
*/
|
115 |
+
public function deactivate() {
|
116 |
+
$deniedIPs = $this->__getDeniedIPs();
|
117 |
+
|
118 |
+
foreach ($deniedIPs as $deniedIP) {
|
119 |
+
$insertion[] = '#deny from ' . $deniedIP;
|
120 |
+
}
|
121 |
+
|
122 |
+
return insert_with_markers($this->__options['htaccess_dir'] . '/.htaccess', 'Brute Force Login Protection', $insertion);
|
123 |
+
}
|
124 |
+
|
125 |
+
/**
|
126 |
+
* Checks if .htaccess file is found, readable and writeable.
|
127 |
+
*
|
128 |
+
* @return void
|
129 |
+
*/
|
130 |
+
public function checkRequirements() {
|
131 |
+
if (!file_exists($this->__options['htaccess_dir'] . '/.htaccess')) { //Not found
|
132 |
+
$this->__showError(__('Brute Force Login Protection error: .htaccess file not found', 'brute-force-login-protection'));
|
133 |
+
} elseif (!is_readable($this->__options['htaccess_dir'] . '/.htaccess')) { //Not readable
|
134 |
+
$this->__showError(__('Brute Force Login Protection error: .htaccess file not readable', 'brute-force-login-protection'));
|
135 |
+
} elseif (!is_writeable($this->__options['htaccess_dir'] . '/.htaccess')) { //Not writeable
|
136 |
+
$this->__showError(__('Brute Force Login Protection error: .htaccess file not writeable', 'brute-force-login-protection'));
|
137 |
+
}
|
138 |
+
}
|
139 |
+
|
140 |
+
/**
|
141 |
+
* Shows settings page and handles user actions.
|
142 |
+
*
|
143 |
+
* @return void
|
144 |
+
*/
|
145 |
+
public function showSettingsPage() {
|
146 |
+
if (isset($_POST['IP'])) {
|
147 |
+
$IP = filter_var($_POST['IP'], FILTER_VALIDATE_IP);
|
148 |
+
|
149 |
+
if (isset($_POST['block'])) { //Manually block IP
|
150 |
+
if ($IP && $this->__denyIP($IP)) {
|
151 |
+
$this->__showMessage(sprintf(__('IP %s blocked', 'brute-force-login-protection'), $IP));
|
152 |
+
} else {
|
153 |
+
$this->__showError(sprintf(__('An error occurred while blocking IP %s', 'brute-force-login-protection'), $IP));
|
154 |
+
}
|
155 |
+
} elseif (isset($_POST['unblock'])) { //Unblock IP
|
156 |
+
if ($IP && $this->__undenyIP($IP)) {
|
157 |
+
$this->__showMessage(sprintf(__('IP %s unblocked', 'brute-force-login-protection'), $IP));
|
158 |
+
} else {
|
159 |
+
$this->__showError(sprintf(__('An error occurred while unblocking IP %s', 'brute-force-login-protection'), $IP));
|
160 |
+
}
|
161 |
+
}
|
162 |
+
}
|
163 |
+
|
164 |
+
include 'settings-page.php';
|
165 |
+
}
|
166 |
+
|
167 |
+
/**
|
168 |
+
* Increase number of attempts for clients IP. Deny IP if max attempts is reached.
|
169 |
+
*
|
170 |
+
* @return void
|
171 |
+
*/
|
172 |
+
public function loginFailed() {
|
173 |
+
$attempts = get_option('bflp_login_attempts');
|
174 |
+
if (!is_array($attempts)) {
|
175 |
+
$attempts = array();
|
176 |
+
add_option('bflp_login_attempts', $attempts, '', 'no');
|
177 |
+
}
|
178 |
+
|
179 |
+
$IP = $this->__getClientIP();
|
180 |
+
$denyIP = false;
|
181 |
+
|
182 |
+
if ($IP && isset($attempts[$IP]) && $attempts[$IP]['time'] > (time() - ($this->__options['reset_time'] * 60))) {
|
183 |
+
$attempts[$IP]['attempts'] ++;
|
184 |
+
if ($attempts[$IP]['attempts'] >= $this->__options['allowed_attempts']) {
|
185 |
+
$denyIP = true;
|
186 |
+
unset($attempts[$IP]);
|
187 |
+
} else {
|
188 |
+
$attempts[$IP]['time'] = time();
|
189 |
+
}
|
190 |
+
} else {
|
191 |
+
$attempts[$IP]['attempts'] = 1;
|
192 |
+
$attempts[$IP]['time'] = time();
|
193 |
+
}
|
194 |
+
|
195 |
+
update_option('bflp_login_attempts', $attempts);
|
196 |
+
|
197 |
+
if ($denyIP) {
|
198 |
+
$this->__denyIP($IP);
|
199 |
+
}
|
200 |
+
}
|
201 |
+
|
202 |
+
/**
|
203 |
+
* Removes IP from bflp_login_attempts if exist.
|
204 |
+
*
|
205 |
+
* @return void
|
206 |
+
*/
|
207 |
+
public function loginSucceeded() {
|
208 |
+
$attempts = get_option('bflp_login_attempts');
|
209 |
+
if (is_array($attempts)) {
|
210 |
+
$IP = $this->__getClientIP();
|
211 |
+
if (isset($attempts[$IP])) {
|
212 |
+
unset($attempts[$IP]);
|
213 |
+
update_option('bflp_login_attempts', $attempts);
|
214 |
+
}
|
215 |
+
}
|
216 |
+
}
|
217 |
+
|
218 |
+
/**
|
219 |
+
* Settings validation functions
|
220 |
+
*/
|
221 |
+
|
222 |
+
/**
|
223 |
+
* Validates bflp_allowed_attempts field.
|
224 |
+
*
|
225 |
+
* @param mixed $input
|
226 |
+
* @return int
|
227 |
+
*/
|
228 |
+
public function validateAllowedAttempts($input) {
|
229 |
+
if (is_numeric($input) && ($input >= 1 && $input <= 100)) {
|
230 |
+
return $input;
|
231 |
+
} else {
|
232 |
+
add_settings_error('bflp_allowed_attempts', 'bflp_allowed_attempts', __('Allowed login attempts must be a number (between 1 and 100)', 'brute-force-login-protection'));
|
233 |
+
return $this->__options['allowed_attempts'];
|
234 |
+
}
|
235 |
+
}
|
236 |
+
|
237 |
+
/**
|
238 |
+
* Validates bflp_reset_time field.
|
239 |
+
*
|
240 |
+
* @param mixed $input
|
241 |
+
* @return int
|
242 |
+
*/
|
243 |
+
public function validateResetTime($input) {
|
244 |
+
if (is_numeric($input) && $input >= 1) {
|
245 |
+
return $input;
|
246 |
+
} else {
|
247 |
+
add_settings_error('bflp_reset_time', 'bflp_reset_time', __('Minutes before resetting must be a number (higher than 1)', 'brute-force-login-protection'));
|
248 |
+
return $this->__options['reset_time'];
|
249 |
+
}
|
250 |
+
}
|
251 |
+
|
252 |
+
/**
|
253 |
+
* Private functions
|
254 |
+
*/
|
255 |
+
|
256 |
+
/**
|
257 |
+
* Registers options (settings).
|
258 |
+
*
|
259 |
+
* @return void
|
260 |
+
*/
|
261 |
+
private function __registerOptions() {
|
262 |
+
register_setting('brute-force-login-protection', 'bflp_allowed_attempts', array($this, 'validateAllowedAttempts'));
|
263 |
+
register_setting('brute-force-login-protection', 'bflp_reset_time', array($this, 'validateResetTime'));
|
264 |
+
register_setting('brute-force-login-protection', 'bflp_htaccess_dir');
|
265 |
+
}
|
266 |
+
|
267 |
+
/**
|
268 |
+
* Fills options with value (from database).
|
269 |
+
*
|
270 |
+
* @return void
|
271 |
+
*/
|
272 |
+
private function __fillOptions() {
|
273 |
+
$this->__options['allowed_attempts'] = get_option('bflp_allowed_attempts', $this->__options['allowed_attempts']);
|
274 |
+
$this->__options['reset_time'] = get_option('bflp_reset_time', $this->__options['reset_time']);
|
275 |
+
$this->__options['htaccess_dir'] = get_option('bflp_htaccess_dir', $this->__options['htaccess_dir']);
|
276 |
+
}
|
277 |
+
|
278 |
+
/**
|
279 |
+
* Returs array of denied IP addresses from .htaccess.
|
280 |
+
*
|
281 |
+
* @return array
|
282 |
+
*/
|
283 |
+
private function __getDeniedIPs() {
|
284 |
+
$lines = extract_from_markers($this->__options['htaccess_dir'] . '/.htaccess', 'Brute Force Login Protection');
|
285 |
+
|
286 |
+
$deniedIPs = array();
|
287 |
+
foreach ($lines as $line) {
|
288 |
+
if (substr($line, 0, 9) === "deny from") {
|
289 |
+
$deniedIPs[] = substr($line, 10);
|
290 |
+
}
|
291 |
+
}
|
292 |
+
|
293 |
+
return $deniedIPs;
|
294 |
+
}
|
295 |
+
|
296 |
+
/**
|
297 |
+
* Adds 'deny from $IP' to .htaccess.
|
298 |
+
*
|
299 |
+
* @param string $IP
|
300 |
+
* @return boolean
|
301 |
+
*/
|
302 |
+
private function __denyIP($IP) {
|
303 |
+
$deniedIPs = $this->__getDeniedIPs();
|
304 |
+
$deniedIPs[] = $IP;
|
305 |
+
|
306 |
+
$insertion[] = '<Files "*">';
|
307 |
+
$insertion[] = 'order allow,deny';
|
308 |
+
foreach ($deniedIPs as $deniedIP) {
|
309 |
+
$insertion[] = 'deny from ' . $deniedIP;
|
310 |
+
}
|
311 |
+
$insertion[] = 'allow from all';
|
312 |
+
$insertion[] = '</Files>';
|
313 |
+
|
314 |
+
return insert_with_markers($this->__options['htaccess_dir'] . '/.htaccess', 'Brute Force Login Protection', array_unique($insertion));
|
315 |
+
}
|
316 |
+
|
317 |
+
/**
|
318 |
+
* Removes 'deny from $IP' from .htaccess.
|
319 |
+
*
|
320 |
+
* @param string $IP
|
321 |
+
* @return boolean
|
322 |
+
*/
|
323 |
+
private function __undenyIP($IP) {
|
324 |
+
$deniedIPs = $this->__getDeniedIPs();
|
325 |
+
|
326 |
+
$insertion[] = '<Files "*">';
|
327 |
+
$insertion[] = 'order allow,deny';
|
328 |
+
foreach ($deniedIPs as $deniedIP) {
|
329 |
+
if ($deniedIP !== $IP) {
|
330 |
+
$insertion[] = 'deny from ' . $deniedIP;
|
331 |
+
}
|
332 |
+
}
|
333 |
+
$insertion[] = 'allow from all';
|
334 |
+
$insertion[] = '</Files>';
|
335 |
+
|
336 |
+
return insert_with_markers($this->__options['htaccess_dir'] . '/.htaccess', 'Brute Force Login Protection', $insertion);
|
337 |
+
}
|
338 |
+
|
339 |
+
/**
|
340 |
+
* Returns the client ip address.
|
341 |
+
*
|
342 |
+
* @return mixed
|
343 |
+
*/
|
344 |
+
private function __getClientIP() {
|
345 |
+
$IP = false;
|
346 |
+
|
347 |
+
if ($_SERVER['HTTP_CLIENT_IP']) {
|
348 |
+
$IP = $_SERVER['HTTP_CLIENT_IP'];
|
349 |
+
} elseif ($_SERVER['HTTP_X_FORWARDED_FOR']) {
|
350 |
+
$IP = $_SERVER['HTTP_X_FORWARDED_FOR'];
|
351 |
+
} elseif ($_SERVER['HTTP_X_FORWARDED']) {
|
352 |
+
$IP = $_SERVER['HTTP_X_FORWARDED'];
|
353 |
+
} elseif ($_SERVER['HTTP_FORWARDED_FOR']) {
|
354 |
+
$IP = $_SERVER['HTTP_FORWARDED_FOR'];
|
355 |
+
} elseif ($_SERVER['HTTP_FORWARDED']) {
|
356 |
+
$IP = $_SERVER['HTTP_FORWARDED'];
|
357 |
+
} elseif ($_SERVER['REMOTE_ADDR']) {
|
358 |
+
$IP = $_SERVER['REMOTE_ADDR'];
|
359 |
+
}
|
360 |
+
|
361 |
+
return $IP;
|
362 |
+
}
|
363 |
+
|
364 |
+
/**
|
365 |
+
* Echoes message with class 'updated'.
|
366 |
+
*
|
367 |
+
* @param string $message
|
368 |
+
* @return void
|
369 |
+
*/
|
370 |
+
private function __showMessage($message) {
|
371 |
+
echo '<div class="updated"><p>' . $message . '</p></div>';
|
372 |
+
}
|
373 |
+
|
374 |
+
/**
|
375 |
+
* Echoes message with class 'error'.
|
376 |
+
*
|
377 |
+
* @param string $message
|
378 |
+
* @return void
|
379 |
+
*/
|
380 |
+
private function __showError($message) {
|
381 |
+
echo '<div class="error"><p>' . $message . '</p></div>';
|
382 |
+
}
|
383 |
+
|
384 |
+
}
|
385 |
+
|
386 |
+
//Instantiate new instance of BruteForceLoginProtection class
|
387 |
+
new BruteForceLoginProtection();
|
readme.txt
ADDED
@@ -0,0 +1,43 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
=== Brute Force Login Protection ===
|
2 |
+
Contributors: Jan-Paul Kleemans
|
3 |
+
Tags: brute force, bruteforce, login, wp-login, protection, shield, security, htaccess, block, ip
|
4 |
+
Requires at least: 2.7.0
|
5 |
+
Tested up to: 3.9.1
|
6 |
+
Stable tag: 1.0
|
7 |
+
License: GPL2
|
8 |
+
|
9 |
+
Protects your website against brute force login attacks using .htaccess
|
10 |
+
|
11 |
+
== Description ==
|
12 |
+
A Brute Force Attack aims at being the simplest kind of method to gain access to a site: it tries usernames and passwords, over and over again, until it gets in. Brute Force Login Protection protects your website against brute force login attacks using .htaccess.
|
13 |
+
|
14 |
+
After a specified limit of login attempts within a specified time, the IP address of the hacker will be blocked.
|
15 |
+
|
16 |
+
Features
|
17 |
+
|
18 |
+
* Limit the number of allowed login attempts
|
19 |
+
* Manually block IP addresses
|
20 |
+
* Manually unblock IP addresses
|
21 |
+
|
22 |
+
Your feedback is highly appreciated!
|
23 |
+
|
24 |
+
== Installation ==
|
25 |
+
1. Install the plugin either via the WordPress.org plugin directory, or by uploading the files to your wp-content/plugin directory.
|
26 |
+
2. Activate the plugin through the WordPress admin panel.
|
27 |
+
3. Customize the settings on the settings page.
|
28 |
+
4. Done!
|
29 |
+
|
30 |
+
== Frequently Asked Questions ==
|
31 |
+
= My own IP is blocked, what do I do? =
|
32 |
+
If you have FTP access to your website edit the .htaccess file and remove the line: \'deny from x.x.x.x\', where x.x.x.x is your own IP address.
|
33 |
+
If you don\'t have FTP access, the only way to unblock your IP is to log in your WordPress admin panel from another IP address and unblock it via the plugin settings page.
|
34 |
+
|
35 |
+
= I get an error: .htaccess file not readable/writeable =
|
36 |
+
Brute Force Login Protection will only work if your .htaccess file is writeable by WordPress. If you get this error, make sure that your .htaccess file has read and write permissions.
|
37 |
+
|
38 |
+
== Screenshots ==
|
39 |
+
1. Plugin settings page
|
40 |
+
|
41 |
+
== Changelog ==
|
42 |
+
= 1.0 =
|
43 |
+
* Initial version
|
settings-page.php
ADDED
@@ -0,0 +1,81 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<style type="text/css">
|
2 |
+
.brute-force-login-protection .postbox-footer{
|
3 |
+
padding:10px;
|
4 |
+
clear:both;
|
5 |
+
border-top:1px solid #ddd;
|
6 |
+
background:#f5f5f5;
|
7 |
+
}
|
8 |
+
.brute-force-login-protection input[type="number"] {
|
9 |
+
width:60px;
|
10 |
+
}
|
11 |
+
</style>
|
12 |
+
|
13 |
+
<div class="wrap brute-force-login-protection">
|
14 |
+
<h2><?php _e('Brute Force Login Protection Settings', 'brute-force-login-protection'); ?></h2>
|
15 |
+
|
16 |
+
<div class="metabox-holder">
|
17 |
+
<div class="postbox">
|
18 |
+
<h3><?php _e('Options', 'brute-force-login-protection'); ?></h3>
|
19 |
+
<form method="post" action="options.php">
|
20 |
+
<?php settings_fields('brute-force-login-protection'); ?>
|
21 |
+
<div class="inside">
|
22 |
+
<p><strong><?php _e('Allowed login attempts before blocking IP', 'brute-force-login-protection'); ?></strong></p>
|
23 |
+
<p><input type="number" min="1" name="bflp_allowed_attempts" value="<?php echo $this->__options['allowed_attempts']; ?>" /></p>
|
24 |
+
|
25 |
+
<p><strong><?php _e('Minutes before resetting login attempts count', 'brute-force-login-protection'); ?></strong></p>
|
26 |
+
<p><input type="number" min="1" name="bflp_reset_time" value="<?php echo $this->__options['reset_time']; ?>" /></p>
|
27 |
+
|
28 |
+
<p><strong><?php _e('.htaccess file location', 'brute-force-login-protection'); ?></strong></p>
|
29 |
+
<p><input type="text" size="50" name="bflp_htaccess_dir" value="<?php echo $this->__options['htaccess_dir']; ?>" /></p>
|
30 |
+
</div>
|
31 |
+
<div class="postbox-footer">
|
32 |
+
<?php submit_button(__('Save', 'brute-force-login-protection'), 'primary', 'submit', false); ?>
|
33 |
+
</div>
|
34 |
+
</form>
|
35 |
+
</div>
|
36 |
+
|
37 |
+
<div class="postbox">
|
38 |
+
<h3><?php _e('Manually block IP', 'brute-force-login-protection'); ?></h3>
|
39 |
+
<form method="post" action="">
|
40 |
+
<div class="inside">
|
41 |
+
<p><strong><?php _e('IP address', 'brute-force-login-protection'); ?></strong></p>
|
42 |
+
<p><input type="text" name="IP" /></p>
|
43 |
+
</div>
|
44 |
+
<div class="postbox-footer">
|
45 |
+
<input type="submit" name="block" value="<?php echo __('Block', 'brute-force-login-protection'); ?>" class="button button-primary" />
|
46 |
+
</div>
|
47 |
+
</form>
|
48 |
+
</div>
|
49 |
+
</div>
|
50 |
+
|
51 |
+
<h3><?php _e('Blocked IPs', 'brute-force-login-protection'); ?></h3>
|
52 |
+
<table class="wp-list-table widefat fixed">
|
53 |
+
<thead>
|
54 |
+
<tr>
|
55 |
+
<th width="5%">#</th>
|
56 |
+
<th width="35%"><?php _e('Address', 'brute-force-login-protection'); ?></th>
|
57 |
+
<th width="60%"><?php _e('Actions', 'brute-force-login-protection'); ?></th>
|
58 |
+
</tr>
|
59 |
+
</thead>
|
60 |
+
<tbody>
|
61 |
+
<?php
|
62 |
+
$i = 1;
|
63 |
+
foreach ($this->__getDeniedIPs() as $deniedIP):
|
64 |
+
?>
|
65 |
+
<tr>
|
66 |
+
<td><?php echo $i; ?></td>
|
67 |
+
<td><strong><?php echo $deniedIP ?></strong></td>
|
68 |
+
<td>
|
69 |
+
<form method="post" action="">
|
70 |
+
<input type="hidden" name="IP" value="<?php echo $deniedIP ?>" />
|
71 |
+
<input type="submit" name="unblock" value="<?php echo __('Unblock', 'brute-force-login-protection'); ?>" class="button" />
|
72 |
+
</form>
|
73 |
+
</td>
|
74 |
+
</tr>
|
75 |
+
<?php
|
76 |
+
$i++;
|
77 |
+
endforeach;
|
78 |
+
?>
|
79 |
+
</tbody>
|
80 |
+
</table>
|
81 |
+
</div>
|