Best Practices to Secure Your Web App
When it comes to application security, in addition to securing your hardware and platform, you also need to write your code securely. This article will explain how to keep your application secure and less vulnerable to hacking. The following are the best habits that a programmer can develop in order to protect his or her application from attack:
- Input data validation
- Guarding against XSS attacks
- Guarding against CSRF attacks
- Preventing SQL Injection attacks
- Protecting the file system
- Protecting session data
- Proper error handling
- Guarding included files
Input Data Validation
While designing your application, you should be striving to guard your app against bad input. The rule of thumb to follow is this: don’t trust user input. Although your app is intended for good people, there is always a chance that some bad user will try to attack your app by entering bad input. If you always validate and filter the incoming data, you can build a secure application.
Always validate data in your PHP code. If you are using JavaScript to validate user input, there is always a chance that the user might have turned off JavaScript in her browser. In this case your app will not be able to validate the input. Validating in JavaScript is okay, but to guard against these types of problems then you should re-validate the data in PHP as well too.
Guarding Against XSS Attacks
Cross-site scripting attack (XSS attack) is an attack based on code injection into vulnerable web pages. The danger is a result of accepting unchecked input data and showing it in the browser.
Suppose you have a comment form in your application that allows users to enter data, and on successful submission it shows all the comments. The user could possibly enter a comment that contains malicious JavaScript code in it. When the form is submitted, the data is sent to the server and stored into the database. Afterward, the comment is fetched from database and shown in the HTML page and the JavaScript code will run. The malicious JavaScript might redirect the user to a bad web page or a phishing website.
To protect your application from these kinds of attacks, run the input data through strip_tags() to remove any tags present in it. When showing data in the browser, apply htmlentities()function on the data.
Guarding Against CSRF Attacks
In a Cross Site Request Forgery (CSRF) attack, the attacker tricks the victim into loading sensitive information or making a transaction without their knowledge. This mainly occurs in web applications that are badly coded to trigger business logic using GET requests.
Ideally, GET requests are Idempotent in nature. Idempotency means the same page can be accessed multiple times without causing any side effects. Therefore, GET requests should be used only for accessing information and not for performing transactions.
The following example shows a how a poorly coded application unknowingly supports CSRF attacks:
<?php
if (isset($_REQUEST["name"], $_REQUEST["amount"]))
{
// process the request and transfer the amount from
// from the logged in user to the passed name.
}
Let’s assume Bob wants to perform a CSRF attack on Alice, and constructs a URL like the following and sends it to Alice in an email:
<a href="http://example.com/process.php?name=Bob&amount=1000">Visit My WebSite</a>
If Alice clicks on this link, and is logged into the website already, this request will deduct $1000 from her account and transfer it to Bob’s! Alternatively, Bob can create an image link whose src attribute points to the URL.
<img src="http://example.com/process.php?name=Bob&amount=1000" width="1" height="1"/>
The browser can’t display any image as expected, but it will still make the request using the URL which will make a transaction without notifying Alice.
The solution is to process any function that changes the database state in POST request, and avoid using $_REQUEST. Use $_GET to retrieve GET parameters, and use $_POST to retrieve POST parameters.
In addition, there should be a random token called a CSRF token associated with each POST request. When the user logins into his/her account, the application should generate a random token and store it in the session. Whenever any form is displayed to the user, the token should be present in the page as a hidden input field. Application logic must check for the token and ensure that it matches the token present in the session.
Preventing SQL Injection Attacks
To perform your database queries, you should be using PDO. With parameterized queries and prepared statements, you can prevent SQL injection.
Take a look at the following example:
<?php
$sql = "SELECT * FROM users WHERE name=:name and age=:age";
$stmt = $db->prepare($sql);
$stmt->execute(array(":name" => $name, ":age" => $age));
In the above code we provide the named parameters :name and :age to prepare(), which informs the database engine to pre-compile the query and attach the values to the named parameters later. When the call to execute() is made, the query is executed with the actual values of the named parameters.
If you code this way, the attacker can’t inject malicious SQL as the query is already compiled and your database will be secure.
Protecting the File System
As a developer you should always write your code in such a way that none of your operations put your file system at risk. Consider the following PHP that downloads a file according to a user supplied parameter:
<?php
if (isset($_GET['filename'])
{
$filename = $_GET['filename'];
header('Content-Type: application/x-octet-stream');
header('Content-Transfer-Encoding: binary');
header('Content-Disposition: attachment; filename="' . $filename . '";');
echo file_get_contents($filename);
}
The script is very dangerous since it can serve files from any directory that is accessible to it, such as the session directory and system directories. The solution is to ensure the script does not try to access files from arbitrary directories.
Protecting Session Data
By default, session information is written to a temp directory. In the case of a shared hosting server, someone other than you can write a script and read session data easily. Therefore, you should not keep sensitive information like passwords or credit card numbers in a session.
A good way to guard your session data is to encrypt the information stored in the session. This does not solve the problem completely since the encrypted data is not completely safe, but at least the information is not readable. You should also consider keeping your session data stored somewhere else, such as a database. PHP provides a method called session_set_save_handler() which can be used to persist data in session in your own way.
As of PHP 5.4 you can pass an object of type SessionHandlerInterface to session_set_save_handler(). Check out the PHP documentation to learn about implementing custom session persistence by implementing SessionHandlerInterface.
Proper Error Handling
It’s good to know about all the errors that occur while we’re developing an application, but when we make the application accessible to end users we should take care to hide the errors. If errors are shown to users, it may make our application vulnerable. So, the best approach is configuring your server differently for development and production environments.
In production mode we need to turn off display_errors and display_start_up_errors settings. error_reporting and log_errors should be on so that we can log errors while hiding those from end users.
You can use set_error_handler to define custom error handlers. However, it has limitations. The custom error handler bypasses the standard error handling mechanism of PHP. It cannot catch errors like E_CORE_ERROR, E_STRICT or E_COMPILER_ERROR in the same file the error handler is defined in. Furthermore, it will fail to handle errors that might occur within the handler itself.
To handle errors elegantly you should perform exception handling through try/catch blocks. Exceptions are represented by the Exception class and its subclasses. If any error occurs inside the try block you can throw an exception and process it in the catch block.
Guarding Included Files
PHP scripts often include other PHP files that contain code for things like connecting to a database, etc. Some developers give the included files an extension like .inc. Files with this extension are not parsed by PHP by default if called directly and will be served as plain text to the users. If an attacker directly accesses the include file that contains database credentials, he now has access to all of your application’s data. Always use the .php extension for included code files and keep them outside of directories directly accessible to users.
Summary
By keeping the above 8 points in mind it’s possible to secure a PHP application to a great extent. The best piece of advice by far is don’t trust user input, but also be sure to guard your file system and database as well.
Labels: PHP Security, PHP, Security, website security, XSS handling
Comments
Post a Comment