In Writing Secure PHP, Writing Secure PHP, Part 2 and Writing Secure PHP, Part 3 I covered many of the common mistakes PHP developers make, and how to avoid some potential security problems. This article covers some of the more advanced security problems common to PHP on the web.
Cross-Site Scripting (XSS)
Cross-site scripting (often abbreviated to XSS) is a form of injection, where an attacker finds a way to have the target site display code they control. In its most basic form, this can be as simple as a site that allows HTML characters in usernames, where someone can specify a username like:
DaveChild<script type="text/javascript" src="http://www.example.com/my_script.js"></script>
Now, whenever someone sees my username on the target site, the script I've added to my username will run. I could potentially use this to grab the person's login information, log their keystrokes - any number of nefarious activities.
As a developer, you can combat this type of attack by encoding or removing HTML characters (watch out for character encoding issues, as outlined next). Even better than stripping out unwanted characters is to allow a whitelist of safe characters in usernames and other fields. Be especially careful with e-commerce sites where you are listing orders in a CMS - an XSS vulnerability may allow an attacker to gain administrative access to your CMS. It is also important to turn off TRACE and TRACK support on the server, as if there is a vulnerability (and always assume that despite your best efforts there will be) these potentially allow an attacker to steal a user's cookie.
As a user you are also vulnerable to this sort of attack, and it is very difficult, at the moment, to make yourself safe against it. Vigilance is key, and to that end I have released a userscript that warns you about third party scripts (for users of GreaseMonkey, Opera or Chrome).
Cross-Site Request Forgery (CSRF)
Despite the similar name, CSRF is unconnected to XSS. CSRF is a form of attack where an authenticated user performs an action on a site without knowing it.
Let's assume that Jack is logged in to his bank, and has a cookie stored on his computer. Each time he sends an HTTP request to the bank (i.e., views a page or an image on a page) his browser sends the cookie along with the request so that the bank knows that it's him making the request.
Jill, meanwhile, runs a different website and has managed to get Jack to visit it. One of the items on the page is in fact loaded from the bank, for example in an iframe. The URL of the iframe or request contains instructions to the bank to transfer money from Jack's account to Jill's. Because the request is coming from Jack's computer, and includes his cookie, the bank assumes it is a legitimate request and the money is transferred.
This type of attack is extremely dangerous and virtually untracable. As a developer, your job is to protect against it, and the best way to do that is to remember Rule Number One: Never, Ever Trust Your Users. No matter how authenticated they are, do not assume every request was intended.
In practical PHP terms, you can combat CSRF with several relatively simple coding habits. Never let the user do anything with a GET request - always use POST. Confirm actions before performing them with a confirmation dialog on a separate page - and make sure both the original action button or link and the confirmation were clicked. Even better, have the user enter information like letters from their password on the confirmation page.
Add a randomly generated token to forms and verify its presence when a request is made. Use frame-breaking JavaScript. Time-out sessions with a short timespan (think minutes, not hours). Encourage the user to log out when they've finished. Check the HTTP_REFERER header (it can be hidden, but is still worth checking as if it is a different domain to that expected it is definitely a CSRF request).
Character Encoding
Character encoding in PHP and associated database systems is worthy of its own series. In any one request, there may be more different character encodings in use than you might think.
For example, a single request and response (uploading a file to a server and writing information to a database) may involve all of the following differently items with different character encodings: the HTTP request headers, post data, PHP's default encoding, the PHP MySQL module, MySQL's default set, the set of each table being used, a file being opened and read, a new file being created and written, the response headers and the response body.
English-speaking developers generally don't have much cause to get embroiled in character encoding issues, and that results in a lot of developers with a serious lack of understanding of how character encodings work and fit together. For those that do have a reason to look at character encodings, usually that interest ends with the setting of the response's character set.
However, character sets are a fundamental part of all web development. English alone can exist in any one of a wide variety of sets, and developers are usually familiar with the most common two: ISO-8859-1 and UTF-8. Fewer are familiar with UCS-2, UTF-16 or windows-1252. Still fewer are familiar with commonly used alternative language sets (e.g, GB2312 for Chinese).
Which, in a very roundabout way, brings me on to the security pitfalls of character encodings. Where data is processed by PHP using one character set, but a database server uses a different character set, a character (or series of characters) deemed safe by PHP may in fact allow SQL injection against the database.
PHP security expert Chris Shiflett has written about this issue and included an example of how it can be exploited to allow SQL injection even where input is sanitized using addslashes().
The solution is to always always use mysql_real_escape_string() rather than addslashes() (or use prepared statements / stored procedures), and to explicitly state character sets at all stages of interaction. Ideally, use the same character set throughout your system (UTF-8 is recommended) and where PHP allows you to specify a character encoding for a function (e.g., htmlspecialchars() or htmlentities()), make use of it.
It's not just SQL that's vulnerable as a result of character encoding bugs. Cross-site scripting is possible even where HTML characters are escaped if character sets are not handled properly. Fortunately, once again that is simple to avoid by properly setting character encodings at all stages of the process and specifying character encoding for functions where possible.
57 Comments
Good article. I just read through your previous articles in the series - good stuff. Security techniques and practices are very important on today's web. Keep it up!
Replies: #34 and #43.
#1, Grant Palin, Canada, 11 September 2008. Reply to this.
I think you should update part 1 of the series - the make_safe function uses addslashes instead of mysql_real_escape_string.
#2, Arne Hormann, Germany, 12 September 2008. Reply to this.
@Grant: Thankyou.
@Arne: Well spotted. Updated :)
#3, DaveChild, United Kingdom, 12 September 2008. Reply to this.
i use this http://htmlpurifier.org/
#4, xdrive, United States, 15 September 2008. Reply to this.
Nice article - keep it up :) +fave
#5, Petr Holub (Tigu), Czech Republic, 15 September 2008. Reply to this.
Excellent article. Myself I hadn't really thought of the security concerns around character encodings, but I guess I was always partially safe for using the right escape function :)
#6, Peter Haza, Norway, 17 September 2008. Reply to this.
As a new developer this info is great to have! Thank you!!
#7, Rob, United States, 19 September 2008. Reply to this.
Thank you for the insite into cross-site forgery, it is a pity we have to protect so much against people that are bored with there lives and want to disrupt others.
Thanks for your hard work keep it up..
All the best from Alan
#8, Anonymous, Canada, 25 September 2008. Reply to this.
Wow. So many things I had to fix. Fortunately, I mostly just added a few lines of code to some files.
And the idea of putting a blank index.html in all folders was nice.
Thanks!
#9, O. Soteland, Norway, 29 September 2008. Reply to this.
A great (and simple) way to prevent session hijacking is to record the IP address of the user when they login in the session alongside the other data you want to store.
Every time a user visits a page, compare their IP address to the one stored in the session. If they dont match then destroy the session and let them know what's happened.
This can prevent 99.9%(*) of session hijacks.
(* 56.8% of all statistics are made up on the spot)
#10, James Moss, Unknown, 12 October 2008. Reply to this.
Great stuff Dave =) I wrote an similar article on security with ASP .. I read your "Secure PHP" and that gave me the idea, so thanks =P
#11, Jason Gaved, United Kingdom, 26 October 2008. Reply to this.
This is a very beautiful website, I have enjoyed my visit here very much. I?m very honoured to sign in your guestbook. Thanking you for the great work that you are doing here.
#12, Security, Unknown, 27 October 2008. Reply to this.
I think this is an amazing article, for those ASP and PHP based wesbites which have loop holes for security. really informative.
#13, Mike, India, 2 November 2008. Reply to this.
great article for anyone who is struggling with ASP
#14, suzanne, United Kingdom, 5 November 2008. Reply to this.
Great set of articles written in plain English. Thanks for making it so easy to understand!
#15, Ewan, Scotland, 20 November 2008. Reply to this.
Very nice summary of secure php coding.
A good reading and kind_a must read for beginners.
i follow this rule of thumb:
- mysql_real_escape_string() or intval() before query any userdata into database.
- htmlspecialchars() _every_ user input before output to browser
- use one-time-tokens in forms to prevent csrf.
Regards
Gizmore
#16, Gizmore, Unknown, 21 December 2008. Reply to this.
This is a good article. I have already deployed similar methods to combat #1 mentioned above for similar reasons, and will look into the other two.
#17, Pete, United States, 24 December 2008. Reply to this.
I knew about PHP security. But this artical stocked my inventory.
Thank for writing this article.
#18, Bapi, Unknown, 10 January 2009. Reply to this.
This is a very good article. I am setting up a database centered website for the first time and knew I had to watch out for security issues. This article has certainly made me feel better, but still paranoid...Thank You!
#19, Daryll, United States, 15 January 2009. Reply to this.
I have just finished reading this series and am blown away at how much I did not know, even though I have been a PHP developer for about 2 years. I am going to learn a lot more about PHP security now.
BTW, You are an excellent writer! These articles were easy to understand. Thank you SO MUCH!
#20, Renee, United States, 15 January 2009. Reply to this.
Thanks all :)
Replies: #43.
#21, DaveChild, United Kingdom, 16 January 2009. Reply to this.
Excellent Article
Thank You Very Much
#22, Mahesh Bisen, India, 20 January 2009. Reply to this.
I just noticed you had part 4 up. Parts 1-3 have been a holy grail around here for our PHP developers. Thank you for your excellent contributions to the dev community with these short and very informative articles.
#23, Martin, United States, 4 February 2009. Reply to this.
Great article! I'm in the middle of writing a login script for my photo album and this article has got me to think seriously about security. Thanks.
#24, Mexabet, Australia, 5 February 2009. Reply to this.
Great article! I'm gona change my programing-habits after reading this series. Keep em coming!
#25, Monkeybrain, Norway, 21 February 2009. Reply to this.
Having been attacked by "script kiddies" many, many times, I know the importance of what you have said here.
I haven't learned php as of yet, but your articles were very easy to understand - even for a novice like myself. I'm going to keep this article in mind as I do start to learn & I'm also going to use these recommendations - where needed - on my various sites.
Thanks for a great set of articles!
#26, Corey, United States, 22 April 2009. Reply to this.
Thanks for the great advice, had been struggling alot!
#27, Joe London, Unknown, 7 May 2009. Reply to this.
An excellent well written article. In the rush many programmers adopt the " I'll make it secure later" and never get round to it, they should know better. This article highlights the importance and should be taken to heart by all, not just the novice.
#28, Andrew Sweeney, United Kingdom, 23 June 2009. Reply to this.
htmlspecialchars() is good for getting rid of XXS. someone typese in
username<script type='text/javascript' src='www.evil.com/exploit.js'>
that is how it would appear. and it would be blantently obvoius. Also you may consider grabbing the person's ip address and date they signed up so you could possibly track them down later if they do do damage.
just make sure to sanatize the ip adress, ive heard of people spoofing an ip so it turns into a sql injection
#29, Eric, United States, 30 July 2009. Reply to this.
Just wanted to say, whilst a lot of this information is available in various places on the net, this is a very all encompassing series of articles well done. As a PHP programmer since around 1999/2000 I have experienced pretty much all of the flaws in security that you mention through my own 'self taught' PHP programming career.
I've worked on some very high traffic sites and cracks in the past have brough my server to it's knees, I learned the hard way about not trusting end users and constantly checking for tainted input from them.
I think your articles will help many to take the right road to good secure design using what I consider the best lanuages and databases available in both PHP and MySQL.
Keep up the good work!
#30, CP, United Kingdom, 11 August 2009. Reply to this.
it's agood idea if you included an example too, so we can adjust our website script, thank,s
#31, alan w.r, Australia, 8 September 2009. Reply to this.
Some very important security tips listed here.. great reference site all round
#32, ???????????? ? ??????, Cyprus, 20 January 2010. Reply to this.
pdf won't open - http://www.addedbytes.com/cheat-sheets/download/regular-expressions-cheat-sheet-v1.pdf
#33, John, USA, 26 January 2010. Reply to this.
You should also stress in you article#1 the point made by Andrew Sweeney about 'I'll make it secure later'.
Also use of sprintf(" ....", %...) in making SQL strings should me mentioned.
Excellent article indeed!!
#34, Sachin, India, 14 July 2010. Reply to this.
Some great advice across all of the secure php articles, good technique.
#35, Essex Web Design, UK, 20 September 2010. Reply to this.
Very enlightening article! Thank you! I'm a recent Web Development graduate and really needed this to send me off in the right direction in my PHP/MySQL journey. Not enough time was spent, unfortunately, on the security side of things during my degree. Again, thank you!
#36, Tim, USA, 29 October 2010. Reply to this.
This is an excellent post, only recent getting back into PHP on the side, as my daily tasks usually revolve around Coldfusion. Definitely bookmarked for future reference.
#37, Thomas Craig Consulting, Canada, 16 November 2010. Reply to this.
Another successful series Dave. It was very informative and helpful, especially since I am just starting to move into more PHP. (I deal with a lot of people running wordpress sites and security is always an issue) I'll certainly be bookmarking this series to use for troubleshooting purposes later on.
#38, Stan, Canada, 8 February 2011. Reply to this.
Excellent article, I learned a few thing there :)
#39, Shaun, Uk, 6 March 2011. Reply to this.
Genius set of articles, really opened my eyes and I thank you for making me a better coder.
I've always tried to avoid security issues, but you've highlighted issues I wasn't aware of. Looks like Ive got some backtracking to do!
Cheers
#40, dsom73, UK, 24 March 2011. Reply to this.
GReat series, just read all 4 parts. One of the best round ups of PHP security I've seen - well written and very informative. Thanks!
#41, Rob Allport, England, 1 April 2011. Reply to this.
you just shave 3 hours of my working hour
#42, Rezon, NL, 25 May 2011. Reply to this.
Wow! i really enjoyed your series on security. I have learnt alot. i wish you could post more.
#43, Wojega Allan, Uganda, 8 July 2011. Reply to this.
Dave, you are a terrific writer, and your technical knowledge and grasp of all the issues related to website security are simply amazing. I read about security and do training on it all the time, but I had never seen such a clear, understandable, practical, and sensible explanation of how the various attacks work and how to defend against them until I found your site. Thanks for your work!
#44, Jeffrey Fox, U.S., 1 September 2011. Reply to this.
Now I read all yours security articles and I can say thanks for all you prepare and share. All articles are very useful and well written so I will suggest them also to others.
#45, Bojan, Slovenia, 9 November 2011. Reply to this.
its reallly a grt job by you . thanks a lot
#46, raj, 28 November 2011. Reply to this.
Dave, this is probably the best set of PHP security articles I've ever read. You keep it simple, while showing the impact each coding example can have on a sites security. I'm going to check out your other articles now. Thanks again!
#47, Clive Walkden, United Kingdom, 5 January 2012. Reply to this.
Brilliant,
So many things to learn.
Thanks for this great piece of work It will help big time
#48, Abdul Wadud Mohammad Mohibur Rashid, Bangladesh, 25 January 2012. Reply to this.
very very useful information for noob developer like my self, thanks a lot
#49, Kris, Indonesia, 2 March 2012. Reply to this.
I'm not sure where even to begin here. Just a few things of the many that stuck out at me over parts 1-4:
You talk about logins and don't mention password hashing/salting and have an example that uses unhashed/unsalted passwords.
You mention Captcha as a method to protect against brute force attacks, it is not such a protection and is merely annoying/limiting. Limiting the number of tried before locking the account is sufficient.
On this page you don't mention using a server-side generated secret that is passed in on forms and re-checked to protect against CSRF, your own method offers limited protection at best and is annoying as well.
You don't mention Prepared Statements when talking about SQL injection.
You don't mention that ' and " are a problem for XSS when placing user entered data into URLs and otherwise inside tags. (<a href = "[value]"> where [value] = [" onMouseOver="oops();])
The problems go on and on. You really shouldn't write blog posts about security when you don't really understand it yourself. You'll get newbie coders hacked, because they will trust you and won't even begin to see it coming.
I see that you mean well, and you do offer up some good info mixed in. You need to go back through and make damn sure that you hit all the bases with this. You can't do security half way, and there are automated scripts that can easily find sites using the code tips you give here and exploit them without human interaction to setup proxies or otherwise zombify their servers.
#50, DampeS8N, USA, 18 April 2012. Reply to this.
DampeS8N: Thanks for the feedback.
Good point about salting - will add that in. I'll also add a note to the XSS section to make it clear that it applies to all user input, not just forms.
Prepared statements are great - and weren't part of MySQL until several months after my post. I'll go back and add them though, as they are very useful.
#51, DaveChild, United Kingdom, 25 April 2012. Reply to this.
Hive Dave,
can you please tel me if you know a secure php freamwork to develope my application
#52, johana, usa, 7 May 2012. Reply to this.
As a user you are also vulnerable to this sort of attack, and it is very difficult, at the moment, to make yourself safe against it
#53, Martin Varesio, Usa, 19 June 2012. Reply to this.
Nice article.. Thanks a lot.. :)
#54, Nilesh G, India, 20 October 2012. Reply to this.
You know i was about writing my series of articles in series just like this and infact i have written 1 or 2. exactly same point like yours. i guess i ll just stop and share your url. Nice security tips. thanks a lot.
#55, Iginla Omotayo, Nigeria, 28 October 2012. Reply to this.
Thank you Dave for your valuable articles.
I heard something about session hijacking. Maybe it's a good topic for your 5th part of Secure PHP.
#56, Mohammad, Iran, 18 November 2012. Reply to this.
I am in the center of composing a sign in program for my scrapbook and this content has got me to think seriously about protection.
#57, Emery Hutchinson, U.S.A, 13 May 2013. Reply to this.