5 PHP Security Tips

5 Helpful Tips for Creating Secure PHP Applications

Dec 26th in PHP by Justin Shreve

PHP is one of the most popular programming languages for the web. Sometimes a feature-friendly language can help the programmer too much, and security holes can creep in, creating roadblocks in the development path. In this tutorial, we will take a look at 5 tips to help you avoid some common PHP security pitfalls and development glitches.

PG

Author: Justin Shreve

Justin Shreve is a freelancer who runs his own business at serenelabs.com.

Tip 1: Use Proper Error Reporting

During the development process, application error reporting is your best friend. Error reports can help you find spelling mistakes in your variables, detect incorrect function usage and much more. However, once the site goes live the same reporting that was an ally during development can turn traitor and tell your users much more about your site than you may want them to know (the software you run, your folder structure, etc).

Once your site goes live, you should make sure to hide all error reporting. This can be done by invoking the following simple function at the top of your application file(s).

error_reporting(0);
Get rid of those public errors!

If something does go wrong, you still want and need to know about it. Therefore, you should always make sure to log your errors to a protected file. This can be done with the PHP function set_error_handler.

Sample Error Log

Tip 2: Disable PHP's "Bad Features"

From its earliest days, PHP's designers have always included some features to make development easier. Or so they thought! Some of these helpful features can have unintended consequences. I call these "bad features" because they have allowed data validation nightmares and created a pathway for bugs to finding their way into scripts. One of the first things you should do when the development process begins is disable certain of these features.

Note: Depending on your host, these may or may not be turned off for you. If you are developing on your own computer or other similar local environment, they probably won't be turned off. Some of these features have also been removed in the upcoming PHP6, but are ubiquitous in PHP4 applications and are only deprecated in PHP5 applications.

Register Globals (register_globals)

In short, register_globals was meant to help rapid application development. Take for example this URL, http://yoursite.tld/index.php?var=1, which includes a query string. The register_globals statement allows us to access the value with $var instead of $_GET['var'] automatically. This might sound useful to you, but unfortunately all variables in the code now have this property, and we can now easily get into PHP applications that do not protect against this unintended consequence. The following code snippet is just one common example you will see in PHP scripts:

if( !empty( $_POST['username'] ) && $_POST['username'] == 'test' && !empty( $_POST['password'] ) && $_POST['password'] == "test123" )
{
	$access = true;
}

If the application is running with register_globals ON, a user could just place access=1 into a query string, and would then have access to whatever the script is running.

Unfortunately, we cannot disable register_globals from the script side (using ini_set, like we normally might), but we can use an .htaccess files to do this. Some hosts also allow you to have a php.ini file on the server.

Disabling with .htaccess

php_flag register_globals 0

Disabling with php.ini

register_globals = Off

Note: If you use a custom php.ini file that is not applicable to the entire server, you must include these declarations in every sub folder that has PHP.

Flow of register global

Magic Quotes (magic_quotes_gpc, magic_quotes_runtime, magic_quotes_sybase)

Magic Quotes was a feature meant to save programmers the trouble of using addslashes() and other similar security features in their code. There are at least three problems associated with magic quotes. One problem with this helpful feature is if both magic quotes and addslashes() are used. If this is the case, then you end up with multiple slashes being added, causing errors. The second problem is if you make the assumption magic quotes is turned on and it actually is not. Then all the input goes unchecked. The third problem is that magic quotes only escapes single and double quotes, but if you are using a database engine, there are also many database-specific characters that also need to be escaped. It is recommended use that you disable this feature and use proper variable validation instead (see below).

Unfortunately, we also cannot disable magic quotes from the script side using ini_set. As with register_globals, we can use .htaccess or php.ini files to do this.

Disabling with .htaccess

php_flag magic_quotes_gpc 0 php_flag magic_quotes_runtime 0

Disabling with php.ini

magic_quotes_gpc = Off
magic_quotes_runtime = Off
magic_quotes_sybase = Off

Note: If you use a custom php.ini file that is not applicable to the entire server, you must include these declarations in every sub folder that has PHP.

Example htaccess file

Tip 3: Validate Input

In addition to escaping characters, another great to way to protect input is to validate it. With many applications, you actually already know what kind of data you are expecting on input. So the simplest way to protect yourself against attacks is to make sure your users can only enter the appropriate data.

For example, say we are creating an application that lists users birthdays and allows users to add their own. We will be wanting to accept a month as a digit between 1-12, a day between 1-31 and a year in the format of YYYY.

Having this kind of logic in your application is simple and regular expressions (regex) are the perfect way to handle input validation. Take the following example:

if ( ! preg_match( "/^[0-9]{1,2}$/", $_GET['month'] ) )
{
	// handle error
}
if ( ! preg_match( "/^[0-9]{1,2}$/", $_GET['day'] ) )
{
	// handle error
}
if ( ! preg_match( "/^[0-9]{4}$/", $_GET['year'] ) )
{
	// handle error
}

In this example, we simply checked (in the first two if statements) for integers [0-9] with a length of one or two {1,2} and we did the same in the third if statement, but checked for a strict length of 4 characters {4}.

In all instances, if the data doesn't match the format we want, we return some kind of error. This type of validation leaves very little room for any type of SQL attack.

Regex expressions like those above can be a little difficult to grasp at first, but explaining them is out of the scope of this article. The php manual has some additional resources to help you with validation. The PEAR database also has a few packages such as the Validate package to help with emails, dates, and URLS.

Below is an example of the above script in action using 200 as an input for a month, abc for the day and just 09 for the year.

Example of a validation script running

Tip 4: Watch for Cross Site Scripting (XSS) Attacks in User Input

A web application usually accepts input from users and displays it in some way. This can, of course, be in a wide variety of forms including comments, threads or blog posts that are in the form of HTML code. When accepting input, allowing HTML can be a dangerous thing, because that allows for JavaScript to be executed in unintended ways. If even one hole is left open, JavasScript can be executed and cookies could be hijacked. This cookie data could then be used to fake a real account and give an illegal user access to the website's data.

There are a few ways you can protect yourself from such attacks. One way is to disallow HTML altogether, because then there is no possible way to allow any JavaScript to execute. However, if you do this then formatting is also disallowed, which is not always an option for forum and blog software.

If you want HTML mostly disabled, but still want to allow simple formatting, you can allow just a few selected HTML tags (without attributes) such as <strong> or <em>. Or, alternatively, you can allow a popular set of tags called "BBCode" or "BB Tags," commonly seen on forums in the format of [b]test[/b]. This can be a perfect way to allow some formatting customization while disallowing anything dangerous. You can implement BBCode using pre-existing packages such as HTML_BBCodeParser or write your own BBCode implementation with regular expressions and a series of preg_replace statements.

Example of BBCode in action

Tip 5: Protecting against SQL Injection

Last, but not least, is one of the most well-known security attacks on the web: SQL injection. SQL injection attacks occur when data goes unchecked, and the application doesn't escape characters used in SQL strings such as single quotes (') or double quotes (").

If these characters are not filtered out users can exploit the system by making queries always true and thus allowing them to trick login systems.

Pesky login box being hacked

Luckily, PHP does offer a few tools to help protect your database input. When you are connected to an sql server you can use these functions with a simple call, and your variables should be safe to use in queries. Most of the major database systems offered with PHP include these protection functions.

MySQLi allows you to do this in one of two ways. Either with the mysqli_real_escape_string function when connected to a server:

$username = mysqli_real_escape_string( $GET['username'] );
mysql_query( "SELECT * FROM tbl_members WHERE username = '".$username."'");

Or with prepared statements.

Prepared statements are a method of separating SQL logic from the data being passed to it. The functions used within the MySQLi library filter our input for us when we bind variables to the prepared statement. This can be used like so (when connected to a server):

$id = $_GET['id'];
$statement = $connection->prepare( "SELECT * FROM tbl_members WHERE id = ?" );
$statement->bind_param( "i", $id );
$statement->execute();

One thing to note when using prepared statements is the "i" in bind_param. i stands for for integer but you can use s for string, d for double, and b for blob depending on what data we are passing.

Although this will protect you in most circumstances, you should still keep in mind proper data validation as mentioned previously.

Closing

This short tutorial can only scratch the surface of web security. Ultimately, it is up to developers to ensure that the applications they build are safe by educating themselves about the dangers of the web and the most common kinds of vulnerabilities and attacks. If you wish to read more about security issues in PHP, there is a section on security in the php manual devoted to them.

What are your tips?

  • Subscribe to the NETTUTS RSS Feed for more daily web development tuts and articles.


Related Posts

Check out some more great tutorials and articles that you might like

Enjoy this Post?

Your vote will help us grow this site and provide even more awesomeness

Plus Members

Source Files, Bonus Tutorials and
More for $9 a month for all TUTS+
sites in one subscription.

Join Now

User Comments

( ADD YOURS )
  1. PG

    Paul December 26th

    Oh wow. I didn’t know that Register Globals worked like that.
    Great Post.

    ( Reply )
  2. PG

    Raymond Selda December 26th

    Thank you for this reminder. The golden rule is DO NOT TRUST the user! Whenever possible sanitize all the incoming inputs.

    ( Reply )
  3. PG

    Meshach December 26th

    Thanks for this great article.

    ( Reply )
  4. PG

    Drew December 26th

    Good read. Nice job!

    ( Reply )
  5. PG

    הסעות December 26th

    wow. thank you!

    ( Reply )
  6. PG

    Roshan Bhattarai December 26th

    If you want to know more about available function in PHP for tighten security functions then you can take a look at here….

    http://roshanbh.com.np/2008/05/tighten-php-security-functions.html

    ( Reply )
  7. PG

    pepe December 26th

    The author must check his website, there are errors in wp like the ones he shows in ‘Use Proper Error Reporting’

    good post

    ( Reply )
  8. PG

    Barttos December 26th

    So and so :)

    ( Reply )
  9. PG

    Barttos December 26th

    http://serenelabs.com/ roflmao
    Warning: main(/home/serene/public_html/wp-includes/compat.php) [function.main]: failed to open stream: No such file or directory in /home/serene/public_html/wp-settings.php on line 238

    Warning: main(/home/serene/public_html/wp-includes/compat.php) [function.main]: failed to open stream: No such file or directory in /home/serene/public_html/wp-settings.php on line 238

    Fatal error: main() [function.require]: Failed opening required ‘/home/serene/public_html/wp-includes/compat.php’ (include_path=’.:/usr/lib/php:/usr/local/lib/php’) in /home/serene/public_html/wp-settings.php on line 238

    and you have to say about secure php apps with err rep(0)? :-)

    ( Reply )
  10. PG

    Justin Shreve December 26th

    Barttos & pepe:

    Good catch but minor slip up. We are working on getting a proper site up right now and the other night I was uploading wordpress. My hard drive decided to crash though and the computer shut down in the middle of the upload and I had to worry about the hard drive before I went back to the upload. Thanks for the catch though.

    Hope you guys enjoyed the article and happy holidays to everyone!

    ( Reply )
  11. PG

    PHP Security Video December 26th

    Yep, but there is so much more to this subject than that. It is just amazing how many holes can be found in PHP applications. I recorded a long 45min video covering a lot of holes most people miss. You can watch it by clicking on my name on the left.

    ( Reply )
  12. PG

    Juul Coolen December 26th

    Thanks, Justin. Very informative.

    Although I am by no means a very experienced PHP developer, I know how to set up a site with it and following some pointers make it secure.

    However one question regarding your article, what is the difference between mysqli_real_escape_string and the one without the “i”? They both seem to reference to the same function in the manual …

    ( Reply )
  13. PG

    smokiie December 26th

    Nice one. Great article.

    ( Reply )
  14. PG

    Pieter Vriesacker December 26th

    This is pretty lame, just a ripoff from some links found in the post about php posted recently.
    Write your own stuff or don’t write.

    ( Reply )
  15. PG

    Justin Shreve December 26th

    “This is pretty lame, just a ripoff from some links found in the post about php posted recently.
    Write your own stuff or don’t write.”

    This tutorial was actually submitted the same day the other PHP article came out. (My tutorial was submitted to NETTUTS on 12/23/08 at 4:44 PM and the PHP tutorial was posted on Dec 23rd as well). My tutorial was written on the 21st and 22nd and I have no access to future articles.

    Thanks.

    ( Reply )
  16. PG

    Justin Shreve December 26th

    @Juul Coolen

    Mysqli should be used if you are running MySQL 4.1. It is updated and recommended. It also provides an OOP interface and some additional features.

    ( Reply )
  17. PG

    Jason December 26th

    Nettuts rocks big time! Its my first time here and there is a hell of a lot of tuts i have to go through! ;-)

    Btw, can anyone by any chance cook up a tutorial on how to create an order form like on this site http://markup-service.com/Order/Create! Not the layout actually, but the way it calculates the amount while selecting and it dynamically outputs the sum without refreshing the page! I know its a combination of php-javascript and i am not really good at that!

    Hopefully someone might help!

    Neways, long live Nettuts!

    ( Reply )
  18. PG

    kucluk December 26th

    wew…
    nice tips..

    i like it

    ( Reply )
  19. PG

    Raphael Caixeta December 26th

    For protecting against SQL injections and XSS you should always build up your own “Safe Data” function that gives your website and users the security you wish for.

    ( Reply )
  20. PG

    Chris Gunther December 26th

    Nice overview of some basic security needs of a PHP application.

    ( Reply )
  21. PG

    Honour Chick December 26th

    wow… awesome article. thxs :)

    ( Reply )
  22. PG

    Ahad December 27th

    Where there are hackers, there will be holes for those hackers to find…Just look at what happened to the Data Mapping on IE. Poor Microsoft, I kinda feel bad for them.

    Good article bro, and Nettuts!

    @Pieter give the man some credit, even if parts are taken from here and there => its the web!!

    ( Reply )
  23. PG

    Maor Hazan December 27th

    What about Cookie Manipulation?

    ( Reply )
  24. PG

    DKumar M. December 27th

    Informative article Justin.. thanks for sharing.

    ( Reply )
  25. PG

    Kevin Quillen December 27th

    Use an established framework like Cake or Zend

    ( Reply )
  26. thanks for the detailed list!
    security gets out of sight much too often.
    so thanks for your “reminder” ;-)

    ( Reply )
  27. PG

    Jordan Ryan Moore December 28th

    You really shouldn’t set “error_reporting” to “0″. Instead, you should set “display_errors” to “0″.

    ( Reply )
  28. PG

    max December 28th

    Security is the backbone of any good web app – to be taken very seriously !

    ( Reply )
  29. PG

    www.askyourpc.com December 28th

    Security issues are important to cover and many PHP books seem to dismiss it. I am thinking about making some PHP based websites and found this article to be very clear. Many articles like this are sometimes boring but this article, along with its nice graphics helped!

    ( Reply )
  30. PG

    Zach Dunn December 28th

    This was a good read. You broke down the points nicely. PHP error mess is one of the worst things to see as a page loads.

    ( Reply )
  31. PG

    fadhli December 29th

    thanks..

    ( Reply )
  32. PG

    insic December 29th

    great tips. very usefull.

    ( Reply )
  33. PG

    Ignas December 29th

    Nothing new, but it’s good to remember it :) I ques I can’t forget these and the golden rule – do not trust anyone :D And of course the article is very good for the beginners. Nice job!

    ( Reply )
  34. PG

    Matt December 29th

    Nice introduction to the topic!

    Was a nice read! Good Job!

    ( Reply )
  35. PG

    Hamy December 30th

    Regex Buddy’s site really helped me understand regexes quickly and quite well. I did end up purchasing their software, because I was hired to make regexes so it became worth it.

    Their articles are some of the best I have found

    http://www.regexbuddy.com/

    ( Reply )
  36. PG

    Drupaller December 31st

    Thanks. Great article. Speccialy , “Magic Quotes” section.

    ( Reply )
  37. PG

    RaiyaRaj December 31st

    Good work

    ( Reply )
  38. PG

    skn December 31st

    nice one

    ( Reply )
  39. PG

    Timothy December 31st

    Nice tutorial. Delicioused

    ( Reply )
  40. PG

    Meint Post January 1st

    Regarding “Validate Input” instead of rolling your own functions via regular expressions (which tend to be quite slow) why not use either ctype functions, http://nl3.php.net/manual/en/ref.ctype.php, or php filters, http://devolio.com/blog/archives/413-Data-Filtering-Using-PHPs-Filter-Functions-Part-one.html

    These functions are especially developed for input validation

    ( Reply )
  41. PG

    Askold January 5th

    Shit Register Globals. Some time ago I had problems with them.

    ( Reply )
  42. PG

    eltey January 6th

    this is wonderful tutorial .. i read it 3 times and get a fantastic results and sure i put a
    copy of this lesson on my site here
    http://www.keenzy.com

    ( Reply )
  43. PG

    Janckos January 6th

    Gracias por los consejos.

    ( Reply )
  44. PG

    ButterflyOfFire January 6th

    Nice article ;) thanks

    ( Reply )
  45. PG

    mbreti3gut January 6th

    great post. thanks

    ( Reply )
  46. PG

    windir January 7th

    Great tut!

    I’ve always used register globals on, just to make things easy during development.
    I’ll probably turn it off in my future projects, it’s not that much work to add the $var = $_GET['var']; for all the variables at the top.

    When it comes to the mysql query protection i allways use

    $query = sprintf(”SELECT * FROM table WHERE field1 = ‘%s’, field2 = ‘%s’”,
    mysql_real_escape_string($field1),
    mysql_real_escape_string($field2) );
    $sth = mysql_query($query);

    It seems to work pretty nice :)

    ( Reply )
  47. PG

    qlb-qs January 7th

    this is wonderful tutorial .. i read it 3 times and get a fantastic results and sure i put a
    copy of this lesson on my site here

    http://www.qlb-qs.com

    ( Reply )
  48. PG

    almamlakatain.com January 8th

    this is wonderful tutorial .. i read it 3 times and get a fantastic results and sure i put a
    copy of this lesson on my site here

    ( Reply )
  49. PG

    Sean January 8th

    Great tips

    You see some of these all the time.

    It’s hard to imagine any website still gets hit by SQL injection, but they do.

    I’ll be linking to this article from my site, http://www.seangw.com/wordpress/

    Thanks!

    ( Reply )
  50. PG

    Fritzo January 12th

    Love it.

    ( Reply )
  51. Great tips.

    ( Reply )
  52. PG

    Araba Oyunları January 19th

    Nice tutorial. Thanks.

    ( Reply )
  53. PG

    Freeman February 1st

    Great reference. Thanks for putting it together. As a newbie to php, I find I work best with cheat sheets. Since I always have my iPhone with me, I keep them there. The best one I’ve found so far is from these guys:

    http://itunes.apple.com/WebObjects/MZStore.woa/wa/viewSoftware?id=302760278&mt=8

    They also have great cheat sheets for CSS and Javascript. Hope this is helpful.

    ( Reply )
  54. PG

    kaint February 16th

    Thanks for this tutorial.

    ( Reply )
  55. PG

    durbah March 2nd

    منتديات , درة , البحرين , قصص , جرائم , رقية شرعية , علاج الجن والسحر والعين والحسد ., برامج , حماية ,الله , الله واكبر, الحمدالله , محمد , رسول , رسول الله , صلى , عليه , وسلم , الكنز , الجنه , النار , الشمس , القمر , الارض , السماء , الصباح , الظهر , المغرب , العصر , الصباح , العشاء , الفضاء , البرامج , التصميم , القصص , الافلام , المسلسلات , المسرحيات , برامج , تصاميم , هواتف , ماسنجر , فوتوشوب , الكاسبر , الحماية , النسخ ,الاسلام , الفن , الادب ,الهوى ,الاسرة ,آدم , حواء , الطفل ,الاطفال ,الرجل ,الشباب ,البنات ,الفتيات ,الاطفال ,الديكور ,الرياضه ,المسابقات ,الغاز,فوتوشوب,ارقام, الصلاه, المغرب, الكعبة, الرقية الشرعية, الرقيه,الجن,السحر,المس,الحسد,العين,العلاج,الامراض,الاعشاب,التداوي,الهلاك.الملائكة,الانبياء,الصحابة,عمر,ابوبكر,عثمان,محمد,علي,احمد.الثقافه,الثقافه الجنسية,الاسرة,الزواج,المتزوجين, العظيم,القادر,البحر,الحب,الشعر,اغاني,افلام,الدين,حياة الشعوب,الرقص,الدنيا,الاخره,الهوى,المدير,التحكم,السفير,الطيور,الحمام,الاسود,الصور,

    ( Reply )
    1. PG

      Jona Goldman March 4th

      Whats this?? I mean, if you can read this article you can also comment on it in English so we can all understand what you are trying to say.. I think it’s fair enough…

      ( Reply )
  56. PG

    G March 2nd

    Good job!

    ( Reply )
  57. PG

    Rashmi March 3rd

    Informative article

    ( Reply )
  58. PG

    youyou March 17th

    nice!

    ( Reply )
  59. PG

    kalis March 23rd

    jaa diezgan jauki, bet tā čaļa video daudz interesantāks :) PHP Securituy video :P

    ( Reply )
  60. PG

    Oyunlar1 March 24th

    Thank you , very good

    ( Reply )
  61. PG

    Rashid Ahmed March 26th

    Thanks Justin

    you have sharing great tips

    Some things are new for me like sql injection that i learned by this article.

    ( Reply )
  62. Nice dude! These 5 tips will make me out more alert to be a good developer. Thanks a lot.

    ( Reply )
  63. PG

    Raja MM May 21st

    Nice tips… but it is better to come with more details…i.e. expecting more tips and more details…

    ( Reply )
  64. Really like these tips and I will endevour to use all of them in my code. Just recently we had someone trying to hack an e-commerce site we made a while back, so we had to put these walls in place.

    Great post, thanks!

    ( Reply )
  65. PG

    Isaac May 29th

    Good starting tips for securing a PHP application. Also, the comments on here have been great at expanding on the information given in the article. Since a comment wouldn’t be complete without useful contribution:

    1. A BBCode implementation must still be secured against HTML injection. Using htmlentities() should take care of a great deal of it, but you’d probably also want to look for the =” combination, as this could be the start of an inline Javascript event handler.

    2. The regex expressions you supplied are mostly secure from user input, but could still trigger an error due to invalid input being converted through a date format for a query. The following regex expressions are (possibly) more accurate:

    1. if ( ! preg_match( “/^(([0]{1}[0-9]{1})|([1]{1}[0-2]{1}))$/”, $_GET['month'] ) )
    2. {
    3. // handle error
    4. }
    5. if ( ! preg_match( “/^(([0-2]{1}[0-9]{1})|([3]{1}[0-1]{1}))$/”, $_GET['day'] ) )
    6. {
    7. // handle error
    8. }

    Great read. Keep up the good work. :-)

    ( Reply )
  66. PG

    Shahriat Hossain June 4th

    Nice post, I am trying to use mysqli prepared statement everywhere in my development work from now which really will help me to get the right protection of code injection :)

    ( Reply )
  67. PG

    Webhostright June 15th

    Thanks very much for the tips on security, would like to see even more security related tips,tutorials and information.

    ( Reply )
  68. PG

    rat32 August 14th

    wonderful information

    ( Reply )
  69. PG

    Matthew Mancuso August 30th

    Thanks for the article :D … I’m thinking of making a website and I’m hoping it’ll get pretty big, so even just these 5 tips give me a lot to think about ;)

    Thanks again and keep up the good work ;)

    ( Reply )
  70. PG

    Patel BhaviK October 21st

    Great going.
    That was really good.

    Thanks for this.

    ( Reply )
  1. Arrow
    Gravatar

    Your Name
    October 21st