Disclaimer: This is a long article. I haven’t collected some nice configuration settings here for the sake of it. There are other hardening guides but some fall short on explaining the functionalities to be enabled or disabled. Every step is shortly, and hopefully clearly, explained so any reader can grasp the main idea of every setting. Following the recommendations in here will solely be under your responsibility. I decline any sort of damage or liability on using the here exposed ideas and/or specific settings.
Apache is the most popular web server software, although in recent years alternatives such as Nginx have appeared. Many companies, organizations and individuals have found Nginx a good, reliable and fast alternative so the market share has diminished for Apache. That said, Apache has many features developed throughout the years that Nginx lacks, which are very useful not only for small projects but for large corporations. You can also combine the two pieces, since Nginx can work as a reverse proxy or a load balancer, while Apache does its best by serving content.
I still remember just a few years back, before getting involved into content creation (which drove me into being an IT professional), asking to a self-employed developer how to segure a web server. Fortunately his answer was utter rubbish which lead me into investigating on my own. I have collected a few pieces of information, through time, some may find relevant for their use case involving Apache. This said this is not the definitive guide on how to harden Apache, but I hope this article helps into that goal.
If you find the articles in Adminbyaccident.com useful to you, please consider making a donation.
Use this link to get $200 credit at DigitalOcean and support Adminbyaccident.com costs.
Get $100 credit for free at Vultr using this link and support Adminbyaccident.com costs.
Mind Vultr supports FreeBSD on their VPS offer.
Before digging into the relevant stuff let me address one important thing. Security is a collection of ideas, attitudes, tools, a combo that can put anyone in a safer condition when doing some activity. I have been a ski instructor, indeed an ISIA fully certified one, and I can talk a bit about security in alpine skiing. One common principle which perfectly translates into the IT field is: one hundred per cent security does not exist. What one can do is mitigate the risks, minimize them, but it is very very difficult to make them completely disappear.
This means, you have to have a security oriented attitude. You do not only have to secure your Apache web server, but the workstation you connect to it too. Your authentication methods have to be secure, for example using ssh keys or SSL certificates. You have to have a firewall protecting your network connections. I could write about some other topics that would complement the list but you get the idea. Your security, or the security of your network, server, organization, is as safe as the weakest link in the chain. You can have a heavily fortified Apache web server, but if you are not using a firewall on that server and ports are open to the wild, and/or your authentication method to the server is just using easy to guess passwords that you happen to have in a plain text file on your cracked and already hacked by someone else Windows XP desktop, your Apache’s web server security is almost null.
Many readers coming to this post already know this concepts but I am afraid remembering them is always good and for those beginning to tinker with this kind of software it’s always good to have some guidance.
One more comment before getting into this how to harden Apache HTTP web server guide. What can be found here is a collection of good practices and recommendations from several known web sites, some exclusively dedicated to security, some others just like to proliferate knowledge and some others are just projects that build the world wide web as we all know it.
Stay current.
This means updating. It is still an issue security wise in many corporations, small, big or worlwide. In the future this may be not only regarded as a mandatory practice but one which may carry a penalty for those not doing it. Updates do not only bring features and bug corrections but they also include security patches. Not applying them is not only a bad practice, it is also a sign of not caring much about your organization, your customers, and the public in general since hijacked servers can cause damage to innocent third parties. For God’s sake, do update or face perennial guilt and shame.
Open ports.
Apache defaults to listen to port 80. This is the standard port for http connections. So make sure this port is open on your server’s firewall and it is opened in the network you are working in, otherwise you won’t get connections in nor out. But why is this a security matter? Well, in many companies there are Apache boxes serving content inwards through port 8080, or 8088, or any other port the administrators have chosen. The security tip here is make sure the ports your Apache instance is listening to and which ones should be closed and which should be open.
If you are just using Apache to serve a small web page of yours, leave port 80 open and that’s it. However if you are in a corporate environment and the content that Apache hosts shouldn’t be exposed into the internet at all, make sure it is not serving it. You can choose several strategies here. From leaving port 80 open and access is granted only to certaing workstations using ACL’s (Access Control List), to changing the port to 8080 (or any other you like) and having that specific port closed at the switch or firewall level, or the combination of ACL’s and changint the port number to one that is closed from external access. It all depends on your needs and your network setup.
Remove the OS type and version banner.
There are some tools you can use to know what kind of operating system a particular web is using and what version of Apache is being used. For example you can scan your network with nmap to discover hosts and services. The same you can do it, someone else can, such as another individual in your organization (trusted or untrusted), or an attacker from the outside world. Here is real example:
$ nmap -sV -p 1-65535 ip-address
Starting Nmap 7.70 ( https://nmap.org ) at 2019-03-09 14:04 CET
Nmap scan report for (ip-address)
Host is up (0.059s latency).
Not shown: 65533 filtered ports
PORT STATE SERVICE VERSION
22/tcp open ssh OpenSSH 7.5 (FreeBSD 20170903; protocol 2.0)
443/tcp open ssl/http Apache httpd 2.4.38 ((FreeBSD) OpenSSL/1.0.2o-freebsd PHP/7.3.1)
Service Info: OS: FreeBSD; CPE: cpe:/o:freebsd:freebsd
Service detection performed. Please report any incorrect results at https://nmap.org/submit/ .
Nmap done: 1 IP address (1 host up) scanned in 2768.06 seconds
$
An nmap scan is launched looking for services, where all the avaiable ports are scanned for an specific ip (you can also use a domain here). The scan results reveal that specific host has two services running publicly available. On port 22 we find the OpenSSH service working on a FreeBSD box. On port 443 we discover an Apache instance working with SSL enabled. Furthermore we find out PHP version 7.3.1 is also working on that server. Again the operating system type is displayed as FreeBSD.
Another method of discovering what software you are running is by looking at the headers response by using telnet and using port 80 such as follows:
$ telnet ipaddres/domainname 80
Trying ipaddress...
Connected to servername.
Escape character is '^]'.
HEAD / HTTP/1.1
HOST: [SERVER]
HTTP/1.1 400 Bad Request
Date: Sat, 09 Mar 2019 20:13:23 GMT
Server: Apache/2.4.38 (FreeBSD) OpenSSL/1.0.2o-freebsd PHP/7.3.1
Connection: close
Content-Type: text/html; charset=iso-8859-1
Connection closed by foreign host.
$
To disable the display of such information we have to edit the ‘httpd.conf’ file. On Debian based systems, such as Ubuntu or Debian itself, this file may be called ‘apache2.conf’. On such file we will add the following two directives:
ServerTokens Prod
ServerSignature Off
After those changes have been applied Apache must be restarted. And as you can see below the fact that Apache is running on the server is not hidden but the version of it, the OS information and other has been removed:
$ telnet ipaddres/domainname 80
Trying ipaddress...
Connected to servername.
Escape character is '^]'.
HEAD / HTTP/1.1
HOST: [SERVER]
HTTP/1.1 400 Bad Request
Date: Sat, 09 Mar 2019 20:12:34 GMT
Server: Apache
Connection: close
Content-Type: text/html; charset=iso-8859-1
Connection closed by foreign host.
$
If we now check this out with nmap we’ll see the information we obtain has also been reduced.
$nmap -sV -p 443 ipaddress
Starting Nmap 7.70 ( https://nmap.org ) at 2019-03-09 21:21 CET
Nmap scan report for servername (ipaddress)
Host is up (0.052s latency).
PORT STATE SERVICE VERSION
443/tcp open ssl/http Apache httpd (PHP 7.3.1)
Service detection performed. Please report any incorrect results at https://nmap.org/submit/ .
Nmap done: 1 IP address (1 host up) scanned in 13.55 seconds
$
If we want to remove information about PHP, we have to edit the php.ini configuration file. The directive should be changed from:
expose_php = On
to:
expose_php = Off
So using nmap the results would be as follows, no OS information, no Apache version, and no framework information either:
$nmap -sV -p 443 ipaddress
Starting Nmap 7.70 ( https://nmap.org ) at 2019-03-09 21:21 CET
Nmap scan report for servername (ipaddress)
Host is up (0.055s latency).
PORT STATE SERVICE VERSION
443/tcp open ssl/http Apache httpd
Service detection performed. Please report any incorrect results at https://nmap.org/submit/ .
Nmap done: 1 IP address (1 host up) scanned in 13.81 seconds
$
These actions, and others found in this how to harden Apache, can be classified as security by obscurity. By hiding information you obtain certain grade of security. However be aware this can be very misleading. You may believe you are getting more than what you actually are. If someone really wants to hack you, eventually they may find an option to do so. This said, making it difficult for those bad actors it in your hands, and taking this obfuscation actions will make them slower in their intent. Some attackers will notice there are some actions already taken, so the system is taken care of, which can be an indicator for them to move to another easier target. However, if that server is relevant to them, this obfuscation won’t stop them.
In a later point in this article you will find a method to mislead possible attackers on the server software type you are using. So instead of hiding this information, you can display another one. So you can be running Apache but tell the world the box is running Internet Information Services from Microsoft.
Enable the logs.
Yes. Do it. Enable logging so the web server will collect information about who is visting, errors and information that may not be relevant at first sight but may be useful for later use. The Apache module in charge of the main functionality is ‘mod_log_config’, make sure it is enabled. There is at least two directives to be declared so logs are correctly registered. Many administrators have virtualhost configuration files where the paths to the logs are declared. For example here at adminbyaccident these are the paths for logs declared on a virtualhost config file:
ErrorLog "/var/log/adminbyaccident.com-error_log"
CustomLog "/var/log/adminbyaccident.com-access_log" common
There is a good in depth explanation on how to log in the Apache project website. And you may also want to take a look to the more generic information page, also from the Apache project. Be aware logs grow along the time so you may need to rotate them.
Since Apache is very flexible on how to format the logging it is strongly recommended to have an in depth read of the links provided above. Just for you to have a small taste of what can be achived logs can collect the request method by setting the option ‘%m’, the specific requested URL with ‘%U’, the status of the connection when the response has been completed with the option ‘%X’, the number of bytes sent and/or received by using options ‘%O’ and ‘%I’ respectively and the time to serve the request by setting the option ‘%D’.
For example setting the log format to ‘common’ sets the options as follows:
"%v %h %l %u %t \"%r\" %>s %b"
To further understand these do please visit this Apache’s page explaining the details. Set the values to your needs. Be advised this can be a critical point to take care of in your organization. Internal regulations may have to be applied and enforced at this level.
Since the advent of GDPR this can get a bit more involved. And if you need to keep logs for any particular reason, from law enforcement regulations that mandate to do so, or the contrary depending on your case, to just the pure need to being able to back trace an intrusion or the first signs of an attack, make sure it is done right. If needed encrypt your communications in case the logs are stored in another node. Encrypting the disks containing such information is a desirable practice too, specially on corporations.
Last, but not least concerning logs. Make sure who has access to them and the permissions set on those files. For example on a WordPress install it is typical to have the files owned by the Apache group and user and the permissions set on the log files as 644 (read and write for the owner, read only for the group, and read only for the rest of the world).
Disable directory browser listing.
Let’s say you haven’t installed anything other than a LAMP or a FAMP on your server. And let’s also say you will not install any WordPress there, you will do something else. Any of the files you place into the web server root directory will be displayed if you don’t protect them. On a FreeBSD box the path to that directory by default is:
DocumentRoot “/usr/local/www/apache24/data”
In order to prevent open wide access to those files placed into that path you can set something the ‘Options’ to ‘None’ or to ‘Indexes’.
<Directory "/usr/local/www/apache24/data">
Options -Indexes
</Directory>
The Indexes directive as stated in the Apache official documentation allows the use of the directives controlling directory indexing (AddDescription, AddIcon, AddIconByEncoding, AddIconByType, DefaultIcon, DirectoryIndex, FancyIndexing, HeaderName, IndexIgnore, IndexOptions, ReadmeName, etc.). By declaring like described here (notice the ‘-’ minus symbol) its behaviour will be not lo list the content. On the contrary if we use the ‘+’ symbol the content will be listed.
<Directory "/usr/local/www/apache24/data">
Options None
</Directory>
The ‘None’ rule is quite explicit so no indexing of the directory content will be shown, plus no other features concerning the Options directive, as stated in the official documentation.
There are some other combinations for the <Directory> directive. The very httpd.conf file has some of them already mentioned.
# Possible values for the Options directive are "None", "All",
# or any combination of:
# Indexes Includes FollowSymLinks SymLinksifOwnerMatch ExecCGI MultiViews
There is another method to achieve this directory index disabling which consists on using the ‘.htaccess’ file. So if we want to allow the index of the content in a certain path we can write this:
Options +Indexes
If we want to avoid the directory content to be listed we’ll use:
Options -Indexes
All this said, at this point you may be asking what to do in case you want some fine grain control over the content of that directory for example allowing some specific users to see it but forbidding others. There are several methods to achieve the desired results. Here there are some useful indications from the Apache project themselves, but you may also want to have a look here.
Enable SSL.
If you are clueless about SSL you can read an introduction to it, and you may complement this reading with the wikipedia entry to https. However I encourage you to have a look at the official Apache documentation in this specific regard.
If you are reading this guide it is more than probable you are already a savy UNIX or Linux user, so all this may be familiar to you. However if you are not this is quite important to you. Why? Let’s say you want to authenticate to a website of yours, or any other service. If you do it through http, instead of https, means you are incoming to the web server through port 80. This is an unencrypted connection, which means your username and password travel through that connection in plain text. Is this bad? Decide by yourself because this actually means anyone in the middle of that connection will be able to see that username and password. It doesn’t matter how complicated the password is, anyone in between will be able to see it. Can you now guess why banks and many other sites have been using https (meaning port 443 and encryption) for so long?
I like using the FreeBSD operating system for many reasons. And here there is a how to enable SSL on Apache. However this guide will show you how to use self signed certificates, which your browser will surely complaint about. If you need certificates already signed by certificate authorities your browser will be very happy about you can buy them online or use letsencrypt for free. Installing letsencrypt certificates on FreeBSD is very similar to renewing them, a process I wrote about some time ago.
Having SSL/TLS certificates enabled is a fundamental step on how to harden Apache. If you happen to perform a WordPress install for a project of yours or to a customer, or any other kind of deployment, you want all the connections and administration panels you log into to have encryption, so no one but you can sniff your password. Here there is a guide on how to securely install wordpress on FreeBSD. If you are a GNU/Linux user you can find similar information online or adapt what you read here to the distribution you use. In any case, anyone willing to administer a WordPress site safely all logins should be performed on encrypted connections and here is how to.
SSL/TLS certificates is a complex topic. You may be using a different OS, a different framework, but the need of encrypted connections persists. Aside of having Apache listening on port 443 and/or redirecting connections from port 80 to the 443, the SSLEngine rule must be set to on, the protocols you want to use have to be also specified as well as what cipher suites you allow to use. The questions are which and what consequences does this have. Because choosing what to use and what not is a nightmare, and very few people really understands encryption (unfortunately for me I am not one of them) the Mozilla team comes to the rescue with an ssl configuration generator.
The tool is very useful since it not only helps Apache administrators, you can get a very decent configuration on other web servers such as Nginx, Lighttpd, reverse proxies/load balancers such as HAProxy and even AWS ELB. You can play with setting three different levels of configuration which will allow support for visitors using modern browsers, leaving old ones out of the equation. Be aware this may break access for those old workstations still running XP and a very outdated version of Internet Explorer. That part of your network has to be hardenned but not at this level (you are already blocking public access). And you can’t update these workstations nor configure this Apache to the highest and greatest security levels because those are in charge of complex SCADA systems no one has updated or can’t update because of any valid reason (let’s say the original vendor does not give support or updates to the systems you have in place). In this case you want to set the value to ‘Old’ so some outdated browsers can visit this Apache containing documentation some operators need to visit regularly. However if you are facing this Apache instance to the wild world, and you want to have the greatest and latest configuration you can choose the modern configuration. You can also set the Apache version in use and the openssl version. As output you will get a virtualhost recommendation to use. Something similar to this:
Another tool where you can check the quality of your SSL setup is a test URL from SSL Labs where you can test your configuration. Be aware the results are shown on the public boards if you don’t toggle the option not to, so be careful since in case you are testing your company’s setup you may find bad results and them being publicly available. Qualifications go from A to F and issues are displayed in detail so administrators know what is wrong, and even some times there are useful guides on how to solve severe problems.
Leaving a default configuration may get you surprising results. Don’t think for a moment you will get an A+ qualification without some tweaking. And no, obatining a certificate from a reputed organization does not guarantee anything other than having a good certificate. Configuration avoiding SSL2 for example has to be put in place, because that protocol is nowadays insecure. You may be in a position where you can’t upgrade your Apache, and you are already avoiding the POODLE attack. But are you avoiding the TLS version of it? Again, think concerned about your particular context.
Install mod_evasive to partially mitigate denial of service attacks.
Mod_evasive is a software designed to help the Apache web server cope with denial of service attacks. Detection is performed by creating an internal dynamic hash table of IP Addresses and URIs, and denying any single IP address from any of the following:
– Requesting the same page more than a few times per second
– Making more than 50 concurrent requests on the same child per second
– Making any requests while temporarily blacklisted (on a blocking list)
As the developer also explains the software is limited by the capacity of the infrastructure in use in terms of cpu consumption and network bandwidth.
Here you can find a guide on how to install mod_evasive on FreeBSD.
Disable .htaccess override.
First things first. The .htaccess file is a source of conflict, confusion and a certain amount of nightmares. The most basic ones being: ‘do I really need one?’, ‘what is it for?’ and screwing up everything related with it. Knowledge comes after reading, making mistakes, more reading and making of even more mistakes.
Let’s address the first thing about this file. Its existance is based on the need to control what the web server does without having access to the main configuration file. This means you may have control over certain directories and permissions to add files, modify them, in a certain portion of the directory structure, but you can’t be root. How do you then manage what content can be displayed in certain directories then? Asking the administrator every single change and modification all the users want to make, when they want to make them? .htaccess files allow to control the web server behaviour at the same directory level they are placed and subdirectories of that. So you can include the same directives, rules, configuration settings you would place in the main configuration file. The Apache project themselves state this kind of files should be avoided if you have access to the main configuration file for the web server, since having them causes a performance penalty.
So if you want to avoid anyone changing the behaviour of your web server, at any level, it is a good idea to disable the capability of having .htaccess files. You can do this by setting the AllowOverride directive as follows:
<Directory />
AllowOverride none
</Directory>
Be aware in doing so some things may break. Like your WordPress install. Some plugins are just dependant on the existance of that .htaccess file so they can write rules into it. For example security plugins like BulletProof Security will place rules to mimic a web application firewall in that particular file. So think twice before removing the .htaccess override, because you could be shooting yourself on the foot by willing to have extra security and not really getting it.
Use a Web Application Firewall, or WAF for short.
Security has to be thought and worked in different levels. People, machinery, environment, etc. On IT the same principle applies. To get the highest level of security different levels of the stack must be protected using different tools, different strategies and ideas. A web server has many components and if you are for example serving content from a CMS like WordPress or a custom build application the same general ideas apply. You secure your networking using a firewall for the system, not only externally with your Cisco/Juniper/F5 network appliances, but also at the device (or VM instance) level. Is it necessary somewhere else? Well… every moving part should have some security tweak put in place. And here comes handy a web application firewall which secures the web server or the web application portion of the stack.
If you happen to be using some CMS like WordPress there are plugins providing the WAF functionality as mentioned a couple of paragraphs above. BulletProof Security and Wordfence are two well known ones. But there are others, like Sucuri’s well reputed one. They are all easy to use, easy to configure and easy to understand. They place some rules into the .htaccess file so they fine tune the necessary conditions on the Apache part they can control and therefore they also provide security on the WordPress install. Is this enough? Well, it depends. Context matters a lot. All I can say is they provide a more than decent level of security with very little headache for the final user. And for a ridiculously low price.
However there is a different way to tackle this issue. Here comes in Mod_security. This software is not a plugin but a program oriented to protect an Apache web server. The system administrator has to install it and configure it to get to work with Apache. There is a set of rules that protect the web server. Those rules go from protecting the ways PHP works, but they also include sets for Java, others help protect from SQL injection attacks, UNIX or Windows shell attacks, denial of service ones and of course some dedicated to protect CMS installs for WordPress or Drupal. If you are interested on how to install mod_security you can read this guide here.
Use an Intrusion Prevention Sofware or IPS for short.
Similar to a WAF Fail2ban is an intrusion prevention software which leverages the firewall at the system level (physical box or VM) in order to protect the system. For example you can set it to reject SSH connections after a specific number of failures. People can be very annoying. I meant bots, sorry. But there are other uses too like protecting the login of a WordPress site.
If you are interested on how this is achieved and you want to protect your SSH connections you can read this article. And if you are using a CMS like WordPress and you want to protect your login page here you can find another dedicated article.
Disable the FileETag directive.
As stated in the official documentation ‘The FileETag directive configures the file attributes that are used to create the ETag (entity tag) response header field when the document is based on a static file.’ You may disable or you may reveal in the response header only the information you want/need to, since the FileETag directive may reveal the last modification time of the file served, the size of the file in bytes or the file’s i-node number.
You will see many people giving the advice to remove the FileETag completely. It’s an option. The question is which is the wisest desition, since there must be a reason for the Apache folks to include this directive. Removing it completely for security reasons doesn’t look good on those nice developers. So where is the issue?
The issue basically falls onto FileETag being able to disclose the disk inode where the file resides. This was a problem on OpenBSD back in 2003, which received a CVE numbered CVE-2003-1418. It is not 2003 and quite probably you are not using OpenBSD (although you might do that if security is THE concern to you). So it is not that much of a concern revealing such information, unless there is a vulnerability in the OS you are using that can leverage such fine grained information about things located in your file system. So do yourself a favor and do not display that. But how?
In your Apache’s main configuration file (called httpd.conf for regular individuals, and apache2.conf on the Debian universe) just add the directives you need to display, that is to say something like:
FileETag MTime Size
This configuration will maintain the display of the last time the file was modified, which is very useful for caching and the size of the file, while the unnecessary to display information is kept out.
If cache is not a concern for you whatsover, then you can consider removing the whole thing altogether by setting the value to none.
FileETag None
Use secure HTTP headers.
By using secure response headers clients connecting to web servers will have an additional security layer. Sometimes servers get compromised or malicious actors get their code inside website’s content, pictures, frames, javascript code, you name it. Employing these secure headers many of current attacks such as XSS (Cross-Site-Scripting), clickjacking, or Javascript injection to name a few will protect users from malicious code running on their systems, information leaks, session hijacking happenning and the internet will be a bit of a safer place.
Set HTTP Stric Transport Security.
This is a web security policy mechanism that helps against protocol downgrade attacks and cookie hijacking. It basically makes the web browser to only interact via https connections with the web server. This policy is enforced by the web server using an specific header. It can be configured with three different settings.
Strict-Transport-Security: max-age=<expire-time>
Strict-Transport-Security: max-age=<expire-time>; includeSubDomains
Strict-Transport-Security: max-age=<expire-time>; preload
The preload directive is relative to a service Google maintains so your website is included in a list inside the Chrome browser.
Since this policy should be enforced for a long time the max-age directive should be as long as possible. A configuration example can be the following one:
Header set Strict-Transport-Security "max-age=31536000"
Set the ‘X-Content-Type-Options’ header.
The utilization of this header will tell the browser what MIME types (Multipurpose Internet Mail Extensions) to use. When those are not declared browsers will guess which to use and thus allowing a type of attacks known as drive-by download or other ones such as MIME-sniffing. Basically this will avoid the browser loading malicious scripts, images, or any malicious content served by the server, since users can upload pictures, text and other stuff to sites. If an attacker gets to upload malicious snippets of code, via POST, when triggered to be served those can then call other servers on the internet to load the rest of the malicious payload to the client. This is a way legitimate websites can be unadvertedly deliverying malware to their visitors.
This header can be set in the main Apache’s configuration file httpd.conf or into the .htaccess file.
<IfModule mod_headers.c>
Header set X-Content-Type-Options nosniff
</IfModule>
Set the ‘Content-Security-Policy’ header.
Although simple in principle this is a very nice header to have set right. Basically it tells the client browser the content’s source to load. This is not trivial, since this will help to load content only from your web server, or trusted sources. If you happen to be serving content that is partially based on other sites, for example by using frames/iframes you will be also able to do so but setting the specific URL’s so only the content you trust will be loaded. Advertisements, anyone?
This header can help avoiding XSS attacks (Cross-Site Scripting) and others like different methods of injection. However there are many things to set into this header if you really want to nail it. Here there is a list you can check with all the directives you can configure for this header. However if you are in a rush you can follow some simple examples.
To set the content to be served only from your server you can set it as:
Header set Content-Security-Policy "default-src 'self';"
Let’s say you have a website where you do also load content from another domain (another website of yours, a customer’s site, another company you are working with,…) you can set this header like follows:
Header set Content-Security-Policy "default-src 'self'; *.trusted.com"
Aside of redirects and enabling SSL/TLS on your web server, there is a way to enforce client browsers to get the content only from https. This is how:
Header set Content-Security-Policy "default-src https://example.com"
If you want to go hardcore and not allow the execution of javascripts for example you can just set it like:
Header set Content-Security-Policy "script-src 'none';"
A slightly friendly version of the above can allow the execution of certain scripts only. And you can put several of those sources. It’d look like similar to:
Header set Content-Security-Policy "script-src <source>"
Header set Content-Security-Policy "script-src <source> <source>"
For more details on scripts sources you can follow this guide. Be aware this header can be a bit tricky when dealing with external content connectors such as those used for Twitter, Facebook and other sources, like advertisements. It can consume time to make it work perfectly and changes on sources may need to be updated regularly depending on what you are doing. Keep this in mind and you will be fine.
Set the ‘X-XSS-Protection’ header for older browsers.
If you are already applying the latest and greatest configuration you are probably leaving aside older browsers, not only older versions of Internet Explorer, but Chrome and Chrome for Android, Safari, and Firefox too. Some of those do not support TLS 1.2 or other modern features. Some of those browsers do not support CSP (Content Security Policy explained just above) which replaced this X-XSS-Protection header from back in the time.
If you are serving content to the modern world just use CSP and avoid using the X-XSS-Protection header. And no, don’t combine both, things will go South pretty quickly. However if you are serving content and you really want to cover everyone, or there are some old systems in your corporation and you want to have every single spot covered, even you know there is a higher level of security in your innet network, you can use this header.
By default browsers supporting it already set the minimal protection (sanitizing the rendered page if an XSS is detected) even if the header is not set, which would be like applying the following:
Header set X-XSS-Protection 1
If you need to disable the protection for whatever the reason (not recommended) you can set it to:
Header set X-XSS-Protection 0
To enable the best protection, which disables the rendering of the page, you will set it as follows:
Header set X-XSS-Protection "1; mode=block"
X-XSS-Protection: 1; report=<reporting-uri>
Set the ‘X-Frame-Options’ header to prevent clickjacking attacks.
This header tells the browser if embedded content can be rendered from a web page. A remote attacker could place embedded content on some unprotected website or one he’s got control on (legitimately or not), so when someone clicks on that embedded content malicious code can be executed on the visitor’s browser.
There are three settings for this header, deny, sameorigin and allow-from an specific URI. One may set it like follows:
X-Frame-Options: deny
–> this means the page cannot be displayed on a frame.
X-Frame-Options: sameorigin
–> this means the page can only be displayed on a frame with the same website origin.
X-Frame-Options: allow-from
https://example.com –> The page can only be displayed in a frame on the specified origin.
Set the Referrer-Policy header.
The referrer-policy header does a bunch of things, but basically allows to control what the browser can see as an origin referral set by the header.
These are all the possibilities for this header:
Referrer-Policy: no-referrer
The ‘no-referrer’ will completely omit the header so no information is sent with the requests.
Referrer-Policy: no-referrer-when-downgrade
With this setting the URL is sent as a referrer when the protocol doesn’t change, as for example from http to http, or from https to https, but it is not sent when downgrading from https to http.
Referrer-Policy: origin
The ‘origin’ setting will send the origin of the document as the referrer in all cases. As an example if the document is https://example.com/doc.html the referrer will be sent as https://example.com.
Referrer-Policy: origin-when-cross-origin
This will send the full URL when handling a same-origin request but will only send the origin of the document for other cases.
Referrer-Policy: same-origin
A referrer will be sent for same-site origins, but cross-origin requests will contain no referrer information.
Referrer-Policy: strict-origin
This will send the origin of the document as the referrer only if the protocol’s security stays the same, which means it will send it if the origin is https as well as the destination, and will not send it if the origin is https and the destination is http.
Referrer-Policy: strict-origin-when-cross-origin
This will send a full URL when performing a same-origin request, so it will only send the origin when the protocol stays the same, as from https to https, but will send no header to a less secure destination as from https to http.
Referrer-Policy: unsafe-url
This will send the full URL wether its a same origin or a cross-origin request. Which is NOT recommended since this will expose protected https urls to unprotected origins.
Tip: To get more information information relative to each header, just grab a browser documentation and carefully read about it. Mozilla’s documentation is great. You can also test your header configuration by visiting the following site.
Secure Session Cookies
Set ‘HttpOnly’ and ‘Secure’ flags on headers.
To fully understand what a session is there is some literature on the internet, but the Apache HTTP official documentation is a good starting point. But in here the objective is simple: protect the session. There are two directives that can be combined to achieve this, ‘HttpOnly’ and ‘Secure’.
The HttpOnly directive will prevent Javascript code to access the session cookies, something malicious actors are very keen to obtain. Already infected browsers can leak this kind of information but servers infected with other kinds of malware will also fiddle with clients connecting to them unless this directive is set.
The ‘Secure’ flag will only allow the transport of cookies via https. Something really nice, specially for session cookies for login into applications, although some TLS protection should already be in place.
In order to set these flags, aside from the mod_headers being enabled, the settings should look like:
Header always edit Set-Cookie (.*) "$1; HttpOnly; Secure"
Set limits to several Apache’s core configuration settings.
As already explained installing mod_evasive can be a helpful measure to deal with DoS (denial of service attacks) but there are other methods too. As the Apache documentation explains there are several settings one can tune to obtain an even better protection. So here there’s a list of those:
– KeepAlive on –> https://httpd.apache.org/docs/2.4/mod/core.html#KeepAlive
The KeepAlive directive will maintain the connection between the server and the client. As the documentation states this allows multiple requests to be sent over the same TCP connection. This setting will not improve the security of Apache or the client’s connection per se, however it will be stronger and faster. Mind the comments in the doc regarding older browsers using HTTP/1.0 clients which will not use KeepAlive connections on dynamic content. By deffault this directive is set to on in HTTP/1.1 clients. The alternative mode is off.
– KeepAliveTimeout –> https://httpd.apache.org/docs/2.4/mod/core.html#KeepAliveTimeout
All this keeping things alive does also have a limit, which is set by default up to five seconds. Tune this at your will. If you are able to serve content in let’s say just one second, why not keeping this up to two seconds instead of five? You could then tune it with something similar to:
KeepAliveTimeout 2
Mind this can have dramatic consequences. Maybe your web server is not very busy today, this week, and you are pushing out content in just one second or under. But how does it behave under heavy load? On the other hand setting a high number can cause problems since connections are open for quite a long time and this can create problems in an already squeezed server.
– LimitRequestBody –> https://httpd.apache.org/docs/current/mod/core.html#limitrequestbody
This directive allows an administrator to set the number of bytes allowed in a request body. The value range goes from 0 up to 2GB (or 2147483647 bytes). Setting it to zero, which is the default value, which means there is no limit. This can result in a DoS (denial of service) condition on the server. If one desires to set a limit of uploading files up to 100 k the configuration will read like this:
LimitRequestBody 102400
– LimitRequestFields –> https://httpd.apache.org/docs/2.4/mod/core.html#limitrequestfields
Through this directive we can limit the number of fields in the request header in an HTTP request. The default value is 100 but most clients will not exceed 20. Here too reducing the default number can reduce some DoS attacks. If we desire to set the number to 50 we can do it like follows.
LimitRequestFields 50
The value can also be set to zero, which sets no limit to it, and the limit can be risen up to 32767.
– LimitRequestFieldSize –> https://httpd.apache.org/docs/2.4/mod/core.html#LimitRequestFieldSize
Setting this directive allows the administrator to limit the allowed size on an HTTP request header field. Depending on the client this value can vary greatly. Authentication negotiations can be expensive in this regard, like in the SPNEGO case which can be up to 12392 bytes. The default value here is of 8190 bytes. As the official documentation statest there is no need to change this value. If one whishes to this can be done by something similar to this:
LimitRequestFieldSize 4094
If this variable is tunned downwards and a complex authentication system is set up, errors might show up. Remember this was changed and fine tune it to your needs.
– LimitRequestLine –> https://httpd.apache.org/docs/2.4/mod/core.html#LimitRequestLine
This directive allows to limit the size of a client’s HTTP request-line. Because the request-line consists of the HTTP methold, URI and protocol version it puts a restriction on the request-URI length to the server. The value needs to be large enough to include all the resources, including all the information for the query part of a GET request. This directive can help on mitigating certain types of DoS attacks where clients have odd behaviour. Unless one has very good reasons the default setting shouldn’t be changed from its original 8190 number of bytes. To change it though a configuration line like the following can be used.
LimitRequestLine 4094
-LimitXMLRequestBody –> https://httpd.apache.org/docs/2.4/mod/core.html#LimitXMLRequestBody
This sets the maximum size of an XML-based request body. The default value is of 100000 bytes. Setting it to zero will make this directive not applicable, so no check will be made.
– MaxClients // MaxRequestWorkers
— > https://httpd.apache.org/docs/2.4/mod/mpm_common.html#maxrequestworkers
On older (and outdated) versions of Apache like any of the 2.2.xx releases the maximum number of clients was set by the MaxClients directive. From version 2.3.13 onwards it is called MaxRequestWorkers.
This directive basically sets the number of simultaneous requests to be served. If the number of connections is over the limit set by this directive those will be queued. The number of maximum requests that can be queued is based on the limit set by the ListenBacklog directive, which is by default set to 511.
For this MaxRequestWorkers directive one must have in account how Apache Multi Processing Module is set up. By default it comes with prefork on but there are two other ways of functioning, worker and event. Aside of recommending a good read the offical documentation (links already provided) in regards of those working models it is strongly recommended to have a read to the very basic introduction page for Multi-Processing Modules in the documentation as well as the page explaining all the related directives.
As a rule of thumb the following applies:
– For prefork the maximum number of child processess that can be opened to serve requests is 256. Although this can be set differently with the ServerLimit directive.
– For worker and event configurations, the MaxRequestWorkers directive restricts the number (in total) of threads available to serve clients. The default value is 16 (processes) multiplied by 25 (this is set by the ThreadsPerChild directive). So, in order to increase this directive one must also increase the ServerLimit directive too.
The default configuration on this directive is usually right so no tuning is necessary unless we are dealing with a heavy traffic web site. This said, one must know what kind of application is intended to be run and under what conditions will Apache run, so choosing the right MPM module is a fundamental step. Prefork is usually more memory intensive than the other two modules, being the ‘event’ one the more desirable one in terms of performance and resource usage.
If a FAMP stack (FreeBSD, Apache, MySQL, PHP) is needed to be in place (similar to LAMP), this is how to deal with it with a prefork configuration. And this is how to do it with an event configuration.
– MaxKeepAliveRequests
https://httpd.apache.org/docs/2.4/mod/core.html#MaxKeepAliveRequests
By using this directive one can limit the number of requests allowed per connection when the KeepAlive directive is on. If this is set to zero an unlimited number of requests will be allowed. The default number is set to 100. It is recommended this number to be high to obtain the maximum performance out of the server. If one wants to change this value it can be done similar to this:
MaxKeepAliveRequests 500
– RequestReadTimeout.
http://httpd.apache.org/docs/current/mod/mod_reqtimeout.html#requestreadtimeout
This directive is used to set timeout values for completing a TLS handshake, receiving the request headers and/or body from a client. It directly depends on the mod_reqtimeout module. For each of the three timeout stages (handshake, header or body) there are three ways to specify the timeout.
To set a fixed timeout value:
stage=timeout
The time in seconds allowed for completing the whole stage. If set to zero there is no time limit.
To disable the module for a vhost:
handshake=0 header=0 body=0
To set the timeout value to be increased when data is received (in bytes per second):
stage=timeout, MinRate=data_rate
To set the timeout but not incrementally as data is received:
stage=timeout-maxtimeout, MinRate=data_rate
This works similar to the above setting but the timeout doesn’t increase as data is being received and it will stop at the second value.
– TimeOut –> https://httpd.apache.org/docs/2.4/es/mod/core.html#timeout
The general timeout directive where Apache will wait for I/O in different circumstances. The default value is 60 seconds. To change it one can apply something similar to this:
TimeOut 30
Disable the TRACE method for http requests.
The TRACE method makes a loopback request to the server. It justs sends back the string sent to the server. However if the client is instructed to make a TRACE request and happens to have a cookie on that particular domain, the cookie will be automatically included in the request header, so it will be back in the response. The directive to allow methods is the mod_allowmethods, which is a substitute of Limit and LimitExcept however to disable TRACE one must use TraceEnable instead.
To disable TRACE the following line has to be added into the httpd.conf file, since it’s on by default.
TraceEnable off
Some more information can be found here. The basic problem with having the TRACE method enabled is cookies can come back and sessions can be stolen. It’s not easy to do by any means but hardening consists on filling the blind spots.
Specify the HTTP request methods.
Following a similar approach as the above mentioned one, the requests methods can be specified to a few. Again the directive to allow methods is the mod_allowmethods, which is a substitute of Limit and LimitExcept.
The method names listed can be one or more of: GET, POST, PUT, DELETE, CONNECT, OPTIONS, PATCH, PROPFIND, PROPPATCH, MKCOL, COPY, MOVE, LOCK, and UNLOCK.
Using the mod_allowmethods directive one may read:
<Location "/">
AllowMethods GET POST OPTIONS
</Location>
Mind the Limit and LimitExcept directives work in a different fashion and mindset. Read the documentation carefully, since the Limit one is used to restrict the access controls to the mentioned HTTP methods and LimitExcept will apply those access controls over the not-mentioned methods. A configuration like follows will impose restrictions to the other methods but not to the specified ones.
<LimitExcept GET POST HEAD>
deny from all
</LimitExcept>
CGI and SSI Scripts
CGI stands for Common Interface Gateway, whereas SSI stands for Server Side Includes. Both are ways for programs to get executed and make the Apache HTTP server to show them off. CGI allows external programs to generate dynamic content, whilst SSI allows to add some pieces to your html pages.
CGI has a dedicated module to be enabled and it usually comes disabled, however a check wouldn’t hurt anyone. Be aware there are two entries for this, regular cgi so to speak and proxy_cgi.
Be aware that by disabling CGI altogether you may be shooting yourslef in the foot, specially with configurations like this one, where you are using the MPM Event Module with PHP-FPM. So mind what you turn on and off. Keep that in mind also and disable the ScriptAlias directive dedicated to CGI scripts if you are not using that particular path if you happen to have the module enabled. For example as follows:
With this command we’ll find out what line shows the ScriptAlias directive.
root@Apache:~ # cat /usr/local/etc/apache24/httpd.conf | nl -ba | grep ScriptAlias
Similar output to this should come out:
364 # client. The same rules about trailing "/" apply to ScriptAlias
367 ScriptAlias /cgi-bin/ "/usr/local/www/apache24/cgi-bin/"
380 # "/usr/local/www/apache24/cgi-bin" should be changed to whatever your ScriptAliased
As we can see the ScriptAlias directive is pointing to a particular path. To disable this just comment the line. It should look like this:
364 # client. The same rules about trailing "/" apply to ScriptAlias
367 #ScriptAlias /cgi-bin/ "/usr/local/www/apache24/cgi-bin/"
380 # "/usr/local/www/apache24/cgi-bin" should be changed to whatever your ScriptAliased
To completely disable CGI scripts just look for the modules enabling them and comment them so they can’t be used. If paranoia strikes you can also disable the ScriptAlias directive.
With a command similar to this you will find the modules.
root@Apache:~ # cat /usr/local/etc/apache24/httpd.conf | nl -ba | grep cgi
82 #LoadModule authnz_fcgi_module libexec/apache24/mod_authnz_fcgi.so
133 #LoadModule proxy_fcgi_module libexec/apache24/mod_proxy_fcgi.so
134 #LoadModule proxy_scgi_module libexec/apache24/mod_proxy_scgi.so
165 #LoadModule cgid_module libexec/apache24/mod_cgid.so
168 #LoadModule cgi_module libexec/apache24/mod_cgi.so
367 #ScriptAlias /cgi-bin/ "/usr/local/www/apache24/cgi-bin/"
371 <IfModule cgid_module>
374 # socket used to communicate with the CGI daemon of mod_cgid.
376 #Scriptsock cgisock
380 # "/usr/local/www/apache24/cgi-bin" should be changed to whatever your ScriptAliased
383 <Directory "/usr/local/www/apache24/cgi-bin">
431 #AddHandler cgi-script .cgi
460 #ErrorDocument 404 "/cgi-bin/missing_handler.pl"
root@Apache:~ #
Just check they are commented with an ‘#’ (hashtag symbol) as you can see in the above example.
To enable SSI, the ‘Options +Includes’ directive must be set as the official documentation states. This can be checked out using the following commands. One will check in the main httpd.conf file and the other one in an .htaccess file. Look for both if disabling SSI is needed.
root@Apache:~ # /usr/local/etc/apache24/extra/httpd.conf | nl -ba | grep Options
root@Apache:~ # cat .htaccess | nl -ba | grep Options
To disable SSI the directive should read, inside httpd.conf or the .htaccess file in use, like:
Options -Includes
If you are working with .htaccess files these are the directives you can use for either CGI, SSI or both.
"Options All"
"Options IncludesNOEXEC"
"Options -Includes"
"Options -ExecCGI"
"Options -Includes -ExecCGI"
"Options MultiViews"
Information about the Options directives can be found in the official documentation, which is very useful.
Run Apache HTTP as a different user and group.
A hiding strategy some people over rate since there are other attack vectors way up in the stack, such as session hijacking. To run apache as a different session and group check your GNU/Linux distribution instructions or UNIX flavour manuals, and create a user and a group named after what you mostly preffer. Make sure the user you create is a nologin one. Remember Apache HTTP needs to run one process as root to function. After that is done the last step is changing the user and group directives in the main configuration file: httpd.conf. If you are running Debian or derivatives this may be called apache2.conf or similar.
Authentication and authorization haven’t been included on purpose. These topics, because of their complexity and depth, will be tackled on another future article.
Sources:
Apache HTTP documentation: https://httpd.apache.org/docs/
OWASP project: https://www.owasp.org/index.php/Main_Page
Mozilla Firefox documentation: https://developer.mozilla.org/en-US/docs/Web/HTTP
Other non-official sites among many others:
https://www.adminbyaccident.com
https://geekflare.com/apache-web-server-hardening-security/
http://www.elladodelmal.com/2007/09/artculo-de-fortificacin-de-apache.html
If you find the articles in Adminbyaccident.com useful to you, please consider making a donation.
Use this link to get $200 credit at DigitalOcean and support Adminbyaccident.com costs.
Get $100 credit for free at Vultr using this link and support Adminbyaccident.com costs.
Mind Vultr supports FreeBSD on their VPS offer.