Tutorials

PHP Security

by Daniel Egeberg on Jun 30, 2008 12:40:06 PM

3. SQL injections

One of the most common problems with security in web applications is SQL injection. To begin with I will present this comic for you:

The comic clearly illustrates the problems with SQL injection. If you do not get it, do not worry, you will in just a moment.

SQL injections work by injecting SQL into the queries you have already written in your script. Often you will pass some sort of variable data to your queries; this data might be influenced by user input. In the above comment we might imagine that the school had a query that looks something like this:

The above snippet works. As long as users input data that conforms to an expected format. Now, the mother in the comic did not provide expected data, rather she injected an entire additional query into the existing query. Let's take a look at how the query looks when we enter the string given by the mother:

(Note: PHP does not support stacking queries with all DBMSs. MySQL in particular)

As you probably know, a semi-colon ends a query and most times it is actually required, but PHP just adds it automatically if you omit it. Therefore, by closing the string and finishing the query by entering the closing parenthesis and a semi-colon we will be able to add an additional query that drops the student table. The two hyphens at the end make whatever comes after it a comment, so whatever remaining characters that might have been in the original query will simply be ignored.

It should not take too much brain power to figure out why this is a bad thing. Malicious users will basically be able to execute any kind of queries they would like to. This can be done for various purposes. It could be retrieving confidential information or destroying your data just to name a few.

3.1. Protecting your script from SQL injections

Fortunately, protecting yourself from SQL injections is rather easy. It is just a matter of calling a single function which make data safe for use in a query. How you should do this depends on which PHP extension you are using. Many people use the regular mysql extension, so let us start with that one. That particular extension has a function called mysql_real_escape_string(). Let us take a look at how that one works with a simple example that illustrates its usage:

As you see, doing it is incredibly easy yet many people fail to do this and only find out when it is too late. Other extensions support something called prepared statements. An example of a such extension is PDO (PHP Data Objects). Let us take a look at how that works:

If you have many fields you need to use in your query then it might be a little difficult remembering the order of all these different question marks which act as place holders for the data. An alternate syntax is using named parameters. In our case it would look like this:

Obviously, in our case this would not have any benefits, but as I said, if you have many parameters then you might find that more useful. There can be other reasons why using prepared statements would be useful, but I will leave that to research for yourself.

The mysqli (MySQL improved) extension has support for prepared statements as well, so if you are using that then check out its documentation to see the syntax.

The golden rule regarding this is that nothing is to be trusted and all data should be escaped.

Additionally, I mentioned earlier that users should not get information from error messages. Not only is it irrelevant, but it may also be information that may aid people with malicious purposes. You may sometimes be told that you should add or die(mysql_error()) to the end of your query calls to functions like mysql_query(). However, you should not do that. By doing that you are no longer using PHP's error and exception handling functionality and you remove the opportunity to control whether errors should be displayed or not. In my opinion the best solution would be to use PHP's exceptions. If you do not want to do that then at least do something like or trigger_error('Query failed: '. mysql_error()). By doing that you are utilizing PHP's built-in functionality and you will be able to use the methods discussed under Error Reporting. Moreover, ending script execution with die() is simply bad practice. You will not be able to give the user a proper error page and you will not be able to do any cleaning up for the rest of the script.

Comments

You wrote a very nice tutorial here. I'm going to keep all these security advices in mind.

1. John McKenzie on Jun 30, 2008 4:07:27 PM

a great article,
definitely bookmarked.

2. HoTDaWg on Jul 1, 2008 9:46:22 PM

I actually created a post in the Forums asking where to find a good tutorial on PHP security: it was right here on the home page!

3. rupertrealbear on Jul 2, 2008 6:40:55 PM

Great tutorial - explains alot of technical stuff definately recommended

4. Wasim Ilyas on Jul 11, 2008 8:36:42 AM

about the mysql injection, how would such a user find out the name of the table/structure of the table so they could put something to damage the database?

is there a way of stopping them finding out the database/table structure?

5. Flames on Jul 24, 2008 8:36:44 AM

Flames: It could be guesswork, but there are also queries that will allow you to see how the tables are laid out. It could also be an open source app, and it that case it would be as simple as checking the source.

6. Daniel Egeberg on Jul 24, 2008 10:00:29 AM

k, i've been trying to stop mysql injection and although its taken time i finally got it to work without random apostrophes being put in places :D.

7. Flames on Jul 24, 2008 11:07:42 AM

Views: 17435 lol

8. dezkit on Jul 26, 2008 9:14:19 PM

One part is missing, security problems related to emails.

9. Hervé Thouzard on Jul 27, 2008 3:11:32 AM

good stuff!

10. libertyct on Jul 28, 2008 10:44:46 AM

Im happy I found this tutorial - dont understand lots of stuff but will re-read so that it sits.

11. Dorothy Wegmueller on Aug 5, 2008 6:14:18 PM

Well, feel free to ask in the forums if there is anything specific in the tutorial you need help with :)

12. Daniel Egeberg on Aug 5, 2008 7:53:32 PM

Very nice tutorial indeed. It is very helpful for newbie's like me.

13. cyberbuff on Aug 11, 2008 4:57:40 AM

Excellent tutorial. I've had experience with other scripting languages and decided to try PHP. This is really a great start what to look out for and how to design with these dangers in mind.

One question about the include(), mostly for db access. Some showed using a config.pm that would contain passwords to the db.

Would you consider this secure?

mkdir public_html/secure
chmod 711 public_html/secure
create the config.pm containing the db access

in the php script, I add
include('../secure/config.pm');

Would it be better to not be in the document root at all?
I notice in the tree, the config dir is in the system root, not doc root.

Thanks

14. budman85 on Aug 12, 2008 8:11:32 PM

The safest way would be to not place it within document root at all.

15. Daniel Egeberg on Aug 13, 2008 11:10:32 AM

Daniel, great article, was a good read and learned a lot (implementing some of this stuff as I'm writing this.) In doing so, I've noticed that the error_reporting and error_log statements in .htaccess files seem to not work unless they are preceeded by php_value, and not php_flag as stated in the article. Feel free to correct me if I'm wrong, I just thought I'd point it out in case anyone else ran into the issue.

Thanks again :)

16. vnums on Aug 15, 2008 11:15:05 AM

the pdf article for download just gets a 404

17. Brad Floyd on Sep 17, 2008 11:13:51 PM

Ooops... sorry about that. It's back up now.

18. Daniel Egeberg on Sep 18, 2008 1:25:08 PM

cool :-) Just thought I'd point it out since security is one aspect people need to pay more attention to I agree

19. Brad Floyd on Sep 18, 2008 7:34:22 PM

However, wouldn't RFI only be an issue if PHP - Register globals is turned on?

Note some shared hosts has it disabled by default.

20. BlueBoden on Nov 11, 2008 4:20:41 AM
Login or register to post a comment.