Web server security – Part 1: Basic hardening

Web server security – Part 1: Basic hardening

A web server is just another computer and requires basic security configuration. In the first part, we show you how to secure your SSH access and configure your firewall. You can use this configuration for any type of server.

Contents

  1. Requirements
  2. Step by step guide for basic server security
  3. Summary
  4. External files

Always stay in the loop!
Subscribe to our RSS/Atom feeds.

Requirements

  • server with operating system installed (e.g. Debian, Ubuntu, CentOS)
  • basic knowledge of shell commands
  • SSH client on your client
  • security token (YubiKey, Nitrokey) or app (FreeOTP) with OATH-TOTP support
  • nmap (optional)

We will use Debian 9 in this series.

Step by step guide for basic server security

First of all, you have to install a server operating system. After that, you will be able to log in using SSH on your computer: $ ssh root@[IP address of your server].

When connecting for the first time, SSH will ask for your confirmation that you connect to the right server. Type yes. After confirming, you have to enter your initial password. That’s all.

Step 0: Check for updates

When you are connected to your server for the first time, use apt (Advanced Package Tool or the package manager of your OS) to check for updates: # apt update and install updates, if available.

Step 1: Install and configure ufw (your firewall)

Download and install ufw (Uncomplicated Firewall) since ufw is one of the easiest ways to configure your firewall: # apt install ufw.

Then, you can check the status of ufw: # ufw status which will most likely show Status: inactive.

Change the default rules to:

  • # ufw default deny incoming
  • # ufw default allow outgoing

Now you have to enable the SSH port 22:

  • # ufw allow ssh

However, this enables this port for everyone on the internet. Every possible IP address can connect to port 22 of your server. There are hundreds of automated bots on the internet which try to connect to port 22 in order to get into your server. We observed these bots for two hours and saw about 8,000 login attempts. A good practice is to enable SSH just for your personal IP address:

  • # ufw allow from [IP address of your computer] to any port 22

The problem is that your ISP may change your public IP address from time to time. This means that you are unable to connect to your server until you get your configured IP address back. You can observe your IP address for several days and most likely you will see that it doesn’t change much. So, you likely want to allow a subnet to connect with your server:

  • # ufw allow from [IP address of your computer]/[subnet] to any port 22

This is the common CIDR notation. Your suffix [subnet] will be “24” to “26” in most cases. This drastically restricts the number of hosts which can connect to your port 22.

Finally, you have to start and enable ufw: # ufw enable. Confirm that this command may disrupt existing SSH connections by entering y. You should see Firewall is active and enabled on system startup.

Please note that we only allow connections to port 22 so far. Later, we will also enable ports 80 (HTTP) and 443 (HTTPS), so people can connect to your website. If you don’t plan to run a web server but other server software, only allow the according port number(s).

Step 2: Create a non-root account

A basic security measure of all operating systems is to use non-root accounts for the most time. This ensures that an attacker doesn’t get all privileges if he exploits a program which runs in a non-root context.

On Debian, enter # adduser [username]. You will be prompted for a new password twice. Skip the user information by pressing Enter several times if you are the only user of this server and confirm this by pressing Y.

The new user doesn’t have the possibility to get administrative rights by default. To change this, you have to add the new user to the sudo group (other operating systems call this group wheel). Enter # usermod -aG sudo [username] to add the new user to the sudo group. You can see all groups of a user by entering # groups [username]. In some cases, you also have to install sudo itself: # apt install sudo.

Finally, switch to your new account: # su [username]. From now on, you have to explicitly enter root mode by preceding each command with sudo, if necessary.

Step 3: Harden your SSH configuration

The default SSH configuration allows password-based and root login. It is considered more secure to allow only non-root users and use keys instead of passwords. Furthermore, there are dozens of legacy algorithms allowed by default which we disable.

Step 3a: Switch from passwords to keys

You have to decide if you want to use RSA keys or newer Ed25519 keys. There is no clear recommendation for either RSA or Ed25519 and there is no big difference when using them.

Generate a key pair on your client, not on your server:

  • $ ssh-keygen -o -f ~/.ssh/id_[servername] -t rsa -b 4096 (4096 bit RSA key pair)
  • $ ssh-keygen -f ~/.ssh/id_[servername] -t ed25519 (256 bit Ed25519 key pair)

This will generate either a RSA or Ed25519 key pair and store it in your local ~/.ssh folder. You can optionally enter a password which you have to enter each time you use your SSH key to connect to your server. You will find two files:

  • id_[servername] (private key)
  • id_[servername].pub (public key)

You now have to copy the public key to the server by using the following command:

  • $ ssh-copy-id -i ~/.ssh/id_[servername].pub [username]@[IP address of your server]

You will have to enter the password of [username] and finally you will see: “Number of key(s) added: 1”. Immediately, open a second terminal windows and try to connect to your server without logging out before: $ ssh [username]@[IP address of your server]. If configured correctly, there will be no password prompt since your new key is used for authentication.

Step 3b: Configure sshd

When you successfully checked that your new key is in use, connect to your server using SSH and open the configuration file of sshd: /etc/ssh/sshd_config. Be sure that you actually open sshd_config. ssh_config (no d) is the configuration file for clients.

Change the file accordingly:

  • #PasswordAuthentication yesPasswordAuthentication no (disable password-based login)
  • PermitRootLogin yesPermitRootLogin no (disable root login)
  • #LogLevel INFOLogLevel VERBOSE (log login attempts)

Below # Ciphers and keying add:

1
2
3
4
5
6
# Ciphers and keying
#RekeyLimit default none
KexAlgorithms curve25519-sha256@libssh.org,diffie-hellman-group-exchange-sha256
Ciphers chacha20-poly1305@openssh.com,aes256-gcm@openssh.com,aes128-gcm@openssh.com
MACs hmac-sha2-512-etm@openssh.com,hmac-sha2-256-etm@openssh.com,umac-128-etm@openssh.com
HostKeyAlgorithms ssh-ed25519,rsa-sha2-256,rsa-sha2-512,ssh-rsa-cert-v01@openssh.com

Below # Authentication: add:

1
2
3
# Authentication:
AllowUsers [username]
AuthenticationMethods publickey

Now, restart sshd: $ sudo systemctl restart sshd (or use the command of your operating system). Immediately, open a second terminal windows and try to connect to your server without logging out before: $ ssh [username]@[IP address of your server]. If configured correctly, you are logged in without any prompts or errors.

Step 4: Enable 2FA for SSH

For even more security, we will enable two-factor authentication using OATH-TOTP now. Connect with your server.

First of all, we have two install libpam-google-authenticator. This is a module for PAM (pluggable authentication module): $ sudo apt install libpam-google-authenticator. This also installs libqrencode3.

After installing, configure the module by entering: $ google-authenticator. When asked “Do you want authentication tokens to be time-based (y/n)”, confirm by pressing y.

Configuring google-authenticator for the first time.
Configuring google-authenticator for the first time. (🔍 Zoom in)

Recommended configuration is:

  • Do you want me to update your “/home/[username]/.google_authenticator” file (y/n) → y
  • Do you want to disallow multiple uses of the same authentication token? → y
  • … we allow an extra token before and after the current time … Do you want to do so? (y/n) → n
  • Do you want to enable rate-limiting (y/n) → y

Now, scan the provided QR code using a TOTP app. Write down the emergency codes and store them in a secure place.

OMG, there is a Google link and Google knows my secret! Yes, there is actually a Google link. You will find your QR code there, on a Google website. Does this mean that Google knows your secret? No, since this link is generated locally on your server. If you don’t click it, no data will be transferred to Google. Google can display any QR code using this URL.

Finally, we must tell SSH that there is a 2FA PAM module. Open your /etc/pam.d/sshd file on the server:

  • change @include common-auth#@include common-auth
  • add auth required pam_google_authenticator.so to the bottom of the file

Save the file and quit.

Now, open /etc/ssh/sshd_config as before. Change:

  • ChallengeResponseAuthentication noChallengeResponseAuthentication yes
  • AuthenticationMethods publickeyAuthenticationMethods publickey,keyboard-interactive

Save all changes, quit and restart sshd: $ sudo systemctl restart sshd. When you try to connect to your server in future, there will be a “Verification code:” prompt. Enter the TOTP generated by your app or your emergency codes when you lost your app/phone etc.

On GitHub, we provide a Gist showing a minimal configuration file: sshd_config on GitHub.

Step 5: Back up your SSH keys

As always, it is important to back up your cryptographic keys. On your client, go to ~/.ssh/ and back up all files in there. You should also back up the /etc/ssh/sshd_config of your server.

Follow us on Mastodon:
@infosechandbook

Step 6: Test the configuration

Finally, we want to test our configuration. Most of the configuration like “Does SSH use my public key?” or “Does SSH use 2FA?” can directly be tested by restarting sshd and connecting with your server again. But you can’t see if SSH uses only modern algorithms.

There are several online tools available, however, we blocked most IP addresses. So we have to use a tool on our local computer which is allowed to connect to port 22. nmap is perfect for this test. Enter nmap -Pn --script ssh2-enum-algos [IP address of your server]. nmap will show:

  • kex_algorithms: (2)
  • server_host_key_algorithms: (1)
  • encryption_algorithms: (3)
  • mac_algorithms: (3)
  • compression_algorithms: (2)
Use nmap to see which algorithms are used by SSH.
Use nmap to see which algorithms are used by SSH. (🔍 Zoom in)

Optional: Configure your local SSH client

You can also tell your client to only use modern and secure algorithms. Open the ~/.ssh/config file on your client and add:

1
2
3
4
5
# Host keys the client accepts - order here is honored by OpenSSH
KexAlgorithms curve25519-sha256@libssh.org,diffie-hellman-group-exchange-sha256
Ciphers chacha20-poly1305@openssh.com,aes256-gcm@openssh.com,aes128-gcm@openssh.com
MACs hmac-sha2-512-etm@openssh.com,hmac-sha2-256-etm@openssh.com,umac-128-etm@openssh.com
HostKeyAlgorithms ssh-ed25519,rsa-sha2-256,rsa-sha2-512,ssh-rsa-cert-v01@openssh.com

Furthermore, add this to your local /etc/ssh/ssh_config file for all users, if needed.

This article is part of the "Web server security" series.
Read other articles of this series.

Summary

Done! You configured your firewall, so only your client can connect with your server using port 22. Moreover, you hardened your SSH configuration and tested it. Finally, you created a backup. An attacker has find a valid IP address to connect with your server and then he has to provide the correct private RSA/Ed25519 key and the TOTP.

Keep in mind that SSH keys are like passwords: Store them securely, only use them on trusted devices, generate new keys from time to time and securely delete old SSH keys.

Advanced users can set an SSHFP record (Secure Shell fingerprint record) on their DNS server.

In an upcoming part of this series, we will show you how to use Fail2ban to block brute-force attempts.

External files

See also