(This is an update of an article I wrote in 2011. Because so much has changed, I've opted for a re-write instead of just updating the original.)

MediaWiki is the software behind Wikipedia, but you can use it to create your own special-purpose sites. I've used it at work to build an internal company knowledge base, and at home to make a Wiki for the fictional worlds of a couple of roleplaying games I've been in.

It's a pretty polished software package, but out of the box it tends to assume that you are creating something like Wikipedia that is visible to (and editable by) the whole wide world. If that's not what you want, it requires some tuning, which I'll describe in detail after the jump.

Why MediaWiki?

An obvious question is, if the MediaWiki software needs tweaking to do what I want, why do I use it and not some other software? A few reasons:

  • It's just a Wiki. I've tried doing similar things with other packages (such as Tiki Wiki), and ended up wasting lots of time fighting with or trying to turn off features I didn't want or need. A CMS or blogging package or groupware suite are great if you need those things, but they all tend to assume a certain workflow and division of responsibility. If you are just trying to create a Wiki, you'll waste time swimming against the current.
  • The Wikitext markup language is reasonably intuitive (simple things are simple, and new users can start contributing right away) but still powerful enough to create complex pages. It's incredibly well-tested, and doesn't flake out in odd corner-cases. There's a huge volume of example pages out there.
  • It's decently fast even on a modest computer, without resorting to esoteric fastcgi and caching tricks.
  • There are lots of useful plugins available.
  • Because of the huge user base, most "How do I...?" questions are answered with a simple web search.

Prerequisites

The remainder of this article will assume that you're starting with a fresh, working install of MediaWiki, and have an admin user set up (and know its password). You'll also need the ability to edit files under the MediaWiki install tree (via command-line access or FTP or any other means).

While the methods I describe should be reasonably general, future releases may involve different ways of doing things. The process described below was tested and is known to work with the following software versions:

  • MediaWiki 1.29.1
  • PHP 7.0.22
  • MySQL 5.7.19
  • Perl 5.22.1
  • Apache 2.4.18

Restricting Access to Registered Users Only

The first step in creating a private Wiki (and one which is well-documented elsewhere) is to make it so that only logged-in users can view or edit pages (other than the login page), and so that only sysops can create new accounts. If you are doing a fresh install using a relatively recent MediaWiki version, the installer will prompt you and set up your LocalSettings.php accordingly.

If you are migrating from an earlier version, or updating the configuration of an existing MediaWiki installation, you will have to edit your LocalSettings.php by hand (in which case, I would strongly advise making a backup copy of the original first; if nothing else, copy it to LocalSettings.php.0).

In any case, confirm that your LocalSettings.php contains the following:

$wgGroupPermissions['*']['createaccount'] = false;
$wgGroupPermissions['*']['edit'] = false;
$wgGroupPermissions['*']['read'] = false;

It's also a good idea to actually test your Wiki at this point to confirm that anonymous reading, anonymous editing and account creation by non-sysop users are all disabled.

Requiring HTTPS

It does little good to require that users authenticate with passwords before using your Wiki if those passwords are flying over the 'net in cleartext -- or if the content protected by those passwords is sent in the clear. Therefore, we want to enforce HTTPS everywhere. (Setting up Apache for HTTPS, and the generation or acquisition of server certificates are beyond the scope of this article.)

Confirm that HTTPS is working in general (by loading a static test page). Confirm that you can access your Wiki via HTTPS. (If you are using a self-signed certificate and/or a local CA, it is a good idea to do these tests with up-to-date versions of all browsers your user base might reasonably be expected to use.)

Edit your Wiki's .htaccess file to include the following:

SSLCipherSuite HIGH:!aNULL:!MD5
RewriteEngine   on
RewriteCond     "%{HTTPS}" "!=on"
RewriteRule     "." "-" [F]

Once the above is in place, verify that 1) you can still access the Wiki via HTTPS and 2) that you get a "403 Forbidden" response when trying to access the Wiki using plain old HTTP.

Also remember that .htaccess files apply to the directory in which they're stored, and to all directories recursively below that. Depending on how your Wiki is installed, it may make sense to put the above rules one or two directories up. For example, I have a Wiki tree stored in a directory like /home/sitename/public_html/wiki/, and I put the HTTPS requirement rules in /home/sitename/public_html/.htaccess.

Enforcing Strong Passwords

MediaWiki now has some good built-in tools for enforcing password strength; see the manual for $wgPasswordPolicy for details. However, for most groups of users, the default settings are insufficient for the purposes of a private Wiki. I would suggest at least the following settings in your LocalSettings.php:

$wgPasswordPolicy['policies']['default']['MinimalPasswordLength'] = 8;
$wgPasswordPolicy['policies']['default']['PasswordCannotBePopular'] = PHP_INT_MAX;

You may want to consider other checks as well. Required reading:

Take-away points from the above include encouraging longer passwords or pass phrases, and not enforcing composition rules (which make passwords harder to remember without making them harder to guess).

Using the PrivateWiki Extension

There are a handful of other security-related problems when (ab)using MediaWiki to host private content. (This in no way reflects badly on MediaWiki or its contributors; after all, that is not the job their software was intended to do.) I have developed a MediaWiki extension called "PrivateWiki" in an attempt to address these problems:

  • MediaWiki offers users the options of session persistence using browser cookies, by way of a "Keep me logged in" check box on the login page. Storing password-equivalent information, in the clear, in the user's browser is not something I want to offer as an option for my Wiki.
  • Popular browsers in default (mis-)configurations will offer to "remember" site passwords. This is a bad idea in general, and certainly not one I want to see applied to my Wiki.
  • MediaWiki shows one error message when you try to log in with an unknown user name, and a different error message if you try to log in with a valid user name but the wrong password. Doing so leaks information about valid user names to potential attackers.
  • When there are too many bad login attempts over too short a period of time, that's probably an attack (rather than just a forgetful user who can type fast). Such attacks should be automatically detected and blocked.

Installation/Upgrade

To install the PrivateWiki extension:

  1. Download the extension tarball. Latest version: privatewiki-1.0.tar.gz 4310B 2017-09-27 MD5: 32d3c68bbb31042ef7093a0d94c2302b
  2. If you already have PrivateWiki installed, remove or rename your extensions/PrivateWiki/ directory.
  3. Unpack the tarball contents in your Wiki's extensions/ directory. Example command:
    cd extensions; tar xvzf /path/to/privatewiki-1.0.tar.gz

    This should create a single directory extensions/PrivateWiki/ containing six files.

  4. Check ownership and permissions of the PrivateWiki/ directory and its contents. If you extracted the tarball as the Wiki user, they should already be correct. If you did so as root, 1) why? and 2) ownership and permissions will need fixing. Example commands:
    cd extensions
    chown -R wikiuser.wikigroup PrivateWiki
    chmod -R ug+rwX,o+rX,o-w PrivateWiki

    (Replace wikiuser and wikigroup with the non-root role user and group as whom your Wiki software runs.)

  5. If you are installing for the first time, add lines like the following to your LocalSettings.php file:
    wfLoadExtension('PrivateWiki');
    $wgPrivateWikiLoginFailDir = "/home/wikiuser/logs";

    If you are upgrading an existing installation, you should already have lines like the above; verify their presence and contents.

    The $wgPrivateWikiLoginFailDir should be set to the full path to a directory to which log files documenting login failures will be written. This directory must exist, must be writable by the user as whom the Wiki software runs, and must be outside the directory made visible via the web server. (In other words, don't put this anywhere under your public_html/ directory!)

If you don't want to record login failures in a file, you can leave $wgPrivateWikiLoginFailDir unset. Note, however, that PrivateWiki does nothing in and of itself to stop repeated failed login attempts from the same source. My suggestion is to use an external program (fail2ban) to accomplish this, and fail2ban relies on these log files to function.

Testing

After installation (or upgrade), Verify that your Wiki and the PrivateWiki extension are operating as expected.

  1. Make sure you can still access your Wiki. (Log out if you are currently logged in, then log in using a valid user name and password.) If you have trouble, it may be helpful to check Apache's error log file and/or add:
    $wgShowExceptionDetails = true;

    to your LocalSettings.php temporarily, until the problem is resolved.

  2. Go to your Wiki's Special:Version page, and confirm that the PrivateWiki extension is listed under the "Installed extensions" section.
  3. Log out. Return to the login page. Verify that the "Keep me logged in" check box is not shown. (If it is, try force-reloading the page. If you are using php-fpm or similar, you may need to restart the fpm server responsible for running your Wiki.)
  4. View the source of the login page. Search for autocomplete="no". You should see this string exactly once, in the attributes for the HTML form element.
  5. Attempt to log in using an invalid user name (with any password). You should see a message like "The supplied credentials could not be authenticated" (or the equivalent in your selected language). You should see a new line in a file in your $wgPrivateWikiLoginFailDir recording the failed login.
  6. Attempt to log in using a valid user name with the wrong password. You should see the exact same message as in the previous test. You should also get another record in the log file recording the failed login.

If any of the above tests don't produce the expected result: stop, investigate and solve the problem before going on. If you make a change to correct a problem, do all the tests from top to bottom to confirm you didn't break anything else in the process.

Blocking Attacks with fail2ban

There's a handy free utility called fail2ban which will -- based on the contents of log files you specify -- spot signs of trouble and ban (e.g., block via firewall rule) the IP address(es) from which the trouble originates.

It may not be appropriate for your particular installation. As far as I know, it runs only on UNIX-y platforms. Making dynamic firewall changes in any kind of shared or managed hosting situation is likely difficult. However, if your Wiki is running on a Linux box you administer, it should work like a champ.

I'm going to assume that you have fail2ban installed and working (if only to keep the hordes of SSH doorknob-twisters at bay), and that you just need to bring your Wiki under its aegis. If you need to install fail2ban from scratch, the obvious searches will yield good instructions. Pre-built packages exist for most Linux distros of recent vintage.

Creating a Filter Rule

The first step to adding your Wiki to the list of things protected by your fail2ban installation is to create a filter rule. The filter rule does two things: 1) allows fail2ban to recognize which log lines represent login failures and 2) tells fail2ban how to extract from such a line the hostname or IP address from which the bad login originated.

To create a filter rule, create a new file /etc/fail2ban/filter.d/privatewiki.conf with contents like:

[Definition]
failregex = auth fail site=.* ip=<HOST>

Once the filter config file is in place, it is a good idea to test it from the command line using the fail2ban-regex tool included with fail2ban. To do so, you will need a PrivateWiki log file showing at least one failed login attempt. If you followed the post-installation test procedure above, you should already have one; otherwise, just log in with the wrong user or password.

In the log file directory specified by $wgPrivateWikiLoginFailDir, you should see a file with a name like YYYY-MM-DD.log where YYYY-MM-DD is the date of the failed login. Inside, you should see lines similar to the following:

[2017-09-27 22:01:50] auth fail site=MySiteName ip=192.168.123.234

Note that the time stamp at the start of each line within the log file is in UTC, as are the dates encoded in the log file names. If you have a caching proxy, load balancer or other intermediate step through which web traffic flows, it is a good idea to check that the IP addresses being logged are those of the actual client device of the user who failed to log in, rather than the address of some part of your own network infrastructure.

To test your filter rule, run the fail2ban-regex command with two arguments: the full path to your filter file, and the full path to a log file. Example command:

fail2ban-regex /home/sitename/logs/2017-09-27.log /etc/fail2ban/filter.d/privatewiki.conf

This should produce output showing a match for the offending line(s):

Lines: 1 lines, 0 ignored, 1 matched, 0 missed [processed in 0.00 sec]

The "matched" number should correspond to the number of login failures recorded in the log file you specified.

Configuring a Jail

Once the filter is working, you can configure fail2ban to use it to protect your Wiki. To do so, add the definition for a new "jail" to your /etc/fail2ban/jail.local:

[mysitename]
enabled = true
banaction = iptables-multiport
filter = privatewiki
port = http,https
logpath = /home/mysitename/logs/*.log
maxretry = 5
findtime = 1500
bantime = 600

The logpath must match the logs directory configured in $wgPrivateWikiLoginFailDir, with "*.log" appended. You may wish to adjust the maxretry, findtime and/or bantime values as appropriate for your site; see the fail2ban jail options documentation for details.

Note that with the above configuration, if fail2ban decides someone is attacking your Wiki, it will block their IP from passing any traffic (even traffic unrelated to your Wiki) to or from your webserver's default HTTP and HTTPS ports (80 and 443, respectively).

If you need to protect multiple Wikis, you can use the same filter rule for all, but you will need a separate jail for each.

Once you have added the jail definition, tell fail2ban to re-load its configuration:

fail2ban-client reload

After you have done so, you should see your new jail appear in the list given by fail2ban-client status:

Status
|- Number of jail:      4
`- Jail list:   mysitename, rainloop, ssh, sshd

(The above is just an example. What you're looking for is a name in the jail list which corresponds to the name you put in square brackets at the start of the jail definition.)

You can also query the status of your new jail using fail2ban-client status mysitename. Note that this will not show any failures until and unless they exceed the threshold specified by maxretry and findtime. Also note that if you specify a global ignoreip list in your jail.local, your Wiki's jail will honor that list. (If you test by doing a bunch of bad logins from inside your local network and don't get banned: this is probably why.)

If, during the course of testing, you manage to ban your test IP and want to un-ban it without waiting for the bantime to expire, you can use a command similar to the following to do so:

fail2ban-client set sitename unbanip 192.168.123.234

Conclusion

Using these instructions will not make your Wiki secure in any absolute sense, but will certainly make it far more secure than a default installation. Use common sense, keep your software up to date, and practice security in depth. Please feel free to comment on this article or contact me by email if you have any suggestions, questions or concerns.

[Edited 2017 Oct 03 DGH to fix rendering error in <HOST> part of fail2ban filter rule. Also clarified wording regarding group membership for PrivateWiki extension files.]