Fast and safe with the Hiawatha secure web server

Safe Passage


Many webmasters believe Apache is too fat and difficult. Hiawatha is a web server alternative with speed, simplicity, and some interesting security functions.

By Tim Schürmann

Pavel Cheiko, 123RF

Hugo Leisink was frustrated. Although he had already tried out various web servers, none of them were really convincing. In his opinion, the configuration tools were cryptic and the security features were limited. Leisink's concerns about the state of the web server craft prompted him to develop his own web server in January 2002.

The end of the story is Hiawatha, a light web server with good performance and some innovative security functions. Hiawatha's small size of just 600KB makes it perfect for deployment on embedded devices or less powerful machines.

Installation

Hiawatha is easy to install: Just download the current package from the homepage [1], unpack it, and run the typical set of three commands:

./configure
make
sudo make install

Hiawatha's minimum requirements are a C compiler and the libc6 (alias glibc2) library (typically libc6-dev, or glibc-devel). If any other dependencies are missing, the configure script will not warn you, but will, instead, simply disable the corresponding functionality.

Thus, it is a good idea to have a quick look at the log created by the script. If you don't have the OpenSSL library (libssl), you will have to do without encryption, and thus https connections. XSLT (Extensible Stylesheet Language Transformations) support is also optional; you need the --enable-xslt configure parameter and the libxslt library to tell the web server to use XSLT.

One for All

Hiawatha parses all of its settings from a single, small configuration file called httpd.conf, which resides in the /usr/local/etc/hiawatha directory by default. httpd.conf has some useful defaults and a number of commented lines you can use as templates, but you should give it a quick check to make sure the settings are correct before you run the web server for the first time.

Additionally, you can simply create a new configuration file on the fly - a feat that would make Apache stand back in amazement. Before you save a new configuration file, make sure you rename the original httpd.conf so you will have a working configuration to fall back on if you have problems.

Connection-Shy

The httpd.conf file starts by telling the web server which port on which network interface to listen on for incoming requests. The port is handled by the binding:

Binding {
      Port = 80
      Interface = 192.169.2.123
}

This command tells Hiawatha to listen on the network interface with the IP address of 192.169.2.123 for incoming requests using the default port for web servers (port 80). As the example shows, the structure of the configuration file is really simple: The web server expects exactly one setting in each line, and each setting comprises a name, an equals sign, and the matching value.

To bind Hiawatha to another interface, simply add a second Binding section, as follows:

Binding {
      Port = 443
      Interface = 192.168.2.124
}

If you want the web server to listen for requests on all your network interfaces, a single Binding section without an Interface specification will suffice.

Service Warehouse

The next thing Hiawatha needs to know is which website to serve up. To define the website, you first specify the subdirectory with the files. Apache users will be familiar with this directory as DocumentRoot, but Hiawatha calls it WebsiteRoot:

WebsiteRoot = /usr/local/var/www/hiawatha

In principle, you can select any folder on your disk; you will find a sample index.html file below /usr/local/var/www/hiawatha. Hiawatha needs the name (or alternatively the IP address) on which the website will be accessible:

Hostname = www.mywebsite.com

This detail is particularly important if you are using virtual hosts (see the box titled "Virtual Hosts" for more details). If you use IPv6 addresses, just use them; you can even mix them with IPv4 addresses.

That's it. This minimal configuration contains just six lines. Listing 1 is a complete httpd.conf file. Comments start with a pound sign, as in shell scripts.

Listing 1: Minimal Configuration for Hiawatha
01 # Listen for requests on port 80 of the network interface with IP address IP 192.168.2.123:
02 Binding {
03         Port = 80
04         Interface = 192.168.2.123
05 }
06
07 # Serve up the following page:
08 WebsiteRoot = /usr/local/var/www/hiawatha
09 Hostname = localhost

Kick Starter

Just to see whether the configuration is working, launch the web server with the following command:

sudo hiawatha

If you receive an error message:

Error binding IP_address

either Hiawatha does not have sufficient privileges (for ports below 1024) or a competing web server is already listening on the same port. A competing web server is actually a common occurrence if you are using a distribution that comes with a pre-configured web server, such as Apache. In that case, you can either change the port or stop the competitor. To stop Apache in Ubuntu, for example, you could enter the command

sudo /etc/init.d/apache2 stop

although you might still see an error message:

Warning: can't write PID file /usr/local/var/run/hiawatha.pid

The preceding message says that Hiawatha does not have access privileges for the /usr/local/var/run directory; one common cause for this message is that the directory just doesn't exist. You can either create the directory manually or live with the error message for the time being. The web server simply stores its process ID in the specified PID file. Alternatively, you can use httpd.conf to point to a different file location:

PIDFile=<filename>

Launching a browser and pointing to localhost will tell you if Hiawatha is running properly. You should see the test page shown in Figure 1.

Figure 1: The Hiawatha test page looks remarkably like the Hiawatha homepage.

A kill signal, as in

<C>sudo killall hiawatha<C>

will stop Hiawatha. Of course, this is a fairly brutal approach, and it is not very elegant if you are thinking of adding the web server startup to your distribution's boot process. The hiawatha script included in the extras subdirectory below the source code directory gives you a more elegant approach to launching and stopping Hiawatha, but you should be aware of a couple of obstacles. First, the script assumes that the web server was able to create the PID file. Also, the script is designed for Debian. If you want to run the script on any other distribution, you need to modify the paths in the first four lines to match your local conditions.

Going Walkabout

As you would expect of a modern web server in these Web 2.0 times, Hiawatha supports the execution of CGI programs. Of course, the administrator first has to explicitly enable this feature

ExecuteCGI = yes

and then specify which file suffix the CGI programs will have:

CGIextension = cgi

In the case of scripts, such as PHP or Python applications, Hiawatha also needs the name and path for the interpreter:

CGIhandler = /usr/bin/php5-cgi:php,php5

When Hiawatha finds a file with a .php or .php5 suffix, it will launch the php5-cgi interpreter in the /usr/bin folder and pass the script in to it. The same approach is used for other script languages:

CGIhandler = /usr/bin/perl:pl
CGIhandler = /usr/bin/python:py

To prevent buggy or malicious CGI programs or scripts from crashing or hijacking the computer, you should restrict their run time. A value of five seconds should do for a start:

TimeForCGI = 5

Security Wing

The settings thus far have produced a basic web server with very little fancy stuff. You still haven't seen much of Hiawatha's advanced security features. Before touring these additional options, start by telling Hiawatha to talk to the logfiles:

SystemLogfile = /usr/local/var/log/hiawatha/system.log
AccessLogfile = /usr/local//var/log/hiawatha/access.log
ErrorLogfile = /usr/local//var/log/hiawatha/error.log
GarbageLogfile = /usr/local//var/log/hiawatha/garbage.log

The SystemLogfile holds the messages from the daemon; access is logged in AccessLogfile, and ErrorLogfile logs errors. The last line collects the garbage - that is, erroneous or incomplete HTTP requests. Incomplete and erroneous requests are often an indication of a break-in attempt.

Thus far, the web server has only listened on port 80. To bind to this port, Hiawatha needs root privileges. However, it is not a good idea to leave the program running as root. To avoid this problem, Hiawatha changes the user to nobody immediately after launching. The ServerId setting tells the web server to change to another user:

ServerId = www-data

Restricting Resources

The next step is to mitigate the danger of denial-of-service attacks (DoS) by reducing the number of simultaneous connections that Hiawatha can handle - both overall and per IP address:

ConnectionsTotal = 150
ConnectionsPerIP = 10

While you're at it, it is also a good idea to limit the internal cache size to, say, 15MB:

CacheSize = 15

Also, you can tell Hiawatha to restrict the cache to files ranging in size from CacheMaxFilesize (in kilobytes) to CacheMinFilesize (in bytes):

CacheMaxFilesize = 128
CacheMinFilesize = 256

Netiquette

In the opinion of Hiawatha developer Hugo Leisink, clients should behave well if they want the web server to answer them. To punish clients that send malformed or overlong HTTP requests, Hiawatha resorts to the medieval method of banning. For example, with the command

BanOnGarbage = 300

Hiawatha will refuse to respond to a client for 300 seconds if it sends a non-standards-compliant HTTP request, and

BanOnMaxReqSize = 60

bans it for 60 seconds in the case of a request with an illegal length. You can combat flooding with the following command:

BanOnFlooding = 10/1:35

This command bans a client for 35 seconds if it has sent more than 10 request to Hiawatha within a second. The following is also useful:

BanOnCMDi = 60

This setting bans a client for 60 seconds if it has attempted a command injection attack. The following does the same for SQL injection attacks:

BanOnSQLi = 70

Hiawatha also supports blacklists or whitelists:

BanlistMask = allow 192.168.2.111, deny 192.168.0.0/16

If a banned client is really naughty and attempts to access Hiawatha again while exiled, the following command automatically resets the ban:

RebanDuringBan = yes

Keys to the Realm

For confidential data, you should always use a secure SSL connection. Before Hiawatha can speak https, you need an X509 SSL certificate, which you can either buy from a trusted third party (i.e., a Certificate Authority) or create yourself with the use of OpenSSL [2]. The commands to accomplish this are shown in Listing 2.

Listing 2: Creating an X509 SSL Certificate
01 openssl genrsa -out serverkey.pem 2048
02 openssl req -new -x509 -days 3650 -key serverkey.pem -out server.crt
03 echo "" >> serverkey.pem
04 cat server.crt >> serverkey.pem
05 echo "" >> serverkey.pem
06 rm -f server.crt

The result of this is a serverkey.pem file, which you need to store along with httpd.conf (typically in /usr/local/etc/hiawatha).

Https connections are typically directed at port 443; the web server thus needs to listen on this port and grab the certificate when it receives a request:

Binding {
   Port = 443
   Interface = 192.168.2.123
   UseSSL = yes
   ServerKey = /usr/local/etc/hiawatha/serverkey.pem
}

The preceding entry means that browsers can only use the https protocol to set up a connection to Hiawatha by way of port 443; in this example, the result would be https://192.168.2.123:443. Additionally, the certificate is only valid for the specified port and interface combination. To use it for all secure connections, put the last line behind the closing bracket.

Figure 2: With support from a PHP interpreter, major portal packages like Joomla will run on Hiawatha - even faster than on Apache.

Mutual Liability

CGI programs are popular targets for attackers, possibly because they are buggy, or because the developer has been lazy with respect to security. To prevent CGI programs from running amok and even taking the web server down, Hiawatha can use a CGI wrapper to confine them. The wrapper will give CGI programs a different user ID if needed.

To set up the jail, you first need to select a directory as your CGI root; the CGI root directory contains all CGI programs and scripts. To match Listing 1, the directory could be /usr/local/var/www/hiawatha/cgi. The wrapper will only run CGI programs and scripts from this directory.

Next you need another configuration file called cgi-wrapper.conf. This file resides in the same directory as httpd.conf - that is, /usr/local/etc/hiawatha. The Hiawatha archive includes a sample of cgi-wrapper.conf with all the lines commented out.

The cgi-wrapper.conf first tells the wrapper what programs it is allowed to run outside the CGI root directory. The list of permissible programs might include interpreters for the script languages you use:

CGIhandler = /usr/bin/php5-cgi
CGIhandler = /usr/bin/perl

Note that CGIhandler is not a good choice of name because this setting has very little in common with the identically named setting in httpd.conf. Once you have configured the wrapper, you can lock away your CGI programs:

Wrap = wrap_id; /usr/local/var/www/hiawatha/cgi; tim

The name of the jail is at the start of the line. Because you will need to add this name to your httpd.conf file, you need to keep it in mind. Following the first semicolon, you have the name of the CGI root directory and then finally the username (or ID) under which CGI programs will be running. If the directory name contains a pipe symbol, as in the following line,

Wrap = wrap_id; /usr/local/var/www/hiawatha|cgi; tim

the CGI wrapper will use the part before the pipe as a chroot directory and put everything else into this environment. In this case, you will need to make sure the CGI handlers are available in the chroot directory.

The CGI wrapper is now ready to rumble, and you just need to tell Hiawatha to use it by adding the following to the httpd.conf file:

WrapCGI = wrap_id

You can define multiple wrappers in cgi-wrapper.conf and then use them for different virtual hosts (see the "Virtual Hosts" box).

Virtual Hosts

Like many other web servers, Hiawatha can serve up multiple, independent Internet sites. To allow this to happen, multiple (domain) names are first assigned to a physical server; this means that all browser requests are automatically directed to the same web server. The server analyzes the URL to determine the Internet site. The client sees multiple, additional virtual machine hosts. ISPs, in particular like this technique because it lets them serve multiple, smaller web presences with a single expensive IP address.

To add virtual hosts to Hiawatha, you need to set up a separate section for each host in httpd.conf, as follows :

VirtualHost {
    WebsiteRoot = /var/www/anothersite/wwwroot
   Hostname =    www.myvirtualsite.com
        ...
}

Inside the brackets, you can use more or less any settings you used for the page itself (the default website in Hiawatha-speak). There are even a couple of functions that you can use only with virtual hosts, including four interesting Prevent security mechanisms. For example:

PreventCMDI = yes

stops command injection attacks by telling Hiawatha to convert backslashes, pipe symbols, and semicolons in the URL and POST data with underscores. Because this fairly rigorous approach also mangles uploaded binaries, it is disabled by default.

The following line prevents cross-site request forgery (CSRF):

PreventCSRF = yes

The virtual host will then ignore any cookies sent to it by the browser if it reaches Hiawatha via an external link. The line

PreventSQLi = yes

combats SQL injection attacks by inserting a slash in front of each tick (`) in the URL, the POST data, and cookies. This feature works like Magic Quotes in PHP; also, you should not enable PreventSQLi if you use PHP scripts. Just like its sibling PreventCSRF, this function could mangle uploaded binaries.

The last security function,

PreventXSS = yes

is designed to prevent cross-site scripting (XSS) attacks by replacing all the less than, greater than, quotes, and ticks in the URL with underscores.

Typography

A final treat is UrlToolkit, which works similarly to the Apache server's mod_rewrite. Each URL the web server reads is compared with predefined patterns. In the case of a match, Hiawatha will perform a predefined action. Any regular expression can serve as a test pattern [2]. Listing 3 gives a small example.

Listing 3: Two Rule Sets with UrlToolkit
01 UrlToolkit {
02     ToolkitID = varioustests
03     Match ^/php/ Return
04     Match /index.php4(.*) Rewrite /index.php$1
05 }
06
07 UrlToolkit {
08     ToolkitID = secret
09     Call varioustests
10     Match /secret(.*) DenyAccess
11 }

Listing 3 looks far more cryptic than it actually is. The listing defines two rule sets. The ToolkitID for the upper rule set is varioustests, whereas I called the second one secret. The varioustests rule set first checks to see whether the URL starts with /php/. If this is the case, Hiawatha stops all further tests with this rule set (Return). If not, it checks to see whether the URL starts with index.php4. In this case, Hiawatha replaces the string with /index.php; that is, it drops the 4 in the file name.

The second rule set, secret, starts by calling its colleague varioustests (Call) and then refuses access to the /secret subdirectory. Table 1 gives an overview of possible actions.

After specifying rules in httpd.conf, you only need to tell the web server which rule set to use:

UseToolkit = secret

Listing 4 shows the complete Hiawatha configuration.

Listing 4: Complete Configuration (httpd.conf)
01 #Basic configuration
02 Binding {
03         Port = 80
04         Interface = 192.168.2.123
05 }
06
07 Binding {
08         Port = 443
09         Interface = 129.168.2.123
10         UseSSL = yes
11         ServerKey = /usr/local/etc/hiawatha/serverkey.pem
12 }
13
14 WebsiteRoot = /usr/local/var/www/hiawatha
15 Hostname = localhost
16
17 #Logfiles
18 SystemLogfile = /usr/local/var/log/hiawatha/system.log
19 AccessLogfile = /usr/local/var/log/hiawatha/access.log
20 ErrorLogfile = /usr/local/var/log/hiawatha/error.log
21 GarbageLogfile = /usr/local/var/log/hiawatha/system.log
22
23 #Cache
24 CacheSize = 15
25 CacheMaxFilesize = 128
26 CacheMinFilesize = 256
27
28 #CGI
29 ExecuteCGI = yes
30 CGIextension = cgi
31 CGIhandler = /usr/bin/php5-cgi:php,php5
32 TimeForCGI = 5
33 #Nutze Wrapper:
34 WrapCGI = wrap_id
35
36 #Security functions
37 ServerId = www-data
38 ConnectionsTotal = 150
39 ConnectionsPerIP = 10
40 BanOnGarbage = 300
41 BanOnMaxReqSize = 60
42 BanOnFlooding = 10/1:35
43 BanOnCMDi = 60
44 BanOnSQLi = 70
45 BanlistMask = allow 192.168.2.111, deny 192.168.0.0/16
46 RebanDuringBan = yes
47
48 #UrlToolkit
49 UrlToolkit {
50         ToolkitID = varioustests
51         Match ^/php/ Return
52         Match /index.php4(.*) Rewrite /index.php$1
53 }
54
55 UrlToolkit {
56         ToolkitID = secret
57         Call varioustests
58         Match /secret(.*) DenyAccess
59 }
60
61 UseToolkit = secret

Conclusions

Besides the security functions described in this article, Hiawatha has a number of other clever capabilities. For example, the web server has a more intelligent approach to Gzip compression than its colleagues, and it gives you the option of an internal error handler. If the client requests an XML file and the matching XSLT file exists, Hiawatha will automatically perform XSL transformation if needed. If the execution speed of CGI scripts is too slow, you can enable the FastCGI mechanism. Hiawatha also has a good understanding of access privileges for directories. You can even throttle the upload speed for specified file types.

If you would like to know more about Hiawatha, check the slightly terse HowTo [2]. The HowTo even discusses the option of combining the web server with AppArmor and Grsecurity [3].

INFO
[1] Hiawatha web server : http://www.hiawatha-Webserver.org
[2] Hiawatha HowTo: http://www.hiawatha-Webserver.org/howto
[3] Grsecurity: http://www.grsecurity.net
THE AUTHOR

Tim Schürmann is a computer scientist and is currently working as a freelance author. Tim starting working with Linux back in the 1990s and has published several books and articles in various languages on the free operating system. Apart from that, he is absolutely convinced that VI must have been invented by the devil himself. For more information, visit Tim's homepage at www.tim-schuermann.de.