A Guide to Hardening an Ubuntu Based LAMP

hardening an Ubuntu based LAMP

In the realm of web development there are many different choices when it comes to putting a site online. Regardless of what specific technological stack you’re using, the following pieces will most likely be involved:

  • An operating system
  • A web server software
  • A database server
  • Your application code

One very popular combination of those is LAMP: Linux Apache MySQL PHP.

From a security perspective, any part of this stack can be subject to attacks so, if you want to be safe, you better learn how to apply best practices throughout the whole system.

In this post I’ll go over the basic measures you want to take in order to have a secure environment for running your LAMP based web applications.

I’m going to assume you’re running an Ubuntu server on some VPS provider, so I’ll skip the physical security aspects, though off course, should this not be your situation, you should also take those into account.

Also I’ll assume you have root access to the server and the LAMP stack is already in place.

As you’ll see throughout this article, many tactics you’ll be implementing will be based on difficulting attacks by relying on non-default configurations which is rather simple to put in place and, at the same time, very effective in practice.

Let’s get started!

Securing Linux

First of all, don’t lose sight of the fact that a web server is a software piece running on a publicly accessible computer and, as such, it relies on the underlying Operating System (In our case an Ubuntu Linux).

So the first line of defense is the Operating System configuration, more specifically, the users, groups and permissions.

Users, groups and permissions

The first thing you want to do is create a new user which will replace the default root user.

The reason behind this is that the root user is too powerful, so you’ll want to prevent yourself from inadvertently damaging the system.

So, first of all, ssh into your server using the root user:

And then create a new user called admin:

Since this will be the user you’ll be using for administrative purposes all along you’ll want it to belong to the sudoers group:

Now all that’s left is disallowing root login. By doing this you’ll be preventing unauthorized access to the server by malicious users who might have got a hold of root’s password.

Simply by creating a user with a non-common name you’re already making it harder for an attacker to succeed in their attempts.

In order to do this you’re going to be changing the default shell for the root user:

This command will ask you to provide a new Login Shell. Just use /usr/sbin/nologin as value for this field.

There, we got the basis covered 🙂

Secure shell access

By default Secure Shell allows the login using the root user. Let’s change that.

Open the file /etc/ssh/sshd_config and make the following changes:

  1. Look for the entry PermitRootLogin and change it from yes to no

Perfect, now let’s change the port to something different than the default 22

  1. Look for the entry Port 22 and change the 22 to another available port such as 2233

Another important measure you want to take is disallowing password based login since they can be subject to brute force attacks.

  1. Look for the entry PasswordAuthentication and change it’s value to no

Now, before you go any further, you need a way to ssh into your server and, since password authentication is not going to be an option any longer you’ll have to set up a key-pair.

If you’ve already created one on your local machine you can use it, no problem.

Just in case, run this command from your terminal:

In case there’s nothing there run the following command to create a new key-pair:

And use this command to install it in the server:

Before moving on try to login without a password by running the command:

It’s very important that you run this check before continuing, if this fails you’ll have a hard time getting into your server next time. 

Finally save the file and restart the ssh daemon to apply the changes:


Now you have a much more secure server.

To make it easier for you to use it, add the following configuration to ~ssh/config

This way all you’ll have to do to enter your server is ssh myserver, which is very convenient if you’re going to be doing that on a regular basis.

Security updates

Allright, now that we’ve taken care of unwanted visitors, let’s make sure the server is updated all the time (at least when it comes to security patches).

First off, if you’re not there yet, ssh into your server.

Then install unattended upgrades:

Using your favorite text editor open the file /etc/apt/apt.conf.d/50unattended-upgrades

If you don’t have one in place already you’ll probably find a sample one at /etc/apt/apt.conf.d/50unattended-upgrades.ucf-dist which can be used as a starting point.

In any case, your file should end up looking similar to:

Then you need to enable the auto-upgrade by editing the file /etc/apt/apt.conf.d/20auto-upgrades to look like this:

There. You’ve successfully set up your server so it will automatically get every security update as soon as it is available.


Next step is setting up a firewall so you can control which ports are open for public connections.

For this you have a simple tool available: UFW (Uncomplicated Firewall).

UFW is part of a standard Ubuntu installation, so all you have to do is set it up and enable it.

We’re going to take a very aggressive approach here: we’ll start denying everything and then we’ll enable those services we actually want to be able to connect to our server.

Start with the following command:

Then enable SSH, HTTP, HTTPS and any other service you may need by issuing these commands:

You can see a complete list of applications you can enable using this command:

Now that your server’s OS is secured it’s time to move to the next level: securing Apache. 

Securing Apache

Apache sensitive information

When it comes to exploiting a computer system’s vulnerabilities, every piece of information an attacker can get can be extremely valuable, thus, it’s your job to hide as much as possible.

In the default Apache configuration a lot of details about the running software are exposed to the public and this can be a source of hints for a malicious user about how your system can be compromised.

Open the file /etc/apache2/conf-available/custom.conf and put the following contents in it:

Use the following commands to enable the Apache headers module and for the configuration to be used: 

And then restart the apache service:

Open Indexes

Next step is making sure that your directory structure is not exposed to the outside world, in order to do that you need to disable Open Indexes by editing the file /etc/apache2/apache2.conf and adding the following:

Note that this configuration is per site so, if you have more than one you need to have several similar entries.

Web Application Firewall

Having a Web Application Firewall in place can significantly reduce your risks of being a victim of successful attacks.

There’s an Apache module you can install which will act as one: mod_security.

Start with the following commands:

Mod Security is a firewall and, as such, it needs rules to properly fulfil its mission, so the next step is to configure a rule set and enable modsecure in each site you’re hosting.

A good starting point is the Core Rule Set defined by OWASP (CRS), which can be found at /etc/modsecurity/modsecurity.conf-recommended.

Start by creating a copy of the file without the -recommended suffix:

Then edit to change the key SecRuleEngine to On

Once you’re done, restart Apache to apply the changes.

Then you’ll want to enforce the new ruleset. In order to do that you first need to remove the existing rule set by running:

Now download the last CRS version straight from git (I assume you have git installed):

Move the default configuration file and the default request exclusion file:

Now let’s add mod-security to Apache’s configuration. 

Edit the file /etc/apache2/mods-available/security2.conf and add the following within <IfModule security2_module>:

Alright! All the base configuration is done, all that’s left is enable mod_security for the websites you want to protect.

This can be done in the Apache configuration file or in the .htaccess file of each one.

For this example, let’s take the website definition within Apache: the file /etc/apache2/sites-enabled/000-default.conf (Replace this with the actual definition file for your website).

Edit it and add SecRuleEngine On to the VirtualHost definition.

And that’s it!

Just remember to restart apache when you’re done setting up mod_security for all of your websites!

Preventing DDoS attacks

One threat mod_security won’t protect against is Denial of Service (DoS). In order to keep your server safe you can install another mod: mod_evasive.

Just run this:

Then open the file /etc/apache2/mods-available/evasive.conf and put the following in it:

And, as expected, restart Apache for changes to take effect 

Securing MySQL

The next piece of the puzzle is the Database, namely, your MySQL instance.

Improving overall security

There are many things to tweak to make for a more secure than default mysql deployment.

Fortunately, MySQL comes bundled with a script called mysql_secure_installation which you can run and, by following the instructions within it, end up with a much safer environment.


As mentioned earlier, by simply moving away from the defaults you’ll be making an attacker’s job much harder.

One thing you can do in this direction is to change the port MySQL server listens to for connection.

Just open up the file /etc/mysql/my.cnf and change the port key to a number different from 3306 (Just check that the new port is not already in use!).

Disable remote authentication

Unless you absolutely need to log into your MySQL from a host other than the one it’s running on, it’s a good idea to disable remote authentication.

In order to that, just add the following to /etc/mysql/my.cnf

Root user

Another good idea is to change the name of the superuser from the default root to something else only you know about. Like this:

And finally, restart the MySQL server

Securing PHP

General configuration

The main configuration file for php is php.ini. This file can be located in several places depending on the specifics of your installation (mainly whether you’re using PHP as an Apache module or php-fpm).

Either way, these are the keys/values you want to have in it in order to avoid tipping attackers:

Disable dangerous functions

By default PHP will allow some potentially dangerous functions to be used, like straight calls to the underlying operating system which, if not properly validated, can lead to malicious visitors exploiting applications.

In order to disable them just add the following to the php.ini file you were editing:

Securing PHP applications

As always, a chain is cut by its weakest link, so, on top of all the effort you put into creating a secure environment for applications to run, you want to make sure the application’s code is written in the safest possible way.

You can find details on how to secure applications built with php in this post


Congratulations on reading this far!

I know we covered a lot of ground and you had a lot to take in so I want to give you a little reward: a tool you can use to run security audits of your servers: Lynis.

With Lynis you’ll be able to get a full system scan in no time and from there you’ll be able to take the remediation measures that are needed.

Installation can be done in several ways, let’s get the latest GitHub version:

Once installed run it with these commands:

Then go through Lynis findings and have fun!

Mauro Chojrin

Mauro Chojrin / About Author

Mauro is a PHP Trainer and Consultant. He’s been involved in the IT Industry since the year 1997 in a wide array of positions, going from which include technical support, development, team leadership, IT Management and, off course, teaching. Currently Mauro’s focus is on in-company training and consulting but also maintaining his blog and YouTube channel where he shares his knowledge with the world. LinkedIn | Twitter