Changeset 913083
- Timestamp:
- 05/13/2014 04:19:10 AM (10 years ago)
- Location:
- lockdown-wp-admin/trunk
- Files:
-
- 16 added
- 4 edited
Legend:
- Unmodified
- Added
- Removed
-
lockdown-wp-admin/trunk/README.md
r770426 r913083 1 1 Lockdown WP Admin 2 2 ============= 3 4 5 3 6 4 7 Lockdown WP Admin conceals the administration and login screen from intruders. It can hide WordPress Admin (/wp-admin/) and and login (/wp-login.php) as well as add HTTP authentication to the login system. We can also change the login URL from wp-login.php to whatever you'd like: /login, /log-in-here, etc. … … 100 103 * Support for WordPress 3.6 101 104 * General Cleaning 105 106 107 108 109 -
lockdown-wp-admin/trunk/lockdown-wp-admin.php
r770426 r913083 5 5 Donate link: http://seanfisher.co/donate/ 6 6 Description: Securing the WordPress Administration interface by concealing the administration dashboard and changing the login page URL. 7 Version: 2. 17 Version: 2. 8 8 Author: Sean Fisher 9 9 Author URI: http://seanfisher.co/ … … 13 13 // This file name 14 14 define('LD_FILE_NAME', __FILE__ ); 15 15 16 16 17 /** … … 29 30 * @access private 30 31 **/ 31 public $ld_admin_version = '2.1';32 public '; 32 33 33 34 /** … … 36 37 * 37 38 * @access public 38 * @ globalstring39 * @ string 39 40 **/ 40 41 public $relm = 'Secure Area'; … … 48 49 49 50 /** 50 * The base to get the login url51 *52 * @access private53 **/54 protected $login_base = FALSE;55 56 /**57 51 * Check if the Auth passed 58 *59 52 * See {@link WP_LockAuth::getAuthPassed()} 60 53 * 61 * @ varboolean54 * @ boolean 62 55 */ 63 56 protected $passed = FALSE; 57 58 59 60 61 62 63 64 65 66 67 68 69 70 64 71 65 72 /** … … 73 80 if (! class_exists('Disable_WPMS_Plugin_LD')) 74 81 require_once( dirname( __FILE__ ) .'/no-wpmu.php' ); 75 76 // Add the action to setup the menu. 77 add_action('admin_menu', array( $this, 'add_admin_menu')); 78 79 // Setup the plugin. 80 $this->setup_hide_admin(); 81 82 // Hide the login form 83 $this->redo_login_form(); 84 } 85 86 /** 87 * Get a username and password from the HTTP auth 88 * 89 * @return array|bool 90 **/ 91 public function get_http_auth_creds() 92 { 93 // Since PHP saves the HTTP Password in a bunch of places, we have to be able to test for all of them 94 $username = $password = NULL; 95 96 // mod_php 97 if (isset($_SERVER['PHP_AUTH_USER'])) 98 { 99 $username = (isset($_SERVER['PHP_AUTH_USER'])) ? $_SERVER['PHP_AUTH_USER'] : NULL; 100 $password = (isset($_SERVER['PHP_AUTH_PW'])) ? $_SERVER['PHP_AUTH_PW'] : NULL; 101 } 82 83 require_once(LD_PLUGIN_DIR.'/src/Lockdown/Application.php'); 84 require_once(LD_PLUGIN_DIR.'/src/Lockdown/Admin.php'); 102 85 103 // most other servers 104 elseif ($_SERVER['HTTP_AUTHENTICATION']) 105 { 106 if (strpos(strtolower($_SERVER['HTTP_AUTHENTICATION']),'basic') === 0) 107 { 108 list($username,$password) = explode(':',base64_decode(substr($_SERVER['HTTP_AUTHENTICATION'], 6))); 109 } 110 } 111 112 // Check them - if they're null a/o empty, they're invalid. 113 if ( is_null($username) OR is_null($password) OR empty($username) OR empty($password)) 114 return FALSE; 115 else 116 return array('username' => $username, 'password' => $password); 117 } 118 119 /** 120 * Update the users 121 * 122 * @access private 123 **/ 124 public function update_users() 125 { 126 if ( ! isset( $_GET['page'] ) ) 127 return; 128 129 if ( $_GET['page'] !== 'lockdown-private-users' ) 130 return; 131 132 // Nonce 133 if ( ! isset( $_REQUEST['_wpnonce'] ) ) 134 return; 135 136 $nonce = $_REQUEST['_wpnonce']; 137 if ( !wp_verify_nonce( $nonce, 'lockdown-wp-admin' ) ) 138 wp_die('Security error, please try again.'); 139 140 // Add a user 141 if ( isset( $_POST['private_username'] ) && isset( $_POST['private_password'] ) ) 142 { 143 if ( $_POST['private_username'] !== '' && $_POST['private_password'] !== '' ) 144 { 145 // Adding a user 146 $users = $this->get_private_users(); 147 $add['user'] = sanitize_user( $_POST['private_username'] ); 148 $add['pass'] = trim( md5( $_POST['private_password'] ) ); 149 150 // See if it exists 151 if ($this->user_exists($users, $add['user'])) : 152 define('LD_ERROR', 'username-exists'); 153 return; 154 endif; 155 156 $users[] = $add; 157 158 update_option('ld_private_users', $users); 159 160 define('LD_WP_ADMIN', TRUE); 161 return; 162 } 163 } 164 165 // Deleting a user. 166 if ( isset( $_GET['delete'] ) ) 167 { 168 // Delete the user. 169 unset( $users ); 170 $users = $this->get_private_users(); 171 $to_delete = (int) $_GET['delete']; 172 173 if ( count( $users ) > 0 ) 174 { 175 foreach( $users as $key => $val ) 176 { 177 if ( $key === $to_delete ) : 178 if( $this->current_user !== '' && $to_delete === $this->current_user ) 179 { 180 // They can't delete themselves! 181 define('LD_ERROR', 'delete-self'); 182 return; 183 } 184 185 unset( $users[$key] ); 186 endif; 187 } 188 } 189 190 update_option('ld_private_users', $users); 191 192 define('LD_WP_ADMIN', TRUE); 193 return; 194 } 195 } 196 197 /** 198 * Update the options 199 * 200 * @access private 201 **/ 202 public function update_options() 203 { 204 if ( !isset( $_GET['page'] ) ) 205 return; 206 207 if ( $_GET['page'] !== 'lockdown-wp-admin' ) 208 return; 209 210 if ( !isset( $_POST['did_update'] ) ) 211 return; 212 213 // Nonce 214 $nonce = $_POST['_wpnonce']; 215 if ( ! wp_verify_nonce($nonce, 'lockdown-wp-admin') ) 216 wp_die('Security error, please try again.'); 217 218 // --------------------------------------------------- 219 // They're updating. 220 // --------------------------------------------------- 221 if ( isset( $_POST['http_auth'] ) ) 222 update_option('ld_http_auth', trim( strtolower( $_POST['http_auth'] ) ) ); 223 else 224 update_option('ld_http_auth', 'none' ); 225 226 if ( ! isset( $_POST['hide_wp_admin'] ) ) 227 { 228 update_option('ld_hide_wp_admin', 'nope'); 229 } 230 else 231 { 232 if ( $_POST['hide_wp_admin'] === 'yep' ) 233 update_option('ld_hide_wp_admin', 'yep'); 234 else 235 update_option('ld_hide_wp_admin', 'nope'); 236 } 237 238 if ( isset( $_POST['login_base'] ) ) 239 { 240 $base = sanitize_title_with_dashes( $_POST['login_base']); 241 $base = str_replace('/', '', $base); 242 243 $disallowed = array( 244 'user', 'wp-admin', 'wp-content', 'wp-includes', 'wp-feed.php', 'index', 'feed', 'rss', 'robots', 'robots.txt', 'wp-login.php', 245 'wp-login', 'wp-config', 'blog', 'sitemap', 'sitemap.xml', 246 ); 247 if ( in_array( $base, $disallowed ) ) 248 { 249 return define('LD_DIS_BASE', TRUE); 250 } 251 else 252 { 253 254 update_option('ld_login_base', $base); 255 $this->login_base = sanitize_title_with_dashes ( $base ); 256 } 257 } 258 259 // Redirect 260 define('LD_WP_ADMIN', TRUE); 261 return; 262 } 263 264 /** 265 * Send headers to the browser that are going to ask for a username/pass 266 * from the browser. 267 * 268 * @access private 269 * @return void 270 **/ 271 private function inauth_headers() 272 { 273 // Disable if there is a text file there. 274 if ( file_exists(dirname(__FILE__).DIRECTORY_SEPARATOR.'disable_auth.txt')) 275 return; 276 277 header('WWW-Authenticate: Basic realm="'.$this->relm.'"'); 278 header('HTTP/1.0 401 Unauthorized'); 279 echo '<h1>Authorization Required.</h1>'; 280 exit; 86 // Instantiate objects 87 $this->admin = new Lockdown_Admin($this); 88 $this->application = new Lockdown_Application($this); 281 89 } 282 90 … … 284 92 * Get the users for the private creds 285 93 * 286 * @ access private94 * @ 287 95 **/ 288 96 public function get_private_users() 289 97 { 290 $opt = get_option('ld_private_users'); 291 if ( !is_array( $opt ) ) 292 return array(); 293 294 return $opt; 295 } 296 297 /** 298 * Setup hiding wp-admin 299 **/ 300 protected function setup_hide_admin() 301 { 302 $opt = get_option('ld_hide_wp_admin'); 303 304 // Nope, they didn't enable it. 305 if ( $opt !== 'yep' ) return; 306 307 // We're gonna hide it. 308 $no_check_files = array('async-upload.php', 'admin-ajax.php', 'wp-app.php'); 309 $no_check_files = apply_filters('no_check_files', $no_check_files); 310 311 $script_filename = empty($_SERVER['SCRIPT_FILENAME']) 312 ? $_SERVER['PATH_TRANSLATED'] 313 : $_SERVER['SCRIPT_FILENAME']; 314 $explode = explode('/', $script_filename); 315 $file = end( $explode ); 316 317 // Disable for WP-CLI 318 if ( defined('WP_CLI') AND WP_CLI ) 319 return $this->passed(true); 320 321 if ( in_array( $file, $no_check_files ) ) 322 return $this->passed(true); 323 324 // We only will hide it if we are in admin (/wp-admin/) 325 if ( is_admin() ) 326 { 327 // Non logged in users. 328 if ( ! is_user_logged_in() ) 329 $this->throw_404(); 330 331 // Setup HTTP auth. 332 $this->setup_http_area(); 333 } 334 } 335 336 /** 337 * Get the current file name 338 * 339 * @return string JUST the file name 340 **/ 341 public function get_file() 342 { 343 // We're gonna hide it. 344 $no_check_files = array('async-upload.php'); 345 $no_check_files = apply_filters('no_check_files', $no_check_files); 346 347 $script_filename = empty($_SERVER['SCRIPT_FILENAME']) 348 ? $_SERVER['PATH_TRANSLATED'] 349 : $_SERVER['SCRIPT_FILENAME']; 350 $explode = explode('/', $script_filename ); 351 return end( $explode ); 352 } 353 354 /** 355 * Setting up the HTTP Auth 356 * Here, we only check if it's enabled 357 * 358 * @access protected 359 **/ 360 protected function setup_http_area() 361 { 362 // We save what type of auth we're doing here. 363 $opt = get_option('ld_http_auth'); 364 365 // What type of auth are we doing? 366 switch( $opt ) 367 { 368 // HTTP auth is going to ask for their WordPress creds. 369 case 'wp_creds' : 370 $creds = $this->get_http_auth_creds(); 371 if (! $creds ) 372 $this->inauth_headers(); // Invalid credentials 373 374 // Are they already logged in as this? 375 $current_uid = get_current_user_id(); 376 377 // We fixed this for use with non WP-MS sites 378 $requested_user = get_user_by('login', $creds['username']); 379 380 // Not a valid user. 381 if (! $requested_user ) 382 $this->inauth_headers(); 383 384 // The correct User ID. 385 $requested_uid = (int) $requested_user->ID; 386 387 // Already logged in? 388 if ( $current_uid === $requested_uid ) 389 return $this->passed(true); 390 391 // Attempt to sign them in if they aren't already 392 if (! is_user_logged_in() ) : 393 // Try it via wp_signon 394 $creds = array(); 395 $creds['user_login'] = $creds['username']; 396 $creds['user_password'] = $creds['password']; 397 $creds['remember'] = true; 398 $user = wp_signon( $creds, false ); 399 400 // In error 401 if ( is_wp_error($user) ) 402 $this->inauth_headers(); 403 endif; 404 405 // They passed! 406 $this->passed(true); 407 break; 408 409 // Private list of users to check 410 case 'private' : 411 $users = $this->get_private_users(); 412 413 // We want a user to exist. 414 // If nobody is found, we won't lock them out! 415 if ( ! $users || ! is_array( $users ) ) 416 return; 417 418 // Let's NOT lock everybody out 419 if ( count( $users ) < 1 ) 420 return; 421 422 // Get the HTTP auth creds 423 $creds = $this->get_http_auth_creds(); 424 425 // Invalid creds 426 if (! $creds ) 427 $this->inauth_headers(); 428 429 // Did they enter a valid user? 430 if ( $this->user_array_check( $users, $creds['username'], $creds['password'] ) ) 431 { 432 $this->passed(true); 433 $this->set_current_user( $users, $creds['username'] ); 434 return; 435 } 436 else 437 { 438 return $this->inauth_headers(); 439 } 440 441 break; 442 443 // Unknown type of auth 444 default : 445 return FALSE; 446 } 447 448 } 449 /** 450 * Check an internal array of users against a passed user and pass 451 * 452 * @access protected 453 * @return bool 454 * 455 * @param array $array The array of users 456 * @param string $user The username to check for 457 * @param string $pass The password to check for (plain text) 458 **/ 459 protected function user_array_check( $array, $user, $pass ) 460 { 461 foreach( $array as $key => $val ) 462 { 463 if ( $val['user'] === $user && md5( $pass ) === $val['pass'] ) 464 return TRUE; 465 } 466 467 return FALSE; 468 } 469 470 /** 471 * See if a user exists in the array 472 * 473 * @access protected 474 * @return boolean 475 * @param array Array of users 476 * @param string 477 */ 478 protected function user_exists($array, $user) 479 { 480 if (count($array) == 0) return FALSE; 481 482 foreach ($array as $k => $v) : 483 if ($v['user'] == $user) 484 return TRUE; 485 endforeach; 486 487 return FALSE; 98 return $this->application->getPrivateUsers(); 488 99 } 489 100 … … 491 102 * Set the current user 492 103 * 493 * @access private 494 * @param array 495 * @param integer 104 * @deprecated Moved to {@see Lockdown_Application::setUser()} 496 105 **/ 497 106 protected function set_current_user( $array, $user ) 498 107 { 499 foreach( $array as $key => $val ) 500 { 501 if ( $val['user'] === $user ) 502 $this->current_user = $key; 503 } 504 } 505 506 /** 507 * Adds the admin menu 508 * 509 * @acces private 510 **/ 511 public function add_admin_menu() 512 { 513 add_menu_page('Lockdown WP', 'Lockdown WP', 'manage_options', 'lockdown-wp-admin', array( $this, 'admin_callback')); 514 add_submenu_page( 'lockdown-wp-admin', 'Private Users', 'Private Users', 'manage_options', 'lockdown-private-users', array( $this, 'sub_admin_callback')); 515 } 516 517 /** 518 * The callback for the admin area 519 * 520 * You need the 'manage_options' capability to get here. 521 **/ 522 public function admin_callback() 523 { 524 // Update the options 525 $this->update_options(); 526 527 // The UI 528 require_once( dirname( __FILE__ ) . '/admin.php' ); 529 } 530 531 /** 532 * The callback for ther private users management. 533 * 534 * You need the 'manage_options' capability to get here. 535 **/ 536 public function sub_admin_callback() 537 { 538 // Update the users options 539 $this->update_users(); 540 541 // The UI 542 $private_users = $this->get_private_users(); 543 require_once( dirname( __FILE__ ) . '/admin-private-users.php' ); 544 } 545 546 /** 547 * Rename the login URL 548 * 549 * @access public 550 **/ 551 public function redo_login_form() 552 { 553 $login_base = get_option('ld_login_base'); 554 555 // It's not enabled. 556 if ( $login_base == NULL || ! $login_base || $login_base == '' ) 557 return; 558 559 $this->login_base = $login_base; 560 unset( $login_base ); 561 562 // Setup the filters for the new login form 563 add_filter('wp_redirect', array( &$this, 'filter_wp_login')); 564 add_filter('network_site_url', array( &$this, 'filter_wp_login')); 565 add_filter('site_url', array( &$this, 'filter_wp_login')); 566 567 // We need to get the URL 568 // This means we need to take the current URL, 569 // strip it of an WordPress path (if the blog is located @ /blog/) 570 // And then remove the query string 571 // We also need to remove the index.php from the URL if it exists 572 573 // The blog's URL 574 $blog_url = trailingslashit( get_bloginfo('url') ); 575 576 // The Current URL 577 $schema = is_ssl() ? 'https://' : 'http://'; 578 $current_url = $schema . $_SERVER['HTTP_HOST'] . $_SERVER['REQUEST_URI']; 579 580 $request_url = str_replace( $blog_url, '', $current_url ); 581 $request_url = str_replace('index.php/', '', $request_url); 582 583 $url_parts = explode( '?', $request_url, 2 ); 584 $base = $url_parts[0]; 585 586 // Remove trailing slash 587 $base = rtrim($base,"/"); 588 $exp = explode( '/', $base, 2 ); 589 $super_base = end( $exp ); 590 591 // Are they visiting wp-login.php? 592 if ( $super_base == 'wp-login.php') 593 $this->throw_404(); 594 595 // Is this the "login" url? 596 if ( $base !== $this->login_base ) 597 return FALSE; 598 599 // We dont' want a WP plugin caching this page 600 @define('NO_CACHE', TRUE); 601 @define('WTC_IN_MINIFY', TRUE); 602 @define('WP_CACHE', FALSE); 603 604 // Hook onto this 605 do_action('ld_login_page'); 606 607 include ABSPATH . '/wp-login.php'; 608 exit; 609 } 610 611 /** 612 * Filters out wp-login to whatever they named it 613 * 614 * @access public 615 **/ 616 public function filter_wp_login( $str ) 617 { 618 return str_replace('wp-login.php', $this->login_base, $str); 619 } 620 621 /** 622 * Launch and display the 404 page depending upon the template 623 * 624 * @param void 625 * @return void 626 **/ 627 public function throw_404() 628 { 629 // Change WP Query 630 global $wp_query; 631 $wp_query->set_404(); 632 status_header(404); 633 634 // Disable that pesky Admin Bar 635 add_filter('show_admin_bar', '__return_false', 900); 636 remove_action( 'admin_footer', 'wp_admin_bar_render', 10); 637 remove_action('wp_head', 'wp_admin_bar_header', 10); 638 remove_action('wp_head', '_admin_bar_bump_cb', 10); 639 wp_dequeue_script( 'admin-bar' ); 640 wp_dequeue_style( 'admin-bar' ); 641 642 // Template 643 $four_tpl = apply_filters('LD_404', get_404_template()); 644 645 // Handle the admin bar 646 @define('APP_REQUEST', TRUE); 647 @define('DOING_AJAX', TRUE); 648 649 if ( empty($four_tpl) OR ! file_exists($four_tpl) ) 650 { 651 // We're gonna try and get TwentyTen's one 652 $twenty_ten_tpl = apply_filters('LD_404_FALLBACK', WP_CONTENT_DIR . '/themes/twentythirteen/404.php'); 653 654 if (file_exists($twenty_ten_tpl)) 655 require($twenty_ten_tpl); 656 else 657 wp_die('404 - File not found!', '', array('response' => 404)); 658 } 659 else 660 { 661 // Their theme has a template! 662 require( $four_tpl ); 663 } 664 665 // Either way, it's gonna stop right here. 666 exit; 667 } 668 669 /** 670 * See if a login base is suggested against 671 * 672 * @return boolean 673 */ 674 public function isSuggestedAgainst() 675 { 676 return (in_array($this->login_base, array( 677 'login', 678 'admin', 679 'user-login', 680 ))); 108 return $this->application->setUser($array, $user); 681 109 } 682 110 683 111 /** 684 112 * Retrieve the Login Base 685 *686 113 * @return string 687 114 */ 688 115 public function getLoginBase() 689 116 { 690 return $this-> login_base;117 return $this->; 691 118 } 692 119 … … 696 123 * @return boolean 697 124 */ 698 public function getAuthPassed() { return $this->passed; } 125 public function getAuthPassed() 126 { 127 return (bool) $this->passed; 128 } 699 129 700 130 /** … … 702 132 * See {@link WP_LockAuth::getAuthPassed()} 703 133 * 704 * @access p rotected134 * @access p 705 135 * @param boolean 706 136 */ 707 p rotectedfunction passed($value)137 p function passed($value) 708 138 { 709 139 $this->passed = (bool) $value; … … 729 159 } 730 160 731 add_action('init', 'ld_setup_auth' );161 add_action('init', 'ld_setup_auth'); 732 162 733 163 /* End of file: lockdown-wp-admin.php */ -
lockdown-wp-admin/trunk/no-wpmu.php
r770426 r913083 1 <?php if (! defined('ABSPATH')) exit;1 <?php 2 2 /** 3 3 * We don't want to allow for this plugin to be used in WP-MS or network wide. … … 85 85 86 86 // The object. 87 $setup_no_wpmu =new Disable_WPMS_Plugin_LD;87 new Disable_WPMS_Plugin_LD; 88 88 89 89 /* End of file: no-wpmu.php */ -
lockdown-wp-admin/trunk/readme.txt
r770426 r913083 4 4 Link: http://seanfisher.co/lockdown-wp-admin/ 5 5 Tags: security, secure, lockdown, vulnerability, website security, wp-admin, login, hide login, rename login, http auth, 404, lockdown, srtfisher, secure 6 Requires at least: 3. 37 Tested up to: 3. 68 Stable tag: 2. 16 Requires at least: 3. 7 Tested up to: 3. 8 Stable tag: 2. 9 9 10 10 Lockdown WP Admin conceals the administration and login screen from intruders. It can hide WordPress Admin (/wp-admin/) and and login (/wp-login.php) as well as add HTTP authentication to the login system. We can also change the login URL from wp-login.php to whatever you'd like: /login, /log-in-here, etc. … … 103 103 * Support for WordPress 3.6 104 104 * General Cleaning 105 106 107 108 109
Note: See TracChangeset
for help on using the changeset viewer.