8 PHP and MYSQL exploit security tips for lazy programmers
I've gathered my personal best practices to secure my home-coded websites against security exploits via XSS, SQL injection, and CRSF. I am not a security expert at all, and I'll probably never be because I am to lazy to sanitize every single variable coming in and out my websites. Even companies with millions to spend like Facebook, MySpace, Google and other big names have/had exploits that exposed them against malware distribution, abuse of user accounts, data loss and other security issues. Even if you might not have sensitive information on your website, an exploit could target getting ownership of your domain or server: think of a fake cpanel login on your site by sending the webmaster to a exploited url. A good example to read and learn from is this story that describes how someone exploited myspace by easily circumventing basic security patches. While it's hard to close every security gap – some hackers go a long way – the tips below are an understandable introduction to programming security and the code examples will steer you in the right direction to fix them.
PHP: Clean up all the user input
One of the most common exploits are the result of unintended user input. User input by URL, forms and cookies has to get cleaned up from any exploitable input before doing anything with it. Most importantly you want html characters (like <,>) to be encoded to their harmless html representative and ', " escaped by a slash to exclude external code to be forced into your site. This script code below runs through the array $_GET, $_POST and $_COOKIE, and cleans up the values passed from the user. Please force integers on user input e.g. ID's by stripping out any other character. It's best to always also have Mod security installed.
These filters (filter_var) only work in PHP5, but with a good regular expression it can be also run in other versions. It's also good to truncate a string to a maximum number of characters or else you could exposed to this. In this script the string allowed is limited to 100 characters this could break alot of systems (like long user comments) so be careful with that.
PHP: Working with forms
Never put any settings in hidden form fields, and expect them to not be exploited. Easily exploitable is the example below where a form value identifies the user by UserID.
These hidden form fields can be set to any value. Look at this example on a major bank website. Another tip is to add a token to a form field that is based on the clients encrypted IP, session and/or cookie so the form is harder to temper with by CRSF exploits. More on tokens and why it's needed in my upcoming post.
Also dropdowns can best be setup like the example below to restrict unexpected values from the user in $dropdown.
PHP: uploading files
Never let people upload stuff to your server, unless you know what you are doing. Best way is to store the uploaded file in a folder unavailable via a url (e.g. next to your public_html folder) and have a file act as a proxy that grabs the uploaded file and forces a innocent extension like .jpg (only when it's an jpeg of course).
If you still want to host the uploaded file on your server available through a direct url make sure it doesn't make flash cross-domain available or other content like text, html, or executable files that could compromise the whole server. Always wonder if you are ready to host anything that isn't yours?Javascript: don't echo/print user input in javascript
A very common exploit like XSS is usably the fault of javascript code being run on the domain. Javascript can steal and forward user cookies, or make any (unintended) user actions like "delete account". For example this javascript php combination:
Could easily be exploited by creating a url /?username=name'+alert(1);+foo+=+'bar. The cleanArray() function above helps some against these problems since it adds slashes, truncates the string and encodes characters.
User management: don't store anything critical in a cookie
It seems tempting to store certain values in a cookie, saving time to retrieve settings from a database for example. For example to give a user administration rights:
Also saving $passwords, $usernames and $userID's (especially incremental $userID's. UserID = 1 having admin capabilities?) in a cookie is a bad idea, cookies are as easy to tamper with as changing a variable in a URL. Best way is to crypt or MD5 settings to identify a user:
When a user has a cookie, and you want to check it's credentials. From the cookie $useridentifier you generate the MD5 by combining your encrypt key and user IP and match that with the users cookie set.
Another best practice is to set the cookie to HTTPOnly, to exclude javascript (AJAX) to read the cookie contents.
PHP: Header forwards
An not widely known behavior on a well known practice: A header forward, without a exit() or die() could continue to load the page if the browser (or an exploiter) continues to load the page.
Mysql: limit your updates and deletes
When performing sql queries that contain dynamic variables always limit your query to 1 (of course unless you need to update or delete a bunch).
Apache: Mod Security
Install mod security. It has a nice black list of rules that block certain (common) exploit attempts from the external environment e.g. blocking exploits in the user agent string, bogus image uploads, injecting XSS, SQL injecting, commands, and url request string. In your .htaccess you set the mod_security to on on most apache installs:
And last be very careful when installing external scripts like Wordpress, Gallery or even Cpanel (web based server control panel) on your server. These scripts could have a security hole that can be exploited throughout your domain and even on the whole server. Always update to the latest version, and don't install in the standard directory so crawl bots won't find it easily.