Web Security: How SQL Injection is done


SQL injection is one of the most common website exploits. For us developers to prevent SQL injection attacks, we must first understand how it’s done. Together, let’s review the basics of SQL injection.

Remember, let’s use our knowledge on good things only, okay?

Okay meme

The scope of this post is only a MySQL injection sample from PHP code. I am on a Windows 7 computer, using Git Bash and running SQL commands via command line, and coding my PHP file via VIM editor. My localhost is run by XAMPP.

I will be using MySQL, not MySQLi or PDO which have prepared statements, because the SQL injection is easier to demonstrate in MySQL.

Create database

I’m running these commands via command line.

What I do is run the mysql command like this: path_to/xampp/mysql/bin/mysql -h localhost -uMyUsername -pMyPassword


Then run use MyDatabaseName; to select a database.

This is a very simple database table with only 3 columns: the primary key “id”, “name”, and “secret”. The value of “secret” column, as its name implies, is user’s secret so nobody else should know about this, especially unauthorized users.

Then insert data using the following commands:

As you can see, those are dirty little secrets of our users, which in my case, are dogs in my house. :3

epi-pinky-asia-dogs-chihuahua

They are cute little chihuahuas who have embarrassing secrets just like humans. But because I’m using their secrets are samples in my database, these secrets won’t be secrets any longer. I also need to not secure my script for demo purpose. Sorry little babies 🙁 The readers don’t know you personally anyway. Hehehe

show tables; in mysql command line should show that 3 rows have been inserted.

Unsecured PHP script that runs MySQL query

Let’s save this as path_to/xampp/htdocs/test_sql_injection.php

Expectation vs. Reality

Expectation: The PHP script should be accessible through this URL: http://localhost/test_sql_injection.php since we saved it in the htdocs folder. We will be able to query the ‘users’ table by supplying a GET param called “name” in the URL, like so: http://localhost/test_sql_injection.php?name=epi and the script will show us the id, name, and secret of the matched row.

It should be safe as long the attacker only searches for a name that he/she already knows, right? Hmm, nope!

Reality: If the attacker passes malicious string as GET parameter, he/she can view ALL rows in our users table.

How? I’ll walk you through it.

Exploiting the vulnerability of PHP script via SQL Injection

Note: Originally, my PHP script had $_POST data instead of $_GET and I was POSTing the ‘name’ for the query via curl --data "name=A' OR 'x'='x" http://localhost/catzie_injection.php. After changing it into GET, I couldn’t hack the script with the same string value passed to ‘name’. I don’t know why. So to make life easier, let’s just hack it by accessing the URL via web browser.

Imagine yourself as a curious person with potential in hacking, and pretend you don’t know what we inserted to the database earlier! 😆

meme challenge accepted

Access the following URL with your browser:

It should should give you information about the user named Epi. Here’s what it shows:

Connected to db
your input param is epi
Your MySQL query is: SELECT * FROM users WHERE name = 'epi'
id: 1, name: Epi, secret: I eat poop. Don't tell mommy.
That is all!

yuck meme

Oops, we saw Epi’s secret! That’s literally a dirty little secret, isn’t it? But that’s ok, because Epi told us we can look up her secret in this database.

Now, aren’t you curious about the secret of other users in the database? Aren’t you tempted to hack into the database?

How can we hack this script so that we could view all database rows, and not just Epi’s details?

Let’s try this URL, with empty query:

Uhhh, nope. I only got this:

Connected to db
missing name param

meme-face-thinking

Ooh, I know! Maybe I can manipulate the query string of the PHP string!!!

Let’s try this now:

I got this result in my browser:

Connected to db
your input param is x' or 'x'='x
Your MySQL query is: SELECT * FROM users WHERE name = 'x' or 'x'='x'
id: 1, name: Epi, secret: I eat poop. Don't tell mommy.
id: 2, name: Pinky, secret: I'm a spoiled brat who's always fighting with other dogs
id: 3, name: Asia, secret: I eat too much my belly is touching the floor
That is all!

success-meme-face

Now we know the secrets of other users. 😆

The danger

You should understand by now that this vulnerability can lead to exposure of even more sensitive information to the attacker, such as passwords, contact numbers, addresses, etc.

Explanation of how we hacked to see all database rows

How did that happen? It’s all in the manipulated query!

The intention of the PHP script is to construct a query like this:

But instead of passing a real name, we passed this value:

Notice that we did not add a quote at the beginning and end of the value. It’s because that value is inserted between the single quotes in my PHP script’s query string, so that it produces this query:

The 'x'='x' in the WHERE clause will always return true, and since “or” only requires one true value, then every row in the database becomes a match for the SELECT query.

How to prevent SQL injection attacks like this?

Sorry but I won’t cover tutorials on how to do that in this post.

meme are you kidding me

Now, now, don’t get mad! Basically, what you need to do is use “prepared statements” with MySQLi or PDO.

Please refer to the following articles that will give you precise instructions on how to secure your PHP scripts and web apps so that you could prevent SQL injection attacks.

Catzie

A Filipino who is a mobile & web developer on weekdays, and a baker on weekends. When I have time to spare, I blog, play video games, watch series (usually anime), cook dishes, draw, or spend a little time with pets. I also like traveling and recording song covers, but I only do so occasionally. Follow on Twitter: @catzie

Leave a Reply

Your email address will not be published. Required fields are marked *