January 20, 2021
Hot Topics:

10 Simple Security Tasks for Locking Down Your LAMP Website

  • By Jason Gilmore
  • Send Email »
  • More Articles »

In their rush to build great website features, many developers overlook security issues -- even though a number of simple tasks can collectively reduce the possibility of a successful outside attack. In this article, I'll highlight 10 powerful security procedures for websites running on the LAMP (Linux, Apache, MySQL, and PHP) stack. These tasks can be implemented so easily that you'll wonder why you didn't use them months ago.

1. Filter All Input

If there were ever an official logo for the world's Web developers, the tagline would surely read "Filter All Input." Neglecting to validate user input properly has allowed the overwhelming majority of successful website attacks since the Web's earliest days, despite being a trivial part of the development process. But before describing the remedy, let's review a typical attack involving a website that has glossed over the filter process.

Suppose you create an online tool that converts currency rates by allowing the user to choose a currency, monetary sum, and a target conversion currency. Given these values, the site talks to a legacy script running on the server to perform the conversion and display the results to the user. Imagine you were in a hurry when creating this feature and decided to forgo filtering user input. An attacker takes advantage of the oversight and enters the following value, resulting in your entire website being deleted:

100; rm -rf 

Although there are a number of ways you can filter user input to ensure that appropriate values are being entered, perhaps the easiest solution is to use PHP's native filtering functions, which are available as of PHP 5.2.0. For instance, you easily could use the following function to filter the monetary value and ensure that only integers are provided:

$money = filter_var($_POST['money'], FILTER_SANITIZE_NUMBER_INT); 

2. Escape All Output

Most developers learn early on in their careers the importance of filtering user input, yet somehow manage to overlook the equally important task of escaping all output. Suppose that you spent a good deal of time refactoring your site to filter all user input, yet managed to miss a form that allows users to insert a new forum message. An attacker could take advantage of the oversight by inserting the following "message" and adding it to the forum:


The next person who visits the page containing the attacker's message immediately will be transported to www.example.com. This website could be a competing forum, or even a site that is designed to look exactly like yours! Avoiding such issues is incredibly simple: just use PHP's htmlentities() function to escape all output that was originally created by a third-party:

echo htmlentities($message); 

Doing so will cause all suspect characters such as > to be converted to their HTML entity equivalent, resulting in the attacker's message being converted to the following string:


3. Use Prepared Statements

Returning to the theme of filtering all user input, one of the most common security exploits is an attack known as SQL injection. SQL injection works by sending data to the database that causes a misinterpretation of the developer's intent and results in the application executing dangerous SQL code. For instance, suppose you provide a form that prompts the user to subscribe to your newsletter by submitting his e-mail address. The INSERT query used to insert the e-mail address into your database looks like this:

$db = new mysqli("localhost", "webuser", "secret", "newsletter");
$email = $_POST['email']; $query = "INSERT into subscribers SET email = '$email'";
if ($db->query($query)) { echo "Thank you for subscribing!"; }

While this snippet may look harmless, it is actually extremely dangerous! Suppose an attacker came across this page and happened to submit the following value as his e-mail address:

blah@example.com'; DROP TABLE subscribers --; 

Doing so would result in the following query being executed:

INSERT into subscribers SET email = 'blah@example.com'; DROP TABLE subscribers --' 

I doubt this is the type of subscriber you were hoping for! To eliminate the possibility of such attacks, you should instead use prepared statements to interact with the database:

$db = new mysqli("localhost", "webuser", "secret", "newsletter");
$email = $_POST['email']; $stmt = $db->prepare("INSERT into subscribers SET email = ?");
$stmt->bind_param('s', $email); if ($stmt->execute()) { echo "Thank you for subscribing!"; }

Due to space limitations I won't go into the details regarding prepared statement syntax. For more information, read this excellent Zend DevZone article, which devotes a section to the MySQLi extension's prepared statement syntax.

4. Disable Error Reporting

PHP's default error-reporting configuration will result in all errors except for notices being reported. Those errors will be displayed to the browser. While you will almost certainly want to leave this behavior untouched during the development process, you should make sure that the display_errors directive is disabled in your production environment. Neglecting to do so could provide an attacker with clues about how to exploit your site using its faulty code. To disable this directive, open your php.ini file and set the display_errors directive like so:

display_errors 0 

If you aren't able to modify the php.ini file in your production environment, you can set this directive within an .htaccess file using this syntax:

php_value display_errors 0 

5. Audit Your httpd.conf File

Apache's default configuration likely has many more options enabled than you need to operate your website, which opens up the possibility for security issues to crop up in areas you're likely not monitoring or don't even know are enabled. For instance, a default installation of Apache on my Ubuntu laptop indicates that 13 modules are enabled, including modules such as Status and CGI. If you're not using features like these, you should disable them.

However, this is only one example of many Apache features that should be disabled. For instance, Apache's default configuration displays information about the server version, operating system, and port at the bottom of pages in which it reports an error such as a 404 or when listing directory contents. You can test this behavior by attempting to access a non-existent page on your Web server. If this behavior hasn't been disabled, you'll see something like this below the error message:

Apache/2.2.12 (Ubuntu) Server at localhost Port 80 

Disable this behavior by opening httpd.conf, locating the ServerSignature and ServerTokens directives, and setting them like so:

ServerSignature Off ServerTokens Prod

Page 1 of 2

This article was originally published on May 26, 2010

Enterprise Development Update

Don't miss an article. Subscribe to our newsletter below.

Thanks for your registration, follow us on our social networks to keep up-to-date