Skip to content

Commit

Permalink
Merge pull request #53 from chesio/release-0.9
Browse files Browse the repository at this point in the history
Release version 0.9
  • Loading branch information
chesio authored Aug 6, 2018
2 parents 930eb6f + 92e3018 commit 4f80aee
Show file tree
Hide file tree
Showing 88 changed files with 3,518 additions and 1,467 deletions.
75 changes: 56 additions & 19 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,9 @@
Helps keeping WordPress websites secure.

## Requirements

* [PHP](https://secure.php.net/) 7.0 or newer
* [WordPress](https://wordpress.org/) 4.7 or newer
* [WordPress](https://wordpress.org/) 4.9 or newer

## Limitations

Expand All @@ -15,16 +16,45 @@ Helps keeping WordPress websites secure.

### Checklist

BC Security features a checklist of common security practices. In the moment, the list consists of following checks:
1. Is editation of plugin and theme PHP files disabled?
BC Security can help you find potential security issues or even signs of breach.

#### Basic checks

Basic checks cover common security practices. They do not require any information from third party sources to proceed and thus do not leak any information about your website:

1. Is backend editing of plugin and theme PHP files disabled?
1. Are directory listings disabled?
1. Is execution of PHP files from uploads directory forbidden?
1. Is display of PHP errors off by default? This check is only run in production environment, ie. when `WP_ENV === 'production'`.
1. Is error log file not publicly available? This check is only run if both `WP_DEBUG` and `WP_DEBUG_LOG` constants are set to true.
1. Are there no common usernames like admin or administrator on the system?
1. Are user passwords hashed with some non-default hashing algorithm?

Checklist check is run whenever a dedicated page in backend is visited, but can be also set to run periodically in the background. Note that in such case, only checks that passed at the time of activation (or update) of background monitoring are run as it makes little sense to report issues that are known already.
#### Advanced checks

Advanced checks require data from external servers (in the moment from WordPress.org only). Because of this, they leak some information about your website. In the moment, **list of installed plugins** (but only those with _readme.txt_ file) is shared with WordPress.org. Also, because of the external HTTP request, the checks take more time to execute.

##### WordPress core integrity check

WordPress core files verification is done in two phases:
1. Official md5 checksums from WordPress.org are used to determine if any of core files have been modified.
1. All files in root directory, _wp-admin_ directory (including subdirectories) and _wp-includes_ directory (including subdirectories) are checked against official checksums list in order to find out any unknown files.

The check uses the same checksums API as [`core verify-checksums`](https://developer.wordpress.org/cli/commands/core/verify-checksums/) command from [WP-CLI](https://wp-cli.org/).

##### Plugins integrity check

Plugin files verification works only for plugins installed from [Plugins Directory](https://wordpress.org/plugins/). The verification process is akin to the core files verification, although the API is slightly different (see [related Trac ticket](https://meta.trac.wordpress.org/ticket/3192) and [specification](https://docs.google.com/document/d/14-SMpaPtDGEBm8hE9ZwnA-vik5OvECDig32KqX8uFlg/edit)).

Important: any plugins under version control (Git or Subversion) are automatically omitted from the check.

##### Removed plugins check

Although plugins can be removed from [Plugins Directory](https://wordpress.org/plugins/) for several reasons (not only because they have [security vulnerability](https://www.wordfence.com/blog/2017/09/display-widgets-malware/)), use of removed plugins is discouraged. Obviously, this check also works only for plugins installed from Plugins Directory.

#### Checklist monitoring

Both basic and advanced checks can be run manually from a dedicated page in backend, but can be also configured to run periodically (once a day) in the background. Basic checks are run via a single cron job, while each of advanced checks is run via a separate cron job.

### WordPress hardening

Expand All @@ -33,16 +63,6 @@ BC Security allows you to:
1. Disable XML RPC methods that require authentication
1. Disable access to REST API to anonymous users

### Checksums verification

BC Security once a day performs integrity check of WordPress core and plugin files. Any file that is evaluated as modified or unknown is [logged](#events-logging) and (optionally) reported via [email notification](#notifications).

WordPress core files verification is done in two phases:
1. Official md5 checksums from WordPress.org are used to determine if any of core files have been modified.
1. All files in root directory, `wp-admin` directory (including subdirectories) and `wp-includes` directory (including subdirectories) are checked against official checksums list to determine if the file is official (known) file.

Plugin files verification works only for plugins hosted at [WordPress Plugins](https://wordpress.org/plugins/) directory. The verification process is akin to the core files verification, although the API is slightly different (see [related Trac ticket](https://meta.trac.wordpress.org/ticket/3192) and [specification](https://docs.google.com/document/d/14-SMpaPtDGEBm8hE9ZwnA-vik5OvECDig32KqX8uFlg/edit)).

### Login security

1. BC Security allows you to limit number of login attempts from single IP address. Implementation of this feature is heavily inspired by popular [Limit Login Attempts](https://wordpress.org/plugins/limit-login-attempts/) plugin with an extra feature of immediate blocking of specific usernames (like _admin_ or _administrator_).
Expand All @@ -63,28 +83,45 @@ BC Security allows to send automatic email notification to configured recipients
1. Theme update is available.
1. User with administrator privileges has logged in.
1. Known IP address has been locked out (see note below).
1. [Checksums verification](#checksums-verification) fails or there are files with non-matching checksum.
1. [Checklist monitoring](#checklist-monitoring) triggers an alert. Note: there is one notification sent if any of basic checks fails, but separate notification is sent if any of advanced checks fails.
1. BC Security plugin has been deactivated.

Note: _Known IP address_ is an IP address from which a successful login attempt had been previously made. Information about successful login attempts is fetched from [event logs](#events-logging).

### Events logging
You can mute all email notifications by setting constant `BC_SECURITY_MUTE_NOTIFICATIONS` to `true` via `define('BC_SECURITY_MUTE_NOTIFICATIONS', true);`. If you run a website in multiple environments (development, staging, production etc.), you may find it disturbing to receive email notifications from development or any environment other than production. Declaring the constant for particular environment only is very easy, if you use a [multi-environment setup](https://github.com/chesio/wp-multi-env-config).

BC Security logs both short and long lockout events (see [Login Security](#login-security) feature) and [checksums verification](#checksums-verification) alerts.
### Events logging

Also, the following events triggered by WordPress core are logged:
BC Security logs both short and long lockout events (see [Login Security](#login-security) feature). Also, the following events triggered by WordPress core are logged:

1. Attempts to authenticate with bad cookie
1. Failed and successful login attempts
1. Requests that result in 404 page

Logs are stored in database and can be viewed on backend. Logs are automatically deleted based on their age and overall size: by default no more than 20 thousands of records are kept and any log records older than 365 days are removed, but these limits can be configured.

## Customization

Some of the modules listed above come with settings panel. Further customization can be done with filters provided by plugin:

* `bc-security/filter:is-admin` - filters boolean value that determines whether current user is considered an admin user. This check determines whether admin login notification should be sent for particular user. By default, any user with `manage_options` capability is considered an admin (or `manage_network` on multisite).
* `bc-security/filter:obvious-usernames` - filters array of common usernames that are being checked via [checklist check](#basic-checks). By default, the array consists of _admin_ and _administrator_ values.
* `bc-security/filter:plugins-to-check-for-integrity` - filters array of plugins that should have their integrity checked. By default, the array consists of all installed plugins that have _readme.txt_ file. Note that plugins under version control are automatically omitted.
* `bc-security/filter:plugins-to-check-for-removal` - filters array of plugins to check for their presence in WordPress.org Plugins Directory. By default, the array consists of all installed plugins that have _readme.txt_ file.
* `bc-security/filter:modified-files-ignored-in-core-integrity-check` - filters array of files that should not be reported as __modified__ in checksum verification of core WordPress files. By default, the array consist of _wp-config-sample.php_ and _wp-includes/version.php_ values.
* `bc-security/filter:unknown-files-ignored-in-core-integrity-check` - filters array of files that should not be reported as __unknown__ in checksum verification of core WordPress files. By default, the array consist of _.htaccess_, _wp-config.php_, _liesmich.html_, _olvasdel.html_ and _procitajme.html_ values.
* `bc-security/filter:ip-blacklist-default-manual-lock-duration` - filters number of seconds that is used as default value in lock duration field of manual IP blacklisting form. By default, the value is equal to one month in seconds.
* `bc-security/filter:is-ip-address-locked` - filters boolean value that determines whether given IP address is currently locked within given scope. By default, the value is based on plugin bookkeeping data.
* `bc-security/filter:log-404-event` - filters boolean value that determines whether current HTTP request that resulted in [404 response](https://en.wikipedia.org/wiki/HTTP_404) should be logged or not. To completely disable logging of 404 events, you can attach [`__return_false`](https://developer.wordpress.org/reference/functions/__return_false/) function to the filter.
* `bc-security/filter:events-with-hostname-resolution` - filters array of IDs of events for which hostname of involved IP address should be resolved via reverse DNS lookup. By default the following events are registered: attempts to authenticate with bad cookie, failed and successful login attempts and lockout events. Note that this functionality only relates to event logs report in backend - in case email notification is sent, hostname of reported IP address (if any) is always resolved separately.
* `bc-security/filter:login-username-blacklist` - filters array of blacklisted usernames that are being immediately locked on login. There are no default values, but the filter operates on usernames set via module settings, so it can be used to enforce blacklisting of particular usernames.

## Credits

1. [Login Security](#login-security) feature is inspired by [Limit Login Attempts](https://wordpress.org/plugins/limit-login-attempts/) plugin by Johan Eenfeldt.
1. Part of [psr/log](https://packagist.org/packages/psr/log) package codebase is shipped with the plugin.
1. [Checksums verification](#checksums-verification) feature is heavily inspired by [Checksum Verifier](https://github.com/pluginkollektiv/checksum-verifier) plugin by Sergej Müller.
1. [WordPress core integrity check](#wordpress-core-integrity-check) is heavily inspired by [Checksum Verifier](https://github.com/pluginkollektiv/checksum-verifier) plugin by Sergej Müller.
1. Some features (like "[Removed plugins check](#removed-plugins-check)") are inspired by [Wordfence Security](https://wordpress.org/plugins/wordfence/) from [Defiant](https://www.defiant.com/).

## Alternatives (and why I do not use them)

Expand Down
14 changes: 14 additions & 0 deletions assets/css/checklist.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
/* Check running */
.bcs-check--running {
opacity: 0.5;
}

/* Check passed */
.bcs-check--ok .dashicons:before {
content: "\f147";
}

/* Check failed */
.bcs-check--ko .dashicons:before {
content: "\f158";
}
67 changes: 67 additions & 0 deletions assets/js/checklist.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
(function($, bc_security_checklist) {
$(function() {
var $checks = $('.bcs-check');
var $run_checks_buttons = $('button.bcs-run-checks');

// Activate "select all" button.
$('#bcs-mark-all-checks').prop('disabled', false).on('click', function() {
$checks.find('input[type="checkbox"]').prop('checked', true);
});

// Activate "select none" button.
$('#bcs-mark-no-checks').prop('disabled', false).on('click', function() {
$checks.find('input[type="checkbox"]').prop('checked', false);
});

// Activate "select only passing" button.
$('#bcs-mark-passing-checks').prop('disabled', false).on('click', function() {
$checks.find('input[type="checkbox"]').prop('checked', function() { return $(this).closest('.bcs-check').hasClass('bcs-check--ok'); });
});

// Activate "Run checks" buttons.
$run_checks_buttons.on('click', function() {
// Disable all "Run checks" buttons.
$run_checks_buttons.prop('disabled', true);

var $button = $(this);
var requests = [];

$checks.filter('.' + $button.data('check-class')).each(function() {
var $check = $(this).removeClass('bcs-check--ok').removeClass('bcs-check--ko').addClass('bcs-check--running');
var $last_run = $('.bcs-check__last-run', $check);
var $message = $('.bcs-check__message', $check).html(bc_security_checklist.messages.check_is_running);

// https://api.jquery.com/jQuery.ajax/
var request = $.ajax({
url : bc_security_checklist.ajaxurl,
method : 'POST',
data : {action: bc_security_checklist.action, _ajax_nonce: bc_security_checklist.nonce, check_id: $check.data('check-id')},
dataType: 'json',
cache : false,
timeout : 0, // no timeout
error : function() {
$message.html(bc_security_checklist.messages.check_failed);
},
success : function(response) {
if (response.success) {
$last_run.text(response.data.timestamp);
if (response.data.status !== null) {
$check.addClass('bcs-check--' + (response.data.status ? 'ok' : 'ko'));
}
}
$message.html(response.data.message);
},
complete : function() {
$check.removeClass('bcs-check--running').addClass('bcs-check--done');
}
});

requests.push(request);
});

$.when.apply($, requests).always(function() {
$run_checks_buttons.prop('disabled', false);
});
});
});
})(jQuery, bc_security_checklist);
6 changes: 3 additions & 3 deletions bc-security.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,12 @@
/**
* Plugin Name: BC Security
* Plugin URI: https://github.com/chesio/bc-security
* Description: Helps keeping WordPress websites secure. Plugin requires PHP 7.0 or newer to run.
* Version: 0.8.1
* Description: Helps keeping WordPress websites secure.
* Version: 0.9.0
* Author: Česlav Przywara <[email protected]>
* Author URI: https://www.chesio.com
* Requires PHP: 7.0
* Requires WP: 4.7
* Requires WP: 4.9
* Tested up to: 4.9
* Text Domain: bc-security
* GitHub Plugin URI: https://github.com/chesio/bc-security
Expand Down
9 changes: 8 additions & 1 deletion classes/BlueChip/Security/Admin.php
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,9 @@ public function addPage(Core\Admin\AbstractPage $page): self
}


/**
* @action https://developer.wordpress.org/reference/hooks/admin_init/
*/
public function initAdminPages()
{
foreach ($this->pages as $page) {
Expand All @@ -64,7 +67,9 @@ public function initAdminPages()


/**
* Make plugin menu - this method is hooked to `admin_menu` hook.
* Make plugin menu.
*
* @action https://developer.wordpress.org/reference/hooks/admin_menu/
*/
public function makeAdminMenu()
{
Expand Down Expand Up @@ -106,6 +111,8 @@ public function makeAdminMenu()
/**
* Filter plugin action links: append link to setup page only.
*
* @filter https://developer.wordpress.org/reference/hooks/plugin_action_links_plugin_file/
*
* @param array $links
* @return array
*/
Expand Down
9 changes: 8 additions & 1 deletion classes/BlueChip/Security/Core/Admin/AbstractPage.php
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,11 @@ abstract class AbstractPage
*/
const SLUG = 'bc-security';

/**
* @var string Name of nonce used for any custom actions on admin pages
*/
const NONCE_NAME = '_wpnonce';

/**
* @var string Page title for menu
*/
Expand Down Expand Up @@ -92,7 +97,7 @@ public function setPageHook(string $page_hook)


/**
* Run on admin initialization.
* Run on admin initialization (in `admin_init` hook).
*/
public function initPage()
{
Expand All @@ -102,6 +107,8 @@ public function initPage()

/**
* Run on page load.
*
* @action https://developer.wordpress.org/reference/hooks/load-page_hook/
*/
public function loadPage()
{
Expand Down
62 changes: 62 additions & 0 deletions classes/BlueChip/Security/Core/Admin/PageWithAssets.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
<?php
/**
* @package BC_Security
*/

namespace BlueChip\Security\Core\Admin;

use BlueChip\Security\Core\AssetsManager;

trait PageWithAssets
{
/**
* @var \BlueChip\Security\Core\AssetsManager
*/
private $assets_manager;


/**
* @param \BlueChip\Security\Core\AssetsManager $assets_manager
*/
protected function useAssetsManager(AssetsManager $assets_manager)
{
$this->assets_manager = $assets_manager;
}


/**
* @param array $assets JS assets to enqueue in [ handle => filename ] format.
*/
protected function enqueueJsAssets(array $assets)
{
add_action('admin_enqueue_scripts', function () use ($assets) {
foreach ($assets as $handle => $filename) {
wp_enqueue_script(
$handle,
$this->assets_manager->getScriptFileUrl($filename),
['jquery'],
filemtime($this->assets_manager->getScriptFilePath($filename)),
true
);
}
}, 10, 0);
}


/**
* @param array $assets CSS assets to enqueue in [ handle => filename ] format.
*/
protected function enqueueCssAssets(array $assets)
{
add_action('admin_enqueue_scripts', function () use ($assets) {
foreach ($assets as $handle => $filename) {
wp_enqueue_style(
$handle,
$this->assets_manager->getStyleFileUrl($filename),
[],
filemtime($this->assets_manager->getStyleFilePath($filename))
);
}
}, 10, 0);
}
}
Loading

0 comments on commit 4f80aee

Please sign in to comment.