Hardening SSH
My first course of action when it came to hardening my server was to harden my SSH configuration. To do this, I followed this guide. At a very basic level, these are the steps I took and why I took them:
sudo vim /etc/ssh/sshd_config
PasswordAuthentication no # Turning off the ability to authenticate with a password
PermitEmptyPasswords no # Turning off the ability to login into accounts without passwords
PermitRootLogin no # Turning off the ability to login as root via SSH
Protocol 2 # Specifying more secure cryptographic protocol
ClientAliveInterval 180 # Drops SSH session after a certain period of inactivity
AllowUsers cyrus # Limiting SSH access to my account only
MaxAuthTries 3 # Limiting the number of login attempts
sudo systemctl restart sshd
I found this to be a very good learning experience with regard to running an SSH server. While I’m fairly familiar with using SSH as a client, I’ve never known too much about how it’s managed on the server side and seeing the different configurations available was definitely very informative, I imagine this is knowledge that will come in very handy in future.
Reconfiguring the Firewall
After setting up my SSL certificate previously, I now wanted to ensure that all connections to my server took place through port 443, the default port for HTTPS connections. Doing this took a very simple reconfiguration of my firewall
sudo ufw allow 'Apache Secure'
sudo ufw delete allow 'Apache'
This configured my firewall so that only port 443 was accessible, rather than both port 80 and 443, as had been the case previously.
While this seemed to work on most browsers I checked it with, I will note that Safari, Edge and particularly Internet Explorer had some issues accessing my site once port 80 was closed. It would appear that these browsers only check port 80 when connecting to website and expect a redirect to port 443 if HTTPS is being used, whereas other browsers do their due diligence and check both ports, in all instances adding a ‘https://’ in front of my domain name would bring me to my site.
It’s unfortunate that this is the case and again I was taught another valuable lesson about the difficulties web admins face in managing security when a lot of web technologies seemingly work against more secure configurations.
Configuring Apache
I then set out to harden my Apache configuration, my first priority was to ensure that Apache wasn’t giving away any information about its version of the version of my OS
sudo vim /etc/apache2/conf-enabled/security.conf
ServerTokens Prod # Preventing disclosure of OS information
ServerSignature Off # Preventing disclosure of Apache version information and errors
systemctl restart apache2
From here, I also set out to configure my HTTP headers as a means of preventing attackes against my actual Wordpress site.
sudo vim /etc/apach2/apache2.conf
Header set Strict-Transport-Security "max-age=31536000; includeSubDomains; preload"
Header always append X-Frame-Options DENY
Header set X-Content-Type-Options nosniff
Header set Content-Security-Policy "default-src 'self';"
Header set X-Permitted-Cross-Domain-Policies "none"
Header set Referrer-Policy "no-referrer"
Header set Expect-CT 'enforce, max-age=43200, report-uri="https://somedomain.com/report"'
Header always set Permissions-Policy "fullscreen 'none' "
Header always set Clear-Site-Data "cache"
This was the part of my hardening activities that really made me come to appreciate how difficult it can be to harden web apps. The amount of headers that needed to be set and the specific ways in which they needed to be configured were already enough of a headache for my simple Wordpress blog, I can only imagine how difficult it would be when trying to create secure headers for a large scale corporate website.
The biggest issue I came across was with configuring my content security policy, predictably the CSP I has set immediately broke all JavaScript running on my Wordpress page:
I knew this could be fixed by generating nonces for the inline JavaScript included on Wordpress template pages and including that nonce in my CSP. To me this solution seemed so obvious that I was certain that a plugin must exist that does it, but alas after days of searching, I found that no such plugin exists. Instead I had to settle for a plugin which turns off CSP in the admin dashboard (the only place broken JS was noticable) and accept that my other JavaScript would just have to remain broken.
The lack of a proper CSP solution was pretty telling, it showed CSP was essentially not being condsidered in the case of most Wordpress sites, which isn’t especially surprising but is a bit dissappointing given that, in my experience, it can be a very effective tool at preventing XSS.
Hardening Wordpress
Hardening Wordpress was another area where I found that security had taken a backseat. When hardening my Wordpress configuration, I took advice from this guide provided by Wordpress themselves.
Once particular part of the guide that I found interesting was the section on file permissions, this section contained advice on which files could have their ownership changed from the web server process to to other accounts. The main purpose of this was to limit damage that could be done if an attacker were to gain RCE via the web server account, the issue with it though was that changing the permissions of these files away from the server account prevented auto update from functioning, a serious security issue in and of itself.
There likely are ways I could have maintained auto update via my user account using some sort of cron job, but I found it interesting that Wordpress would give a hardening strategy that may potentially leave a Wordpress site more vulnerable if not handled correctly.
Setting up Auto-Updates
My final course of action was to set my server to update its packages automatically, this wasn’t something mentioned in a guide but I thought it would be a good opportunity to make my server a bit more secure and learn about cron at the same time.
Turns out the process was fairly simple, I quickly added the following lines to /etc/crontab:
And just like that I was done.
This was about the extent of my server hardening, I did look into rkhunter and fail2ban as other potential hardening strategies I could learn about, but I ultimately ran out of time.
Overall, this taught me yet another rather stark lesson about how difficult it can be to implement proper security on a web application.