Currently viewing the tag: "Security"

This is a how to guide for generating a dynamic WordPress website into a static HTML version. It is aimed at WordPress administrators that wish to serve up their WordPress deployments in static format, for security, scaling and performance purposes.

In this tutorial, we will give you the abilities for running a staging WordPress server that is effectively used for development (that very well could be deployed offline), and a scripted method for generating the dynamic version of the development website that can be pushed to a production web server that will serve the generated static HTML using Puppet.

Below is a quick overview of the platform and some common security techniques. If you’re only interested in the nuts and bolts of making this work, skip ahead here.


WordPress is hands down one of the most popular content management systems out there. It’s a modular platform that provides simplicity for website owners to publish web content, and is very easy to use.

On the flip side of the coin, a major concern is the poor security track record of the WordPress platform and its installable plugins (the plugins are of even greater concern, as it is near impossible to truly gauge the security posture and coding practices of the developers that maintain them). This has led to countless web site and web server compromises, and has resulted in the publication of many Common Vulnerabilities and Exposure (CVE) identifiers.

Common Mitigation Techniques

It can be quite cumbersome to properly, and proactively secure an average WordPress installation. Here are a few techniques that are more commonly used:

Apache ModSecurity

One of the known methods for mitigating a great deal of attack vectors against WordPress is through Apache ModSecurity. – A web application firewall (WAF) that is essentially an Apache module that provides security features and request filtering for Apache web server.

ModSecurity is a great product, but it carries a substantial performance cost to the web server that is running it. One must be very fluent with the module for proper configuration and hands on with ongoing maintenance. This also does nothing for those running WordPress on other web server software platforms, such as Nginx, with out having to run Apache on the backend.

Log Monitoring & Active Response

There are some other common software and scripting suites that WordPress site owners implement for attack mitigation, such as OSSEC and Fail2Ban. These two components have great capabilities, but one of the problems I have with going solely with an active log monitoring solution is that the attack may have already have taken place long before the software catches the attack. This doesn’t do very much good for those that do not read the alerts around the clock, and whom aren’t prepared for doing a forensic attack analysis every time an alert comes in.

In a scenario where an attacker were able to pull off a remote file include (RFI) attack or some sort of cross site scripting (XSS) attack, and the alert went unnoticed by administrators (if there even was such an alert), the attacker could cause considerable damages and headaches to the organization (it isn’t fun having to deal with threatening correspondences from your ISP stating that your web server is serving up malware or other unsightly files, when it was due to a compromise of your WordPress deployment).

The fact of the matter is there are many options out there for protecting WordPress deployments, but it’s a lot of work and isn’t 100% effective in all scenarios.

The Solution: Static HTML

There really is no better way to protect a resource hungry and historically dangerous content management system than serving up it’s generated HTML content statically… period. With static HTML there is not much to defend against in comparison.

Bonus #1: Performance!

Since WordPress is database-driven, requiring PHP and MySQL out of the box, the platform can be quite resource intensive. There are ways to mitigate performance issues, such as using various server-side caching components like Memcached, APC, Varnish, and etc. These do come at an extra cost of required system resources, software and overall maintenance.

Bonus #2: Scalability!

In my past career, I have architected and deployed fairly complex load-balanced, high-performance and high-availability WordPress deployments that allow for running multiple WordPress installations, on multiple servers using some custom rsync scripts, MySQL replication, Memcached replication (Memcachedrep), and a small laundry list of other components.

Having that said, there is obviously nothing easier than scaling out a static clone of WordPress.

Bonus #3: Cost Savings!

The economics are simple:

  • Less system resources required = Less money spent on system resources
  • Less time spent on security and maintenance = Less money spent on administration costs

…and more time for focusing on the stuff that needs your attention (presuming you value your time or the time you’re paying for your admin’s to spend)

The Script

There were a few reasons I decided to write this script, the first reason being that none of the WordPress static HTML generation plugins work! (also, some of them are quite spammy in their WordPress admin pages).

The second primary reason I’m wrote it is because I would much rather have the flexibilities of using a shell script for cloning (i.e. copying files/directories outside of WordPress that exist on the same vhost), and there isn’t a need for logging in to the web interface to perform the task.

Things to Note

One couple of things to keep in mind is that the server running this script must have a hosts file entry in /etc/hosts pointing to the right IP address. You will also need to ensure that your local machine has a hosts file entry setup properly for viewing the development or the production server if you are going to use a staging server setup using the same DNS hostname for WordPress (the staging server and production server both using www.yourdomain.tld that is).

I have a variation of this script that allows me to run WordPress on an internal server with the hostname: staging.mydomain.tld, whereby the staging/development WordPress can be cloned, and the script will automatically swap out the hostname with www.myserver.tld upon cloning.

You may wish to implement this vs using the same DNS hostnames for both servers (or use our contact page for contracting the work to us if you need any timely customization).

Also, you will lose the default comment functionality with a static HTML version of your WordPress site. There are alternatives (such as using Disquss) that will fix this up.

Here is the script – copy and paste friendly, but it can be alternatively downloaded here: wordpress-static-html.sh

Yep, that’s it! It’s actually quite a simple script. In a nutshell, here’s what it does:

  • Creates a base directory for housing the cloned files in $SAVETO and uses it for the prefix (-P)
  • Uses wget to recursively mirror a copy of the entire WordPress website (–mirror, –html-extension, -p)
  • Wget excludes the files and directories defined in $EXCLUDE (-X)
  • Loops through the pages, and creates named copies of them as directories (i.e. /blog/blogpost/)
  • Moves the pages as index files (i.e. /blog/blogpost/index.html)
  • Copies any additional files to the WordPress $SAVETO base directory, defined in $OTHERSTUFFTOCOPY

The user configurable variables are commented and should be changed to match your requirements.


The Puppet configuration would be pretty simple. You would just need to ensure you have something like the the example below (adapted for your Puppet environment of course):

As always, use at your own risk as this site assumes no responsibility!

It’s a good practice to monitor your web server logs for erroneous HTTP status codes in the requests made to your web server. There are several reasons for doing this routinely, including troubleshooting web server problems, identifying web server attacks, SEO purposes, and providing an overall improved experience for the end users.

Many system administrators and developers will monitor web server logs for issues. A lot of the time, it isn’t a routine practice and can be inadvertently neglected. This is a quick and dirty bash one-liner that will grab all of the matching 4xx & 5xx web server log entries, and send them to you in a daily email digest for further analysis, which automates the practice of proactively monitoring web server logs for issues.

Consider the following…


The web server access log format displays:

Log Location

The web server access logs are located in:

Where VHOST is your virtual host(s).

The Script

The above one-liner will effectively find all of your individual Apache virtual host access log files, and extract all of the HTTP 4xx and 5xx entries for a given date (in the format of “18/Mar/2012“).

A variation of the above script caters to server deployments that run Nginx along side of Apache, where the server is logging to two different default web server locations (/var/log/apache2/ and /var/log/nginx/), and will perform the script operations on both sets of access log files:

Adding this to cron, running it a 11:59pm daily will give you the digest. A consideration before deploying this is the possibility of having large access logs that might potentially produce thousands of lines of log entries. A better solution in those types of deployments for this script example is to use uuencode for attaching the output as a compressed attachment instead.

Depending on your tolerance for the email size, one can incorporate simple logic using wc to count the lines in the output, and send it raw via the mail utility if the lines are less than X or use uuencode if the lines are greater than Y.

For those that are fairly new to web server administration or HTTP in general, here are some of the most common HTTP server and client error codes you will encounter:


HTTP error codes beginning with 5 indicate a problem with the server. Most commonly these fire in scenarios such as:

500 Internal Server Error

  • A server misconfiguration prevents the request from being processed

502 Bad Gateway

  • Can indicate server in your load balance pool are down at the time of the request

503 Service Unavailable

  • May indicate that there isn’t enough capacity on the back-end servers or back-end server threads to handle the traffic


These HTTP codes relate problems with the client request, but the most common scenarios are:

401 Unauthorized

  • The client attempted to access a resource that requires authentication
  • Large amounts of HTTP 401′s in your server logs can indicate a brute force attempt against resources protected by HTTP authentication

403 Forbidden

  • The client attempted to access a resource that the server’s configuration denies permission to
  • Can be caused by file permissions, an htaccess directive, a client attempting to view directory indexes, and so forth

404 Not Found

  • Files no longer exist or were moved
  • Broken links or paths, best remedied with 301 or 302 redirects to the new location

444 No Response (Nginx specific)

  • This is a special Nginx HTTP response code that simply does not respond and resets the connection to the client
  • Useful for protecting web server assets and mitigating requests from known-bad user agents, and has several other advantages


This is a tutorial for setting up your own open-source anti-theft software in OSX. Upon completion of this tutorial, you will have a system daemon that will securely perform, nearly all of the core functions as many paid Macbook / Macbook Pro anti-theft applications provide.

Be advised, this guide is aimed towards those with a bit of Unix/Linux knowledge, so if there are points that are not clear to novice users, do not hesitate to contact me for clarifications.

Why are you reading this?

Some time ago, I read about an individual who had their Macbook Pro stolen from them and recovered it with the help of the police and an app called “hiddenapp”. Since I had actually had a 2008 Macbook Pro stolen from me in the past, I decided to purchase (waste my money on) the app for my current machine.

Right after I performed the installation, I immediately noticed an outbound request from a curl to hiddenapp.com, thanks to Little Snitch. Before allowing this egress traffic, I decided to fire up Wireshark to inspect the traffic leaving my machine and network, whereby I found the following unacceptable application behaviors:

  • Traffic leaves the Mac unencrypted to hiddenapp.com, to port 80
  • The outbound request for status (Stolen, Test Mode, or OK) attempted to send my Macbook Pro’s serial number in plain text in the curl (GET) request URI!

Having witnessed this, I attempted to reconstruct the request via curl over HTTPS, only to find that the domain does not offer SSL encryption whatsoever. Not being able to live with the fact this app transmits my Macbook Pro’s serial number in the clear back to the mothership, I took it upon myself to identify the core features this self proclaimed “Most advanced theft tracking software for your Mac”, and replicate the functionality securely on my own setup.


This tutorial is for the adventurous. You must have the following at your disposal to complete the setup:

  • ImageSnap
  • Web Server with shell access (I’m using Nginx for this tutorial)
  • Intermediate Unix commandline fu (or the motivation to follow some the steps in this guide)

Step #1: Install and Test ImageSnap

As stated above, you have two options for getting the imagesnap binary installed. Grab the ImageSnap source code either way, as it contains a precompiled binary that you can copy into your $PATH or desired location, or compile your own using the Xcode project file contained within.

Once you have the imagesnap binary in $PATH, run the following test to ensure that it works as expected (taking a picture with the iSight camera):

The above should produce a Jpeg image in the current directory.

Step #2: Configure Your Nginx vhost with SSL and Auth Basic

We will now setup an Nginx vhost to handle our application’s requests. Our web server will have the following attributes:

  • SSL encryption for our status requests
  • User / Password authentication for status requests
  • Custom user agent filtering with Nginx to provide an added layer of authentication
  • A single web page containing your Mac’s status code (Stolen or Not)

Generate a self signed SSL certificate:

* If you do not have htpasswd installed to create the passwd file, see this.

Test the Nginx configuration:

Create the file on the remote server with:

In this example, we will use the number “0″ as the status message indicating our machine is not stolen. Anything other than “0″ will indicate the device is in fact stolen. We’ll come back to this file later.

Step #3: Test Authentication with cURL

Now let’s ensure that we can authenticate with the web server before proceeding. Use the following curl command to test:

Confirm the status code in the server response headers is “HTTP/1.1 200 OK” and not “HTTP/1.1 401 Unauthorized“. If you get a 401, fix your configuration in Step #2.

Step #4: Generate an SSH Key and Server User Account

For this setup, we will use an SSH key for a user account on the remote server with limited privileges. If our status check indicates the machine is stolen, the machine will transfer the picture taken with the iSight camera to the remote server via the SCP protocol, so we can retrieve the photo(s) taken.

We will configure the remote user’s SSH authorized_keys file to only allow SCP access to the remote user’s /home/ folder.

ssh-keygen -t rsa -b 2048 -C "some text you can identify"

It would be a good idea to bury this key somewhere in the filesystem, but make note of the path.

Next, setup a user account on the remote server (useradd -m -s /bin/bash someusername). Create the user’s .ssh folder in it’s home account and create an authorized_keys file (a shortcut for this is to run: su – someusername , then run ssh localhost). Do not give the new user account a password (although you shouldn’t be allowing users to login with passwords from the get go).

Add the contents of the SSH .pub file to the authorized_keys file, save it then chmod 700 the file. Run a test to ensure the Mac can ssh to the server (ssh someusername@someserverip -i /path/to/ssh/private/key).

We will need to restrict what this user can do once SSH’d to the remote server. A quick way to accomplish this, is to edit the authorized_keys, preceding it with a “command=”. In our case, we will be using a basic Perl script (scp-wrapper) to ensure that this user only be allowed to copy files. Download the file here and copy this to the remote server as /usr/bin/scp-wrapper (make sure to chmod +x the file).

Edit the user’s authorized_keys file, so the beginning of the file looks like this:
command=”/usr/bin/scp-wrapper” ssh-rsa <rest of key bits>
Test that the user’s functionality is limited to scp only:
ssh someusername@someserverip -p someport -i /ssh/private/key “uptime”
Should return the following:

/usr/bin/scp-wrapper: account restricted: only scp allowed (“uptime”)

Now test scp functionality:
scp -P someport -i /ssh/private/key test.txt someusername@someserverip:
test.txt 100% 0 0.0KB/s 00:00

This is not the only method available for restricting commands for remote user accounts, so once again feel free to be creative.

Step #5: Create Script to Perform Status Checks

Next, we will create a simple bash script to perform the status checks. The below script is rather simple in form, but performs the basic functions needed for operation (let your creativity improve on this if desired).

Make this script executable (chmod +x) and note the location.

Step #6: Test Run

It’s now time to test our script. Run the file from the terminal, and ensure that it has scp’d a copy of a fresh iSight snapshot to the remote server (since our status page is set to “123″ indicating a status other than not stolen).

Congratulations, you’re almost done.

Step #7: Create a Launch Daemon

We’ll now create a launch daemon to load and run our script. Become root in a terminal and create the following file: /Library/LaunchDaemons/com.myapp.SomeName.plist.

Grab the template file here

I’ve set the integer value to 300 (seconds), edit to your taste (don’t forget to set the script path in the file). Be sure to set remote status page content to “0″, then load the plist with: launchctl load -w /Library/LaunchDaemons/com.myapp.SomeName.plist and you’re in business ;-)

Step #7: Dummy Account & Conclusion

Setup a new “dummy” user account on your Mac, require no login. If you are unlucky enough to have your Mac swiped, the perpetrator will hopefully be dumb enough to fall for the non-password protected user account and the script will do it’s job thereafter (so long as they connect to the internet).

You can check your Nginx server logs for the IP they have connencted from, and provide the information to law enforcement along with any pictures that were successfully uploaded to the server. With any luck, karma will be on your side and you will recover your machine.

Once again, creativity is at your disposal here. One could setup a custom Twitter page and change the status to something indicating the machine is stolen (if you do not prefer to have to SSH in to change the status page of the static HTML on the server).

You could even setup a custom email account on the server and use a procmail recipe to modify the status code on the file if certain regex expressions are met in the Subject and/or body of the email sent to the server. Anything is possible.

Hiddenapp should change their name to Hiddencrap.

This is a guide to installing Snorby running on an Ubuntu Server machine, for integration with a Snort instance on pfSense. This howto should also work on Debian and other Debian-based distributions, however I HIGHLY recommend NOT using Debian itself in any production environment, due to the distributions lack of compile time security options in its packages (blog about this to come).

Furthermore, this how-to guide should also work just fine (with minor tweaks) for installing Snorby on a seperate machine to integration with a standalone Snort instance.

I highly recommend purchasing the premium rules subscription from Sourcefire, which carries an annual cost of $29.95 so that your system has the most current rules.

Let’s get started

Software Installation and MySQL Setup

pfSense Configuration

For the sake of brevity, I will not cover the installation and initial interface configuration of Snort on pfSense. Please refer to the Setup Snort Package from the pfSenseDocs to do this.

  1. From the pfSense web GUI, navigate to the Snort service menu: Services » Snort
  2. Edit the interface (repeat for each interface you wish to use with Snorby) and navigate to the Barnyard2 tab
  3. Check the Enable Barnyard2 on this Interface checkbox
  4. In the Log to a Mysql Database dialog, use the folowing format (change the values appropriately):
  5. In the If Settings tab, check the box for Log Alerts to a snort unified2 file.
  6. Save the configuration and restart the Snort service on pfSense.

Snorby Web UI

Navigate the the following URL to access the Snorby web UI: