You are on page 1of 35

12 Tips To Secure Your PHP

Web Applications
24 September 2015 PHP By Mohit Gangrade 22 Comments

1101
Shares

Facebook Twitter

Here are some easy to understand PHP security tips.

Security should be the first concern of every Web Developer.

Every day thousands of websites across the world get hacked. Some
hackers hack for fun, some hack to learn, and some hack for money.
But not all hackers are bad guys. Big companies like Facebook and
Yahoo pay hackers to find vulnerabilities. I am sure you dont have a
big 6-8 figures budget to pay hackers to find vulnerabilities. Following
this guide will help you protect your Web Applications without
spending a dime.

No matter how much experience one has, he can never make a web
application completely secure. Even Googles Malaysian website got
hacked a few weeks ago. The hacker was an anonymous Bangladeshi
hacker who calls himself TigerMate. The hacker didnt harm the web
application, he just switched their DNS to display his own website.

Writing secure PHP code wont protect your web apps, there are a lot more
things to consider. As mentioned in the usage statistics by W3Techs.com,
Around 82% of the websites are using PHP. If a hacker finds a vulnerability
in the core of PHP, he will try to exploit the vulnerability on all the websites.

Not only PHP but sometimes the Web Server you are running might
be vulnerable. Here, By Web Server I mean Software like Apache,
Nginx, IIS, etc.

In this article, I will share with you some tips to help you protect your
Web Applications from hackers. I will try to make sure that all the tips
are easy to understand.

Now, lets head to the tips:

1. Start Hashing Passwords


When I was a beginner, I used to store passwords as plain text in the
database. But then I came to know about Password Hashing.
Password Hashing is a simple technique of storing unreadable
passwords in the database. This unreadable text is a passwords
hash. All the password hashes stored in the database will be useless
to a hacker even if he steals them all. If he tries to brute force the
hash, it will take 100s of years to crack it.

The name of the technique sounds scary. But the technique itself is
really easy to learn and use.
In this technique, You hash passwords with PHPs Password Hashing
API and store them into the database. After that, whenever your users
try to log in, you retrieve the password hash from the database and
use PHPs Password Hashing API to verify the hash. The verification
is pretty simple, PHP regenerates a new hash for the given password
and checks it with the hash provided.

Heres how you hash passwords in PHP:

1 // The Password You Received From Registration Form


2 $password = $_POST['password'];
3
4 // The Password Hash
5 $password_hash = password_hash( $password , PASSWORD_DEFAULT );

In the above code snippet, we are using


PHPs password_hash function. It accepts two required and one
optional argument. The first argument is the password to hash and the
second argument is the Algorithm to use. We are passing
the PASSWORD_DEFAULT constant as the second parameter.
The value of this constant is the default and the strongest algorithm
available. The value of this constant will change with newer PHP
versions and stronger algorithms.

Just one line of code hashes the password. Now, to verify the
password we use the function password_verify:

1 // Code To Retrieve The Password Hash From The Database


2
3 $hashed_password = 'A_REALLY_LONG_HASH_RETRIEVED_FROM_THE_DATABASE';
4
5 // The Password Entered By The User On The Login Form
6 $password = "My_Password";
7
8 if( password_verify( $password , $hashed_password ) )
9 {
10 // The User Has entered A Valid Password
11 }
12 else
13 {
14 // The User Has entered An Invalid Password
15 }

The password_verify function accepts two arguments. First is the


password entered by the user and the second is the password hash to
verify. The function returns a Boolean indicating if the password was
valid.

2. Escape Input Before Using In An SQL


Statement
You must escape user input to prevent your Web App from SQL
injections. An SQL Injection is an attempt by the attacker to inject
malicious code into your SQL queries. Hackers use SQL Injections to
perform operations on your applications database. If a hacker finds
even one vulnerability that allows SQL Injections, your database gets
into grave danger. The hacker can do anything with your database if
he finds an SQL Injection vulnerability.

Escaping user input saves you from SQL Injections. Let me explain
SQL Injections with the help of the below example.

Think of a scenario when you have a page that displays a users bio
by querying the database. Your script performs a query based on the
user_id passed in the URL. And you are using the below code to
create the SQL query:

1 $user_input = $_GET['user_id'];
2 $SQL = "SELECT * FROM users WHERE id = '$user_input';";
Assuming the user entered 12 as the user id in the query string. The
URL will look something like this:

http://your-site.com/about.php?user_id=12

If you echo the SQL statement generated, you will see the following
result:

1 SELECT * FROM users WHERE id = '12';

The above query is correct and safe. But what if the user(Hacker in
the case) enters some malicious SQL code as the query string
parameter?

Assume that the hacker enters 14 or 1=1; without quotes as the


user id. The SQL query will become:

1 SELECT * FROM users WHERE id = '14' or 1=1--';

The above SQL query will select all the users as 1 is always equal to
1. In the above SQL Query, the hacker is using which denotes a
comment in SQL. He is commenting out the

A hacker wont always enter commands like this one. He might enter a
command to DROP the complete users table.

Assume that the hacker enters 13; DROP TABLE users without
quotes as the user id. The SQL query will become:
1 SELECT * FROM users WHERE id = 13; DROP TABLE users;

The above SQL query when runs will completely delete the users
table. The above SQL query will first select the user with the user id
13 and then will drop the complete database.

To prevent this, you need to escape any data you use in your SQL
queries. Escaping is nothing more than adding a slash at the
beginning of a quote. But you should never do this manually instead
you should use the functions already available in PHP.

To escape the user input, you should use


MySQLis real_escape_string function. Have a look at the below
example:

1 $user_input = $_GET['user_id'];
2 $user_input = $mysqli->real_escape_string( $user_input );
3 $SQL = "SELECT * FROM users WHERE id = $user_input;";

In the above example, I am assuming that you have already created


an instance of the MySQLi class and named it $mysqli. Now, if the
user tries to enter some malicious code then the SQL query will
become:

1 SELECT * FROM users WHERE id = '14\' or 1=1--';

The above query isnt malicious but might produce an error when
executed. It is now safe from hackers.
Wait. Thats not all for this tip. Heres a cool Meme I found over the
internet about SQL Injections:

You can also use prepared statements for the purpose. But they are
out of the course of this article.

3. Never Trust JavaScript For Input


Validation
Many websites use JavaScript for Input validation. And it is a good
idea to use JavaScript for basic input validation. It improves the User
Experience. But you should never rely on JavaScript for User Input
Validation.
A hacker can easily disable JavaScript. Once he does that, he can
send malicious input to your scripts. This malicious input can be
harmful to the users of your website or the web app itself.

You should only use JavaScript for simple client-side input validation.
JavaScript should only be used to improve user experience.

A form that notifies users if they enter invalid credit card numbers is a
good example.

You should always validate user input on the server side.

4. Never Use eval


Eval is a PHP construct used to run the specified PHP code. Eval
works just like a function. Have a look at the below code to understand
how it works:

1 $string = "Something"; # Create A Test String


2 eval("\$string = 'Mohit';"); # Evaluate The PHP Code
3 echo $string; # Will Output Mohit Instead of Something

If you execute the above code, you will see Mohit as the output
instead of Something. You should never use this construct unless
necessary. Its use is discouraged by the PHP documentation.

If eval() is the answer, youre almost certainly asking the wrong


question Rasmus Lerdorf, The creator of PHP.

Even the creator of PHP discourages the use of eval.

You might be thinking, What can go wrong with eval?

Well, Everything. Have a look at the example below:


1 $user_input = $_GET['user_input']; # Store The User Input
2
3 $string = "Something"; # Create A Test String
4
5 eval("\$string = '$user_input';"); # Evaluate The PHP Code
6
7 echo $string; # Should Output The User Input Instead of Something

As you can see, The above code snippet contains the example I used
at the beginning of this tip. This time we are allowing the user to
change the value of $string variable. We are storing the user input in
the $user_input variable and passing it to eval.

If the user enters Mohit as the user_input parameter, the output will
be Mohit. But if a hacker enters the below string as the parameter:

1 ';echo "You have been hacked";//

The output will be You have been hacked because the code
becomes:

1 $string = '';echo "You have been hacked";//';

This is like SQL injections but even worse. If a hacker finds this
vulnerability in your website, he can easily delete your complete
website along with the database.
Eval should be used only when you dont have any other options. And
if this is the case, you should always first thoroughly filter any user
input that goes into the construct. A hacker can execute malicious
code that can delete your database completely or clean out your
complete web application directory.

I have never used eval in my life till now. And I always discourage its use.

PHP allows developers to disable specific functions in the php.ini file.


But sadly, eval is not a function but a language construct and hence
cannot be disabled with php.ini file.

5. Disable Register
Globals(register_globals) PHP Directive
Register Globals is a PHP feature that converts query parameters in
the URL into variables automatically. Although this feature has been
removed as of PHP 5.4. Still, it is a good idea to disable this feature as
your web host might be using an older version of PHP that uses this
feature.

Have a look at the below example:

1 // Check If The Admin Has Logged In


2 if( is_admin_logged_in() ){
3 # The Admin Has Logged In
4 $is_admin = true;
5 }
6
7 if( $is_admin ){
8
9 // Display The Admin Interface
10
11 }
12 else{
13
14 // Redirect To Login Page
15
16 }

Lets assume that you are using the above code to test if an admin is
logged in. And if he is then displaying him the admin interface else
redirecting him to the login page.

If register globals directive is set to on, A hacker can change the value
of the variable $is_admin to true. Assuming you are using the above
code in admin.php file, a hacker can use the below URL to change the
variables value:

http://your-site.com/admin.php?is_admin=1

If a hacker uses the above URL to access your script, the value of the
variable $is_admin becomes 1. Now, the $is_admin variable will
evaluate to true in the if statement resulting in the display of your
Admin interface to the hacker.

To disable register_globals, you can either edit your php.ini file or .htaccess
file. There are three ways to disable Register Globals directive:

1. Edit your htaccess file

To disable register_globals directive with htaccess file, add the below


code to it:

1 php_flag register_globals off

The above code will use php_flag to change the value of


register_global directive to off.

2. Custom php.ini file


A custom php.ini file is an ini file saved as php.ini in the root directory
of your website. A custom ini doesnt affect any other web sites on
your web host. It will only affect your websites working.

To create a custom php.ini file, open up notepad and add the below
code to it:

1 [PHP]
2 register_globals=0;

Now, save the file as php.ini and upload it to the root directory of your
website. The above two lines will disable register globals for all your
PHP scripts.

3. Edit the main php.ini file


The main php.ini file is not available to you on shared web hosts. But it
is available on dedicated and VPS servers.

Note: Editing the main php.ini file will affect the working of all the
websites present on the web server.

To disable register globals, open up your php.ini file in a text editor.


Now, search for register_globals in the ini file. Once you find it,
change it to

1 register_globals=Off

The third method is not possible in shared web hosting plans. In that
case, you should try the first and the second methods.
You will see a 500 internal server error if your web host doesnt allow
you to modify PHP directives with a htaccess file. If this is the case,
first remove the code from the htaccess file and then contact your web
host administrator to disable register globals directive for you.

6. Prevent Session Hijacking


Session hijacking is simply the stealing of a users cookies and using
them to access the website on their behalf.

Have a look at the below example to understand how session


hijacking works:

Assume that a user logs into your website. Once a user logs in, PHP
generates a session for the user and sends the browser a session
cookie. This session is valid until the cookie expires or gets deleted. If
a hacker somehow steals this session cookie, he can login to your
website as the user himself.

The cookie generated is named PHPSESSID. The value of this cookie


is an unreadable md5 hash. This cookie will allow your server to
identify the current session.

Have a look at the below image to understand how Session Hijacking


works:
There are a lot of ways to prevent session hijacking. Below are some
tips to prevent session hijacking:

1. Lock Users Session


To lock a session, we save the users browser or IP address in the
session. After that, we check for a change in browser or IP address.
And if the current IP address or the browser is different from the one
saved in session, we destroy the session and ask the user to log in
once again. Session locking is the easiest way to prevent session
hijacking.

There are three ways to lock a users session:

1. Lock With Users Browser


2. Lock With Users IP Address
3. Lock With Both

I prefer to use both. This prevents session hijacking even if one


method fails. Use the below code to save the User-Agent string and IP
address of the user in session:

1 # Start The Session


2 session_start();
3
4 $_SESSION['user_agent'] = $_SERVER['HTTP_USER_AGENT']; # Save The User Agent
5 $_SESSION['ip_address'] = $_SERVER['REMOTE_ADDR']; # Save The IP Address

In the above code snippet, first, we are starting the session. After that,
we are storing the User-Agent string and the IP Address of the user in
the $_SESSION variable found in HTTP_USER_AGENT and
REMOTE_ADDR $_SERVER variable keys respectively.
Once you save the users User-Agent(browser) string and IP Address
in the session, its time to check if a session is hijacked. To check if a
session is hijacked, use the below code:

1 if( $_SESSION['user_agent'] === $_SERVER['HTTP_USER_AGENT'] )


2 {
3 # The User's Browser Hasn't Changed Since The Last Login
4
5 if( $_SESSION['ip_address'] === $_SERVER['REMOTE_ADDR'] ){
6
7 # The User's IP Address Hasn't Changed Since The Last Login
8 // The User's Session Isn't Hijacked. You Can Continue :)
9 }
10 else{
11
12 # The User's IP Address Has Changed.
13 # This Might Be A Session Hijacking Attempt
14
15 # Destroy The Session
16 $_SESSION = null;
17 session_destroy(); # Destroy The Session
18 session_unset(); # Unset The Session
19
20 }
21 }
22 else
23 {
24 # The User's Browser Has Changed.
25 # This Might Be A Session Hijacking Attempt
26
27 # Destroy The Session
28 $_SESSION = null;
29 session_destroy(); # Destroy The Session
30 session_unset(); # Unset The Session
31 }

In the above code snippet, we are first checking if the users browser
is the same as the one we stored in the session. If the users browser
hasnt changed since the last request, we are checking users IP for
changes. And if any one of these two has changed then we are
destroying the session.

2. Keep On Regenerating Session ID


Regenerating a new session id will replace users current session id
with the new one without any loss in session data. This method makes
a stolen session id unusable for the hacker. Once we regenerate
session id, all the old session ids become useless resulting in logging
out the hacker. This method will not help you much in preventing
session hijacking but will cure a hijacked session.

You can regenerate the session id:

1. On Every Request
2. After A Specific Number Of Requests. For example, every 10 requests.
3. After A Specific Time. For example, every 5 minutes.
My preferred way is to regenerate a session id every 5-10 requests
from the user. To regenerate session id, we use
PHPs session_regenerate_id function. The function accepts only one
Boolean argument. Passing true as the argument replaces the current
session id with a new one and deletes the old session id. Deleting the
old session makes sure that the stolen session id is no longer
recognized by the server.

You can use the below code to regenerate session ids after a specific
number of requests:

1 # Start The Session


2 session_start();
3
4 # Regenerate Session ID After Every 5 Requests.
5 $regenerate_session_id_requests_count = 5;
6
7 // Check If The Request Counter Is Already Set
8 if( isset( $_SESSION['number_of_requests'] ) ){
9
10 # The Request Counter Is Set. We Are Good To Go.
11
12 // Increment The Request Counter
13 ++$_SESSION['number_of_requests'];
14
15 if( $_SESSION['number_of_requests'] >= $regenerate_session_id_requests_count )
16 {
17 // The User Has Already Made 5 Requests
18 // Regenerate The Session ID
19 // And Delete The Old One
20 session_regenerate_id( true );
21
22 // Reset The Request Counter
23 $_SESSION['number_of_requests'] = 0;
24 }
25
26 }
27 else
28 {
29 # The Request Counter Isn't Set Already
30 $_SESSION['number_of_requests'] = 0;
31 }

On the first line of the above code snippet, we are starting the session.
After that, we are checking if our request counter already exists, if not
then we are initializing it with zero. And If our request counter already
exists then we are incrementing it on each request.

We are also checking if the user has already made requests more
than the number specified. And if he has, we are regenerating the
session id and deleting the old one.

Add this code to the function or code you use to check for user login.

7. Stop Execution On Redirect


Whenever you redirect a user, you use PHPs header function to send
a location header. The header function doesnt stop the execution on
redirect. This means even after sending a location header, your PHP
code is executed.

Have a look at the below example:

Assume that the below code is from a script that displays charts to the
website admin. It displays a chart with the chart id provided in the
URL.
1 // Check And Store The Chart ID Received In The Query String
2 $chart_id = $_GET['chart_id'];
3
4 // Check If The User Is An Administrator
5 if( !is_admin() )
6 {
7 # The User Is Not An Admin
8
9 // Redirect To The Login Page
10 header( "location:login.php" );
11 }
12 else
13 {
14 // Escape The Chart ID
15 $chart_id = $mysqli->real_escape_string( $chart_id );
16 }
17
18 // Query The Database
19 $result = $mysqli->query("SELECT * FROM charts WHERE chart_id = '$chart_id';");
20
21 // Process The Result And Display The Chart

On the first line of the above code, we are storing the chart id received
in the GET parameter chart_id. After that, we are checking if the
current user is an admin. And if he isnt an admin, we are redirecting
him to the login page.

And if the user is an admin, we are escaping the chart id to use it in the
MySQL query.

The above code looks secure but doesnt work as expected. It


continues the execution even after redirecting the user to login page.
This is because sending a location header doesnt end the execution
of the script.

Many beginners make the mistake of not terminating the execution


when redirecting.

If a hacker who is logged in as a user but not as an admin uses the


below URL:
http://your-site.com/admin/chart.php?chart_id= OR 1=1;

The script becomes vulnerable to SQL injections because the code is


only escaping chart id if the user is an admin.

To fix this problem, just add an exit command after the header:

1 // Redirect To The Login Page


2 header( "location:login.php" );
3 exit;

8. Prevent Cross-Site Request


Forgery(CSRF)
Cross Site Request Forgery is exploitation of GET requests. This is
generally seen in web applications that use GET requests to perform
operations.

GET requests should never be used to perform operations. POST


requests should be used instead to perform any operations.

CSRF attacks are really easy for a hacker to perform and dont take
much time.

To help you understand it, heres an example:


Assume that you have a script named delete_account.php that allow
users to delete their accounts on your website. And the script does
that with the help of GET requests. Have a look at the below code:

1 # delete_account.php
2
3 if( isset( $_GET['account_id'] ) && is_numeric( $_GET['account_id'] && is_logged_in() ) ){
4 // Delete This User's Account From The Database.
5}

The above code is from the script named delete_account.php which is


vulnerable to CSRF. The above code first checks if a valid numeric
account id is passed as the GET parameter. After that, it checks if the
user is logged in. And if the user is logged in, it deletes the user
account from the database.

If a hacker wants to delete a users account, he needs to login to the


application as the user. But sadly, He doesnt have the credentials and
hence cant delete the users account.

Because the hacker cant delete the users account himself, he will
make the user delete it without actually knowing. There are a lot of
ways a hacker can do this. I have listed two below:

1. Make The user Open The Link


This is a common practice, A hacker asks the user to open the link
while he is logged into the website. This makes the user delete his
account without even knowing.

To make the user open the link, a hacker cloaks the link with the help
of a URL shortener. The short URL becomes unidentifiable for the
user. And once the user opens up the short URL, an unwanted action
is performed on his behalf. In our case, the action is to delete the
users account.
2. Open The URL Without Users Consent
To do this, a hacker will use an image with the src of the link to be
opened. He will use the below code to do so:

1 <img src="http://your-site.com/delete_account.php?account_id=1234" alt="My New Cat" />

Once a user opens hackers website, his browser will make a GET
request to the script. And your server will think that the user is making
the request himself. The user will not be able to see any image. But
this will result in deletion of users account.

Now that we are all terrified, here are some tips to prevent Cross-Site
Request Forgery attacks:

1. Use POST For Sensitive Operations


This is the easiest way to prevent Cross-Site Request Forgery
attacks. If you use POST for the previous example script, the hacker
will no longer be able to send false requests to your script. Using
POST requests alone will not save you from Cross-Site Request
Forgery attacks. But will make sure a hacker is no longer able to send
a false request without users consent.

Your script isnt safe yet, A hacker can create a form on his website
that sends a POST request to the script on our website. And if
the user clicks the submit button on the form, his account will get
deleted without his consent.

A hacker doesnt even need to display the form to the user. He can
create a hidden form with hidden fields. And use JavaScript to submit
the form without users consent.
This is a misconception that POST requests can save your users from
CSRF. Implementing POST will help you implement other techniques
for prevention.

2. Use CSRF Tokens


CSRF tokens are nothing, but just unpredictable tokens(text, number
or combination) generated on the server side. These CSRF tokens are
attached as hidden fields to each form generated and are verified on
each submission.

1 <form action="some-action.php">
2 <input type="hidden" name="CSRF-Token" value="41ca36dffcdc5e5a32bbdbd5d585b12d" />
3 <!-- Your Input Fields -->
4 </form>

The above HTML form contains a hidden CSRF-Token field. This


token is generated from the server side and is nothing but just a
random md5 hash. This token will be sent to the server with the user
input.

Once the script receives the token, it will verify if it. And will only
perform any action if the token is valid.

The CSRF Token will make all CSRF attacks fruitless because a hacker cant
predict our CSRF token unless he knows how we are generating it.
A CSRF token must be as much random and unpredictable as
possible. Heres my preferred way of generating CSRF tokens:

1 // Generate A CSRF Token


2 $csrf_token = md5( time() . uniqid( mt_rand( 0 , mt_getrandmax() ) ) );
The above code will generate an unpredictable CSRF Token.

Many developers prefer storing these tokens in the database. I prefer


storing these tokens in the users session. Storing tokens in users
session saves you from a lot of Database Queries.

You should generate a new CSRF token for each action. CSRF
tokens not only prevent CSRF attacks but also prevent multiple form
re submissions.

Generating, securing and validating CSRF tokens is a broad topic and


deserves a separate article. I am working on the article and it will be
available in 3-4 days.

3. Ask For Confirmation


Asking for confirmation will help you prevent basic CSRF attacks. A
user should always be asked for confirmation when performing
sensitive operations such as cancellation of an account.

Moreover, you can ask the users to re-enter their password for
confirmation of sensitive operations. Asking for the password will also
prevent hackers with hijacked sessions from performing the action.

You can also add an additional Captcha code for confirmation.

9. Disable Directory Listing


Apache searches for an index file whenever you request a directory of
a website. And if Apache doesnt find an index file in the directory, it
lists all the contents of the directory as seen in the above image.

It not only lists all the contents on the directory but also sensitive
information like the PHP, Apache, and OpenSSL version. This
information shouldnt be displayed publically. If a hacker knows a
vulnerability specific to your PHP version, he will surely attempt to
exploit your application.

Directory listing allows a hacker to find all the files and directories
present in your website. All this information is really helpful to a
hacker.

Here are two ways to disable directory listing:

1. Edit htaccess File


If you wish to use a htaccess file, add the below code to it:
1 IndexIgnore *

The above tells Apache not to index any of the files in the current
directory. If apache doesnt index any files, then it will not be able to
list them to the users.

A htaccess file will only affect its current directory. It will not stop
directory listing in parent or child directories. This means you need to
add a htaccess file in all your directories.

2. Creating An Index File


Instead of creating a htaccess file in all the directories, you can add an
empty index file to each of your directories. The extension of the index
file doesnt matter, it can be index.php or index.html or whatever you
like.

WordPress uses this technique to disable directory listing. It has an


index.php file in every directory with the comment Silence Is Golden.

10. Do Not Store Unnecessary Data


A hacker cannot steal the data that you dont have.

This is a really good practice. You should never store unnecessary


data that you dont need. This not only prevents hackers from stealing
information but also reduces the size of the database.

Lets assume, you sell ebooks on your website. And you use a
payment gateway that handles all the payments. In this case, you
should never store users credit card information on your server. If you
dont store the credit card number of the user, hackers would not be
able to steal it from your website. Payment Gateways are PCI
Compliant and store credit card information in a secure manner.

If a hacker finds any vulnerability in your website and gets access to


your database, he cant steal user data like credit card information.
Because you dont have any credit card information.
11. Prevent Brute Force Attacks
Brute Force attack is guessing of passwords. In this type of attack, A
hacker uses a random brute force tool to send random username and
password combinations to your scripts.

The hacker uses a tool that sends username and password


combinations to your script. The username is generally known, but the
password is completely random. The tool continues this process until
it is able to guess the correct password.

BruteForce attacks need a lot of time and hardware resources. A


BruteForce attack can take months. And can still prove fruitless.

Here are two ways to prevent brute force attacks in PHP:

1. Delay Execution After Each Login Attempt


This is the easiest way to prevent brute force attacks. Delaying
execution after each login attempt will slow down the Bruteforce attack
and will render it ineffective.

The idea is simple, you delay the execution of your script by 0.2-0.3 seconds.
A user will not be able to notice this delay as it is too small. But will be
effective in the prevention of BruteForce attacks.

Due to this small delay in execution, A BruteForce attack will take


100s of years to crack a password.
To delay execution of a PHP script, we use PHPs sleep
functions. PHP has a lot of functions that can delay the execution of
our scripts. But in our case, we want to make sure that our scripts
dont consume much CPU resources when delayed.

PHPs sleep functions dont consume much CPU resources and are
perfect for our purpose.

To delay execution for a specific number of seconds we use sleep:


1 sleep( 10 ); # Sleep( Delay Execution ) For 10 Seconds

To delay execution for a specific number of milliseconds, we use


usleep:

1 sleep( 10000 ); # Sleep( Delay Execution ) For 10 Seconds

On login forms for the users, you should add a 0.2-0.3 second
execution delay. And for admin(You) login forms, you can you a bigger
2 5 second delay. You should add a delay on all type of
unsuccessful login attempts. No matter what made the login attempt
unsuccessful. Whether it was an empty field or invalid field, add an
execution delay.

This small execution delay will make the brute-force attack worthless.

2. Add A Captcha Test


Brute force software cant fill in captcha as it is completely random
every time you request the page. This will make sure that your login
form only allows users to log in and not any brute force software.

Adding a captcha helps in many cases. But it relies on sessions which


rely on cookies. Cookies can be changed or removed by the client any
time or on each page request.

Never rely on just Captcha for security purposes.


Adding a captcha breaks the User Experience. Users have to read
ugly an unreadable captcha codes and enter them. Personally, I hate
captcha tests on forms. And I hate Googles ugly captcha codes the
most. Have a look at the below image:

The above captcha text is fairly easy to read. But sometimes, Google
generates captchas that are really unreadable for any human being.

Google has come up with a new type of reCaptcha that looks


something like this:

Googles website says that it should turn to the below image for
humans:

But each time I try it, It shows me a list of images and asks me to
select something:
I dont get it. Why should I select pancakes?

Google says that humans dont need to do anything to solve the


Captcha. They just need to click the checkbox to verify. I have never
been able to pass Googles reCaptcha without selecting images of
some random fucking things. This is why I hate it.

Getting back to the topic, Integrating Googles reCaptcha is really


easy and is not covered in this article. Visit Googles reCaptcha site
here.
12. Prevent XSS
XSS or Cross Site Scripting is a type of attack in which a hacker tries
to inject scripts with malicious code into your pages. The script is
generally JavaScript code.

It is named XSS instead of CSS to prevent confusion between


Cascading Style Sheets and Cross Site Scripting.

There are a lot of things a hacker can do if he finds an XSS


vulnerability in your web application. Here are some of them:

1. He can steal users cookies


2. He can redirect the user to his malicious site
3. He can display ads on your website

Usually, a hacker uses XSS to steal users cookies.

This vulnerability generally arises in applications that output data


without validating it. This data can be anything from user input to data
stored in the database. Yes, you heard it right. Data stored in a
database can be malicious.

Beginners make the mistake of not validating data received from


a database. I always recommend people to distrust all the data your
application receives to make it completely secure.

Have a look at the below code to understand how XSS works:

1 <?php
2 $search_term = $_GET['s'];
3
4 echo "You Searched For $search_term";
5
6 // Code To Display Search Results Goes Here
Lets assume that the above code searches the database and displays
the search results. It also displays the search term to the user. Have a
look at the below URL assuming the code is stored in search.php file:

http://some-site.com/search.php?s=RandomSearchTerm

If a user uses the above URL, He will just see the search results for
his search term. But if he enters some JavaScript code in the query
parameter, he will be able to execute it. Have a look at the below URL

http://some-
site.com/search.php?s=<script>alert(You%20Have%20Been%20Hacked
);</script>

If the user uses the above URL, he can easily execute JavaScript
code. Most of the modern browsers these days have an XSS auditor
which protects users from XSS scripts. But a lot of browsers dont
have one.

To fix this problem, Just validate and filter all the data you output. To
fix this problem in the above example, we need to use PHPs
htmlspecialchars function which convert HTML characters into their
respective HTML special chars. Like less than(<) symbol to &lt; and
greater than(>) symbol to &gt;.

1 $search_term = $_GET['s'];
2
3 $search_term = htmlspecialchars( $search_term );
4
5 echo "You Searched For $search_term";

Now, the above code is invulnerable to XSS attacks.

Conclusion
These 12 tips wont help you make your web application completely
secure. But will help you secure your website from a lot of different
attacks. I made this list because I wasnt able to find a good guide for
security in PHP.

All the guides available online are either too difficult or are outdated or
dont contain any examples at all. I hope this list helps you understand
some basic PHP security concepts. I will be updating this list regularly.
So, stay tuned. This list will never become outdated.

Always remember, you can never make a web application completely


secure. All the web applications are vulnerable.

Not just your PHP code but also your web server software can be
vulnerable. Always install software from trusted providers.

Some References and Further Reading:


Owasps PHP Security Cheat Sheet OWASP is Open Web Application
Security Project. The website includes a lot of security cheat sheets
for different programming languages. And this one is for PHP.
PHP Security Consortiums Guide To PHP Security Contains articles
and Projects on PHP security.
Hack This Site Might seem irrelevant to this article. But it is not just
for hackers. You can learn different hacking techniques to protect your
website as a web developer.
PHP Manuals Security Entry PHP manuals entry about security in
PHP.

You might also like