|As more and more attacks are being carried out over the HTTP layer there is a growing need to push the envelope and bring Web security to new levels. Most existing tools work on the TCP/IP level, failing to use the specifics of the HTTP protocol in their operation. The need for increased security has lead to the creation of application gateways, tools that are essentially reverse proxies with the added capability of protocol analysis. Many commercial solutions are available. This article will demonstrate how you can build your own application gateway with little effort, using open source components that are widely available.
The case for the reverse proxy
Our task is to protect one or more Web servers residing on an internal network, providing services to outside clients. Internal clients, such as employees are also considered to be outside clients for the purpose of this article. We are working under the assumption that you have at least two or more Web servers, a database server, and possibly other internal servers. The more servers there are, the more useful the reverse proxy concept becomes.
A proxy, by definition, is a device that stands between two entities participating in a conversation. What is normally called a proxy in everyday life is better described as a forward proxy: a device that stands between a client and all other servers. A reverse proxy does exactly the opposite: it stands between a server and all its clients. In a wider sense, one reverse proxy will be used for all internal Web servers.
The main benefit of using a reverse proxy is one of centralization. Once we get all traffic to go through a single point we can apply other tools from our arsenal to our benefit. We will quickly consider here the advantages and disadvantages of using a reverse proxy:
- Single point of access. With a single point of access you can enforce access control for all Web servers with little effort. You will most likely want to implement access control based on infrastructure parameters such as IP address here. As an additional benefit, you will also have centralized request logging in place making activity monitoring much simpler.
- HTTP level firewalling. Even without further effort a proxy will help because it usually does not pass the initial request to the Web server, but instead creates a new request based on it. But, as you will see shortly, having an application gateway software here will enable you to perform a wider set of checks, monitor traffic and react to attacks in real time.
- Increased performance. Because the reverse proxy is installed on a separate machine this means you have additional CPU resources to use. Caching can be implemented for both static and dynamic content. SSL traffic can be terminated at the proxy, freeing the actual Web server to focus on responding to incoming requests. Finally, the outgoing traffic can be transparently compressed, lowering total bandwidth requirements.
- Network isolation. In this case the reverse proxy introduces another firewall layer. Instead of having multiple Web servers and operating systems exposed directly, you hide them all behind a single proxy.
- Network topology hidden from the outside world. This is good for at least two reasons. First, it gives less information to the attackers. Second, it decouples the implementation of the network from its interface. Changes can be made to the network as required without the public being aware of them.
- Increased complexity. This is how you pay for the increased security, by increasing the complexity of the network.
- Single point of failure. For mission critical operations, having a single point of failure is unacceptable. This problem can be solved by having two reverse proxies in a cluster, but the effect of that is even more complexity added to the network.
ModSecurity is an Apache module that adds intrusion detection and prevention features to the Web server. In principle it is similar to an IDS you would use to analyse your network traffic, except that it works on the HTTP level and understands it really well. Because of this it allows you to do things that are normal from the HTTP point of view but are difficult to do from an classical IDS. This difference will become clearer later when we examine several examples of what mod_security can do.
In addition to detection, mod_security also supports attack prevention. Because it stands between the client and the server, if it finds that the request contains a malicious payload it can reject the request, performing any of a number of built-in actions.
As it is a module like any other, you can use mod_security as part of any Apache installation. The overhead that comes from additional processing can be negligible when it is configured properly. However, if you take into account that an incident is much more costly and that mod_security can also protect you from attacks the overhead becomes insignificant.
So, how is mod_security helpful? We can't go into exhaustive detail on all the capabilities, but here is a short overview of what is happening on every request (for more details go to the mod_security Website and download the reference manual):
- Parse the request. This is mostly straightforward, except when you have to deal with a POST request where getting the body of the request can be difficult.
- Perform canonization and anti-evasion actions. A series of transformations is performed to transform the input into a form that is suitable for analysis. This step will fight against various evasive techniques attackers may use: null byte attacks, self-referencing directories, multiple slash characters, using backslash characters on Windows, etc.
- Perform special built-in checks. This step contains more complicated validations such URL encoding validation and Unicode encoding validation. You can also choose to allow only certain byte values in the request to fight shellcode.
- Execute input rules. This is where your custom rules come into action. They work by allowing you to analyse every aspect of a request using regular expressions. In addition to this, several rules can be combined for more complex analysis.
The request is then allowed to reach the handler where it executes. After the request:
- Execute output rules. Output rules are applied to the response body. They are very useful to prevent information leaks.
- Log the request. Log the complete request consisting of input and output headers, and the request body. To prevent excessive logging, mod_security can be configured to log only what's relevant, such as requests that have triggered a response from mod_security.
Installation and configuration
Installation is surprisingly simple. We will assume that the server you want to protect is using the private address 192.168.254.10, and that you have configured the public domain name (www.modsecurity.org) to point to the reverse proxy server.
On the reverse proxy you need to install the Apache 2 Web server, making sure you compile in mod_proxy, mod_proxy_http, and mod_security. We will not spend time on this step assuming you are already familiar with it. If not, have a look at the related links section where you will find links to a couple of very good articles covering the Apache installation process. It would also be a good idea to add mod_rewrite to the module mix since it can work in tandem in mod_proxy significantly increasing what you can do.
Although any Web server can become a reverse proxy simply by adding the modules I mentioned above it is not recommended to mix the two roles together. The reverse proxy will be the only server exposed to the public and you will want to minimize the amount of code it contains, to minimize the risk of exploitable vulnerabilities.
Apache 2.x is a better choice for a reverse proxy because it contains the new filtering API, allowing modules to see and interact with the request body as it comes in and with the response as it comes out. This is important for an application gateway since it must check the information that passes through before it reaches the recipient.
Once you have the proxy installed here is how you will configure a virtual host:
<VirtualHost www.modsecurity.org> # Just the bare minimum of directives ServerName www.modsecurity.org DocumentRoot /rproxy/nowhere # While the following line is not strictly necessary it's here to emphasize # the fact that the reverse proxy does not use this directive. In fact, turning # it On without proper access control creates an open proxy! ProxyRequests Off ProxyPass / http://192.168.254.10/ ProxyPassReverse / http://192.168.254.10/ # Yes, we want to use mod_security SecFilterEngine On # Scan request body SecFilterScanPOST On # Scan response body SecFilterScanOutput On # Check URL encoding SecFilterCheckURLEncoding On # This setting should be set to On only if the Web site is # using the Unicode encoding. Otherwise it may interfere with # the normal Web site operation. SecFilterCheckUnicodeEncoding Off # Only allow certain byte values to be a part of the request. # This is pretty relaxed, most applications where only English # is used will happily work with a range 32 - 126. SecFilterForceByteRange 1 255 # Audit log logs complete requests. Configured as below it # will only log invalid requests for further analysis. SecAuditEngine RelevantOnly SecAuditLog logs/audit_log # You may need this later but we don't log anything # here for now. Excessive debug logging may slow down # the server. SecFilterDebugLevel 0 SecFilterDebugLog logs/modsec_debug_log # By default, deny requests with status 500 SecFilterDefaultAction "deny,log,status:500" # Put your mod_security rules here # ... </VirtualHost>
In this section I will demonstrate what the typical mod_security rule set looks like. You should always start with rules that have a broad scope, leading to more specific issues. All mod_security rules (and configuration options) can be applied on the per-virtual host or per-directory basis so you can have areas with completely different security configurations.
Detecting common attacks
These rules will target the common Web application attacks:
# Command execution attacks SecFilter /etc/password SecFilter /bin/ls # Directory traversal attacks SecFilter "\.\./" # XSS attacks SecFilter "<(.|\n)+>" SecFilter "<[[:space:]]*script" # SQL injection attacks SecFilter "delete[[:space:]]+from" SecFilter "insert[[:space:]]+into" SecFilter "select.+from" # MS SQL specific SQL injection attacks SecFilter xp_enumdsn SecFilter xp_filelist SecFilter xp_availablemedia SecFilter xp_cmdshell SecFilter xp_regread SecFilter xp_regwrite SecFilter xp_regdeletekey
Protecting a vulnerable script
Some PHP applications are vulnerable when a register_globals configuration option is turned on, allowing attackers to set an internal variable to a value of their choice. This usually leads to attacker executing some code on the server. Here is an example from the real world (Cafelog b2, http://www.securityfocus.com/bid/7786):
|SecFilterSelective ARG_b2inc "!^$"
Protecting from XSS attacks through the PHP session cookie
PHP versions prior to 4.3.2 are vulnerable to XSS attacks carried out through the session identifier (http://www.securityfocus.com/bid/7761). If you can't upgrade your PHP version to the latest version you can still protect yourself:
SecFilterSelective ARG_PHPSESSID "!^[0-9a-z]*$" SecFilterSelective COOKIE_PHPSESSID "!^[0-9a-z]*$"
Stop FormMail from being used to send spam
Some versions of FormMail can be used to send email to arbitrary email addresses. The following rule demonstrates how you can have a filter applied only to certain locations, in this case just the FormMail script. The request will be rejected if the email is intended to any address except the one ending in "@modsecurity.org":
<Location /cgi-bin/FormMail> SecFilterSelective "ARG_recipient" "!@modsecurity\.org$" </Location>
Restrict administrative login to an IP address
Here is a nice one. I have this application where the administrator logs in through the same log in panel as other users, but I still wanted to restrict administration login to certain IP addresses. So I used two chained rules. The second rule will apply only if the first rule matches; in this case - if the incoming username is "admin".
SecFilterSelective ARG_username admin chain SecFilterSelective REMOTE_ADDR "!^ADMIN_IP_ADDRESS_HERE$"
Preventing information leak
In all versions of PHP, if a fatal error occurs the script will be terminated immediately (standard error handling routine will not be invoked). Information leak through these problems can be prevented by scanning the output and preventing it from reaching the user if it contains error messages.
SecFilterSelective OUTPUT "Fatal error:"
Output filtering can also be used to detect successful intrusions. These rules will monitor output and detect typical keywords resulting from a command execution on the server.
SecFilterSelective OUTPUT "Volume Serial Number" SecFilterSelective OUTPUT "Command completed" SecFilterSelective OUTPUT "Bad command or filename" SecFilterSelective OUTPUT "file(s) copied" SecFilterSelective OUTPUT "Index of /cgi-bin/" SecFilterSelective OUTPUT ".*uid\=\("
What other rules you may find useful depends on the types of applications and Web servers you have behind the reverse proxy. On the mod_security Website you can find a large number of rules automatically converted from Snort rules. Download the list and simply remove the rules that do not apply.
This article has just scratched the surface of a complex issue. I suggest that you browse through the related links section, as it contains a list of Websites, tools, and papers that you can use to familiarize yourself with other aspects we did not cover here, such as reverse proxy load balancing and clustering or transparent reverse proxy configuration. Going the other way, download the mod_security reference manual and get to know its features. It also contains other features not mentioned here. Finally, contact me to request new mod_security features if you have a need that is not covered by what already exists.