Tutorial: Improving Web Server Security and Availability
By Dr. Amir Qayyum & Hashim Nouman
Disclaimer: This tutorial is a summary of training titled “Open Standards Everywhere”, conducted by Internet Society. ISOC also has a project by the same name and more details about it can be found here : https://www.internetsociety.org/issues/open-standards-everywhere/
The aim of this article is to improve the global connectivity of web servers and make them more secure and trustworthy by using open standards, which are agreed-upon voluntary standards that anyone can use to connect with other systems. This tutorial focuses on improving the security of a self hosted or CDN hosted web server. The commands structure will be that for Apache and Nginx web servers, along with configuration guidelines for CDN hosted webservers.
There are two aspects that will be covered: Availability and Security. From the availability perspective, reachability of the webserver over IPv6 and HTTP2 will be discussed. Native IPv6 connections are desired for new networks, particularly mobile networks. With the HTTP/2, connections are faster and work better for low bandwidth connections, like on mobiles. From the security standpoint, enabling TLS and DNSSEC will be the main topic, as it forms the basic of website security and trustworthiness.
To establish a baseline, admins are encouraged to go to www.internet.nl and https://http2.pro/ to check for the above mentioned criteria and get a score on how their website is doing. The internet.nl website is developed by NLNet Labs, with support from ISOC and many organizations, and also provides detailed insight into what is checked and assessed, and how it can be improved.
Reference Servers
These reference servers have an almost perfect score on www.Internet.nl and also pass the http2 test:
- https://ose-apache.internetsociety.org/
- https://ose-apache-cdn.internetsociety.org/
- https://ose-nginx.internetsociety.org/
- https://ose-nginx-cdn.internetsociety.org/
Detailed information is also available on GitHub: https://github.com/internetsociety/ose-documentation
Types of Hosting for Web Servers
There are typically 3 ways in which a website is hosted:
1. Self hosted on a server or VM
2. Hosting with a website hosting provider
3. Content Delivery Network (CDN) in front of the hosted or self-hosted website
The type of hosting also dictates the access and control one have over their website, in order to make and changes or updates. For a self hosted website, it is straight forward to go in the command line mode and make all the changes (some are defined below). For a hosted website, one has the least access and will have to depend on the hosting company to make the changes, if they are willing to do so, because some of the improvements may require significant changes at their end. For CDNs, there are usually automated processes that can be done to make the changes. A big advantage of CDN is that it can provide IPv6, HTTP/2, TLS, DNSSEC, etc., even if the original web server does not provide it. CDNs can improve speed and protect website from many attacks. Some CDNs offer free service, although many require payment for their services.
1. Enabling access on IPv6, and making content available on IPv6
It is important to have IPv6 access. If unable to get IPv6 address, the website owner should consider using a CDN.
In order to check, if IPv6 is there, use the command:
dig +short aaaa <domain name> (domain name can be www.isoc.pk)
1.1. Apache Server
Apache is configured by default to listen to all available interfaces and IP addresses on a server, both IPv4 and IPv6. Once it is confirmed that IPv6 connectivity is available on the web server, it should just start replying to HTTP requests over IPv6.
It can be confirmed from the virtual host file, which is often httpd.conf or apache.conf. There should be a line:
<VirtualHost *:443>
Note that this configures your web server to respond to incoming HTTP requests on any IPv6 addresses on the server. If a web server has multiple IPv6 addresses and the web server should respond to only a single IPv6 address, then change the above line to something like this:
<VirtualHost xxx.xxx.xxx.xxx:443 [xxxx:xxxx:xxxx:xxxx:xxxx:xxxx:xxxx:xxxx]:443>
1.2. NGINX Server
Once it is confirmed that IPv6 connectivity is available on the web server, then open the NGINX configuration file (usually nginx.conf file), and add the second line below, with [::]:
server {
listen 443 ssl;
listen [::]:443 ssl; <--- (add this line)
include snippets/ssl-params.conf;
server_name example.com www.example.com;
root /var/www/example.com/html;
index index.html index.htm index.nginx-debian.html;
}
After doing this modification, restart the NGINX service.
Note: This allows a web server to respond to incoming HTTP requests on any IPv6 addresses on the server. If a web server has multiple IPv6 addresses and the web server should respond to only a single IPv6 address, then replace the :: in [::] with the specific IPv6 address.
1.3. CDN
Content Delivery Networks (CDNs) provide a mechanism to easily add IPv6 support to the website. Many CDNs do not require that the original web server (called an 'origin server' in CDN terminology) supports IPv6, and it can continue to work over IPv4. Many CDNs support IPv6 by default. Therefore, there is no further action to do once an account has been set up with the CDN. For example, as of now, the following CDNs enable IPv6 by default:
Akamai
Amazon CloudFront
BelugaCDN
Cloudflare
Fastly
Limelight
OVH
1.4. IPv6 and DNS
It is important to note that the web server must have a AAAA record in the DNS, and that the DNS server must have a AAAA record in DNS to be reachable from the IPv6-only networks!
2. HTTP2
HTTP/2 speeds up the web server performance and reduces the latency by various techniques.
2.1. Apache Server
During a default install on Debian, Apache is configured with mpm_prefork (this will not work with HTTP/2. Use either mpm_event or mpm_worker).
Stop apache
apachectl stop
Install php7.3-fpm. Install the php-fpm from your PHP repository. This package name depends on the vendor.
apt-get install php7.1-fpm
Enable module for fast cgi
a2enmod proxy_fcgi setenvif
Enable the php-fpm configuration. Again, this depends on the PHP vendor.
a2enconf php7.3-fpm
Disable mod_php
a2dismod php7.3 # This disables mod_php.
Disable mpm_prefork. This disables the prefork MPM. Only one MPM can run at a time.
a2dismod mpm_prefork
Enable mpm_event. Enable event MPM. You can also enable mpm_worker.
a2enmod mpm_event
Enable the HTTP/2 module
a2enmod http2
Restart Apache
apachectl start
2.2. NGINX
Open the configuration file (we will use default.conf as an example)
Edit the listen direction by adding “http2”. It should look like the following, assuming both IPv4 and IPv6 are used.
listen 443 http2 ssl;
listen [::]:443 http2 ssl;
Save file and restart NGINX
2.3. CDN
Enabling HTTP2 on Akamai
This should be enabled by default. But to confirm, you can do the following:
Login to your Akamai account
Go to the CDN/Properties section, and select the property you want to verify
Under the Active Production Version, click on the hyperlink on the Version section
Under the Behaviors section, look for an HTTP/2 section. It should show HTTP/2 is Enabled. If it is enabled, no further action is required.
If it has been disabled, re-enable and deploy the updated configuration
3. TLS
Transport Layer Security (TLS) encrypts connection between browser and the web server. It was previously known as SSL. Web servers should support latest versions of TLS (1.2 and 1.3). If TLS is not active on the server, install “Let’s Encrypt” from https://letsencrypt.org, which is free and no cost for TLS certificates.
3.1. Apache Server
Go to the directory Let’s Encrypt is installed, typically /etc/letsencrypt
Edit the file options-ssl-apache.conf
Find the SSLProtocol line and set it to the following (if it is not already set):
SSLProtocol all -SSLv2 -SSLv3 -TLSv1 -TLSv1.1
Save the file.
Restart Apache
3.2. NGINX
Go to the directory where Let's Encrypt is installed, typically /etc/letsencrypt
Edit the file options-ssl-nginx.conf
Find the ssl_protocols directive and set to the following:
ssl_protocols TLSv1.2 TLSv1.3;
Save the file.
Restart NGINX
3.3. CDNs
Akamai
1. Login to your Akamai account
2. Go to the CDN/Certificates section
3. Under Actions, select View and Edit Deployment Settings
4. Click Edit on the Advanced Network Configuration section
5. Selection Disable specific TLS versions and Check TLS 1.0 and TLS 1.1
6. Click Submit and wait until deployment of the configuration is complete
Cloudflare
1. Login to your Cloudflare account
2. Click on SSL/TLS icon
3. Click on Edge Certificates
4. Scroll to Minimum TLS Version section
5. Select TLS 1.2 (this will automatically set the TLS version to 1.2 or higher.)
4. HSTS
HTTP Strict Transport Security (HSTS) requires web browsers, after first visit, to connect only via HTTPS. It can protect against man-in-the-middle attacks.
4.1. Apache
To enable HSTS (within a virtual host config), make sure mod_headers and mod_rewrite are enabled. It can be checked by going to the file:
/etc/apache/mods-enabled
and look for: rewrite.load and headers.load
If not loaded, run the following commands
a2enmods rewrite
a2enmods headers
Then restart the apache server.
Then open the virtual host file to edit. This might be httpd.conf, apache.conf, or a separate file for the virtual host.
If file has section for HTTP (port 80), add the following:
RewriteEngine on
RewriteCond %{SERVER_NAME} = <your hostname>
RewriteRule ^ https://%{SERVER_NAME}%{REQUEST_URI} [END,NE,R=permanent]
Following is an example for ISOC server:
RewriteEngine on
RewriteCond %{SERVER_NAME} = ose-apache.internetsociety.org
RewriteRule ^ https://%{SERVER_NAME}%{REQUEST_URI} [END,NE,R=permanent]
Add the header statement in the (443) section. It is recommended to have HSTS max-age of at least six months (31536000 seconds).
Header Set Strict-Transport-Security "max-age=31536000"
Restart apache
4.2. NGINX
In a server block, add the following to redirect to HTTPS. If Let's Encrypt is used, then choose to redirect at the time when the certificate was created, which will create the same config:
if ($host = ose-nginx.internetsociety.org) {
return 301 https://$host$request_uri;
}
Now add the header either in the main config, or use an externally called config file. If the header is added in the main config file, add the following line. You can adjust the max-age directive, but it is recommended to not go below 31536000 (which is 6 months)
add_header Strict-Transport-Security "max-age=31536000";
On our server we created an ssl-params.conf file where we store server settings. This is what our file looks like:
ssl_protocols TLSv1.2 TLSv1.3;
ssl_prefer_server_ciphers on;
ssl_dhparam /etc/ssl/certs/dhparam.pem;
ssl_ciphers ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:ECDHE-ECDSA-AES128-GCM-SHA256:ECDH$ssl_ecdh_curve secp384r1; # Requires nginx>= 1.1.0
ssl_session_timeout 10m;
ssl_session_cacheshared:SSL:10m;
ssl_session_tickets off; # Requires nginx>= 1.5.9
ssl_stapling on; # Requires nginx>= 1.3.7
ssl_stapling_verify on; # Requires nginx => 1.3.7
resolver 8.8.8.8 8.8.4.4 valid=300s;
resolver_timeout 5s;
add_header X-Frame-Options DENY;
add_header X-Content-Type-Options nosniff;
add_header X-XSS-Protection "1; mode=block";
add_header Referrer-Policy no-referrer;
add_header Strict-Transport-Security "max-age=31536000";
5. DNSSEC
5.1. Apache or NGINX
The DNS servers that host your domain must "sign" the domain. These DNS servers might be ones that you operate directly. They might be at a DNS hosting provider, or at a domain name registrar.
Information from the DNS server about the signatures (a "DS record") must be given to the domain name registrar. The domain name registrar will send that DS record to the "registry" for the top-level domain for your domain (for example, .COM, .ORG, .NL, .MX). Once this has been completed, your domain will be signed.