Security basics · Lesson 8 · 35 min

Web server hardening & HTTPS

Your website is the one door you want everyone to walk through. That makes it the part of the server strangers look at most. Out of the box, Apache is friendly: it tells visitors its version, lists folders that have no index page, and talks in plain text anyone on the network can read. Let's make it polite but tight-lipped, and encrypted.

You will learn

  • Reading what your server gives away with curl -I
  • ServerTokens, ServerSignature and turning off folder listings
  • Security headers, and where Apache's config lives on each family
  • HTTPS: a test certificate now, a free real one from Let's Encrypt later

Where the config lives

You set up Apache in Linux Basics, lesson 14. The two families split its config up differently:

WhatRocky / RHEL (httpd)Ubuntu / Debian (apache2)
Main file/etc/httpd/conf/httpd.conf/etc/apache2/apache2.conf
Your own settingsa new file in /etc/httpd/conf.d//etc/apache2/conf-available/ + a2enconf
Turn a module oninstall it (dnf install mod_ssl)sudo a2enmod NAME
Sites<VirtualHost> blocks in conf.d/*.confsites-available/ + a2ensite
Check before reloadingsudo apachectl configtestsudo apache2ctl configtest

Say less: version numbers and folder listings

Ask your own server what it tells visitors: curl -I http://localhost. The Server: line shows Apache's exact version, and often the OS. Like the nmap -sV result in lesson 4, that saves an attacker a step. Two settings fix it:

ServerTokens Prod        # Server: header just says "Apache"
ServerSignature Off      # no version line at the bottom of error pages

Next, folder listings. Both families ship Options Indexes for the web folder. That means a folder with no index.html shows a list of every file in it, including ones you never linked to. Take Indexes out of the Options line and Apache answers "403 Forbidden" instead.

Hiding the version isn't the fix

Just like changing the SSH port, hiding the version is a small extra, not protection. Bots try their tricks on every server whatever it says. Updating Apache is what actually closes holes (Linux Basics 2, lesson 6).

Security headers

Headers are little notes your server sends with every page, telling the browser how to behave. A few cost nothing and block whole kinds of attacks:

HeaderWhat it tells the browser
X-Content-Type-Options: nosniffTrust the file type I send; don't guess (stops some sneaky script tricks)
X-Frame-Options: SAMEORIGINDon't let other sites show my pages inside a frame (stops "clickjacking")
Strict-Transport-SecurityOnly ever talk to this site over HTTPS, for the next year (HSTS)
Content-Security-PolicyExactly which places scripts, styles and images may come from. Powerful, so test it carefully

They're set with Header always set NAME "value". On Rocky, mod_headers is already on. On Ubuntu, turn it on first with sudo a2enmod headers, or the config check fails with "Invalid command 'Header'".

HTTPS

Without HTTPS, everything between the browser and your server (passwords, form data, cookies) travels as plain text. HTTPS encrypts it and proves the visitor is talking to the real server. That proof is a certificate.

TaskRocky / RHELUbuntu / Debian
HTTPS with a test certificatesudo dnf install mod_ssl (makes localhost.crt)sudo a2enmod ssl + sudo a2ensite default-ssl (the "snakeoil" cert)
Open the firewallsudo firewall-cmd --permanent --add-service=https
sudo firewall-cmd --reload
sudo ufw allow 'Apache Full'
A real certificate (needs a domain name)sudo dnf install certbot python3-certbot-apache (EPEL)sudo apt install certbot python3-certbot-apache
Get it and set it upsudo certbot --apache -d club.example.org same on both
Renewals (every 90 days, automatic)sudo certbot renew --dry-run same on both

A self-signed certificate encrypts just as well, but nobody vouches for it, so browsers warn and curl refuses unless you add -k. That's fine for a lab. Let's Encrypt gives real, trusted certificates for free, but it has to reach your server by its domain name from the internet, which a home lab can't do. Use certbot when your site has a real name.

Once HTTPS works, send plain-HTTP visitors over to it with a redirect in the port-80 site:

<VirtualHost *:80>
    Redirect permanent / https://192.168.1.50/
</VirtualHost>

Try it: lock up the club website 🔐

The club site runs on the server with the factory settings. Work through it the way you would on a real server: look, change one thing, test, reload.

Quick check

1. curl http://localhost/files/ shows "Index of /files" and a list of files. What's wrong?

2. On Ubuntu, you add Header always set X-Frame-Options "SAMEORIGIN" and configtest says "Invalid command 'Header'". Why?

3. Your lab site uses a self-signed certificate. Is the traffic encrypted?

Finished the missions and the quiz? Mark it done to track your progress.