Overview
CIRCL warns very often about security issues in Content Management Systems (e.g. Wordpress, Joomla, Typo3, Drupal) or their plug-ins 1. To minimize the risk, the best practice is to always keep track of the installed plug-ins, uninstall unnecessary components and keep the plug-ins and main software components up-to-date. Unfortunately Content Management Systems are sometimes vulnerable to 0-day exploits for which by definition no protection is available. Also, sometimes plug-ins are no longer maintained, or the administrators are just busy with other important tasks so that they cannot react to new known threats in a timely manner.
“the only winning move is not to play” - Joshua, Wargames 2
So another advice from CIRCL is: whenever it is possible, the Content Management System component should be removed from the attack surface.
This article gives an example about how to set up a system that exposes only static websites without dynamically generated content to the user and attacker.
Pros & Cons
Benefits
What are the benefits of a static exported website? First of all, the security impact cannot be greater. No dynamic pages are created on access, only static pages are served. That also means, no database queries are made, which themselves are also prone to various attacks. Also, the speed and scalability of a static website is much better than dynamically generated pages.
Drawbacks
On the negative side, not all options of a CMS can be transposed to static content. The built-in comments system, search or contact forms need to be replaced by other means.
Setup
This technical document gives just an example for the following environment, demonstrating that such a setup is possible and feasible to implement for many installations.
Components
Design
- Static website will be accessible on
- http://example.com
- http://www.example.com
- https://example.com
- https://www.example.com
- Dynamic website hosting with WordPress will be accessible on
- http://admin.example.com (redirecting to https)
- https://admin.example.com
The dynamic website will be password protected with additional Basic access authentication.
The maintainer of the website can prepare and preview the content on https://admin.example.com and when satisfied with the editorial result, the website can be exported to the static website.
Assumptions
For this text it is assumed that the WordPress instance for example.com is installed at /srv/www/example.com/
Installation
Assuming that Apache and WordPress are already installed, the only missing component is ‘staticpress’, which comes either as a plug-in from WordPress or the more recent version from github.
cd /srv/www/example.com/wp-content/plugins
git clone git@github.com:megumiteam/staticpress.git
Activate the plug-in in the admin interface of WordPress.
Configuration
WordPress Configuration:
WordPress Admin interface -> Settings -> General:
WordPress-Adresse (URL): http://admin.example.com
Website-Adresse (URL): http://admin.example.com
StaticPress Configuration:
WordPress Admin interface -> StaticPress -> StaticPress Options:
Static URL: http://example.com/
Save DIR (Document root): /srv/www/example.com/static/
(OPTION) BASIC Auth User: [YOUR BASIC AUTH USER]
(OPTION) BASIC Auth Password: [YOUR BASIC AUTH PASSWORD]
(OPTION) Request Timeout: 5
Apache Configuration
Apache vhost config (/etc/apache2/sites-available/example.com)
<VirtualHost *:443>
ServerName admin.example.com
SSLEngine on
SSLCertificateFile /etc/ssl/certs/ssl-cert-example.com.pem
SSLCertificateKeyFile /etc/ssl/private/ssl-cert-example.com.key
SSLCertificateChainFile /etc/ssl/certs/ssl-intermediate.pem
ErrorLog /var/log/apache2/example.com-ssl-error.log
CustomLog /var/log/apache2/example.com-ssl-access.log combined
VirtualDocumentRoot /srv/www/example.com
Options -Indexes
<Location />
AuthType Basic
AuthName "Admin Area"
AuthUserFile "/etc/apache2/htpasswd-example.com"
require valid-user
RewriteEngine On
RewriteBase /
RewriteRule ^index\.php$ - [L]
RewriteCond %{HTTP_HOST} ^admin.example.com$ [NC]
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule . /index.php [L]
</Location>
</VirtualHost>
<VirtualHost *:443>
ServerName example.com
ServerAlias www.example.com
SSLEngine on
SSLCertificateFile /etc/ssl/certs/ssl-cert-example.com.pem
SSLCertificateKeyFile /etc/ssl/private/ssl-cert-example.com.key
SSLCertificateChainFile /etc/ssl/certs/ssl-intermediate.pem
ErrorLog /var/log/apache2/example.com-ssl-error.log
CustomLog /var/log/apache2/example.com-ssl-access.log combined
VirtualDocumentRoot /srv/www/example.com/static
Options -Indexes
<Location />
Satisfy any
</Location>
</VirtualHost>
<VirtualHost *:80>
NameVirtualHost *:80
ServerName admin.example.com
UseCanonicalName Off
VirtualDocumentRoot /srv/www/example.com
ErrorLog /var/log/apache2/example.com-error.log
CustomLog /var/log/apache2/example.com-access.log combined
Options -Indexes
RewriteEngine On
RewriteCond %{HTTPS} !=on
# This doesn't seem to work:
#RewriteCond %{REMOTE_ADDR} !%{SERVER_ADDR}
# Instead, specify the IP address of the server:
RewriteCond %{REMOTE_ADDR} !10\.0\.0\.1
RewriteRule ^/?(.*) https://%{SERVER_NAME}/$1 [R,L]
<Location />
AuthType Basic
AuthName "Admin Area"
AuthUserFile "/etc/apache2/htpasswd-example.com"
require valid-user
RewriteEngine On
RewriteBase /
RewriteRule ^index\.php$ - [L]
RewriteCond %{HTTP_HOST} ^admin.example.com$ [NC]
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule . /index.php [L]
</Location>
</VirtualHost>
<VirtualHost *:80>
NameVirtualHost *:80
ServerName example.com
ServerAlias www.example.com
UseCanonicalName Off
VirtualDocumentRoot /srv/www/example.com/static
ErrorLog /var/log/apache2/example.com-error.log
CustomLog /var/log/apache2/example.com-access.log combined
Options -Indexes
<Location />
Satisfy any
</Location>
</VirtualHost>
Password
The password file for the additional authentication must be created once finished:
htpasswd -c /etc/apache2/htpasswd-example.com [YOUR BASIC AUTH USER]
The password is being prompted on the command line.
These credentials must be in line with the settings of StaticPress.
Restart of Apache and test
A restart of Apache is required:
sudo service apache2 restart
From now on, everything should be correctly setup.
To test the configuration, got to the WordPress -> StaticPress -> Rebuild This takes a few minutes to execute, since now all the pages are being rebuilt. If everything is correct, the static export of the website should have been generated in
/srv/www/example.com/static/
and consequently should be able to be accessed via http://example.com
Classification of this document
TLP:WHITE information may be distributed without restriction, subject to copyright controls.
References
Revision
- Version 1.1 April 29, 2015 (TLP:WHITE) - got rid of all .htaccess files previously mentioned.
- Version 1.0 April 28, 2015 (TLP:WHITE)