Hardening Website Security – Part 3: Website Database Security

Introduction

It feels like almost every week there’s another news item about personal information being stolen because yet another company’s website got hacked.

Most of these attacks are perpetrated through social engineering, persuading somebody to hand over some detail which allows the hacker to gain additional privileges and, eventually, access to personal information. However, a lot are still carried out due to poor security or misconfigured websites.

This is the third in a series of articles which will aim to demystify some of the concepts you must get your head around if you hope to run a secure website in the 21st century.

Contents

The series will be consist of the following topics:

  1. HTTP Security Headers
    o 1a. HSTS Preloading
  2. User Session Security
  3. Database Security
  4. Safely Handling User Input

Topics Not Covered

I have opted not to cover server infrastructure security concerns at this time due to the huge number of possible configurations (hosting packages or VPS, operating systems, dashboard systems, firewalls, etc.) While I may cover some of these specifically in future articles, there’s too much nuance in these subjects to be able to do them justice here.

Disclaimer

Everything presented in this article is the result of years of experience, or trial and (frequent) error. Messing around with the code and/or settings on a live site can (and likely will) lead to unexpected, possibly disastrous results. The information presented here is correct to the best of the author’s knowledge, but in matters of security we strictly advise the reader to make sure they carry out additional research and understand the dangers before making any changes to their own systems or web sites. Any code presented here is done so as example only, and may be incomplete or contain errors. Readers should be careful when copying and pasting code from any website, this site included. Int64 Software Ltd, its employees and its representatives accept no liability for damage caused by the misuse, either intentional or unintentional, of the information presented in its posts and articles.

OverLAPS is a self-hosted web interface for Microsoft's Local Administrator Password Solution (LAPS)

Section Overview

For the third part of this article series we will look at best practices for securing your website’s database. We aim to cover database infrastructure design and security, connection and authentication, and common database attacks including SQL Injection.

Due to the range of database engines and development languages, it would be impractical to extensively list examples and mitigation methods in this article. However, hopefully it will point you in the right direction and help you to locate the specific resources you need to secure your data.

Common Database Attacks

SQL Injection Attack

Without a doubt the largest database-specific attack vector. Improperly formed and uncleaned SQL statements can allow attackers to execute arbitrary SQL statements very easily through your own web interface.

Consider, for example, a website that has a comment form on a page. When a user submits a comment, its text is read unfiltered or modified from POST into a variable called “commentFromForm“, it is then inserted into the database using the SQL statement:

“INSERT INTO page_comments (comment) VALUES (“ + commentFromForm + “);”

Just like that the web page is open to an SQL Injection attack as all an attacker needs to do is submit a comment such as:

“); DROP TABLE users;(”

When processed into the final SQL statement by your code, this gets turned into:

"INSERT INTO page_comments (comment) VALUES (); DROP TABLE users;();

The intented INSERT command now does nothing, but is immediately followed by the DROP TABLE command which executes, and now the website has just lost its users table. This is a very simple example, but an important lesson to learn.

There are two points during this process where an attack like this can be defeated:

  1. The proper handling of user inputted data (see Part 4 of this article series),
  2. The use of Prepared Statements and parameterised variables.

It is recommended that both of these are used side-by-side to increase your Defence in Depth protection again SQL Injection attacks.

Prepared statements are SQL commands which replace variable values with parameters instead, the variables themselves are then bound to the SQL command and identified by their appropriate parameter names.

For example, running a simple insert command in C# to a SQL Server database may now look like this:

using(SqlCommand cmd = new SqlCommand(dbconn))
{
	//SQL statement with variable instead of value
	cmd.CommandText = “INSERT INTO page_comments (comment) VALUES (@commentFromFormVar)”;
	//Bind the submitted value to the variable
	cmd.Parameters.AddWithValue()(“@commentFromFormVar”, commentFromForm);
	cmd.ExecuteNonQuery();
}

Or in PHP and MySQL using PDO:

$sql = “INSERT INTO page_comments (comment) VALUES (:commentFromFormVar)”;
$cmd = $conn->prepare($sql);
$cmd->bindParam(“:commentFromFormVar”, $commentFromForm, PDO::PARAM_STR);
$cmd->execute();

By preparing and then binding parameters to the statement, the database engine will automatically treat it as a variable and not attempt to run any contained SQL statements.

Script Injection Attacks

Even if you use Prepared Statements to store user submitted data in the database, you must still be aware of the contents of that data in order to prevent Script Injection attacks.

If you display the comments that users submit on your page, then without care and attention an attacker could submit HTML or JavaScript code which would be stored in your database and then loaded and displayed in a way that impacts on your other users. This could include anything from malware delivery mechanisms to card skimming scripts.

As this related more to handling the user input rather than being a specific database attack, this subject is covered more in Part 4 of this series.

Backup Data Exposure

Of course your database is backed up regularly (… it is, right?). However, ensuring that the backups are encrypted and stored securely is also critically important. The theft of an improperly stored backup could lead to the exposure of user data or database or server credentials which could lead to additional attacks.

The current advice for the storage of backups is:

  1. All backup data should be encrypted to a suitably high standard
  2. Store backups physically away from your database, such as on another server in another part of the country
  3. Use the backup verification technologies available for your database engine to make sure the backup was successful before processing it further
  4. Backups should only be accessible by those responsible for restoring data in the event it is needed

Database Infrastructure Design and Security

One of the most important things to remember when designing your database infrastructure is that the database should never be open to, or accept connections from the internet. Connections should be allowed from your own servers only. This will prevent brute-force attacks against your database user accounts, and adds defence-in-depth against zero-day exploits against your database server software.

Without going in to things like load balancing or API access, most websites will either run a single-server setup where the HTTP server software (IIS, Apache, etc.) are all hosted together.

Web Server and Database Share a Single Host
Web Server and Database Share a Single Host

Or separate their database server off onto a dedicated server (or multiple load-balanced servers) to improve security and performance.

Database Hosted Separately from Web Server (Isolated from Internet)

In both of these setups, the web server has a firewall only allowing ports 80 (only if unencrypted HTTP is required, not recommended in most cases) and 443 (SSL/TLS) through (excluding the need for other services such as SSH access). This means that none of the database ports are open to the internet and it is effectively isolated except through the data received and transmitted by your web server.

Remember: Defence in Depth

Your web server’s security should be layered so that a single in-route doesn’t compromise your entire server. With regards to databases this may best be represented as making sure that the user account used by your web server is not the same account that runs the database. By remembering this you make it harder for an attacker to migrate through the system as they need to identify or break different credentials for each system.

Keep Up-to-Date

Security patches for database servers come out frequently, make sure you stay abreast of them and keep your database (and the rest of your web server) patched.

Don’t Use Security-by-Obscurity

It may be tempting, when seeing the option, to change the default port used to accept connections to your database. However, the first thing an attacker will do when looking at your website will be to run a port scan. This is relatively quick and easy to do, will show up any open ports and, in the case of many tools, make a good guess at the intent of the port.

A database server which accepts connections from the internet and has a different host port is just as vulnerable as one using the default port.

Lock Down Directory Security

Ensure that only the database service account (the account the database service is running under) should have read and write access to the database directory, the same as with Defence in Depth.

Disable Remote Access

If you’re using a single-server model (web host and database on the same server) then you can safely disable remote access to the database altogether, meaning that it will not accept any connections except from those on the same server.

Remove Test/Demo Data

If your database comes pre-installed with some demonstration data, remove it. The same goes for anonymous or demonstration user accounts.

Expertise Matrix

Connection and Authentication

Always Use Single-Purpose Credentials

When creating a new database on your database server, make sure to create a unique user which only has access to that specific database, and use that user to connect from your website. If you share user accounts between databases and one database gets compromised, all of the others get compromised too.

Least Privilege Model

When configuring your single-purpose user for your website to connect to the database, only grant access to the commands that website requires to work. There is no point granting the DROP TABLE command if the account never deletes any tables, neglecting this small detail however opens up the possibilities should an attacker gain access to that account.

Also, never grant non-admin users access to system effecting commands. In MySQL, for example, the FILE command is used for importing and exporting data into files, but could be used by an attacker on a compromised database to write code anywhere on your server that the database service account has access to, allowing for traversal to other databases or server functions.

Disable and/or Remove Anonymous Access Accounts

Anonymous accounts (accounts which connect without having to provide a username or password) should never be allowed, for obvious reasons.

Never Allow Blank Passwords, and Always Enforce Strong Passwords

Do not allow accounts to be created with empty passwords, and always use cryptographically strong passwords of more than 16 characters (ideally 25 or over) which feature letters of upper and lower case, numbers and special characters.

What’s Next

Next time we’ll take a look at Safely Handling User Input on your website, and mitigating the various attacks that can be made against this.


Like the article? Share with your friends: