Powered by Invision Power Board
 Welcome Guest ( Log In | Register )

saboteurweb.com main | Help | Search | Members | Calendar

 
Lessons in PHP, part 1
« Next Oldest | Next Newest » Track this topic | Email this topic | Print this topic
Saboteur
Posted: Feb 23 2006, 03:41 PM


saboteurweb.com sysadmin


Group: Admin
Posts: 319
Member No.: 1
Joined: 8-October 02



The 3 most important things to remember when writing secure PHP code:

1) Never trust any data that is in any way, directly or indirectly, derived from the user.

2) Never trust any data that is in any way, directly or indirectly, derived from the user.

3) Never trust any data that is in any way, directly or indirectly, derived from the user.

If you know how to apply these 3 simple rules, you're well on your way to becoming a successful web application developer.

(Guess who spotted some potential exploits in quite a few of his applications today?)

The implications of neglecting those rules at any point in your application logic chain are vast.

Did you cut some corners in your administrative systems code because you know you can trust the users of that subsystem not to do foolish or malicious things? Yes, that's a valid and reasonable assertion. But don't forget to look at the big picture. What if something unfathomable happens with your login/user validation system? Once that backfires on you - and it might not even be down to you - the damage will increase exponentially for every component that isn't securely coded.

Did you cut corners in your input data validation because your interface doesn't leave room for mistakes? I know that at a time, it felt redundant to double- or triple-check input data, especially when dealing with POST forms. But you have to understand that even though you have an interface, it doesn't mean it can't be bypassed. You may have taken steps to prevent that, but you should always prepare for the impossible to happen at some (any) point in your chain of application logic. That way you won't get caught with your pants down.

POST data is user input. It's not entirely trivial to fake it, but if you build a system that accounts for nothing but the average user's input security, you're setting yourself up for a fall. I'm sure everyone who is in the business of coding knows this, but you'd be surprised how easy it is to tell yourself "this is just development, non-mission-critical test code, I don't have to set the bar that high" and then gradually let your standards slip in unexpected places.

All this is doubly true for GET data. As an example, in one of my applications I use a lot of redirect-confirmation screens, where a user is given a reason or explanation of something happening, and then a plain link to continue. Often it's just error messages. I made a quick bouncer script that took the error message as a GET argument and then displayed it. I didn't think I wanted to input all my messages into a database because there might be tons of them, and I might want to change them.

All perfectly logical and fair, yes? After all, it's just a quick bouncer script. And for an average user, this is all well and good! But... What if some point along the way, a malicious user comes along? Possibly just someone who wants to see if they can spot flaws in the program. Very soon I would have users creating fake links with HTML code put in as the message for the bouncer, and I'd have on my hands redirect screens that look like they're part of my application, but display a vulgar goatse image and offensive text. Just to think of an example. These fake links would get passed around and my application would get very negative publicity.

Sounds amateurish? Well, this actually happened to the Finnish Broadcasting Company, Finland's national public service broadcasting company. One of their webpages that featured coverage on independence day festivities a couple of years back featured an interface driven by variables being handed to the script in GET-queries. But the developers had designed their system with only the average user in mind (or so it seemed), because very soon fake links started cropping up in IRC chats and e-mails that led to the official independence day's party story, but with completely fake images that were pulled from random websites on the net. They had trusted their own interface and did not double-check the variables from the GET-queries. Which - are - user - input. I refer you now back to the first rule I mentioned about producing secure PHP code...

So, the point of this rant is: Whenever you write $_GET or $_POST in your PHP code, you are dealing with user input, whether it feels like it or not. Always double-check it. Always.

And to those PHP coders out there who are only beginning their immersion into the world of PHP, I'll tell you how I resolved the potential exploit in my application. All the redirects I made went through a function that printed out Location-headers for the client, that instructed them to move to the bouncer script. The bouncer script could be called on its own, completely from outside the application. One way of fixing this would be to check the REFERER data of the http connection, and validate that the application domain is the referer. However, this is not very secure because REFERER and AGENT data... are user input! Anything that does not come from your server is user input.

Instead, I added a new table into my database for storing all my redirect messages. Now, whenever I called the redirect function, it first added the message into the database, and then redirected the user to the bouncer script - but as an argument for the bouncer, instead of the full error text, I just gave a unique ID number that pointed to the database. As soon as the bouncer page was loaded, the message would also be removed from the database. This way user input can not affect the process in any way - everything comes from the server. And even though they can see the ID, it is only valid for the duration of the request, so it's not exploitable.

As an optional bonus, you could generate an md5 hash from the id returned by the database, store the hash, and then pass the hash to the client. It does not make the script more secure, nor more effective (in fact it does the opposite from an optimization standpoint), but it does obfuscate your underlying data structures, which never hurts (but is not to be trusted upon as a method of secure coding).

--------------------
"Unrequited love. It's fantastic, because it never has to change, it never has to grow up and it never has to die!"

- saboteur
.. sabot909@gmail.com
.. https://www.saboteurweb.com/ | https://www.saboteurweb.com
 
       Top
0 replies since Feb 23 2006, 03:41 PM Track this topic | Email this topic | Print this topic

<< Back to News Archive