5 More PHP Security Vulnerabilities

In a previous article, I talked about some common security vulnerabilities that can affect your PHP web application. But there are other things besides those ten (okay, seven) attacks to think about when you’re developing. And so, this article offers a compendium of miscellaneous things that are security related; things you should do, things you shouldn’t do, things that other people might try to do, whatever it takes to make an article long enough for my editor to be satisfied with it.

PHP’s Security Profile

Sometimes when you’re hanging around in a bar you hear people (not the regulars, of course, but folks who just wander in) insinuating that PHP is not a very secure language. In the past, there were some legitimate grounds for such feelings.

Most of the historical problems can be traced to poor default settings. A good example is register globals which was deprecated in 5.3 and then removed entirely in 5.4. There’s no sense talking about it; it’s no longer an issue, but let’s just say that it gave people the opportunity to write potentially insecure code. Insecurities that were a result of php.ini settings have now been mostly neutralized or else are better publicized.

One big thing in PHP’s favor is that it’s a server-side language. Unlike JavaScript or HTML which is executed directly in the user’s browser or which the code can be viewed, PHP code is interpreted on the server with only the results sent to the browser. Of course if the server is misconfigured it could cause PHP code to be downloaded to the browser, but that’s not the sort of thing that happens every day.

One thing that sometimes gets in PHP’s way, but which is also one of its strengths, is its flexibility. Not everything has to be just such and so for a PHP app to run. You can have loose ends and the code will still work. Unfortunately, loose ends are what keep hackers in business, and so writing secure code often starts with starting with tight, technically correct code.

Filter Input Data

It sounds so simple: inspect data that is entered on your web pages and make sure it is not dangerous. Like who wouldn’t do that, man? The answer is a surprising number of people.

There is a certain school of thought that argues that anyone who is a computer science major should be subjected to a semester’s worth of scams. You know, people coming up to them and offering to give then $100 if they can just advance them the $20 required to get their “check” cashed. Or emails from a lawyer somewhere telling them that a relative they have never heard of has just left them 25-million pounds and all they need to do to claim it is to send their bank information. The goal, of course, is to teach computer people that the world is not a friendly place. We set up a 256-character area for the user to enter comments and some people will use that space to enter SQL commands in an attempt to do an SQL injection attack. Is there no honor anymore?

If you set up a page that allows any type of free form entry on it, you need to review that input carefully and make sure that you keep anything bad from being entered. Use the function filter_input() to ensure that bad people don’t put bad things in your forms (or even in your files if your input comes with a file transfer or some other I/O operation).

Fortunately, there are already two great articles on SitePoint related to that. The first is Input Validation Using Filter Functions by Toby Osbourn, and the second is ClamAV as a Validation Filter in Zend Framework by Matthew Setter. Between these two articles you should be able to get the lowdown on the downlow about this potential problem.

Error Reporting

You might want error messages to appear on your screen during development to give you a hint when something goes wrong with your script. But when you are in production, do you really want this to happen? Every bit of information is life food for a hacker, and even information about your failures may be helpful to those whose only desire is to destroy your site. Consequently, you should never display errors on the screen when you are in production.

How do you control this? Thank goodness the php.ini file is there because it is for just such needs as this that it exists. In fact, there are four flags that can be set in php.ini to configure error reporting just as you like.

  • error_reporting – this flag decides whether you want to know about errors or not. Obviously, you want to know about everything, so the sensible thing is to set this to E_ALL in both production and test.
  • display_errors – this flag indicates whether you want the error messages to be displayed on the screen or not. Set this to “on” during development and “off” for production. There’s no sense in giving hackers any more info than necessary; let them do the heavy lifting.
  • log_errors – indicates whether errors should be logged to a log file. Obviously, you would want this on for production.
  • error_log – indicates the path to the file where the error messages will be written to. Obviously, for this to take effect, we need to have log_errors turned on.

With these flags set, you can get all of the information you need during testing while yet protecting yourself in production.

Session Fixation

In the previous article I talked about session hijacking, but there is another type of attack that can happen to your sessions: session fixation. Fixation is where you are tricked into using a session ID provided by the hacker. How does this happen?

Generally session fixation occurs when you click on a link that contains a PHPSESSID parameter that carries the session ID an attacker wants you to use. That link could take you to a form on which your identity is verified and now your identity is tied to the session ID the attacker has given you, allowing them to view any page in your site and access the data associated with that page.

Fortunately, preventing this is relatively easy. Start by checking out the following values in php.ini:

  • session.use_cookies – controls the persistence of the session ID when cookies are used. It should be set to 1 or not set at all.
  • session.use_only_cookies – keeps the ID from being overridden by GET parameters and should be set to 1.
  • Session.use_trans_sid – makes PHP change the output so that the session ID in links, etc. will persist. This should be set to 0.
  • session.name – the name of the session parameter. Generally this is set to “PHPSESSID” and knowing that makes it a little easier for hackers. Change this to a more obscure value.

Also, a good rule of thumb to follow is to always perform a session_regenerate_id() call just before the redirect to any request to authenticate the users. This will ensure that the session ID is not the one provided by the attacker and help protect you from this situation.

User Data Concerns

If your site makes use of user data in any way then you have additional problems you need to keep an eye out for. First and probably foremost you want to think about the data traffic to and from the browser and server. This is an easy target many times, especially with so many people working on public open wireless networks. You might want to consider using an SSL connection (HTTPS for example) on your web site for all of your transactions. Even encrypting the database connection that PHP establishes may not be out of the question depending on the nature of your configuration.

Another thing to be concerned with in this area is the ability of an attacker to steal passwords from your database. This is slightly outside of the scope of this article since it deals mostly with how to encrypt your passwords so that they can’t be easily recovered using rainbow tables or other attack methods. I suggest checking out the article Why You Should Use Bcrypt to Hash Stored Passwords by Callum Hopkins for more information.

Summary

Is this all? I mean if you do everything in both this and my previous article will your site be secure? Yeah, sure. And keep all your money in a paper bag and carry it with you at all times.

There are more ways to circumvent security than there are, well, than there are lots of other things. In the end, if you only do one thing, think suspiciously about ways that you could be vulnerable, and don’t trust any user input unless it is scrupulously scrubbed.

Yeah, I know that was two things. Do you get my point about not trusting people?

Image via Fotolia

Free book: Jump Start HTML5 Basics

Grab a free copy of one our latest ebooks! Packed with hints and tips on HTML5's most powerful new features.

  • João Ferreira

    Great post David, I was wondering here, what are the benefits of using a Bcrypt over something like SHA1+salt, am I truly safer with Bcrypt?

    • Dave Shirey

      To be perfectly honest, I am probably not expert enough to answer this – but what the heck. A quick perusal of the literature seems to favor BCRYPT. The main points behind it are first, that it is backed by BLOWFISH, second that it is a password Hash vs a cryptographic hash like SHA1, and third that it is slow which means that every try by a would be hacker takes much longer than it would under SHA1 or other schemes and so the cost to the hacker for trying to penetrate that site is much higher. I would suggest taking a look at a very nice article by Brian Krebs, http://krebsonsecurity.com/2012/06/how-companies-can-beef-up-password-security/, for a good take on this issue. Or you can google ‘BCRYPT vs SHA1′ for a variety of opinions.

      • http://www.damiengrass.com Damien

        That’s pretty much correct. BCRYPT is generally favoured because it’s slow (not that that is the only reason) and it works. It’s been around for 10 years and hasn’t had a problem before nor has it been compromised. No matter the password, nor the processing power on the machine, BCRYPT will always take the same amount of time, inevitably slowing down of the number of attempted password cracks per X amount of time. BCRYPT is designed to keep up with Moore’s Law http://en.wikipedia.org/wiki/Moore's_law – there are a few more details about BCRYPT that make it the best to use that I won’t go in to. The points you’ve supplied, Dave, about Blowfish, etc are also correct.

        Hope that helps!

  • Les

    Simply put we’ve had this before have we not? Apart from filtering user input you need to validate a unique hash against any POST data on every submit and also to validate against a unique hash held in the SESSION on every request.

    The SESSION hash is usually a hash of the browser user agent so if the hash don’t match you kill off the session. Also for user submitted content if its not required to be searchable, I would encode the content via base64, after filtering of course… just in case something was missed.

    • Dave Shirey

      Sounds like good advice, Les. Thanks.