PHP Developers are still stuck in the stone age
Unfortunately, I feel that the title of this article is all too accurate for the state of some developers in the PHP community today. We have just recently seen the release of PHP7 and the removal of PHP's MySQL functions with it. However, much to my amazement, there are still PHP developers out there who are using exceptionally old methods of coding simple systems which have been not only vulnerable but also insanely difficult to maintain.
Today I read an article posted to the PHP Developers community on Google+ entitled "Script For Register,Login,Logout Using PHP, MySQL and Bootstrap" and decided that I would take a look to see how things are being done with all the changes that have been taking place.
I will let you read the article for yourself, however the key points to take away from this article are:
- Using procedurally written files
- Calling Database functions directly inside views
- Using unfiltered data in SQL queries
Any PHP developer should see the inherent problems with what I have just stated above.
Using procedurally written files
While this particular point is not always in the wrong under certain conditions, using this style of coding for achieving things such as authentication and user management is just plain wrong (especially with the later points). There are significantly easier ways of of handling which page should be displayed and what functions should occur on those pages. This is precisely the reason why we have Routing.
One of my personal favourites for handling routing in PHP is Flight which can also be installed and deployed using composer:
composer require mikecao/flight
This is a very simple and powerful example of how you can utilize Flight to perform routing while utilising a database object.
require 'flight/Flight.php';
$db = new Database($config);
Flight::route('GET /users(/@account)', function($account) use ($db){
$user = new User($db);
if( !empty($account) && $db->check( ['id'=>$account], 'users') ) {
// Display display specific user account
$user->displayAccount($account);
} else {
// Display list of all users
$user->listAccounts();
}
});
Flight::route('POST /delete/@account', function($account) use ($db){
if( !empty($account) && $db->check( ['id'=>$account], 'users') ) {
$user = new User($db);
if( $user->deleteAccount($account) ) {
header('Content-Type: application/json');
echo json_encode(['status'=>'success']);
}
} else {
header("HTTP/1.0 401 Unauthorized");
exit;
}
});
Flight::start();
The above script in a nutshell does 2 things, it will show user accounts and it will allow you to delete an account.
What should be quite apparent though, is the flexibility this micro-framework provides. In this particualr example, I have excluded including any form of external template and have directly chosen to output the results. However, once you start using the concept of a MVC and using the controller to handle specifics, the flexibility is really quite amazing.
Calling Database functions directly inside views
Now this point, in my eyes, should always be a big stopping point for any project. There should be absolutely no reason to be calling these functions directly within a template or view, even in the world of procedural programming. Not only is it smelly code, but when you have a large system that utilises this sort of coding style, debugging can be an absolute nightmare. If there was ever a reason to code procedurally (ie. PHP <5), then you should be looking at upgrading your code to PHP 5.3 at very least to make use of Objects and start to seriously think about starting a redevelopment plan.
Using unfiltered data in SQL queries
Finally, don't trust user input. If you are still wondering what that means, then I suggest you go and read up on SQL Injection.
This is possibly the largest bane of any PHP developers existence, especially those who have to deal with this problem due to the poor judgement of other developers.
In simple terms, ANY PHP application is vulnerable if the SQL queries are unfiltered.
Example:
$check_user="select * from users WHERE user_email='$user_email' AND user_pass='$user_pass'";
Both of these types of queries can be EASILY exploited into doing something that you had not intended.
Lets start off with the above example, maybe we would like to login without having an account on the system?
Login Screen:
Email: ' OR '1'='1' --
Password: somerandompassword
Once the above has been entered into the login form, PHP will automatically insert the SQL injection into the SQL query which will then execute. So what the query will look like afterwards is:
$check_user="select * from users WHERE user_email='' OR '1'='1' --' AND user_pass='somerandompassword'";
Anyone who is farmiliar with MySQL will see the problem here. The --
in a MySQL query means that anything that follows is a comment, not part of the actual query itself. Not only that, but we can see that the query will check to see if the user_email = ''
which shouldn't return anything... OR if '1'='1'
which is correct, 1 does equal 1. This means the query will return every user account in the table.
Now just imagine what would happen if they were to include some extra MySQL commands in that query to say... drop your entire database?