Security: The mindset of a secure programmer
In this new age of social media and the internet, web applications have fast become the primary computing platform, and with this change has come new and better ways to write those applications. The rise of PHP and Javascript is in full swing! However, because of how easy it is to pick up these languages and start writing very powerful applications with very little training, the new generation of programmers lack sufficient knowledge in securing their own programs, leaving the doors wide open for even the most basic of attacks to gain access to critical systems and data that need to be protected.
The problem lies in the mindset of the programmer. When you’re thinking in code, your brain has routines for finding patterns and structures to estimate the outcome of an algorithm. These routines are constantly being run as you code, determining the best solution for a set of given problems (fulfilling the purpose of your program) that also meets certain requirements (e.g: low memory footprint, result caching, or whatever.) Most novice programmers, when setting out to develop an application, don’t include security in the list of requirements when creating their solution algorithms.
Most people when starting off believe that the key to being a good programmer is to *think* like a programmer. In fact, most people in general will agree with that logic. I’m here to tell you that’s exactly what you shouldn’t do, ever. A programmer’s primary goal is to get something to *work*. As long as it works, you’ve done your job. The problem with focusing on getting something to work means you’re only seeing the perspective of what the computer is doing, and this is a bad thing. The computer on which your application runs is predictable – the human beings that will be using your application are *not*. You must NEVER trust that any user is going to do what you expect them to do when using your application. When your application wants a number from the user, assume it is going to be given everything but a number. You must think like someone who wants to break your program; you must think like a hacker.
The Practical Stuff
Okay, so, let’s begin with some very basic things that all PHP programmers MUST know and MUST make a habit of. Number one, sanitizing SQL inputs. I know that to many of you this might seem like one of those “No, duh, you think?” things, but believe it or not there are a ridiculous amount of sites that suffer from the problem of simply NOT SANITIZING SQL. Also, to those shouting “BUT WHAT ABOUT MAGIC QUOTEZ?!”, NO! Bad dog! Magic quotes will not save you! What happens if you move to a system which doesn’t have Magic Quotes, and you’ve developed that habit of using it to sanitize? What happens if your code gets ported to such a system? Game over, that’s what, and you’ll have no one to blame but yourself.
Example 1:
If you still think that the code above is perfectly fine to use, go back to the top of this document and read everything again.
Problem: In this example, you have a blog site or news site which stores posts in a MySQL database, in a table named “items”. Each news item has an incrementing “id” field. Your code is supposed to take a number from the get variable “itemid”, and search the “items” table for a row with that corresponding number as it’s id, and then retrieve the row. What’s the issue here? Well, for starters, what happens if anything other than a number is the value of the get variable? What if the value were “zomgwtfbbq”? Then the sql query would become “SELECT * FROM `items` WHERE `id` = zomgwtfbbq”. Or what if the value were “1 OR 1=1″? It would become “SELECT * FROM `items` WHERE `id` = 1 OR 1=1″. Do you see the problem? EVEN IF you had Magic Quotes on, you’re STILL screwed because you haven’t put quotes around the value in the query because you’re expecting an integer! But that doesn’t matter, you should never rely on Magic Quotes, ever. EVER.
Solution: First of all, you should ALWAYS enclose any user-supplied input data within your query string in single quotes, since everything within those single quotes will be interpreted only as data and NOT as SQL commands, EVEN when expecting an integer. Second of all, ANY time you are using ANY USER-SUPPLIED DATA in your query, you must ALWAYS use the mysql_real_escape_string function on that data before it is inserted into the query string. The reason for this is so that if a user tries to break out of the quotes by entering “1′ OR 1=1″ as the itemid, the ‘ will be escaped properly, preventing the “OR 1=1″ part from being interpreted as SQL commands.
This is the way that code SHOULD be written:
As you can see in the above example, I have also included a check to make sure that the $itemid is an integer (contains ONLY digits, no other characters). This is probably the most important habit for any programmer: always make sure the data you receive is the data you’re expecting. Any experienced programmer in any field will tell you this is one of the golden rules of good code. Here we used a built-in function of PHP called ctype_digit to verify that $itemid contains only numbers. In PHP, the “ctype” set of functions are an invaluable tool in verifying data, and you should check them out in the documentation.
Example 2:
Let’s say you’ve decided to use PHP includes as an easy way of having separate pages. Each page has it’s own content, but you want certain bits of content/code to be shared across *all* pages without having to write it out multiple times for every page. Each page is stored in separate .php files, with one index.php file which will load pages specified in the url. Many beginners often use a method of solving this problem similar to this:
Problem: Raise your hand if you can see what’s wrong here and why. Anyone? Yes, you, up the front row! Oh, that’s just my ridiculously good looking reflection in the mirror, my bad. What’s wrong here is that the file path is being provided by the user, completely unfiltered and unmodified. Why is that bad? The user can cause your script to execute ANY FILE ON THE SYSTEM! Still unsure? Those familiar with Linux-based web servers should know that Linux account passwords are stored in the /etc/passwd file. This is a plain text file which only the “root” user can access. If, for example, your instance of Apache/PHP is running with privileges that allow the accessing of said passwd file, a user can then cause your script to print out the contents of the entire passwd file, and it’s game over. All one would need to do is put “/etc/passwd” as the value for “page” in the url (example: www.yoursitename.com/index.php?page=/etc/passwd), and the ENTIRE SYSTEM is compromised. If it still hasn’t clicked in your mind just how catastrophically bad that is, go back to the top of this document and read everything again. Do not pass go, do not collect $200.
Never, and I repeat, NEVER allow the user to specify a path on the local file system, or a file name of a file on the local file system. Ever. You might be thinking “But what if I just appended it to the end of a path string? Won’t that restrict the user to only executing files in that directory?” Short answer: No. Long answer: Nooooooooooooooooooooooooooo. Say for instance you changed your include to be this: include(“/path/to/webroot/some/directory/”.$page); If the user enters /etc/passwd, when it’s appended to the path string in the code it will end up being “/path/to/webroot/some/directory/etc/passwd” which obviously doesn’t exist, and won’t show your passwords to the whole internet. Solved? Think again. In Linux file systems, by entering “../” in a file path (when working on the command line, for example, like “cd ../”), you’re moving *up* one directory. You can chain several of these to move up several directories. This is called “Directory traversal”, and works even with a full file path in front of it. So if a user sends “../../../../../../../../../../../../../../etc/passwd” to your script, that translates to “/path/to/webroot/some/directory/../../../../../../../../../../../../../../etc/passwd“. They’ve just traversed up by 14 directory levels, so since the path is only 5 directories deep this will guarantee the file pointer reaches the root directory, and will stop traversing up until it reads “/etc/passwd“, where it will then access the passwd file. Game over.
Solution: Let me demonstrate one great way to prevent server Armageddon while still achieving the same goal:
Can you see what’s going on here? Let me explain. First, all of the php files which are going to be your “pages” are stored in a specific directory called “pages”, which is relative to the path that your index.php file is in (so, if your index.php file is in /var/www, then the pages directory will be /var/www/pages). Also, instead of the “page” variable containing the whole filename, we only expect it to contain part of the filename, without the .php extension. We simply append the “.php” to the end when doing our checks. Our code checks the $page variable to see whether a blank value was assigned, and if so it sets the default value to “home”, so it will load the file with the content for the home page. We then set a variable containing the name of our relative directory path containing the pages, which in this case is “pages”. We then use the “scandir” function to scan that directory and list all the files and folders in an array. We use this function as the first argument of the “array_diff” function, with the second argument being an array of “.” and “..”. The reason for this is because all directories contain reference pointers to itself (“.”) and the directory above (“..”), so we have to filter those out of our array because they aren’t the files we want. We then use the “in_array” function to check whether our array contains a file with the name specified in our $page variable with the extension “.php”, and if it doesn’t, we use include to load a generic 404 page file. If the file is found to exist in our array, then we append the directory path from $dir, the file name, and the extension together, and include it.
Voila! We have just achieved the same functionality, but in a much more secure way. Granted it does take more code, but it’s absolutely essential. The moral of this story is: if you’re not going to spend the extra time necessary to write SECURE code, then don’t write any code at all. Also, check, re-check, and re-check your code constantly. And finally, remember to think like a hacker.
Happy coding.



